diff options
189 files changed, 30868 insertions, 19973 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 | |||
| 4 | AllowShortIfStatementsOnASingleLine: false | 4 | AllowShortIfStatementsOnASingleLine: false |
| 5 | BreakBeforeBraces: Attach | 5 | BreakBeforeBraces: Attach |
| 6 | AlignConsecutiveMacros: true | 6 | AlignConsecutiveMacros: true |
| 7 | ColumnLimit: 140 | 7 | ColumnLimit: 100 |
| 8 | IndentPPDirectives: AfterHash | 8 | IndentPPDirectives: AfterHash |
| 9 | SortIncludes: Never | 9 | SortIncludes: Never |
| 10 | AllowShortEnumsOnASingleLine: false | 10 | AllowShortEnumsOnASingleLine: false |
diff --git a/.github/NPTest.cache b/.github/NPTest.cache index d488d1b9..6b463e74 100644 --- a/.github/NPTest.cache +++ b/.github/NPTest.cache | |||
| @@ -38,8 +38,8 @@ | |||
| 38 | 'NP_MYSQL_LOGIN_DETAILS' => '-u root -d test', | 38 | 'NP_MYSQL_LOGIN_DETAILS' => '-u root -d test', |
| 39 | 'NP_MYSQL_SERVER' => 'localhost', | 39 | 'NP_MYSQL_SERVER' => 'localhost', |
| 40 | 'NP_MYSQL_SOCKET' => '/var/run/mysqld/mysqld.sock', | 40 | 'NP_MYSQL_SOCKET' => '/var/run/mysqld/mysqld.sock', |
| 41 | 'NP_MYSQL_WITH_SLAVE' => '', | 41 | 'NP_MYSQL_WITH_REPLICA' => '', |
| 42 | 'NP_MYSQL_WITH_SLAVE_LOGIN' => '', | 42 | 'NP_MYSQL_WITH_REPLICA_LOGIN' => '', |
| 43 | 'NP_NO_NTP_SERVICE' => 'localhost', | 43 | 'NP_NO_NTP_SERVICE' => 'localhost', |
| 44 | 'NP_PORT_TCP_PROXY' => '3128', | 44 | 'NP_PORT_TCP_PROXY' => '3128', |
| 45 | 'NP_SMB_SHARE' => '', | 45 | 'NP_SMB_SHARE' => '', |
diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec index 5cae3e59..5878a862 100644 --- a/.github/monitoring-plugins.spec +++ b/.github/monitoring-plugins.spec | |||
| @@ -88,6 +88,9 @@ BuildRequires: postgresql-devel | |||
| 88 | # check_radius | 88 | # check_radius |
| 89 | BuildRequires: radcli-devel | 89 | BuildRequires: radcli-devel |
| 90 | 90 | ||
| 91 | # check_snmp | ||
| 92 | BuildRequires: net-snmp-devel | ||
| 93 | |||
| 91 | %description | 94 | %description |
| 92 | Common files for Monitoring Plugins | 95 | Common files for Monitoring Plugins |
| 93 | 96 | ||
| @@ -191,9 +194,7 @@ Requires: %{name}-nt | |||
| 191 | Requires: %{name}-ntp | 194 | Requires: %{name}-ntp |
| 192 | Requires: %{name}-ntp_peer | 195 | Requires: %{name}-ntp_peer |
| 193 | Requires: %{name}-ntp_time | 196 | Requires: %{name}-ntp_time |
| 194 | Requires: %{name}-nwstat | ||
| 195 | Requires: %{name}-oracle | 197 | Requires: %{name}-oracle |
| 196 | Requires: %{name}-overcr | ||
| 197 | Requires: %{name}-pgsql | 198 | Requires: %{name}-pgsql |
| 198 | Requires: %{name}-ping | 199 | Requires: %{name}-ping |
| 199 | Requires: %{name}-procs | 200 | Requires: %{name}-procs |
| @@ -651,19 +652,6 @@ Provides check_nagios of the Monitoring Plugins. | |||
| 651 | 652 | ||
| 652 | 653 | ||
| 653 | 654 | ||
| 654 | # check_nt | ||
| 655 | %package nt | ||
| 656 | Summary: Monitoring Plugins - check_nt | ||
| 657 | Requires: %{name} = %{version}-%{release} | ||
| 658 | |||
| 659 | %description nt | ||
| 660 | Provides check_nt of the Monitoring Plugins. | ||
| 661 | |||
| 662 | %files nt | ||
| 663 | %{plugindir}/check_nt | ||
| 664 | |||
| 665 | |||
| 666 | |||
| 667 | # check_ntp | 655 | # check_ntp |
| 668 | %package ntp | 656 | %package ntp |
| 669 | Summary: Monitoring Plugins - check_ntp | 657 | Summary: Monitoring Plugins - check_ntp |
| @@ -703,19 +691,6 @@ Provides check_ntp_time of the Monitoring Plugins. | |||
| 703 | 691 | ||
| 704 | 692 | ||
| 705 | 693 | ||
| 706 | # check_nwstat | ||
| 707 | %package nwstat | ||
| 708 | Summary: Monitoring Plugins - check_nwstat | ||
| 709 | Requires: %{name} = %{version}-%{release} | ||
| 710 | |||
| 711 | %description nwstat | ||
| 712 | Provides check_nwstat of the Monitoring Plugins. | ||
| 713 | |||
| 714 | %files nwstat | ||
| 715 | %{plugindir}/check_nwstat | ||
| 716 | |||
| 717 | |||
| 718 | |||
| 719 | # check_oracle | 694 | # check_oracle |
| 720 | %package oracle | 695 | %package oracle |
| 721 | Summary: Monitoring Plugins - check_oracle | 696 | Summary: Monitoring Plugins - check_oracle |
| @@ -729,19 +704,6 @@ Provides check_oracle of the Monitoring Plugins. | |||
| 729 | 704 | ||
| 730 | 705 | ||
| 731 | 706 | ||
| 732 | # check_overcr | ||
| 733 | %package overcr | ||
| 734 | Summary: Monitoring Plugins - check_overcr | ||
| 735 | Requires: %{name} = %{version}-%{release} | ||
| 736 | |||
| 737 | %description overcr | ||
| 738 | Provides check_overcr of the Monitoring Plugins. | ||
| 739 | |||
| 740 | %files overcr | ||
| 741 | %{plugindir}/check_overcr | ||
| 742 | |||
| 743 | |||
| 744 | |||
| 745 | # check_pgsql | 707 | # check_pgsql |
| 746 | %package pgsql | 708 | %package pgsql |
| 747 | Summary: Monitoring Plugins - check_pgsql | 709 | Summary: Monitoring Plugins - check_pgsql |
diff --git a/.github/os_detect.sh b/.github/os_detect.sh index ee9c145d..3c5956de 100644 --- a/.github/os_detect.sh +++ b/.github/os_detect.sh | |||
| @@ -1,10 +1,17 @@ | |||
| 1 | #!/bin/sh -e | 1 | #!/bin/sh -e |
| 2 | |||
| 3 | . /etc/os-release | ||
| 4 | |||
| 2 | # workaround for really bare-bones Archlinux containers: | 5 | # workaround for really bare-bones Archlinux containers: |
| 3 | if [ -x "$(command -v pacman)" ]; then | 6 | if [ -x "$(command -v pacman)" ]; then |
| 4 | pacman --noconfirm -Sy | 7 | pacman --noconfirm -Sy |
| 5 | pacman --noconfirm -S grep gawk sed | 8 | pacman --noconfirm -S grep gawk sed |
| 6 | fi | 9 | fi |
| 7 | 10 | ||
| 11 | if [ ${ID} == "fedora" -a ${VERSION_ID} -gt 41 ]; then | ||
| 12 | dnf install -y gawk | ||
| 13 | fi | ||
| 14 | |||
| 8 | os_release_file= | 15 | os_release_file= |
| 9 | if [ -s "/etc/os-release" ]; then | 16 | if [ -s "/etc/os-release" ]; then |
| 10 | os_release_file="/etc/os-release" | 17 | os_release_file="/etc/os-release" |
| @@ -15,4 +22,7 @@ else | |||
| 15 | return 1 | 22 | return 1 |
| 16 | fi | 23 | fi |
| 17 | export distro_id=$(grep '^ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') | 24 | export distro_id=$(grep '^ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') |
| 25 | export version_id=$(grep '^VERSION_ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') | ||
| 18 | export platform_id=$(grep '^PLATFORM_ID=' /etc/os-release|awk -F = '{print $2}'|sed 's/\"//g'| cut -d":" -f2) | 26 | export 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 | ||
| 28 | if [ -z $platform_id ]; then export platform_id=$(echo ${distro_id:0:1}${version_id}); fi | ||
diff --git a/.github/prepare_debian.sh b/.github/prepare_debian.sh index 3640e500..cffe98c5 100755 --- a/.github/prepare_debian.sh +++ b/.github/prepare_debian.sh | |||
| @@ -24,6 +24,7 @@ apt-get -y install perl \ | |||
| 24 | libpq-dev \ | 24 | libpq-dev \ |
| 25 | libradcli-dev \ | 25 | libradcli-dev \ |
| 26 | libnet-snmp-perl \ | 26 | libnet-snmp-perl \ |
| 27 | libsnmp-dev \ | ||
| 27 | procps \ | 28 | procps \ |
| 28 | libdbi0-dev \ | 29 | libdbi0-dev \ |
| 29 | libdbd-sqlite3 \ | 30 | libdbd-sqlite3 \ |
| @@ -59,9 +60,11 @@ apt-get -y install perl \ | |||
| 59 | mariadb-server \ | 60 | mariadb-server \ |
| 60 | mariadb-client \ | 61 | mariadb-client \ |
| 61 | libmariadb-dev \ | 62 | libmariadb-dev \ |
| 63 | libmariadb-dev-compat \ | ||
| 62 | cron \ | 64 | cron \ |
| 63 | iputils-ping \ | 65 | iputils-ping \ |
| 64 | iproute2 | 66 | iproute2 \ |
| 67 | libjson-perl | ||
| 65 | 68 | ||
| 66 | # remove ipv6 interface from hosts | 69 | # remove ipv6 interface from hosts |
| 67 | sed '/^::1/d' /etc/hosts > /tmp/hosts | 70 | sed '/^::1/d' /etc/hosts > /tmp/hosts |
| @@ -109,6 +112,8 @@ mkdir -p /var/lib/snmp/mib_indexes | |||
| 109 | sed -e 's/^agentaddress.*/agentaddress 127.0.0.1/' -i /etc/snmp/snmpd.conf | 112 | sed -e 's/^agentaddress.*/agentaddress 127.0.0.1/' -i /etc/snmp/snmpd.conf |
| 110 | service snmpd start | 113 | service snmpd start |
| 111 | 114 | ||
| 115 | sed 's/^mibs ://' -i /etc/snmp/snmp.conf | ||
| 116 | |||
| 112 | # start cron, will be used by check_nagios | 117 | # start cron, will be used by check_nagios |
| 113 | cron | 118 | cron |
| 114 | 119 | ||
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c402e0cf..bd1037f4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | name: "CodeQL" | 13 | name: "CodeQL" |
| 14 | 14 | ||
| 15 | on: | 15 | on: |
| 16 | workflow_dispatch: {} | ||
| 16 | push: | 17 | push: |
| 17 | branches: [master] | 18 | branches: [master] |
| 18 | pull_request: | 19 | pull_request: |
| @@ -40,11 +41,11 @@ jobs: | |||
| 40 | 41 | ||
| 41 | steps: | 42 | steps: |
| 42 | - name: Checkout repository | 43 | - name: Checkout repository |
| 43 | uses: actions/checkout@v4 | 44 | uses: actions/checkout@v6 |
| 44 | 45 | ||
| 45 | # Initializes the CodeQL tools for scanning. | 46 | # Initializes the CodeQL tools for scanning. |
| 46 | - name: Initialize CodeQL | 47 | - name: Initialize CodeQL |
| 47 | uses: github/codeql-action/init@v3 | 48 | uses: github/codeql-action/init@v4 |
| 48 | with: | 49 | with: |
| 49 | languages: ${{ matrix.language }} | 50 | languages: ${{ matrix.language }} |
| 50 | # If you wish to specify custom queries, you can do so here or in a config file. | 51 | # If you wish to specify custom queries, you can do so here or in a config file. |
| @@ -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: | |
| @@ -70,4 +82,4 @@ jobs: | |||
| 70 | make | 82 | make |
| 71 | 83 | ||
| 72 | - name: Perform CodeQL Analysis | 84 | - name: Perform CodeQL Analysis |
| 73 | uses: github/codeql-action/analyze@v3 | 85 | uses: github/codeql-action/analyze@v4 |
diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 72f7c7eb..f19cc920 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | name: Spellcheck | 2 | name: Spellcheck |
| 3 | 3 | ||
| 4 | on: | 4 | on: |
| 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@v6 |
| 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..a7e9b9d6 100644 --- a/.github/workflows/test-next.yml +++ b/.github/workflows/test-next.yml | |||
| @@ -2,7 +2,13 @@ | |||
| 2 | name: Tests Debian:Testing and Fedora:Rawhide | 2 | name: Tests Debian:Testing and Fedora:Rawhide |
| 3 | 3 | ||
| 4 | on: | 4 | on: |
| 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@v6 |
| 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@v6 |
| 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..5a0b2943 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml | |||
| @@ -2,6 +2,13 @@ | |||
| 2 | name: Tests | 2 | name: Tests |
| 3 | 3 | ||
| 4 | on: | 4 | on: |
| 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@v6 |
| 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@v6 |
| 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 |
| @@ -103,6 +103,8 @@ NP-VERSION-FILE | |||
| 103 | /lib/libmonitoringplug.a | 103 | /lib/libmonitoringplug.a |
| 104 | /lib/Makefile | 104 | /lib/Makefile |
| 105 | /lib/Makefile.in | 105 | /lib/Makefile.in |
| 106 | /lib/vendor/cJSON/.deps | ||
| 107 | /lib/vendor/cJSON/.dirstamp | ||
| 106 | 108 | ||
| 107 | # /lib/tests/ | 109 | # /lib/tests/ |
| 108 | /lib/tests/base64.Po | 110 | /lib/tests/base64.Po |
| @@ -112,7 +114,6 @@ NP-VERSION-FILE | |||
| 112 | /lib/tests/Makefile.in | 114 | /lib/tests/Makefile.in |
| 113 | /lib/tests/test_base64 | 115 | /lib/tests/test_base64 |
| 114 | /lib/tests/test_cmd | 116 | /lib/tests/test_cmd |
| 115 | /lib/tests/test_disk | ||
| 116 | /lib/tests/test_tcp | 117 | /lib/tests/test_tcp |
| 117 | /lib/tests/test_utils | 118 | /lib/tests/test_utils |
| 118 | /lib/tests/utils_base.Po | 119 | /lib/tests/utils_base.Po |
| @@ -151,6 +152,8 @@ NP-VERSION-FILE | |||
| 151 | /plugins/check_dbi | 152 | /plugins/check_dbi |
| 152 | /plugins/check_dig | 153 | /plugins/check_dig |
| 153 | /plugins/check_disk | 154 | /plugins/check_disk |
| 155 | plugins/check_disk.d/.deps/ | ||
| 156 | plugins/check_disk.d/.dirstamp | ||
| 154 | /plugins/check_dns | 157 | /plugins/check_dns |
| 155 | /plugins/check_dummy | 158 | /plugins/check_dummy |
| 156 | /plugins/check_fping | 159 | /plugins/check_fping |
| @@ -171,12 +174,9 @@ NP-VERSION-FILE | |||
| 171 | /plugins/check_netsaint | 174 | /plugins/check_netsaint |
| 172 | /plugins/check_nntp | 175 | /plugins/check_nntp |
| 173 | /plugins/check_nntps | 176 | /plugins/check_nntps |
| 174 | /plugins/check_nt | ||
| 175 | /plugins/check_ntp | 177 | /plugins/check_ntp |
| 176 | /plugins/check_ntp_peer | 178 | /plugins/check_ntp_peer |
| 177 | /plugins/check_ntp_time | 179 | /plugins/check_ntp_time |
| 178 | /plugins/check_nwstat | ||
| 179 | /plugins/check_overcr | ||
| 180 | /plugins/check_pgsql | 180 | /plugins/check_pgsql |
| 181 | /plugins/check_ping | 181 | /plugins/check_ping |
| 182 | /plugins/check_pop | 182 | /plugins/check_pop |
| @@ -196,6 +196,8 @@ NP-VERSION-FILE | |||
| 196 | /plugins/check_udp | 196 | /plugins/check_udp |
| 197 | /plugins/check_ups | 197 | /plugins/check_ups |
| 198 | /plugins/check_users | 198 | /plugins/check_users |
| 199 | /plugins/check_users.d/.deps | ||
| 200 | /plugins/check_users.d/.dirstamp | ||
| 199 | /plugins/check_vsz | 201 | /plugins/check_vsz |
| 200 | /plugins/config.h | 202 | /plugins/config.h |
| 201 | /plugins/config.h.in | 203 | /plugins/config.h.in |
| @@ -221,7 +223,7 @@ NP-VERSION-FILE | |||
| 221 | /plugins/tests/Makefile | 223 | /plugins/tests/Makefile |
| 222 | /plugins/tests/Makefile.in | 224 | /plugins/tests/Makefile.in |
| 223 | /plugins/tests/test_utils | 225 | /plugins/tests/test_utils |
| 224 | /plugins/tests/test_disk | 226 | /plugins/tests/test_check_disk |
| 225 | /plugins/tests/test_check_swap | 227 | /plugins/tests/test_check_swap |
| 226 | /plugins/tests/.deps | 228 | /plugins/tests/.deps |
| 227 | /plugins/tests/.dirstamp | 229 | /plugins/tests/.dirstamp |
| @@ -230,6 +232,14 @@ NP-VERSION-FILE | |||
| 230 | /plugins/check_swap.d/.deps | 232 | /plugins/check_swap.d/.deps |
| 231 | /plugins/check_swap.d/.dirstamp | 233 | /plugins/check_swap.d/.dirstamp |
| 232 | 234 | ||
| 235 | # /plugins/check_snmp.d | ||
| 236 | /plugins/check_snmp.d/.deps | ||
| 237 | /plugins/check_snmp.d/.dirstamp | ||
| 238 | |||
| 239 | # /plugins/check_curl.d | ||
| 240 | /plugins/check_curl.d/.deps | ||
| 241 | /plugins/check_curl.d/.dirstamp | ||
| 242 | |||
| 233 | # /plugins-root/ | 243 | # /plugins-root/ |
| 234 | /plugins-root/.deps | 244 | /plugins-root/.deps |
| 235 | /plugins-root/.libs | 245 | /plugins-root/.libs |
| @@ -238,6 +248,8 @@ NP-VERSION-FILE | |||
| 238 | /plugins-root/check_dhcp | 248 | /plugins-root/check_dhcp |
| 239 | /plugins-root/check_icmp | 249 | /plugins-root/check_icmp |
| 240 | /plugins-root/pst3 | 250 | /plugins-root/pst3 |
| 251 | /plugins-root/check_icmp.d/.dirstamp | ||
| 252 | /plugins-root/check_icmp.d/.deps | ||
| 241 | 253 | ||
| 242 | # /plugins-scripts/ | 254 | # /plugins-scripts/ |
| 243 | /plugins-scripts/Makefile | 255 | /plugins-scripts/Makefile |
diff --git a/Makefile.am b/Makefile.am index 5959f70f..f8e3de45 100644 --- a/Makefile.am +++ b/Makefile.am | |||
| @@ -7,8 +7,7 @@ EXTRA_DIST = config.rpath \ | |||
| 7 | NP-VERSION-GEN REQUIREMENTS SUPPORT THANKS \ | 7 | NP-VERSION-GEN REQUIREMENTS SUPPORT THANKS \ |
| 8 | NPTest.pm pkg \ | 8 | NPTest.pm pkg \ |
| 9 | config_test/Makefile config_test/run_tests config_test/child_test.c \ | 9 | config_test/Makefile config_test/run_tests config_test/child_test.c \ |
| 10 | perlmods tools/build_perl_modules \ | 10 | perlmods tools/build_perl_modules |
| 11 | tools/tinderbox_build | ||
| 12 | 11 | ||
| 13 | ACLOCAL_AMFLAGS = -I gl/m4 -I m4 | 12 | ACLOCAL_AMFLAGS = -I gl/m4 -I m4 |
| 14 | 13 | ||
| @@ -15,6 +15,8 @@ use warnings; | |||
| 15 | use Cwd; | 15 | use Cwd; |
| 16 | use File::Basename; | 16 | use File::Basename; |
| 17 | 17 | ||
| 18 | use JSON; | ||
| 19 | |||
| 18 | use IO::File; | 20 | use IO::File; |
| 19 | use Data::Dumper; | 21 | use Data::Dumper; |
| 20 | 22 | ||
| @@ -617,6 +619,8 @@ sub testCmd { | |||
| 617 | chomp $output; | 619 | chomp $output; |
| 618 | $object->output($output); | 620 | $object->output($output); |
| 619 | 621 | ||
| 622 | eval { $object->{'mp_test_result'} = decode_json($output) }; | ||
| 623 | |||
| 620 | alarm(0); | 624 | alarm(0); |
| 621 | 625 | ||
| 622 | my ($pkg, $file, $line) = caller(0); | 626 | my ($pkg, $file, $line) = caller(0); |
diff --git a/REQUIREMENTS b/REQUIREMENTS index f3b1c01d..8f1befbd 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS | |||
| @@ -26,7 +26,7 @@ check_curl: | |||
| 26 | other SSL implementations are currently not supported | 26 | other SSL implementations are currently not supported |
| 27 | - uriparser 0.7.5 or later | 27 | - uriparser 0.7.5 or later |
| 28 | https://uriparser.github.io/ | 28 | https://uriparser.github.io/ |
| 29 | 29 | ||
| 30 | check_fping: | 30 | check_fping: |
| 31 | - Requires the fping utility distributed with SATAN. Either | 31 | - Requires the fping utility distributed with SATAN. Either |
| 32 | download and install SATAN or grab the fping program from | 32 | download and install SATAN or grab the fping program from |
| @@ -87,20 +87,12 @@ check_ifstatus/check_ifoperstatus | |||
| 87 | - Requires Net::SNMP perl module | 87 | - Requires Net::SNMP perl module |
| 88 | http://www.perl.com/CPAN/modules/by-authors/id/D/DT/DTOWN/ | 88 | http://www.perl.com/CPAN/modules/by-authors/id/D/DT/DTOWN/ |
| 89 | 89 | ||
| 90 | check_nwstat: | ||
| 91 | - Requires MRTGEXT NLM for Novell Servers | ||
| 92 | http://forge.novell.com/modules/xfmod/project/?mrtgext | ||
| 93 | |||
| 94 | check_nt: | ||
| 95 | - Requires NSClient to run on the NT server to monitor | ||
| 96 | http://nsclient.ready2run.nl/ | ||
| 97 | |||
| 98 | check_ups: | 90 | check_ups: |
| 99 | - Requires Network UPS Tools (>= 1.4) to run on the server to monitor | 91 | - Requires Network UPS Tools (>= 1.4) to run on the server to monitor |
| 100 | http://www.networkupstools.org/ | 92 | http://www.networkupstools.org/ |
| 101 | 93 | ||
| 102 | check_ide_smart: | 94 | check_ide_smart: |
| 103 | - Uses the Linux specific SMART interface [http://smartlinux.sourceforge.net/smart/index.php]. | 95 | - Uses the Linux specific SMART interface [http://smartlinux.sourceforge.net/smart/index.php]. |
| 104 | 96 | ||
| 105 | OS Specific Issues | 97 | OS Specific Issues |
| 106 | ------------------ | 98 | ------------------ |
| @@ -426,3 +426,4 @@ Eunice Remoquillo | |||
| 426 | Louis Sautier | 426 | Louis Sautier |
| 427 | Sven Hartge | 427 | Sven Hartge |
| 428 | Alvar Penning | 428 | Alvar Penning |
| 429 | Michael Jeanson | ||
diff --git a/configure.ac b/configure.ac index 204fc6e3..2bf94014 100644 --- a/configure.ac +++ b/configure.ac | |||
| @@ -181,10 +181,10 @@ fi | |||
| 181 | 181 | ||
| 182 | # Finally, define tests if we use libtap | 182 | # Finally, define tests if we use libtap |
| 183 | if test "$enable_libtap" = "yes" ; then | 183 | if test "$enable_libtap" = "yes" ; then |
| 184 | EXTRA_TEST="test_utils test_disk test_tcp test_cmd test_base64" | 184 | EXTRA_TEST="test_utils test_tcp test_cmd test_base64" |
| 185 | AC_SUBST(EXTRA_TEST) | 185 | AC_SUBST(EXTRA_TEST) |
| 186 | 186 | ||
| 187 | EXTRA_PLUGIN_TESTS="tests/test_check_swap" | 187 | EXTRA_PLUGIN_TESTS="tests/test_check_swap tests/test_check_disk" |
| 188 | AC_SUBST(EXTRA_PLUGIN_TESTS) | 188 | AC_SUBST(EXTRA_PLUGIN_TESTS) |
| 189 | fi | 189 | fi |
| 190 | 190 | ||
| @@ -796,7 +796,7 @@ elif ps axwo 'stat comm vsz rss user uid pid ppid etime args' 2>/dev/null | \ | |||
| 796 | then | 796 | then |
| 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, | |||
| 1470 | AS_IF([test -n "$PATH_TO_SNMPGET"], [ | 1470 | AS_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]) |
| 1494 | fi | 1483 | fi |
| 1495 | 1484 | ||
| 1485 | dnl Check whether DES encryption is available (might not on RHEL) | ||
| 1486 | AC_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 | |||
| 1496 | AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat) | 1495 | AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat) |
| 1497 | AC_PATH_PROG(PATH_TO_QSTAT,qstat) | 1496 | AC_PATH_PROG(PATH_TO_QSTAT,qstat) |
| 1498 | AC_ARG_WITH(qstat_command, | 1497 | AC_ARG_WITH(qstat_command, |
| @@ -1519,21 +1518,47 @@ then | |||
| 1519 | fi | 1518 | fi |
| 1520 | 1519 | ||
| 1521 | AC_PATH_PROG(PATH_TO_FPING,fping) | 1520 | AC_PATH_PROG(PATH_TO_FPING,fping) |
| 1522 | AC_PATH_PROG(PATH_TO_FPING6,fping6) | ||
| 1523 | 1521 | ||
| 1524 | AC_ARG_WITH(fping_command, | 1522 | AC_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) |
| 1527 | AC_ARG_WITH(fping6_command, | 1525 | if test -n "$PATH_TO_FPING"; then |
| 1528 | ACX_HELP_STRING([--with-fping6-command=PATH], | ||
| 1529 | [Path to fping6 command]), PATH_TO_FPING6=$withval) | ||
| 1530 | |||
| 1531 | if test -n "$PATH_TO_FPING" | ||
| 1532 | then | ||
| 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 |
| 1538 | else | 1563 | else |
| 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]) |
| @@ -1807,11 +1832,6 @@ if test -n "$PATH_TO_APTGET" ; then | |||
| 1807 | fi | 1832 | fi |
| 1808 | 1833 | ||
| 1809 | 1834 | ||
| 1810 | if test -f $srcdir/plugins/check_nt.c ; then | ||
| 1811 | EXTRAS="$EXTRAS check_nt\$(EXEEXT)" | ||
| 1812 | fi | ||
| 1813 | |||
| 1814 | |||
| 1815 | dnl used in check_dhcp | 1835 | dnl used in check_dhcp |
| 1816 | AC_CHECK_HEADERS(sys/sockio.h) | 1836 | AC_CHECK_HEADERS(sys/sockio.h) |
| 1817 | 1837 | ||
diff --git a/lib/Makefile.am b/lib/Makefile.am index dc3ee893..27a08278 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am | |||
| @@ -4,11 +4,23 @@ SUBDIRS = . tests | |||
| 4 | 4 | ||
| 5 | noinst_LIBRARIES = libmonitoringplug.a | 5 | noinst_LIBRARIES = libmonitoringplug.a |
| 6 | 6 | ||
| 7 | AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ | 7 | AM_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 | ||
| 10 | libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c | 10 | libmonitoringplug_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 |
| 11 | EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h utils_cmd.h parse_ini.h extra_opts.h maxfd.h | 11 | |
| 12 | EXTRA_DIST = utils_base.h \ | ||
| 13 | utils_tcp.h \ | ||
| 14 | utils_cmd.h \ | ||
| 15 | parse_ini.h \ | ||
| 16 | extra_opts.h \ | ||
| 17 | maxfd.h \ | ||
| 18 | perfdata.h \ | ||
| 19 | output.h \ | ||
| 20 | thresholds.h \ | ||
| 21 | states.h \ | ||
| 22 | vendor/cJSON/cJSON.h \ | ||
| 23 | monitoringplug.h | ||
| 12 | 24 | ||
| 13 | if USE_PARSE_INI | 25 | if USE_PARSE_INI |
| 14 | libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c | 26 | libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c |
| @@ -16,4 +28,3 @@ endif USE_PARSE_INI | |||
| 16 | 28 | ||
| 17 | test test-debug: | 29 | test test-debug: |
| 18 | cd tests && make $@ | 30 | cd tests && make $@ |
| 19 | |||
diff --git a/lib/extra_opts.c b/lib/extra_opts.c index 88787336..3fe69014 100644 --- a/lib/extra_opts.c +++ b/lib/extra_opts.c | |||
| @@ -27,27 +27,32 @@ | |||
| 27 | 27 | ||
| 28 | /* FIXME: copied from utils.h; we should move a bunch of libs! */ | 28 | /* FIXME: copied from utils.h; we should move a bunch of libs! */ |
| 29 | bool is_option2(char *str) { | 29 | bool is_option2(char *str) { |
| 30 | if (!str) | 30 | if (!str) { |
| 31 | return false; | 31 | return false; |
| 32 | else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) | 32 | } |
| 33 | |||
| 34 | if (strspn(str, "-") == 1 || strspn(str, "-") == 2) { | ||
| 33 | return true; | 35 | return true; |
| 34 | else | 36 | } |
| 35 | return false; | 37 | |
| 38 | return false; | ||
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | /* this is the externally visible function used by plugins */ | 41 | /* this is the externally visible function used by plugins */ |
| 39 | char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { | 42 | char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { |
| 40 | np_arg_list *extra_args = NULL, *ea1 = NULL, *ea_tmp = NULL; | ||
| 41 | char **argv_new = NULL; | ||
| 42 | char *argptr = NULL; | ||
| 43 | int i, j, optfound, argc_new, ea_num = *argc; | ||
| 44 | |||
| 45 | if (*argc < 2) { | 43 | if (*argc < 2) { |
| 46 | /* No arguments provided */ | 44 | /* No arguments provided */ |
| 47 | return argv; | 45 | return argv; |
| 48 | } | 46 | } |
| 49 | 47 | ||
| 50 | for (i = 1; i < *argc; i++) { | 48 | np_arg_list *extra_args = NULL; |
| 49 | np_arg_list *ea1 = NULL; | ||
| 50 | np_arg_list *ea_tmp = NULL; | ||
| 51 | char *argptr = NULL; | ||
| 52 | int optfound; | ||
| 53 | size_t ea_num = (size_t)*argc; | ||
| 54 | |||
| 55 | for (int i = 1; i < *argc; i++) { | ||
| 51 | argptr = NULL; | 56 | argptr = NULL; |
| 52 | optfound = 0; | 57 | optfound = 0; |
| 53 | 58 | ||
| @@ -56,8 +61,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { | |||
| 56 | /* It is a single argument with value */ | 61 | /* It is a single argument with value */ |
| 57 | argptr = argv[i] + 13; | 62 | argptr = argv[i] + 13; |
| 58 | /* Delete the extra opts argument */ | 63 | /* Delete the extra opts argument */ |
| 59 | for (j = i; j < *argc; j++) | 64 | for (int j = i; j < *argc; j++) { |
| 60 | argv[j] = argv[j + 1]; | 65 | argv[j] = argv[j + 1]; |
| 66 | } | ||
| 67 | |||
| 61 | i--; | 68 | i--; |
| 62 | *argc -= 1; | 69 | *argc -= 1; |
| 63 | } else if (strcmp(argv[i], "--extra-opts") == 0) { | 70 | } else if (strcmp(argv[i], "--extra-opts") == 0) { |
| @@ -65,8 +72,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { | |||
| 65 | /* It is a argument with separate value */ | 72 | /* It is a argument with separate value */ |
| 66 | argptr = argv[i + 1]; | 73 | argptr = argv[i + 1]; |
| 67 | /* Delete the extra-opts argument/value */ | 74 | /* Delete the extra-opts argument/value */ |
| 68 | for (j = i; j < *argc - 1; j++) | 75 | for (int j = i; j < *argc - 1; j++) { |
| 69 | argv[j] = argv[j + 2]; | 76 | argv[j] = argv[j + 2]; |
| 77 | } | ||
| 78 | |||
| 70 | i -= 2; | 79 | i -= 2; |
| 71 | *argc -= 2; | 80 | *argc -= 2; |
| 72 | ea_num--; | 81 | ea_num--; |
| @@ -74,8 +83,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { | |||
| 74 | /* It has no value */ | 83 | /* It has no value */ |
| 75 | optfound = 1; | 84 | optfound = 1; |
| 76 | /* Delete the extra opts argument */ | 85 | /* Delete the extra opts argument */ |
| 77 | for (j = i; j < *argc; j++) | 86 | for (int j = i; j < *argc; j++) { |
| 78 | argv[j] = argv[j + 1]; | 87 | argv[j] = argv[j + 1]; |
| 88 | } | ||
| 89 | |||
| 79 | i--; | 90 | i--; |
| 80 | *argc -= 1; | 91 | *argc -= 1; |
| 81 | } | 92 | } |
| @@ -94,34 +105,37 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { | |||
| 94 | /* append the list to extra_args */ | 105 | /* append the list to extra_args */ |
| 95 | if (extra_args == NULL) { | 106 | if (extra_args == NULL) { |
| 96 | extra_args = ea1; | 107 | extra_args = ea1; |
| 97 | while ((ea1 = ea1->next)) | 108 | while ((ea1 = ea1->next)) { |
| 98 | ea_num++; | 109 | ea_num++; |
| 110 | } | ||
| 99 | } else { | 111 | } else { |
| 100 | ea_tmp = extra_args; | 112 | ea_tmp = extra_args; |
| 101 | while (ea_tmp->next) { | 113 | while (ea_tmp->next) { |
| 102 | ea_tmp = ea_tmp->next; | 114 | ea_tmp = ea_tmp->next; |
| 103 | } | 115 | } |
| 104 | ea_tmp->next = ea1; | 116 | ea_tmp->next = ea1; |
| 105 | while ((ea1 = ea1->next)) | 117 | while ((ea1 = ea1->next)) { |
| 106 | ea_num++; | 118 | ea_num++; |
| 119 | } | ||
| 107 | } | 120 | } |
| 108 | ea1 = ea_tmp = NULL; | 121 | ea1 = ea_tmp = NULL; |
| 109 | } | 122 | } |
| 110 | } /* lather, rince, repeat */ | 123 | } /* lather, rince, repeat */ |
| 111 | 124 | ||
| 112 | if (ea_num == *argc && extra_args == NULL) { | 125 | if (ea_num == (size_t)*argc && extra_args == NULL) { |
| 113 | /* No extra-opts */ | 126 | /* No extra-opts */ |
| 114 | return argv; | 127 | return argv; |
| 115 | } | 128 | } |
| 116 | 129 | ||
| 117 | /* done processing arguments. now create a new argv array... */ | 130 | /* done processing arguments. now create a new argv array... */ |
| 118 | argv_new = (char **)malloc((ea_num + 1) * sizeof(char **)); | 131 | char **argv_new = (char **)malloc((ea_num + 1) * sizeof(char **)); |
| 119 | if (argv_new == NULL) | 132 | if (argv_new == NULL) { |
| 120 | die(STATE_UNKNOWN, _("malloc() failed!\n")); | 133 | die(STATE_UNKNOWN, _("malloc() failed!\n")); |
| 134 | } | ||
| 121 | 135 | ||
| 122 | /* starting with program name */ | 136 | /* starting with program name */ |
| 123 | argv_new[0] = argv[0]; | 137 | argv_new[0] = argv[0]; |
| 124 | argc_new = 1; | 138 | int argc_new = 1; |
| 125 | /* then parsed ini opts (frying them up in the same run) */ | 139 | /* then parsed ini opts (frying them up in the same run) */ |
| 126 | while (extra_args) { | 140 | while (extra_args) { |
| 127 | argv_new[argc_new++] = extra_args->arg; | 141 | argv_new[argc_new++] = extra_args->arg; |
| @@ -130,8 +144,9 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { | |||
| 130 | free(ea1); | 144 | free(ea1); |
| 131 | } | 145 | } |
| 132 | /* finally the rest of the argv array */ | 146 | /* finally the rest of the argv array */ |
| 133 | for (i = 1; i < *argc; i++) | 147 | for (int i = 1; i < *argc; i++) { |
| 134 | argv_new[argc_new++] = argv[i]; | 148 | argv_new[argc_new++] = argv[i]; |
| 149 | } | ||
| 135 | *argc = argc_new; | 150 | *argc = argc_new; |
| 136 | /* and terminate. */ | 151 | /* and terminate. */ |
| 137 | argv_new[argc_new] = NULL; | 152 | argv_new[argc_new] = NULL; |
diff --git a/lib/maxfd.c b/lib/maxfd.c index ca5b6e54..a0f79949 100644 --- a/lib/maxfd.c +++ b/lib/maxfd.c | |||
| @@ -19,7 +19,6 @@ | |||
| 19 | *****************************************************************************/ | 19 | *****************************************************************************/ |
| 20 | 20 | ||
| 21 | #include "./maxfd.h" | 21 | #include "./maxfd.h" |
| 22 | #include <errno.h> | ||
| 23 | 22 | ||
| 24 | long mp_open_max(void) { | 23 | long mp_open_max(void) { |
| 25 | long maxfd = 0L; | 24 | long maxfd = 0L; |
| @@ -31,10 +30,11 @@ long mp_open_max(void) { | |||
| 31 | #ifdef _SC_OPEN_MAX | 30 | #ifdef _SC_OPEN_MAX |
| 32 | errno = 0; | 31 | errno = 0; |
| 33 | if ((maxfd = sysconf(_SC_OPEN_MAX)) < 0) { | 32 | if ((maxfd = sysconf(_SC_OPEN_MAX)) < 0) { |
| 34 | if (errno == 0) | 33 | if (errno == 0) { |
| 35 | maxfd = DEFAULT_MAXFD; /* it's indeterminate */ | 34 | maxfd = DEFAULT_MAXFD; /* it's indeterminate */ |
| 36 | else | 35 | } else { |
| 37 | die(STATE_UNKNOWN, _("sysconf error for _SC_OPEN_MAX\n")); | 36 | die(STATE_UNKNOWN, _("sysconf error for _SC_OPEN_MAX\n")); |
| 37 | } | ||
| 38 | } | 38 | } |
| 39 | #elif defined(OPEN_MAX) | 39 | #elif defined(OPEN_MAX) |
| 40 | return OPEN_MAX | 40 | return OPEN_MAX |
diff --git a/lib/monitoringplug.h b/lib/monitoringplug.h new file mode 100644 index 00000000..a555d736 --- /dev/null +++ b/lib/monitoringplug.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "./states.h" | ||
| 4 | #include "./utils_base.h" | ||
| 5 | #include "./thresholds.h" | ||
| 6 | #include "./maxfd.h" | ||
| 7 | #include "./output.h" | ||
diff --git a/lib/output.c b/lib/output.c new file mode 100644 index 00000000..f283969f --- /dev/null +++ b/lib/output.c | |||
| @@ -0,0 +1,636 @@ | |||
| 1 | #include "./output.h" | ||
| 2 | #include "./utils_base.h" | ||
| 3 | #include "../plugins/utils.h" | ||
| 4 | |||
| 5 | #include <assert.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include <strings.h> | ||
| 9 | // #include <cjson/cJSON.h> | ||
| 10 | #include "./vendor/cJSON/cJSON.h" | ||
| 11 | #include "perfdata.h" | ||
| 12 | #include "states.h" | ||
| 13 | |||
| 14 | // == Global variables | ||
| 15 | static mp_output_format output_format = MP_FORMAT_DEFAULT; | ||
| 16 | static mp_output_detail_level level_of_detail = MP_DETAIL_ALL; | ||
| 17 | |||
| 18 | // == Prototypes == | ||
| 19 | static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, | ||
| 20 | unsigned int indentation); | ||
| 21 | static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck); | ||
| 22 | |||
| 23 | // == Implementation == | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Generate output string for a mp_subcheck object | ||
| 27 | */ | ||
| 28 | static inline char *fmt_subcheck_perfdata(mp_subcheck check) { | ||
| 29 | char *result = strdup(""); | ||
| 30 | int added = 0; | ||
| 31 | |||
| 32 | if (check.perfdata != NULL) { | ||
| 33 | added = asprintf(&result, "%s", pd_list_to_string(*check.perfdata)); | ||
| 34 | } | ||
| 35 | |||
| 36 | if (check.subchecks == NULL) { | ||
| 37 | // No subchecks, return here | ||
| 38 | return result; | ||
| 39 | } | ||
| 40 | |||
| 41 | mp_subcheck_list *subchecks = check.subchecks; | ||
| 42 | |||
| 43 | while (subchecks != NULL) { | ||
| 44 | if (added > 0) { | ||
| 45 | added = asprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck)); | ||
| 46 | } else { | ||
| 47 | // TODO free previous result here? | ||
| 48 | added = asprintf(&result, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); | ||
| 49 | } | ||
| 50 | |||
| 51 | subchecks = subchecks->next; | ||
| 52 | } | ||
| 53 | |||
| 54 | return result; | ||
| 55 | } | ||
| 56 | |||
| 57 | /* | ||
| 58 | * Initialiser for a mp_check object. Always use this to get a new one! | ||
| 59 | * It sets useful defaults | ||
| 60 | */ | ||
| 61 | mp_check mp_check_init(void) { | ||
| 62 | mp_check check = { | ||
| 63 | .evaluation_function = &mp_eval_check_default, | ||
| 64 | }; | ||
| 65 | return check; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* | ||
| 69 | * Initialiser for a mp_subcheck object. Always use this to get a new one! | ||
| 70 | * It sets useful defaults | ||
| 71 | */ | ||
| 72 | mp_subcheck mp_subcheck_init(void) { | ||
| 73 | mp_subcheck tmp = {0}; | ||
| 74 | tmp.default_state = STATE_UNKNOWN; // Default state is unknown | ||
| 75 | tmp.state_set_explicitly = false; | ||
| 76 | return tmp; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* | ||
| 80 | * Add a subcheck to a (the one and only) check object | ||
| 81 | */ | ||
| 82 | int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck subcheck) { | ||
| 83 | assert(subcheck.output != NULL); // There must be output in a subcheck | ||
| 84 | |||
| 85 | mp_subcheck_list *tmp = NULL; | ||
| 86 | |||
| 87 | if (check->subchecks == NULL) { | ||
| 88 | check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list)); | ||
| 89 | if (check->subchecks == NULL) { | ||
| 90 | die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed"); | ||
| 91 | } | ||
| 92 | |||
| 93 | check->subchecks->subcheck = subcheck; | ||
| 94 | check->subchecks->next = NULL; | ||
| 95 | } else { | ||
| 96 | // Search for the end | ||
| 97 | tmp = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list)); | ||
| 98 | if (tmp == NULL) { | ||
| 99 | die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed"); | ||
| 100 | } | ||
| 101 | |||
| 102 | tmp->subcheck = subcheck; | ||
| 103 | tmp->next = check->subchecks; | ||
| 104 | |||
| 105 | check->subchecks = tmp; | ||
| 106 | } | ||
| 107 | |||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | |||
| 111 | /* | ||
| 112 | * Add a mp_perfdata data point to a mp_subcheck object | ||
| 113 | */ | ||
| 114 | void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata perfData) { | ||
| 115 | if (check->perfdata == NULL) { | ||
| 116 | check->perfdata = pd_list_init(); | ||
| 117 | } | ||
| 118 | pd_list_append(check->perfdata, perfData); | ||
| 119 | } | ||
| 120 | |||
| 121 | /* | ||
| 122 | * Add a mp_subcheck object to another one. The seconde mp_subcheck (argument) is the lower in the | ||
| 123 | * hierarchy | ||
| 124 | */ | ||
| 125 | int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) { | ||
| 126 | if (subcheck.output == NULL) { | ||
| 127 | die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, | ||
| 128 | "Sub check output is NULL"); | ||
| 129 | } | ||
| 130 | |||
| 131 | mp_subcheck_list *tmp = NULL; | ||
| 132 | |||
| 133 | if (check->subchecks == NULL) { | ||
| 134 | check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list)); | ||
| 135 | if (check->subchecks == NULL) { | ||
| 136 | die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed"); | ||
| 137 | } | ||
| 138 | |||
| 139 | tmp = check->subchecks; | ||
| 140 | } else { | ||
| 141 | // Search for the end | ||
| 142 | tmp = check->subchecks; | ||
| 143 | |||
| 144 | while (tmp->next != NULL) { | ||
| 145 | tmp = tmp->next; | ||
| 146 | } | ||
| 147 | |||
| 148 | tmp->next = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list)); | ||
| 149 | if (tmp->next == NULL) { | ||
| 150 | die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed"); | ||
| 151 | } | ||
| 152 | |||
| 153 | tmp = tmp->next; | ||
| 154 | } | ||
| 155 | |||
| 156 | tmp->subcheck = subcheck; | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* | ||
| 162 | * Add a manual summary to a mp_check object, effectively replacing | ||
| 163 | * the autogenerated one | ||
| 164 | */ | ||
| 165 | void mp_add_summary(mp_check check[static 1], char *summary) { check->summary = summary; } | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Generate the summary string of a mp_check object based on it's subchecks | ||
| 169 | */ | ||
| 170 | char *get_subcheck_summary(mp_check check) { | ||
| 171 | mp_subcheck_list *subchecks = check.subchecks; | ||
| 172 | |||
| 173 | unsigned int ok = 0; | ||
| 174 | unsigned int warning = 0; | ||
| 175 | unsigned int critical = 0; | ||
| 176 | unsigned int unknown = 0; | ||
| 177 | while (subchecks != NULL) { | ||
| 178 | switch (subchecks->subcheck.state) { | ||
| 179 | case STATE_OK: | ||
| 180 | ok++; | ||
| 181 | break; | ||
| 182 | case STATE_WARNING: | ||
| 183 | warning++; | ||
| 184 | break; | ||
| 185 | case STATE_CRITICAL: | ||
| 186 | critical++; | ||
| 187 | break; | ||
| 188 | case STATE_UNKNOWN: | ||
| 189 | unknown++; | ||
| 190 | break; | ||
| 191 | default: | ||
| 192 | die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary"); | ||
| 193 | } | ||
| 194 | subchecks = subchecks->next; | ||
| 195 | } | ||
| 196 | char *result = NULL; | ||
| 197 | asprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown); | ||
| 198 | return result; | ||
| 199 | } | ||
| 200 | |||
| 201 | mp_state_enum mp_compute_subcheck_state(const mp_subcheck subcheck) { | ||
| 202 | if (subcheck.evaluation_function == NULL) { | ||
| 203 | return mp_eval_subcheck_default(subcheck); | ||
| 204 | } | ||
| 205 | return subcheck.evaluation_function(subcheck); | ||
| 206 | } | ||
| 207 | |||
| 208 | /* | ||
| 209 | * Generate the result state of a mp_subcheck object based on its own state and its subchecks | ||
| 210 | * states | ||
| 211 | */ | ||
| 212 | mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck) { | ||
| 213 | if (subcheck.evaluation_function != NULL) { | ||
| 214 | return subcheck.evaluation_function(subcheck); | ||
| 215 | } | ||
| 216 | |||
| 217 | if (subcheck.state_set_explicitly) { | ||
| 218 | return subcheck.state; | ||
| 219 | } | ||
| 220 | |||
| 221 | mp_subcheck_list *scl = subcheck.subchecks; | ||
| 222 | |||
| 223 | if (scl == NULL) { | ||
| 224 | return subcheck.default_state; | ||
| 225 | } | ||
| 226 | |||
| 227 | mp_state_enum result = STATE_OK; | ||
| 228 | |||
| 229 | while (scl != NULL) { | ||
| 230 | result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck)); | ||
| 231 | scl = scl->next; | ||
| 232 | } | ||
| 233 | |||
| 234 | return result; | ||
| 235 | } | ||
| 236 | |||
| 237 | mp_state_enum mp_compute_check_state(const mp_check check) { | ||
| 238 | // just a safety check | ||
| 239 | if (check.evaluation_function == NULL) { | ||
| 240 | return mp_eval_check_default(check); | ||
| 241 | } | ||
| 242 | return check.evaluation_function(check); | ||
| 243 | } | ||
| 244 | |||
| 245 | /* | ||
| 246 | * Generate the result state of a mp_check object based on it's own state and it's subchecks states | ||
| 247 | */ | ||
| 248 | mp_state_enum mp_eval_check_default(const mp_check check) { | ||
| 249 | assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here | ||
| 250 | |||
| 251 | mp_subcheck_list *scl = check.subchecks; | ||
| 252 | mp_state_enum result = STATE_OK; | ||
| 253 | |||
| 254 | while (scl != NULL) { | ||
| 255 | result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck)); | ||
| 256 | scl = scl->next; | ||
| 257 | } | ||
| 258 | |||
| 259 | return result; | ||
| 260 | } | ||
| 261 | |||
| 262 | /* | ||
| 263 | * Generate output string for a mp_check object | ||
| 264 | * Non static to be available for testing functions | ||
| 265 | */ | ||
| 266 | char *mp_fmt_output(mp_check check) { | ||
| 267 | char *result = NULL; | ||
| 268 | |||
| 269 | switch (output_format) { | ||
| 270 | case MP_FORMAT_MULTI_LINE: { | ||
| 271 | if (check.summary == NULL) { | ||
| 272 | check.summary = get_subcheck_summary(check); | ||
| 273 | } | ||
| 274 | |||
| 275 | asprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary); | ||
| 276 | |||
| 277 | mp_subcheck_list *subchecks = check.subchecks; | ||
| 278 | |||
| 279 | while (subchecks != NULL) { | ||
| 280 | if (level_of_detail == MP_DETAIL_ALL || | ||
| 281 | mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) { | ||
| 282 | asprintf(&result, "%s\n%s", result, | ||
| 283 | fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); | ||
| 284 | } | ||
| 285 | subchecks = subchecks->next; | ||
| 286 | } | ||
| 287 | |||
| 288 | char *pd_string = NULL; | ||
| 289 | subchecks = check.subchecks; | ||
| 290 | |||
| 291 | while (subchecks != NULL) { | ||
| 292 | if (pd_string == NULL) { | ||
| 293 | asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); | ||
| 294 | } else { | ||
| 295 | asprintf(&pd_string, "%s %s", pd_string, | ||
| 296 | fmt_subcheck_perfdata(subchecks->subcheck)); | ||
| 297 | } | ||
| 298 | |||
| 299 | subchecks = subchecks->next; | ||
| 300 | } | ||
| 301 | |||
| 302 | if (pd_string != NULL && strlen(pd_string) > 0) { | ||
| 303 | asprintf(&result, "%s|%s", result, pd_string); | ||
| 304 | } | ||
| 305 | |||
| 306 | break; | ||
| 307 | } | ||
| 308 | case MP_FORMAT_TEST_JSON: { | ||
| 309 | cJSON *resultObject = cJSON_CreateObject(); | ||
| 310 | if (resultObject == NULL) { | ||
| 311 | die(STATE_UNKNOWN, "cJSON_CreateObject failed"); | ||
| 312 | } | ||
| 313 | |||
| 314 | cJSON *resultState = cJSON_CreateString(state_text(mp_compute_check_state(check))); | ||
| 315 | cJSON_AddItemToObject(resultObject, "state", resultState); | ||
| 316 | |||
| 317 | if (check.summary == NULL) { | ||
| 318 | check.summary = get_subcheck_summary(check); | ||
| 319 | } | ||
| 320 | |||
| 321 | cJSON *summary = cJSON_CreateString(check.summary); | ||
| 322 | cJSON_AddItemToObject(resultObject, "summary", summary); | ||
| 323 | |||
| 324 | if (check.subchecks != NULL) { | ||
| 325 | cJSON *subchecks = cJSON_CreateArray(); | ||
| 326 | |||
| 327 | mp_subcheck_list *sc = check.subchecks; | ||
| 328 | |||
| 329 | while (sc != NULL) { | ||
| 330 | cJSON *sc_json = json_serialize_subcheck(sc->subcheck); | ||
| 331 | cJSON_AddItemToArray(subchecks, sc_json); | ||
| 332 | sc = sc->next; | ||
| 333 | } | ||
| 334 | |||
| 335 | cJSON_AddItemToObject(resultObject, "checks", subchecks); | ||
| 336 | } | ||
| 337 | |||
| 338 | result = cJSON_PrintUnformatted(resultObject); | ||
| 339 | break; | ||
| 340 | } | ||
| 341 | default: | ||
| 342 | die(STATE_UNKNOWN, "Invalid format"); | ||
| 343 | } | ||
| 344 | |||
| 345 | return result; | ||
| 346 | } | ||
| 347 | |||
| 348 | /* | ||
| 349 | * Helper function to properly indent the output lines when using multiline | ||
| 350 | * formats | ||
| 351 | */ | ||
| 352 | static char *generate_indentation_string(unsigned int indentation) { | ||
| 353 | char *result = calloc(indentation + 1, sizeof(char)); | ||
| 354 | |||
| 355 | for (unsigned int i = 0; i < indentation; i++) { | ||
| 356 | result[i] = '\t'; | ||
| 357 | } | ||
| 358 | |||
| 359 | return result; | ||
| 360 | } | ||
| 361 | |||
| 362 | /* | ||
| 363 | * Helper function to generate the output string of mp_subcheck | ||
| 364 | */ | ||
| 365 | static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, | ||
| 366 | unsigned int indentation) { | ||
| 367 | char *result = NULL; | ||
| 368 | mp_subcheck_list *subchecks = NULL; | ||
| 369 | |||
| 370 | switch (output_format) { | ||
| 371 | case MP_FORMAT_MULTI_LINE: { | ||
| 372 | char *tmp_string = NULL; | ||
| 373 | if ((tmp_string = strchr(check.output, '\n')) != NULL) { | ||
| 374 | // This is a multiline string, put the correct indentation in before proceeding | ||
| 375 | char *intermediate_string = ""; | ||
| 376 | bool have_residual_chars = false; | ||
| 377 | |||
| 378 | while (tmp_string != NULL) { | ||
| 379 | *tmp_string = '\0'; | ||
| 380 | asprintf(&intermediate_string, "%s%s\n%s", intermediate_string, check.output, | ||
| 381 | generate_indentation_string( | ||
| 382 | indentation + 1)); // one more indentation to make it look better | ||
| 383 | |||
| 384 | if (*(tmp_string + 1) != '\0') { | ||
| 385 | check.output = tmp_string + 1; | ||
| 386 | have_residual_chars = true; | ||
| 387 | } else { | ||
| 388 | // Null after the \n, so this is the end | ||
| 389 | have_residual_chars = false; | ||
| 390 | break; | ||
| 391 | } | ||
| 392 | |||
| 393 | tmp_string = strchr(check.output, '\n'); | ||
| 394 | } | ||
| 395 | |||
| 396 | // add the rest (if any) | ||
| 397 | if (have_residual_chars) { | ||
| 398 | char *tmp = check.output; | ||
| 399 | xasprintf(&check.output, "%s\n%s%s", intermediate_string, | ||
| 400 | generate_indentation_string(indentation + 1), tmp); | ||
| 401 | } else { | ||
| 402 | check.output = intermediate_string; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), | ||
| 406 | state_text(mp_compute_subcheck_state(check)), check.output); | ||
| 407 | |||
| 408 | subchecks = check.subchecks; | ||
| 409 | |||
| 410 | while (subchecks != NULL) { | ||
| 411 | asprintf(&result, "%s\n%s", result, | ||
| 412 | fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); | ||
| 413 | subchecks = subchecks->next; | ||
| 414 | } | ||
| 415 | return result; | ||
| 416 | } | ||
| 417 | default: | ||
| 418 | die(STATE_UNKNOWN, "Invalid format"); | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | static inline cJSON *json_serialise_pd_value(mp_perfdata_value value) { | ||
| 423 | cJSON *result = cJSON_CreateObject(); | ||
| 424 | |||
| 425 | switch (value.type) { | ||
| 426 | case PD_TYPE_DOUBLE: | ||
| 427 | cJSON_AddStringToObject(result, "type", "double"); | ||
| 428 | break; | ||
| 429 | case PD_TYPE_INT: | ||
| 430 | cJSON_AddStringToObject(result, "type", "int"); | ||
| 431 | break; | ||
| 432 | case PD_TYPE_UINT: | ||
| 433 | cJSON_AddStringToObject(result, "type", "uint"); | ||
| 434 | break; | ||
| 435 | case PD_TYPE_NONE: | ||
| 436 | die(STATE_UNKNOWN, "Perfdata type was None in json_serialise_pd_value"); | ||
| 437 | } | ||
| 438 | cJSON_AddStringToObject(result, "value", pd_value_to_string(value)); | ||
| 439 | |||
| 440 | return result; | ||
| 441 | } | ||
| 442 | |||
| 443 | static inline cJSON *json_serialise_range(mp_range range) { | ||
| 444 | cJSON *result = cJSON_CreateObject(); | ||
| 445 | |||
| 446 | if (range.alert_on_inside_range) { | ||
| 447 | cJSON_AddBoolToObject(result, "alert_on_inside", true); | ||
| 448 | } else { | ||
| 449 | cJSON_AddBoolToObject(result, "alert_on_inside", false); | ||
| 450 | } | ||
| 451 | |||
| 452 | if (range.end_infinity) { | ||
| 453 | cJSON_AddStringToObject(result, "end", "inf"); | ||
| 454 | } else { | ||
| 455 | cJSON_AddItemToObject(result, "end", json_serialise_pd_value(range.end)); | ||
| 456 | } | ||
| 457 | |||
| 458 | if (range.start_infinity) { | ||
| 459 | cJSON_AddStringToObject(result, "start", "inf"); | ||
| 460 | } else { | ||
| 461 | cJSON_AddItemToObject(result, "start", json_serialise_pd_value(range.end)); | ||
| 462 | } | ||
| 463 | |||
| 464 | return result; | ||
| 465 | } | ||
| 466 | |||
| 467 | static inline cJSON *json_serialise_pd(mp_perfdata pd_val) { | ||
| 468 | cJSON *result = cJSON_CreateObject(); | ||
| 469 | |||
| 470 | // Label | ||
| 471 | cJSON_AddStringToObject(result, "label", pd_val.label); | ||
| 472 | |||
| 473 | // Value | ||
| 474 | cJSON_AddItemToObject(result, "value", json_serialise_pd_value(pd_val.value)); | ||
| 475 | |||
| 476 | // Uom | ||
| 477 | cJSON_AddStringToObject(result, "uom", pd_val.uom); | ||
| 478 | |||
| 479 | // Warn/Crit | ||
| 480 | if (pd_val.warn_present) { | ||
| 481 | cJSON *warn = json_serialise_range(pd_val.warn); | ||
| 482 | cJSON_AddItemToObject(result, "warn", warn); | ||
| 483 | } | ||
| 484 | if (pd_val.crit_present) { | ||
| 485 | cJSON *crit = json_serialise_range(pd_val.crit); | ||
| 486 | cJSON_AddItemToObject(result, "crit", crit); | ||
| 487 | } | ||
| 488 | |||
| 489 | if (pd_val.min_present) { | ||
| 490 | cJSON_AddItemToObject(result, "min", json_serialise_pd_value(pd_val.min)); | ||
| 491 | } | ||
| 492 | if (pd_val.max_present) { | ||
| 493 | cJSON_AddItemToObject(result, "max", json_serialise_pd_value(pd_val.max)); | ||
| 494 | } | ||
| 495 | |||
| 496 | return result; | ||
| 497 | } | ||
| 498 | |||
| 499 | static inline cJSON *json_serialise_pd_list(pd_list *list) { | ||
| 500 | cJSON *result = cJSON_CreateArray(); | ||
| 501 | |||
| 502 | do { | ||
| 503 | cJSON *pd_value = json_serialise_pd(list->data); | ||
| 504 | cJSON_AddItemToArray(result, pd_value); | ||
| 505 | list = list->next; | ||
| 506 | } while (list != NULL); | ||
| 507 | |||
| 508 | return result; | ||
| 509 | } | ||
| 510 | |||
| 511 | static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) { | ||
| 512 | cJSON *result = cJSON_CreateObject(); | ||
| 513 | |||
| 514 | // Human readable output | ||
| 515 | cJSON *output = cJSON_CreateString(subcheck.output); | ||
| 516 | cJSON_AddItemToObject(result, "output", output); | ||
| 517 | |||
| 518 | // Test state (aka Exit Code) | ||
| 519 | cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck))); | ||
| 520 | cJSON_AddItemToObject(result, "state", state); | ||
| 521 | |||
| 522 | // Perfdata | ||
| 523 | if (subcheck.perfdata != NULL) { | ||
| 524 | cJSON *perfdata = json_serialise_pd_list(subcheck.perfdata); | ||
| 525 | cJSON_AddItemToObject(result, "perfdata", perfdata); | ||
| 526 | } | ||
| 527 | |||
| 528 | if (subcheck.subchecks != NULL) { | ||
| 529 | cJSON *subchecks = cJSON_CreateArray(); | ||
| 530 | |||
| 531 | mp_subcheck_list *sc = subcheck.subchecks; | ||
| 532 | |||
| 533 | while (sc != NULL) { | ||
| 534 | cJSON *sc_json = json_serialize_subcheck(sc->subcheck); | ||
| 535 | cJSON_AddItemToArray(subchecks, sc_json); | ||
| 536 | sc = sc->next; | ||
| 537 | } | ||
| 538 | |||
| 539 | cJSON_AddItemToObject(result, "checks", subchecks); | ||
| 540 | } | ||
| 541 | |||
| 542 | return result; | ||
| 543 | } | ||
| 544 | |||
| 545 | /* | ||
| 546 | * Wrapper function to print the output string of a mp_check object | ||
| 547 | * Use this in concrete plugins. | ||
| 548 | */ | ||
| 549 | void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); } | ||
| 550 | |||
| 551 | /* | ||
| 552 | * Convenience function to print the output string of a mp_check object and exit | ||
| 553 | * the program with the resulting state. | ||
| 554 | * Intended to be used to exit a monitoring plugin. | ||
| 555 | */ | ||
| 556 | void mp_exit(mp_check check) { | ||
| 557 | mp_print_output(check); | ||
| 558 | if (output_format == MP_FORMAT_TEST_JSON) { | ||
| 559 | exit(0); | ||
| 560 | } | ||
| 561 | |||
| 562 | exit(mp_compute_check_state(check)); | ||
| 563 | } | ||
| 564 | |||
| 565 | /* | ||
| 566 | * Function to set the result state of a mp_subcheck object explicitly. | ||
| 567 | * This will overwrite the default state AND states derived from it's subchecks | ||
| 568 | */ | ||
| 569 | mp_subcheck mp_set_subcheck_state(mp_subcheck check, mp_state_enum state) { | ||
| 570 | check.state = state; | ||
| 571 | check.state_set_explicitly = true; | ||
| 572 | return check; | ||
| 573 | } | ||
| 574 | |||
| 575 | /* | ||
| 576 | * Function to set the default result state of a mp_subcheck object. This state | ||
| 577 | * will be used if neither an explicit state is set (see *mp_set_subcheck_state*) | ||
| 578 | * nor does it include other subchecks | ||
| 579 | */ | ||
| 580 | mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state) { | ||
| 581 | check.default_state = state; | ||
| 582 | return check; | ||
| 583 | } | ||
| 584 | |||
| 585 | char *mp_output_format_map[] = { | ||
| 586 | [MP_FORMAT_MULTI_LINE] = "multi-line", | ||
| 587 | [MP_FORMAT_TEST_JSON] = "mp-test-json", | ||
| 588 | }; | ||
| 589 | |||
| 590 | /* | ||
| 591 | * Function to parse the output from a string | ||
| 592 | */ | ||
| 593 | parsed_output_format mp_parse_output_format(char *format_string) { | ||
| 594 | parsed_output_format result = { | ||
| 595 | .parsing_success = false, | ||
| 596 | .output_format = MP_FORMAT_DEFAULT, | ||
| 597 | }; | ||
| 598 | |||
| 599 | for (mp_output_format i = 0; i < (sizeof(mp_output_format_map) / sizeof(char *)); i++) { | ||
| 600 | if (strcasecmp(mp_output_format_map[i], format_string) == 0) { | ||
| 601 | result.parsing_success = true; | ||
| 602 | result.output_format = i; | ||
| 603 | break; | ||
| 604 | } | ||
| 605 | } | ||
| 606 | |||
| 607 | return result; | ||
| 608 | } | ||
| 609 | |||
| 610 | void mp_set_format(mp_output_format format) { output_format = format; } | ||
| 611 | |||
| 612 | mp_output_format mp_get_format(void) { return output_format; } | ||
| 613 | |||
| 614 | void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; } | ||
| 615 | |||
| 616 | mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; } | ||
| 617 | |||
| 618 | mp_state_enum mp_eval_ok(mp_check overall) { | ||
| 619 | (void)overall; | ||
| 620 | return STATE_OK; | ||
| 621 | } | ||
| 622 | |||
| 623 | mp_state_enum mp_eval_warning(mp_check overall) { | ||
| 624 | (void)overall; | ||
| 625 | return STATE_WARNING; | ||
| 626 | } | ||
| 627 | |||
| 628 | mp_state_enum mp_eval_critical(mp_check overall) { | ||
| 629 | (void)overall; | ||
| 630 | return STATE_CRITICAL; | ||
| 631 | } | ||
| 632 | |||
| 633 | mp_state_enum mp_eval_unknown(mp_check overall) { | ||
| 634 | (void)overall; | ||
| 635 | return STATE_UNKNOWN; | ||
| 636 | } | ||
diff --git a/lib/output.h b/lib/output.h new file mode 100644 index 00000000..c63c8e3f --- /dev/null +++ b/lib/output.h | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../config.h" | ||
| 4 | #include "./perfdata.h" | ||
| 5 | #include "./states.h" | ||
| 6 | |||
| 7 | /* | ||
| 8 | * A partial check result | ||
| 9 | */ | ||
| 10 | typedef struct mp_subcheck mp_subcheck; | ||
| 11 | struct mp_subcheck { | ||
| 12 | mp_state_enum state; // OK, Warning, Critical ... set explicitly | ||
| 13 | mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly | ||
| 14 | bool state_set_explicitly; // was the state set explicitly (or should it be derived from | ||
| 15 | // subchecks) | ||
| 16 | |||
| 17 | char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP | ||
| 18 | // connection to..") | ||
| 19 | pd_list *perfdata; // Performance data for this check | ||
| 20 | struct subcheck_list *subchecks; // subchecks deeper in the hierarchy | ||
| 21 | |||
| 22 | // the evaluation_functions computes the state of subcheck | ||
| 23 | mp_state_enum (*evaluation_function)(mp_subcheck); | ||
| 24 | }; | ||
| 25 | |||
| 26 | /* | ||
| 27 | * A list of subchecks, used in subchecks and the main check | ||
| 28 | */ | ||
| 29 | typedef struct subcheck_list { | ||
| 30 | mp_subcheck subcheck; | ||
| 31 | struct subcheck_list *next; | ||
| 32 | } mp_subcheck_list; | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Possible output formats | ||
| 36 | */ | ||
| 37 | typedef enum output_format { | ||
| 38 | MP_FORMAT_MULTI_LINE, | ||
| 39 | MP_FORMAT_TEST_JSON, | ||
| 40 | } mp_output_format; | ||
| 41 | |||
| 42 | #define MP_FORMAT_DEFAULT MP_FORMAT_MULTI_LINE | ||
| 43 | |||
| 44 | /* | ||
| 45 | * Format related functions | ||
| 46 | */ | ||
| 47 | void mp_set_format(mp_output_format format); | ||
| 48 | mp_output_format mp_get_format(void); | ||
| 49 | |||
| 50 | // Output detail level | ||
| 51 | |||
| 52 | typedef enum output_detail_level { | ||
| 53 | MP_DETAIL_ALL, | ||
| 54 | MP_DETAIL_NON_OK_ONLY, | ||
| 55 | } mp_output_detail_level; | ||
| 56 | |||
| 57 | void mp_set_level_of_detail(mp_output_detail_level level); | ||
| 58 | mp_output_detail_level mp_get_level_of_detail(void); | ||
| 59 | |||
| 60 | /* | ||
| 61 | * The main state object of a plugin. Exists only ONCE per plugin. | ||
| 62 | * This is the "root" of a tree of singular checks. | ||
| 63 | * The final result is always derived from the children and the "worst" state | ||
| 64 | * in the first layer of subchecks | ||
| 65 | */ | ||
| 66 | typedef struct mp_check mp_check; | ||
| 67 | struct mp_check { | ||
| 68 | char *summary; // Overall summary, if not set a summary will be automatically generated | ||
| 69 | mp_subcheck_list *subchecks; | ||
| 70 | |||
| 71 | // the evaluation_functions computes the state of check | ||
| 72 | mp_state_enum (*evaluation_function)(mp_check); | ||
| 73 | }; | ||
| 74 | |||
| 75 | mp_check mp_check_init(void); | ||
| 76 | mp_subcheck mp_subcheck_init(void); | ||
| 77 | |||
| 78 | mp_subcheck mp_set_subcheck_state(mp_subcheck, mp_state_enum); | ||
| 79 | mp_subcheck mp_set_subcheck_default_state(mp_subcheck, mp_state_enum); | ||
| 80 | |||
| 81 | int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck); | ||
| 82 | int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck); | ||
| 83 | |||
| 84 | void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], mp_perfdata); | ||
| 85 | |||
| 86 | void mp_add_summary(mp_check check[static 1], char *summary); | ||
| 87 | |||
| 88 | mp_state_enum mp_compute_check_state(mp_check); | ||
| 89 | mp_state_enum mp_compute_subcheck_state(mp_subcheck); | ||
| 90 | |||
| 91 | mp_state_enum mp_eval_ok(mp_check overall); | ||
| 92 | mp_state_enum mp_eval_warning(mp_check overall); | ||
| 93 | mp_state_enum mp_eval_critical(mp_check overall); | ||
| 94 | mp_state_enum mp_eval_unknown(mp_check overall); | ||
| 95 | mp_state_enum mp_eval_check_default(mp_check check); | ||
| 96 | mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck); | ||
| 97 | |||
| 98 | typedef struct { | ||
| 99 | bool parsing_success; | ||
| 100 | mp_output_format output_format; | ||
| 101 | } parsed_output_format; | ||
| 102 | parsed_output_format mp_parse_output_format(char *format_string); | ||
| 103 | |||
| 104 | // TODO free and stuff | ||
| 105 | // void mp_cleanup_check(mp_check check[static 1]); | ||
| 106 | |||
| 107 | char *mp_fmt_output(mp_check); | ||
| 108 | |||
| 109 | void mp_print_output(mp_check); | ||
| 110 | |||
| 111 | /* | ||
| 112 | * ================== | ||
| 113 | * Exit functionality | ||
| 114 | * ================== | ||
| 115 | */ | ||
| 116 | |||
| 117 | void mp_exit(mp_check) __attribute__((noreturn)); | ||
diff --git a/lib/parse_ini.c b/lib/parse_ini.c index 1289aae2..db337622 100644 --- a/lib/parse_ini.c +++ b/lib/parse_ini.c | |||
| @@ -40,26 +40,29 @@ typedef struct { | |||
| 40 | char *stanza; | 40 | char *stanza; |
| 41 | } np_ini_info; | 41 | } np_ini_info; |
| 42 | 42 | ||
| 43 | static char *default_ini_file_names[] = {"monitoring-plugins.ini", "plugins.ini", "nagios-plugins.ini", NULL}; | 43 | static char *default_ini_file_names[] = {"monitoring-plugins.ini", "plugins.ini", |
| 44 | "nagios-plugins.ini", NULL}; | ||
| 44 | 45 | ||
| 45 | static char *default_ini_path_names[] = { | 46 | static char *default_ini_path_names[] = { |
| 46 | "/usr/local/etc/monitoring-plugins/monitoring-plugins.ini", "/usr/local/etc/monitoring-plugins.ini", | 47 | "/usr/local/etc/monitoring-plugins/monitoring-plugins.ini", |
| 47 | "/etc/monitoring-plugins/monitoring-plugins.ini", "/etc/monitoring-plugins.ini", | 48 | "/usr/local/etc/monitoring-plugins.ini", "/etc/monitoring-plugins/monitoring-plugins.ini", |
| 49 | "/etc/monitoring-plugins.ini", | ||
| 48 | /* deprecated path names (for backward compatibility): */ | 50 | /* deprecated path names (for backward compatibility): */ |
| 49 | "/etc/nagios/plugins.ini", "/usr/local/nagios/etc/plugins.ini", "/usr/local/etc/nagios/plugins.ini", "/etc/opt/nagios/plugins.ini", | 51 | "/etc/nagios/plugins.ini", "/usr/local/nagios/etc/plugins.ini", |
| 50 | "/etc/nagios-plugins.ini", "/usr/local/etc/nagios-plugins.ini", "/etc/opt/nagios-plugins.ini", NULL}; | 52 | "/usr/local/etc/nagios/plugins.ini", "/etc/opt/nagios/plugins.ini", "/etc/nagios-plugins.ini", |
| 53 | "/usr/local/etc/nagios-plugins.ini", "/etc/opt/nagios-plugins.ini", NULL}; | ||
| 51 | 54 | ||
| 52 | /* eat all characters from a FILE pointer until n is encountered */ | 55 | /* eat all characters from a FILE pointer until n is encountered */ |
| 53 | #define GOBBLE_TO(f, c, n) \ | 56 | #define GOBBLE_TO(f, c, n) \ |
| 54 | do { \ | 57 | do { \ |
| 55 | (c) = fgetc((f)); \ | 58 | (c) = fgetc((f)); \ |
| 56 | } while ((c) != EOF && (c) != (n)) | 59 | } while ((c) != EOF && (c) != (n)) |
| 57 | 60 | ||
| 58 | /* internal function that returns the constructed defaults options */ | 61 | /* internal function that returns the constructed defaults options */ |
| 59 | static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts); | 62 | static bool read_defaults(FILE *defaults_file, const char *stanza, np_arg_list **opts); |
| 60 | 63 | ||
| 61 | /* internal function that converts a single line into options format */ | 64 | /* internal function that converts a single line into options format */ |
| 62 | static int add_option(FILE *f, np_arg_list **optlst); | 65 | static int add_option(FILE *filePointer, np_arg_list **optlst); |
| 63 | 66 | ||
| 64 | /* internal functions to find default file */ | 67 | /* internal functions to find default file */ |
| 65 | static char *default_file(void); | 68 | static char *default_file(void); |
| @@ -71,7 +74,8 @@ static char *default_file_in_path(void); | |||
| 71 | * into its separate parts. | 74 | * into its separate parts. |
| 72 | */ | 75 | */ |
| 73 | static void parse_locator(const char *locator, const char *def_stanza, np_ini_info *i) { | 76 | static void parse_locator(const char *locator, const char *def_stanza, np_ini_info *i) { |
| 74 | size_t locator_len = 0, stanza_len = 0; | 77 | size_t locator_len = 0; |
| 78 | size_t stanza_len = 0; | ||
| 75 | 79 | ||
| 76 | /* if locator is NULL we'll use default values */ | 80 | /* if locator is NULL we'll use default values */ |
| 77 | if (locator != NULL) { | 81 | if (locator != NULL) { |
| @@ -87,8 +91,9 @@ static void parse_locator(const char *locator, const char *def_stanza, np_ini_in | |||
| 87 | i->stanza = strdup(def_stanza); | 91 | i->stanza = strdup(def_stanza); |
| 88 | } | 92 | } |
| 89 | 93 | ||
| 90 | if (i->stanza == NULL) | 94 | if (i->stanza == NULL) { |
| 91 | die(STATE_UNKNOWN, _("malloc() failed!\n")); | 95 | die(STATE_UNKNOWN, _("malloc() failed!\n")); |
| 96 | } | ||
| 92 | 97 | ||
| 93 | /* check whether there's an @file part */ | 98 | /* check whether there's an @file part */ |
| 94 | if (stanza_len == locator_len) { | 99 | if (stanza_len == locator_len) { |
| @@ -99,39 +104,46 @@ static void parse_locator(const char *locator, const char *def_stanza, np_ini_in | |||
| 99 | i->file_string_on_heap = true; | 104 | i->file_string_on_heap = true; |
| 100 | } | 105 | } |
| 101 | 106 | ||
| 102 | if (i->file == NULL || i->file[0] == '\0') | 107 | if (i->file == NULL || i->file[0] == '\0') { |
| 103 | die(STATE_UNKNOWN, _("Cannot find config file in any standard location.\n")); | 108 | die(STATE_UNKNOWN, _("Cannot find config file in any standard location.\n")); |
| 109 | } | ||
| 104 | } | 110 | } |
| 105 | 111 | ||
| 106 | /* | 112 | /* |
| 107 | * This is the externally visible function used by extra_opts. | 113 | * This is the externally visible function used by extra_opts. |
| 108 | */ | 114 | */ |
| 109 | np_arg_list *np_get_defaults(const char *locator, const char *default_section) { | 115 | np_arg_list *np_get_defaults(const char *locator, const char *default_section) { |
| 110 | FILE *inifile = NULL; | ||
| 111 | np_arg_list *defaults = NULL; | ||
| 112 | np_ini_info i; | ||
| 113 | int is_suid_plugin = mp_suid(); | 116 | int is_suid_plugin = mp_suid(); |
| 114 | 117 | ||
| 115 | if (is_suid_plugin && idpriv_temp_drop() == -1) | 118 | if (is_suid_plugin && idpriv_temp_drop() == -1) { |
| 116 | die(STATE_UNKNOWN, _("Cannot drop privileges: %s\n"), strerror(errno)); | 119 | die(STATE_UNKNOWN, _("Cannot drop privileges: %s\n"), strerror(errno)); |
| 120 | } | ||
| 117 | 121 | ||
| 118 | parse_locator(locator, default_section, &i); | 122 | FILE *inifile = NULL; |
| 119 | inifile = strcmp(i.file, "-") == 0 ? stdin : fopen(i.file, "r"); | 123 | np_ini_info ini_info; |
| 124 | parse_locator(locator, default_section, &ini_info); | ||
| 125 | inifile = strcmp(ini_info.file, "-") == 0 ? stdin : fopen(ini_info.file, "r"); | ||
| 120 | 126 | ||
| 121 | if (inifile == NULL) | 127 | if (inifile == NULL) { |
| 122 | die(STATE_UNKNOWN, _("Can't read config file: %s\n"), strerror(errno)); | 128 | die(STATE_UNKNOWN, _("Can't read config file: %s\n"), strerror(errno)); |
| 123 | if (!read_defaults(inifile, i.stanza, &defaults)) | 129 | } |
| 124 | die(STATE_UNKNOWN, _("Invalid section '%s' in config file '%s'\n"), i.stanza, i.file); | ||
| 125 | 130 | ||
| 126 | if (i.file_string_on_heap) { | 131 | np_arg_list *defaults = NULL; |
| 127 | free(i.file); | 132 | if (!read_defaults(inifile, ini_info.stanza, &defaults)) { |
| 133 | die(STATE_UNKNOWN, _("Invalid section '%s' in config file '%s'\n"), ini_info.stanza, ini_info.file); | ||
| 134 | } | ||
| 135 | |||
| 136 | if (ini_info.file_string_on_heap) { | ||
| 137 | free(ini_info.file); | ||
| 128 | } | 138 | } |
| 129 | 139 | ||
| 130 | if (inifile != stdin) | 140 | if (inifile != stdin) { |
| 131 | fclose(inifile); | 141 | fclose(inifile); |
| 132 | free(i.stanza); | 142 | } |
| 133 | if (is_suid_plugin && idpriv_temp_restore() == -1) | 143 | free(ini_info.stanza); |
| 144 | if (is_suid_plugin && idpriv_temp_restore() == -1) { | ||
| 134 | die(STATE_UNKNOWN, _("Cannot restore privileges: %s\n"), strerror(errno)); | 145 | die(STATE_UNKNOWN, _("Cannot restore privileges: %s\n"), strerror(errno)); |
| 146 | } | ||
| 135 | 147 | ||
| 136 | return defaults; | 148 | return defaults; |
| 137 | } | 149 | } |
| @@ -143,54 +155,58 @@ np_arg_list *np_get_defaults(const char *locator, const char *default_section) { | |||
| 143 | * be extra careful about user-supplied input (i.e. avoiding possible | 155 | * be extra careful about user-supplied input (i.e. avoiding possible |
| 144 | * format string vulnerabilities, etc). | 156 | * format string vulnerabilities, etc). |
| 145 | */ | 157 | */ |
| 146 | static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { | 158 | static bool read_defaults(FILE *defaults_file, const char *stanza, np_arg_list **opts) { |
| 147 | int c = 0; | ||
| 148 | bool status = false; | 159 | bool status = false; |
| 149 | size_t i, stanza_len; | ||
| 150 | enum { | 160 | enum { |
| 151 | NOSTANZA, | 161 | NOSTANZA, |
| 152 | WRONGSTANZA, | 162 | WRONGSTANZA, |
| 153 | RIGHTSTANZA | 163 | RIGHTSTANZA |
| 154 | } stanzastate = NOSTANZA; | 164 | } stanzastate = NOSTANZA; |
| 155 | 165 | ||
| 156 | stanza_len = strlen(stanza); | 166 | size_t stanza_len = strlen(stanza); |
| 157 | 167 | ||
| 158 | /* our little stanza-parsing state machine */ | 168 | /* our little stanza-parsing state machine */ |
| 159 | while ((c = fgetc(f)) != EOF) { | 169 | int current_char = 0; |
| 170 | while ((current_char = fgetc(defaults_file)) != EOF) { | ||
| 160 | /* gobble up leading whitespace */ | 171 | /* gobble up leading whitespace */ |
| 161 | if (isspace(c)) | 172 | if (isspace(current_char)) { |
| 162 | continue; | 173 | continue; |
| 163 | switch (c) { | 174 | } |
| 175 | switch (current_char) { | ||
| 164 | /* globble up comment lines */ | 176 | /* globble up comment lines */ |
| 165 | case ';': | 177 | case ';': |
| 166 | case '#': | 178 | case '#': |
| 167 | GOBBLE_TO(f, c, '\n'); | 179 | GOBBLE_TO(defaults_file, current_char, '\n'); |
| 168 | break; | 180 | break; |
| 169 | /* start of a stanza, check to see if it matches */ | 181 | /* start of a stanza, check to see if it matches */ |
| 170 | case '[': | 182 | case '[': { |
| 171 | stanzastate = WRONGSTANZA; | 183 | stanzastate = WRONGSTANZA; |
| 184 | size_t i; | ||
| 172 | for (i = 0; i < stanza_len; i++) { | 185 | for (i = 0; i < stanza_len; i++) { |
| 173 | c = fgetc(f); | 186 | current_char = fgetc(defaults_file); |
| 174 | /* strip leading whitespace */ | 187 | /* strip leading whitespace */ |
| 175 | if (i == 0) | 188 | if (i == 0) { |
| 176 | for (; isspace(c); c = fgetc(f)) | 189 | for (; isspace(current_char); current_char = fgetc(defaults_file)) { |
| 177 | continue; | 190 | } |
| 191 | } | ||
| 178 | /* nope, read to the end of the line */ | 192 | /* nope, read to the end of the line */ |
| 179 | if (c != stanza[i]) { | 193 | if (current_char != stanza[i]) { |
| 180 | GOBBLE_TO(f, c, '\n'); | 194 | GOBBLE_TO(defaults_file, current_char, '\n'); |
| 181 | break; | 195 | break; |
| 182 | } | 196 | } |
| 183 | } | 197 | } |
| 198 | |||
| 184 | /* if it matched up to here and the next char is ']'... */ | 199 | /* if it matched up to here and the next char is ']'... */ |
| 185 | if (i == stanza_len) { | 200 | if (i == stanza_len) { |
| 186 | c = fgetc(f); | 201 | current_char = fgetc(defaults_file); |
| 187 | /* strip trailing whitespace */ | 202 | /* strip trailing whitespace */ |
| 188 | for (; isspace(c); c = fgetc(f)) | 203 | for (; isspace(current_char); current_char = fgetc(defaults_file)) { |
| 189 | continue; | 204 | } |
| 190 | if (c == ']') | 205 | if (current_char == ']') { |
| 191 | stanzastate = RIGHTSTANZA; | 206 | stanzastate = RIGHTSTANZA; |
| 207 | } | ||
| 192 | } | 208 | } |
| 193 | break; | 209 | } break; |
| 194 | /* otherwise, we're in the body of a stanza or a parse error */ | 210 | /* otherwise, we're in the body of a stanza or a parse error */ |
| 195 | default: | 211 | default: |
| 196 | switch (stanzastate) { | 212 | switch (stanzastate) { |
| @@ -201,12 +217,12 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { | |||
| 201 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); | 217 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); |
| 202 | /* we're in a stanza, but for a different plugin */ | 218 | /* we're in a stanza, but for a different plugin */ |
| 203 | case WRONGSTANZA: | 219 | case WRONGSTANZA: |
| 204 | GOBBLE_TO(f, c, '\n'); | 220 | GOBBLE_TO(defaults_file, current_char, '\n'); |
| 205 | break; | 221 | break; |
| 206 | /* okay, this is where we start taking the config */ | 222 | /* okay, this is where we start taking the config */ |
| 207 | case RIGHTSTANZA: | 223 | case RIGHTSTANZA: |
| 208 | ungetc(c, f); | 224 | ungetc(current_char, defaults_file); |
| 209 | if (add_option(f, opts)) { | 225 | if (add_option(defaults_file, opts)) { |
| 210 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); | 226 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); |
| 211 | } | 227 | } |
| 212 | status = true; | 228 | status = true; |
| @@ -225,13 +241,12 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { | |||
| 225 | * --option[=value] | 241 | * --option[=value] |
| 226 | * appending it to the linked list optbuf. | 242 | * appending it to the linked list optbuf. |
| 227 | */ | 243 | */ |
| 228 | static int add_option(FILE *f, np_arg_list **optlst) { | 244 | static int add_option(FILE *filePointer, np_arg_list **optlst) { |
| 229 | np_arg_list *opttmp = *optlst, *optnew; | 245 | char *linebuf = NULL; |
| 230 | char *linebuf = NULL, *lineend = NULL, *optptr = NULL, *optend = NULL; | 246 | bool done_reading = false; |
| 231 | char *eqptr = NULL, *valptr = NULL, *valend = NULL; | 247 | const size_t read_sz = 8; |
| 232 | short done_reading = 0, equals = 0, value = 0; | 248 | size_t linebuf_sz = 0; |
| 233 | size_t cfg_len = 0, read_sz = 8, linebuf_sz = 0, read_pos = 0; | 249 | size_t read_pos = 0; |
| 234 | size_t opt_len = 0, val_len = 0; | ||
| 235 | 250 | ||
| 236 | /* read one line from the file */ | 251 | /* read one line from the file */ |
| 237 | while (!done_reading) { | 252 | while (!done_reading) { |
| @@ -239,74 +254,101 @@ static int add_option(FILE *f, np_arg_list **optlst) { | |||
| 239 | if (linebuf == NULL || read_pos + read_sz >= linebuf_sz) { | 254 | if (linebuf == NULL || read_pos + read_sz >= linebuf_sz) { |
| 240 | linebuf_sz = linebuf_sz > 0 ? linebuf_sz << 1 : read_sz; | 255 | linebuf_sz = linebuf_sz > 0 ? linebuf_sz << 1 : read_sz; |
| 241 | linebuf = realloc(linebuf, linebuf_sz); | 256 | linebuf = realloc(linebuf, linebuf_sz); |
| 242 | if (linebuf == NULL) | 257 | if (linebuf == NULL) { |
| 243 | die(STATE_UNKNOWN, _("malloc() failed!\n")); | 258 | die(STATE_UNKNOWN, _("malloc() failed!\n")); |
| 259 | } | ||
| 244 | } | 260 | } |
| 245 | if (fgets(&linebuf[read_pos], (int)read_sz, f) == NULL) | 261 | |
| 246 | done_reading = 1; | 262 | if (fgets(&linebuf[read_pos], (int)read_sz, filePointer) == NULL) { |
| 247 | else { | 263 | done_reading = true; |
| 264 | } else { | ||
| 248 | read_pos = strlen(linebuf); | 265 | read_pos = strlen(linebuf); |
| 249 | if (linebuf[read_pos - 1] == '\n') { | 266 | if (linebuf[read_pos - 1] == '\n') { |
| 250 | linebuf[--read_pos] = '\0'; | 267 | linebuf[--read_pos] = '\0'; |
| 251 | done_reading = 1; | 268 | done_reading = true; |
| 252 | } | 269 | } |
| 253 | } | 270 | } |
| 254 | } | 271 | } |
| 255 | lineend = &linebuf[read_pos]; | 272 | |
| 273 | char *lineend = &linebuf[read_pos]; | ||
| 256 | /* all that to read one line, isn't C fun? :) now comes the parsing :/ */ | 274 | /* all that to read one line, isn't C fun? :) now comes the parsing :/ */ |
| 257 | 275 | ||
| 258 | /* skip leading whitespace */ | 276 | /* skip leading whitespace */ |
| 259 | for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++) | 277 | char *optptr = NULL; |
| 260 | continue; | 278 | for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++) { |
| 279 | } | ||
| 280 | |||
| 261 | /* continue to '=' or EOL, watching for spaces that might precede it */ | 281 | /* continue to '=' or EOL, watching for spaces that might precede it */ |
| 282 | char *eqptr = NULL; | ||
| 283 | char *optend = NULL; | ||
| 262 | for (eqptr = optptr; eqptr < lineend && *eqptr != '='; eqptr++) { | 284 | for (eqptr = optptr; eqptr < lineend && *eqptr != '='; eqptr++) { |
| 263 | if (isspace(*eqptr) && optend == NULL) | 285 | if (isspace(*eqptr) && optend == NULL) { |
| 264 | optend = eqptr; | 286 | optend = eqptr; |
| 265 | else | 287 | } else { |
| 266 | optend = NULL; | 288 | optend = NULL; |
| 289 | } | ||
| 267 | } | 290 | } |
| 268 | if (optend == NULL) | 291 | |
| 292 | if (optend == NULL) { | ||
| 269 | optend = eqptr; | 293 | optend = eqptr; |
| 294 | } | ||
| 295 | |||
| 270 | --optend; | 296 | --optend; |
| 297 | |||
| 271 | /* ^[[:space:]]*=foo is a syntax error */ | 298 | /* ^[[:space:]]*=foo is a syntax error */ |
| 272 | if (optptr == eqptr) | 299 | if (optptr == eqptr) { |
| 273 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); | 300 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); |
| 301 | } | ||
| 302 | |||
| 274 | /* continue from '=' to start of value or EOL */ | 303 | /* continue from '=' to start of value or EOL */ |
| 275 | for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr); valptr++) | 304 | char *valptr = NULL; |
| 276 | continue; | 305 | for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr); valptr++) { |
| 306 | } | ||
| 307 | |||
| 277 | /* continue to the end of value */ | 308 | /* continue to the end of value */ |
| 278 | for (valend = valptr; valend < lineend; valend++) | 309 | char *valend = NULL; |
| 279 | continue; | 310 | for (valend = valptr; valend < lineend; valend++) { |
| 311 | } | ||
| 312 | |||
| 280 | --valend; | 313 | --valend; |
| 314 | |||
| 281 | /* finally trim off trailing spaces */ | 315 | /* finally trim off trailing spaces */ |
| 282 | for (; isspace(*valend); valend--) | 316 | for (; isspace(*valend); valend--) { |
| 283 | continue; | 317 | } |
| 318 | |||
| 284 | /* calculate the length of "--foo" */ | 319 | /* calculate the length of "--foo" */ |
| 285 | opt_len = (size_t)(1 + optend - optptr); | 320 | size_t opt_len = (size_t)(1 + optend - optptr); |
| 286 | /* 1-character params needs only one dash */ | 321 | /* 1-character params needs only one dash */ |
| 287 | if (opt_len == 1) | 322 | size_t cfg_len = 0; |
| 323 | if (opt_len == 1) { | ||
| 288 | cfg_len = 1 + (opt_len); | 324 | cfg_len = 1 + (opt_len); |
| 289 | else | 325 | } else { |
| 290 | cfg_len = 2 + (opt_len); | 326 | cfg_len = 2 + (opt_len); |
| 327 | } | ||
| 328 | |||
| 329 | size_t val_len = 0; | ||
| 330 | bool equals = false; | ||
| 331 | bool value = false; | ||
| 291 | /* if valptr<lineend then we have to also allocate space for "=bar" */ | 332 | /* if valptr<lineend then we have to also allocate space for "=bar" */ |
| 292 | if (valptr < lineend) { | 333 | if (valptr < lineend) { |
| 293 | equals = value = 1; | 334 | equals = value = true; |
| 294 | val_len = (size_t)(1 + valend - valptr); | 335 | val_len = (size_t)(1 + valend - valptr); |
| 295 | cfg_len += 1 + val_len; | 336 | cfg_len += 1 + val_len; |
| 296 | } | 337 | } else if (valptr == lineend) { |
| 297 | /* if valptr==valend then we have "=" but no "bar" */ | 338 | /* if valptr==valend then we have "=" but no "bar" */ |
| 298 | else if (valptr == lineend) { | 339 | equals = true; |
| 299 | equals = 1; | ||
| 300 | cfg_len += 1; | 340 | cfg_len += 1; |
| 301 | } | 341 | } |
| 342 | |||
| 302 | /* a line with no equal sign isn't valid */ | 343 | /* a line with no equal sign isn't valid */ |
| 303 | if (equals == 0) | 344 | if (!equals) { |
| 304 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); | 345 | die(STATE_UNKNOWN, "%s\n", _("Config file error")); |
| 346 | } | ||
| 305 | 347 | ||
| 306 | /* okay, now we have all the info we need, so we create a new np_arg_list | 348 | /* okay, now we have all the info we need, so we create a new np_arg_list |
| 307 | * element and set the argument... | 349 | * element and set the argument... |
| 308 | */ | 350 | */ |
| 309 | optnew = malloc(sizeof(np_arg_list)); | 351 | np_arg_list *optnew = malloc(sizeof(np_arg_list)); |
| 310 | optnew->next = NULL; | 352 | optnew->next = NULL; |
| 311 | 353 | ||
| 312 | read_pos = 0; | 354 | read_pos = 0; |
| @@ -329,11 +371,13 @@ static int add_option(FILE *f, np_arg_list **optlst) { | |||
| 329 | optnew->arg[read_pos] = '\0'; | 371 | optnew->arg[read_pos] = '\0'; |
| 330 | 372 | ||
| 331 | /* ...and put that to the end of the list */ | 373 | /* ...and put that to the end of the list */ |
| 332 | if (*optlst == NULL) | 374 | if (*optlst == NULL) { |
| 333 | *optlst = optnew; | 375 | *optlst = optnew; |
| 334 | else { | 376 | } else { |
| 335 | while (opttmp->next != NULL) | 377 | np_arg_list *opttmp = *optlst; |
| 378 | while (opttmp->next != NULL) { | ||
| 336 | opttmp = opttmp->next; | 379 | opttmp = opttmp->next; |
| 380 | } | ||
| 337 | opttmp->next = optnew; | 381 | opttmp->next = optnew; |
| 338 | } | 382 | } |
| 339 | 383 | ||
| @@ -344,7 +388,8 @@ static int add_option(FILE *f, np_arg_list **optlst) { | |||
| 344 | static char *default_file(void) { | 388 | static char *default_file(void) { |
| 345 | char *ini_file; | 389 | char *ini_file; |
| 346 | 390 | ||
| 347 | if ((ini_file = getenv("MP_CONFIG_FILE")) != NULL || (ini_file = default_file_in_path()) != NULL) { | 391 | if ((ini_file = getenv("MP_CONFIG_FILE")) != NULL || |
| 392 | (ini_file = default_file_in_path()) != NULL) { | ||
| 348 | return ini_file; | 393 | return ini_file; |
| 349 | } | 394 | } |
| 350 | 395 | ||
| @@ -357,19 +402,25 @@ static char *default_file(void) { | |||
| 357 | } | 402 | } |
| 358 | 403 | ||
| 359 | static char *default_file_in_path(void) { | 404 | static char *default_file_in_path(void) { |
| 360 | char *config_path, **file; | 405 | char *config_path; |
| 361 | char *dir, *ini_file, *tokens; | 406 | char **file; |
| 407 | char *dir; | ||
| 408 | char *ini_file; | ||
| 409 | char *tokens; | ||
| 362 | 410 | ||
| 363 | if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL) | 411 | if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL) { |
| 364 | return NULL; | 412 | return NULL; |
| 413 | } | ||
| 365 | /* shall we spit out a warning that NAGIOS_CONFIG_PATH is deprecated? */ | 414 | /* shall we spit out a warning that NAGIOS_CONFIG_PATH is deprecated? */ |
| 366 | 415 | ||
| 367 | if ((tokens = strdup(config_path)) == NULL) | 416 | if ((tokens = strdup(config_path)) == NULL) { |
| 368 | die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); | 417 | die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); |
| 418 | } | ||
| 369 | for (dir = strtok(tokens, ":"); dir != NULL; dir = strtok(NULL, ":")) { | 419 | for (dir = strtok(tokens, ":"); dir != NULL; dir = strtok(NULL, ":")) { |
| 370 | for (file = default_ini_file_names; *file != NULL; file++) { | 420 | for (file = default_ini_file_names; *file != NULL; file++) { |
| 371 | if ((asprintf(&ini_file, "%s/%s", dir, *file)) < 0) | 421 | if ((asprintf(&ini_file, "%s/%s", dir, *file)) < 0) { |
| 372 | die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); | 422 | die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); |
| 423 | } | ||
| 373 | if (access(ini_file, F_OK) == 0) { | 424 | if (access(ini_file, F_OK) == 0) { |
| 374 | free(tokens); | 425 | free(tokens); |
| 375 | return ini_file; | 426 | return ini_file; |
diff --git a/lib/perfdata.c b/lib/perfdata.c new file mode 100644 index 00000000..2930a8bc --- /dev/null +++ b/lib/perfdata.c | |||
| @@ -0,0 +1,649 @@ | |||
| 1 | #include "./perfdata.h" | ||
| 2 | #include "../plugins/common.h" | ||
| 3 | #include "../plugins/utils.h" | ||
| 4 | #include "utils_base.h" | ||
| 5 | |||
| 6 | #include <assert.h> | ||
| 7 | #include <limits.h> | ||
| 8 | #include <stdlib.h> | ||
| 9 | |||
| 10 | char *pd_value_to_string(const mp_perfdata_value pd) { | ||
| 11 | char *result = NULL; | ||
| 12 | |||
| 13 | assert(pd.type != PD_TYPE_NONE); | ||
| 14 | |||
| 15 | switch (pd.type) { | ||
| 16 | case PD_TYPE_INT: | ||
| 17 | asprintf(&result, "%lli", pd.pd_int); | ||
| 18 | break; | ||
| 19 | case PD_TYPE_UINT: | ||
| 20 | asprintf(&result, "%llu", pd.pd_int); | ||
| 21 | break; | ||
| 22 | case PD_TYPE_DOUBLE: | ||
| 23 | asprintf(&result, "%f", pd.pd_double); | ||
| 24 | break; | ||
| 25 | default: | ||
| 26 | // die here | ||
| 27 | die(STATE_UNKNOWN, "Invalid mp_perfdata mode\n"); | ||
| 28 | } | ||
| 29 | |||
| 30 | return result; | ||
| 31 | } | ||
| 32 | |||
| 33 | char *pd_to_string(mp_perfdata pd) { | ||
| 34 | assert(pd.label != NULL); | ||
| 35 | char *result = NULL; | ||
| 36 | |||
| 37 | if (strchr(pd.label, '\'') == NULL) { | ||
| 38 | asprintf(&result, "'%s'=", pd.label); | ||
| 39 | } else { | ||
| 40 | // we have a illegal single quote in the string | ||
| 41 | // replace it silently instead of complaining | ||
| 42 | for (char *ptr = pd.label; *ptr == '\0'; ptr++) { | ||
| 43 | if (*ptr == '\'') { | ||
| 44 | *ptr = '_'; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); | ||
| 50 | |||
| 51 | if (pd.uom != NULL) { | ||
| 52 | asprintf(&result, "%s%s", result, pd.uom); | ||
| 53 | } | ||
| 54 | |||
| 55 | if (pd.warn_present) { | ||
| 56 | asprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn)); | ||
| 57 | } else { | ||
| 58 | asprintf(&result, "%s;", result); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (pd.crit_present) { | ||
| 62 | asprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit)); | ||
| 63 | } else { | ||
| 64 | asprintf(&result, "%s;", result); | ||
| 65 | } | ||
| 66 | if (pd.min_present) { | ||
| 67 | asprintf(&result, "%s;%s", result, pd_value_to_string(pd.min)); | ||
| 68 | } else { | ||
| 69 | asprintf(&result, "%s;", result); | ||
| 70 | } | ||
| 71 | |||
| 72 | if (pd.max_present) { | ||
| 73 | asprintf(&result, "%s;%s", result, pd_value_to_string(pd.max)); | ||
| 74 | } | ||
| 75 | |||
| 76 | /*printf("pd_to_string: %s\n", result); */ | ||
| 77 | |||
| 78 | return result; | ||
| 79 | } | ||
| 80 | |||
| 81 | char *pd_list_to_string(const pd_list pd) { | ||
| 82 | char *result = pd_to_string(pd.data); | ||
| 83 | |||
| 84 | for (pd_list *elem = pd.next; elem != NULL; elem = elem->next) { | ||
| 85 | asprintf(&result, "%s %s", result, pd_to_string(elem->data)); | ||
| 86 | } | ||
| 87 | |||
| 88 | return result; | ||
| 89 | } | ||
| 90 | |||
| 91 | mp_perfdata perfdata_init() { | ||
| 92 | mp_perfdata pd = {}; | ||
| 93 | return pd; | ||
| 94 | } | ||
| 95 | |||
| 96 | pd_list *pd_list_init() { | ||
| 97 | pd_list *tmp = (pd_list *)calloc(1, sizeof(pd_list)); | ||
| 98 | if (tmp == NULL) { | ||
| 99 | die(STATE_UNKNOWN, "calloc failed\n"); | ||
| 100 | } | ||
| 101 | tmp->next = NULL; | ||
| 102 | return tmp; | ||
| 103 | } | ||
| 104 | |||
| 105 | mp_range mp_range_init() { | ||
| 106 | mp_range result = { | ||
| 107 | .alert_on_inside_range = OUTSIDE, | ||
| 108 | .start = {}, | ||
| 109 | .start_infinity = true, | ||
| 110 | .end = {}, | ||
| 111 | .end_infinity = true, | ||
| 112 | }; | ||
| 113 | |||
| 114 | return result; | ||
| 115 | } | ||
| 116 | |||
| 117 | mp_range mp_range_set_start(mp_range input, mp_perfdata_value perf_val) { | ||
| 118 | input.start = perf_val; | ||
| 119 | input.start_infinity = false; | ||
| 120 | return input; | ||
| 121 | } | ||
| 122 | |||
| 123 | mp_range mp_range_set_end(mp_range input, mp_perfdata_value perf_val) { | ||
| 124 | input.end = perf_val; | ||
| 125 | input.end_infinity = false; | ||
| 126 | return input; | ||
| 127 | } | ||
| 128 | |||
| 129 | void pd_list_append(pd_list pdl[1], const mp_perfdata pd) { | ||
| 130 | assert(pdl != NULL); | ||
| 131 | |||
| 132 | if (pdl->data.value.type == PD_TYPE_NONE) { | ||
| 133 | // first entry is still empty | ||
| 134 | pdl->data = pd; | ||
| 135 | } else { | ||
| 136 | // find last element in the list | ||
| 137 | pd_list *curr = pdl; | ||
| 138 | pd_list *next = pdl->next; | ||
| 139 | |||
| 140 | while (next != NULL) { | ||
| 141 | curr = next; | ||
| 142 | next = next->next; | ||
| 143 | } | ||
| 144 | |||
| 145 | if (curr->data.value.type == PD_TYPE_NONE) { | ||
| 146 | // still empty | ||
| 147 | curr->data = pd; | ||
| 148 | } else { | ||
| 149 | // new a new one | ||
| 150 | curr->next = pd_list_init(); | ||
| 151 | curr->next->data = pd; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | void pd_list_free(pd_list pdl[1]) { | ||
| 157 | while (pdl != NULL) { | ||
| 158 | pd_list *old = pdl; | ||
| 159 | pdl = pdl->next; | ||
| 160 | free(old); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | /* | ||
| 165 | * returns -1 if a < b, 0 if a == b, 1 if a > b | ||
| 166 | */ | ||
| 167 | int cmp_perfdata_value(const mp_perfdata_value a, const mp_perfdata_value b) { | ||
| 168 | // Test if types are different | ||
| 169 | if (a.type == b.type) { | ||
| 170 | |||
| 171 | switch (a.type) { | ||
| 172 | case PD_TYPE_UINT: | ||
| 173 | if (a.pd_uint < b.pd_uint) { | ||
| 174 | return -1; | ||
| 175 | } else if (a.pd_uint == b.pd_uint) { | ||
| 176 | return 0; | ||
| 177 | } else { | ||
| 178 | return 1; | ||
| 179 | } | ||
| 180 | break; | ||
| 181 | case PD_TYPE_INT: | ||
| 182 | if (a.pd_int < b.pd_int) { | ||
| 183 | return -1; | ||
| 184 | } else if (a.pd_int == b.pd_int) { | ||
| 185 | return 0; | ||
| 186 | } else { | ||
| 187 | return 1; | ||
| 188 | } | ||
| 189 | break; | ||
| 190 | case PD_TYPE_DOUBLE: | ||
| 191 | if (a.pd_int < b.pd_int) { | ||
| 192 | return -1; | ||
| 193 | } else if (a.pd_int == b.pd_int) { | ||
| 194 | return 0; | ||
| 195 | } else { | ||
| 196 | return 1; | ||
| 197 | } | ||
| 198 | break; | ||
| 199 | default: | ||
| 200 | die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | // Get dirty here | ||
| 205 | long double floating_a = 0; | ||
| 206 | |||
| 207 | switch (a.type) { | ||
| 208 | case PD_TYPE_UINT: | ||
| 209 | floating_a = a.pd_uint; | ||
| 210 | break; | ||
| 211 | case PD_TYPE_INT: | ||
| 212 | floating_a = a.pd_int; | ||
| 213 | break; | ||
| 214 | case PD_TYPE_DOUBLE: | ||
| 215 | floating_a = a.pd_double; | ||
| 216 | break; | ||
| 217 | default: | ||
| 218 | die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__); | ||
| 219 | } | ||
| 220 | |||
| 221 | long double floating_b = 0; | ||
| 222 | switch (b.type) { | ||
| 223 | case PD_TYPE_UINT: | ||
| 224 | floating_b = b.pd_uint; | ||
| 225 | break; | ||
| 226 | case PD_TYPE_INT: | ||
| 227 | floating_b = b.pd_int; | ||
| 228 | break; | ||
| 229 | case PD_TYPE_DOUBLE: | ||
| 230 | floating_b = b.pd_double; | ||
| 231 | break; | ||
| 232 | default: | ||
| 233 | die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__); | ||
| 234 | } | ||
| 235 | |||
| 236 | if (floating_a < floating_b) { | ||
| 237 | return -1; | ||
| 238 | } | ||
| 239 | if (floating_a == floating_b) { | ||
| 240 | return 0; | ||
| 241 | } | ||
| 242 | return 1; | ||
| 243 | } | ||
| 244 | |||
| 245 | char *mp_range_to_string(const mp_range input) { | ||
| 246 | char *result = ""; | ||
| 247 | if (input.alert_on_inside_range == INSIDE) { | ||
| 248 | asprintf(&result, "@"); | ||
| 249 | } | ||
| 250 | |||
| 251 | if (input.start_infinity) { | ||
| 252 | asprintf(&result, "%s~:", result); | ||
| 253 | } else { | ||
| 254 | asprintf(&result, "%s%s:", result, pd_value_to_string(input.start)); | ||
| 255 | } | ||
| 256 | |||
| 257 | if (!input.end_infinity) { | ||
| 258 | asprintf(&result, "%s%s", result, pd_value_to_string(input.end)); | ||
| 259 | } | ||
| 260 | return result; | ||
| 261 | } | ||
| 262 | |||
| 263 | mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { | ||
| 264 | return mp_set_pd_value_double(pd, value); | ||
| 265 | } | ||
| 266 | |||
| 267 | mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { | ||
| 268 | pd.value.pd_double = value; | ||
| 269 | pd.value.type = PD_TYPE_DOUBLE; | ||
| 270 | return pd; | ||
| 271 | } | ||
| 272 | |||
| 273 | mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { | ||
| 274 | return mp_set_pd_value_long_long(pd, (long long)value); | ||
| 275 | } | ||
| 276 | |||
| 277 | mp_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 | |||
| 281 | mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { | ||
| 282 | return mp_set_pd_value_long_long(pd, (long long)value); | ||
| 283 | } | ||
| 284 | |||
| 285 | mp_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 | |||
| 289 | mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { | ||
| 290 | return mp_set_pd_value_long_long(pd, (long long)value); | ||
| 291 | } | ||
| 292 | |||
| 293 | mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) { | ||
| 294 | return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); | ||
| 295 | } | ||
| 296 | |||
| 297 | mp_perfdata mp_set_pd_value_long_long(mp_perfdata pd, long long value) { | ||
| 298 | pd.value.pd_int = value; | ||
| 299 | pd.value.type = PD_TYPE_INT; | ||
| 300 | return pd; | ||
| 301 | } | ||
| 302 | |||
| 303 | mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata pd, unsigned long long value) { | ||
| 304 | pd.value.pd_uint = value; | ||
| 305 | pd.value.type = PD_TYPE_UINT; | ||
| 306 | return pd; | ||
| 307 | } | ||
| 308 | |||
| 309 | mp_perfdata_value mp_create_pd_value_double(double value) { | ||
| 310 | mp_perfdata_value res = {0}; | ||
| 311 | res.type = PD_TYPE_DOUBLE; | ||
| 312 | res.pd_double = value; | ||
| 313 | return res; | ||
| 314 | } | ||
| 315 | |||
| 316 | mp_perfdata_value mp_create_pd_value_float(float value) { | ||
| 317 | return mp_create_pd_value_double((double)value); | ||
| 318 | } | ||
| 319 | |||
| 320 | mp_perfdata_value mp_create_pd_value_char(char value) { | ||
| 321 | return mp_create_pd_value_long_long((long long)value); | ||
| 322 | } | ||
| 323 | |||
| 324 | mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { | ||
| 325 | return mp_create_pd_value_u_long_long((unsigned long long)value); | ||
| 326 | } | ||
| 327 | |||
| 328 | mp_perfdata_value mp_create_pd_value_int(int value) { | ||
| 329 | return mp_create_pd_value_long_long((long long)value); | ||
| 330 | } | ||
| 331 | |||
| 332 | mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { | ||
| 333 | return mp_create_pd_value_u_long_long((unsigned long long)value); | ||
| 334 | } | ||
| 335 | |||
| 336 | mp_perfdata_value mp_create_pd_value_long(long value) { | ||
| 337 | return mp_create_pd_value_long_long((long long)value); | ||
| 338 | } | ||
| 339 | |||
| 340 | mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { | ||
| 341 | return mp_create_pd_value_u_long_long((unsigned long long)value); | ||
| 342 | } | ||
| 343 | |||
| 344 | mp_perfdata_value mp_create_pd_value_long_long(long long value) { | ||
| 345 | mp_perfdata_value res = {0}; | ||
| 346 | res.type = PD_TYPE_INT; | ||
| 347 | res.pd_int = value; | ||
| 348 | return res; | ||
| 349 | } | ||
| 350 | |||
| 351 | mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long value) { | ||
| 352 | mp_perfdata_value res = {0}; | ||
| 353 | res.type = PD_TYPE_UINT; | ||
| 354 | res.pd_uint = value; | ||
| 355 | return res; | ||
| 356 | } | ||
| 357 | |||
| 358 | char *fmt_range(range foo) { return foo.text; } | ||
| 359 | |||
| 360 | typedef struct integer_parser_wrapper { | ||
| 361 | int error; | ||
| 362 | mp_perfdata_value value; | ||
| 363 | } integer_parser_wrapper; | ||
| 364 | |||
| 365 | typedef struct double_parser_wrapper { | ||
| 366 | int error; | ||
| 367 | mp_perfdata_value value; | ||
| 368 | } double_parser_wrapper; | ||
| 369 | |||
| 370 | typedef struct perfdata_value_parser_wrapper { | ||
| 371 | int error; | ||
| 372 | mp_perfdata_value value; | ||
| 373 | } perfdata_value_parser_wrapper; | ||
| 374 | |||
| 375 | double_parser_wrapper parse_double(const char *input); | ||
| 376 | integer_parser_wrapper parse_integer(const char *input); | ||
| 377 | perfdata_value_parser_wrapper parse_pd_value(const char *input); | ||
| 378 | |||
| 379 | mp_range_parsed mp_parse_range_string(const char *input) { | ||
| 380 | if (input == NULL) { | ||
| 381 | mp_range_parsed result = { | ||
| 382 | .error = MP_RANGE_PARSING_FAILURE, | ||
| 383 | }; | ||
| 384 | return result; | ||
| 385 | } | ||
| 386 | |||
| 387 | if (strlen(input) == 0) { | ||
| 388 | mp_range_parsed result = { | ||
| 389 | .error = MP_RANGE_PARSING_FAILURE, | ||
| 390 | }; | ||
| 391 | return result; | ||
| 392 | } | ||
| 393 | |||
| 394 | mp_range_parsed result = { | ||
| 395 | .range = mp_range_init(), | ||
| 396 | .error = MP_PARSING_SUCCES, | ||
| 397 | }; | ||
| 398 | |||
| 399 | if (input[0] == '@') { | ||
| 400 | // found an '@' at beginning, so invert the range logic | ||
| 401 | result.range.alert_on_inside_range = INSIDE; | ||
| 402 | |||
| 403 | // advance the pointer one symbol | ||
| 404 | input++; | ||
| 405 | } | ||
| 406 | |||
| 407 | char *working_copy = strdup(input); | ||
| 408 | if (working_copy == NULL) { | ||
| 409 | // strdup error, probably | ||
| 410 | mp_range_parsed result = { | ||
| 411 | .error = MP_RANGE_PARSING_FAILURE, | ||
| 412 | }; | ||
| 413 | return result; | ||
| 414 | } | ||
| 415 | input = working_copy; | ||
| 416 | |||
| 417 | char *separator = index(working_copy, ':'); | ||
| 418 | if (separator != NULL) { | ||
| 419 | // Found a separator | ||
| 420 | // set the separator to 0, so we have two different strings | ||
| 421 | *separator = '\0'; | ||
| 422 | |||
| 423 | if (input[0] == '~') { | ||
| 424 | // the beginning starts with '~', so it might be infinity | ||
| 425 | if (&input[1] != separator) { | ||
| 426 | // the next symbol after '~' is not the separator! | ||
| 427 | // so input is probably wrong | ||
| 428 | result.error = MP_RANGE_PARSING_FAILURE; | ||
| 429 | free(working_copy); | ||
| 430 | return result; | ||
| 431 | } | ||
| 432 | |||
| 433 | result.range.start_infinity = true; | ||
| 434 | } else { | ||
| 435 | // No '~' at the beginning, so this should be a number | ||
| 436 | result.range.start_infinity = false; | ||
| 437 | perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input); | ||
| 438 | |||
| 439 | if (parsed_pd.error != MP_PARSING_SUCCES) { | ||
| 440 | result.error = parsed_pd.error; | ||
| 441 | free(working_copy); | ||
| 442 | return result; | ||
| 443 | } | ||
| 444 | |||
| 445 | result.range.start = parsed_pd.value; | ||
| 446 | result.range.start_infinity = false; | ||
| 447 | } | ||
| 448 | // got the first part now | ||
| 449 | // advance the pointer | ||
| 450 | input = separator + 1; | ||
| 451 | } | ||
| 452 | |||
| 453 | // End part or no separator | ||
| 454 | if (input[0] == '\0') { | ||
| 455 | // the end is infinite | ||
| 456 | result.range.end_infinity = true; | ||
| 457 | } else { | ||
| 458 | perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input); | ||
| 459 | |||
| 460 | if (parsed_pd.error != MP_PARSING_SUCCES) { | ||
| 461 | result.error = parsed_pd.error; | ||
| 462 | return result; | ||
| 463 | } | ||
| 464 | result.range.end = parsed_pd.value; | ||
| 465 | result.range.end_infinity = false; | ||
| 466 | } | ||
| 467 | free(working_copy); | ||
| 468 | return result; | ||
| 469 | } | ||
| 470 | |||
| 471 | double_parser_wrapper parse_double(const char *input) { | ||
| 472 | double_parser_wrapper result = { | ||
| 473 | .error = MP_PARSING_SUCCES, | ||
| 474 | }; | ||
| 475 | |||
| 476 | if (input == NULL) { | ||
| 477 | result.error = MP_PARSING_FAILURE; | ||
| 478 | return result; | ||
| 479 | } | ||
| 480 | |||
| 481 | char *endptr = NULL; | ||
| 482 | errno = 0; | ||
| 483 | double tmp = strtod(input, &endptr); | ||
| 484 | |||
| 485 | if (input == endptr) { | ||
| 486 | // man 3 strtod says, no conversion performed | ||
| 487 | result.error = MP_PARSING_FAILURE; | ||
| 488 | return result; | ||
| 489 | } | ||
| 490 | |||
| 491 | if (errno) { | ||
| 492 | // some other error | ||
| 493 | // TODO maybe differentiate a little bit | ||
| 494 | result.error = MP_PARSING_FAILURE; | ||
| 495 | return result; | ||
| 496 | } | ||
| 497 | |||
| 498 | result.value = mp_create_pd_value(tmp); | ||
| 499 | return result; | ||
| 500 | } | ||
| 501 | |||
| 502 | integer_parser_wrapper parse_integer(const char *input) { | ||
| 503 | integer_parser_wrapper result = { | ||
| 504 | .error = MP_PARSING_SUCCES, | ||
| 505 | }; | ||
| 506 | |||
| 507 | if (input == NULL) { | ||
| 508 | result.error = MP_PARSING_FAILURE; | ||
| 509 | return result; | ||
| 510 | } | ||
| 511 | |||
| 512 | char *endptr = NULL; | ||
| 513 | errno = 0; | ||
| 514 | long long tmp = strtoll(input, &endptr, 0); | ||
| 515 | |||
| 516 | // validating *sigh* | ||
| 517 | if (*endptr != '\0') { | ||
| 518 | // something went wrong in strtoll | ||
| 519 | if (tmp == LLONG_MIN) { | ||
| 520 | // underflow | ||
| 521 | result.error = MP_RANGE_PARSING_UNDERFLOW; | ||
| 522 | return result; | ||
| 523 | } | ||
| 524 | |||
| 525 | if (tmp == LLONG_MAX) { | ||
| 526 | // overflow | ||
| 527 | result.error = MP_RANGE_PARSING_OVERFLOW; | ||
| 528 | return result; | ||
| 529 | } | ||
| 530 | |||
| 531 | // still wrong, but not sure why, probably invalid characters | ||
| 532 | if (errno == EINVAL) { | ||
| 533 | result.error = MP_RANGE_PARSING_INVALID_CHAR; | ||
| 534 | return result; | ||
| 535 | } | ||
| 536 | |||
| 537 | // some other error, do catch all here | ||
| 538 | result.error = MP_RANGE_PARSING_FAILURE; | ||
| 539 | return result; | ||
| 540 | } | ||
| 541 | |||
| 542 | // no error, should be fine | ||
| 543 | result.value = mp_create_pd_value(tmp); | ||
| 544 | return result; | ||
| 545 | } | ||
| 546 | |||
| 547 | perfdata_value_parser_wrapper parse_pd_value(const char *input) { | ||
| 548 | // try integer first | ||
| 549 | integer_parser_wrapper tmp_int = parse_integer(input); | ||
| 550 | |||
| 551 | if (tmp_int.error == MP_PARSING_SUCCES) { | ||
| 552 | perfdata_value_parser_wrapper result = { | ||
| 553 | .error = tmp_int.error, | ||
| 554 | .value = tmp_int.value, | ||
| 555 | }; | ||
| 556 | return result; | ||
| 557 | } | ||
| 558 | |||
| 559 | double_parser_wrapper tmp_double = parse_double(input); | ||
| 560 | perfdata_value_parser_wrapper result = {}; | ||
| 561 | if (tmp_double.error == MP_PARSING_SUCCES) { | ||
| 562 | result.error = tmp_double.error; | ||
| 563 | result.value = tmp_double.value; | ||
| 564 | } else { | ||
| 565 | result.error = tmp_double.error; | ||
| 566 | } | ||
| 567 | return result; | ||
| 568 | } | ||
| 569 | |||
| 570 | mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value) { | ||
| 571 | perfdata.max = value; | ||
| 572 | perfdata.max_present = true; | ||
| 573 | return perfdata; | ||
| 574 | } | ||
| 575 | |||
| 576 | mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value) { | ||
| 577 | perfdata.min = value; | ||
| 578 | perfdata.min_present = true; | ||
| 579 | return perfdata; | ||
| 580 | } | ||
| 581 | |||
| 582 | double mp_get_pd_value(mp_perfdata_value value) { | ||
| 583 | assert(value.type != PD_TYPE_NONE); | ||
| 584 | switch (value.type) { | ||
| 585 | case PD_TYPE_DOUBLE: | ||
| 586 | return value.pd_double; | ||
| 587 | case PD_TYPE_INT: | ||
| 588 | return (double)value.pd_int; | ||
| 589 | case PD_TYPE_UINT: | ||
| 590 | return (double)value.pd_uint; | ||
| 591 | default: | ||
| 592 | return 0; // just to make the compiler happy | ||
| 593 | } | ||
| 594 | } | ||
| 595 | |||
| 596 | mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right) { | ||
| 597 | if (left.type == right.type) { | ||
| 598 | switch (left.type) { | ||
| 599 | case PD_TYPE_DOUBLE: | ||
| 600 | left.pd_double *= right.pd_double; | ||
| 601 | return left; | ||
| 602 | case PD_TYPE_INT: | ||
| 603 | left.pd_int *= right.pd_int; | ||
| 604 | return left; | ||
| 605 | case PD_TYPE_UINT: | ||
| 606 | left.pd_uint *= right.pd_uint; | ||
| 607 | return left; | ||
| 608 | default: | ||
| 609 | // what to here? | ||
| 610 | return left; | ||
| 611 | } | ||
| 612 | } | ||
| 613 | |||
| 614 | // Different types, oh boy, just do the lazy thing for now and switch to double | ||
| 615 | switch (left.type) { | ||
| 616 | case PD_TYPE_INT: | ||
| 617 | left.pd_double = (double)left.pd_int; | ||
| 618 | left.type = PD_TYPE_DOUBLE; | ||
| 619 | break; | ||
| 620 | case PD_TYPE_UINT: | ||
| 621 | left.pd_double = (double)left.pd_uint; | ||
| 622 | left.type = PD_TYPE_DOUBLE; | ||
| 623 | break; | ||
| 624 | } | ||
| 625 | |||
| 626 | switch (right.type) { | ||
| 627 | case PD_TYPE_INT: | ||
| 628 | right.pd_double = (double)right.pd_int; | ||
| 629 | right.type = PD_TYPE_DOUBLE; | ||
| 630 | break; | ||
| 631 | case PD_TYPE_UINT: | ||
| 632 | right.pd_double = (double)right.pd_uint; | ||
| 633 | right.type = PD_TYPE_DOUBLE; | ||
| 634 | break; | ||
| 635 | } | ||
| 636 | |||
| 637 | left.pd_double *= right.pd_double; | ||
| 638 | return left; | ||
| 639 | } | ||
| 640 | |||
| 641 | mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor) { | ||
| 642 | if (!range.end_infinity) { | ||
| 643 | range.end = mp_pd_value_multiply(range.end, factor); | ||
| 644 | } | ||
| 645 | if (!range.start_infinity) { | ||
| 646 | range.start = mp_pd_value_multiply(range.start, factor); | ||
| 647 | } | ||
| 648 | return range; | ||
| 649 | } | ||
diff --git a/lib/perfdata.h b/lib/perfdata.h new file mode 100644 index 00000000..e51ef5fd --- /dev/null +++ b/lib/perfdata.h | |||
| @@ -0,0 +1,219 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../config.h" | ||
| 4 | |||
| 5 | #include <inttypes.h> | ||
| 6 | #include <stdbool.h> | ||
| 7 | |||
| 8 | // Enum for the specific type of a perfdata_value | ||
| 9 | typedef enum pd_value_type { | ||
| 10 | PD_TYPE_NONE = 0, | ||
| 11 | PD_TYPE_INT, | ||
| 12 | PD_TYPE_UINT, | ||
| 13 | PD_TYPE_DOUBLE | ||
| 14 | } pd_value_type; | ||
| 15 | |||
| 16 | typedef struct { | ||
| 17 | enum pd_value_type type; | ||
| 18 | union { | ||
| 19 | long long pd_int; | ||
| 20 | unsigned long long pd_uint; | ||
| 21 | double pd_double; | ||
| 22 | }; | ||
| 23 | } mp_perfdata_value; | ||
| 24 | |||
| 25 | #define MP_OUTSIDE false | ||
| 26 | #define MP_INSIDE true | ||
| 27 | |||
| 28 | /* | ||
| 29 | * New range type with generic numerical values | ||
| 30 | */ | ||
| 31 | typedef struct { | ||
| 32 | mp_perfdata_value start; | ||
| 33 | bool start_infinity; /* false (default) or true */ | ||
| 34 | |||
| 35 | mp_perfdata_value end; | ||
| 36 | bool end_infinity; | ||
| 37 | |||
| 38 | bool alert_on_inside_range; /* OUTSIDE (default) or INSIDE */ | ||
| 39 | } mp_range; | ||
| 40 | |||
| 41 | /* | ||
| 42 | * Old range type with floating point values | ||
| 43 | */ | ||
| 44 | typedef struct { | ||
| 45 | double start; | ||
| 46 | bool start_infinity; | ||
| 47 | double end; | ||
| 48 | bool end_infinity; | ||
| 49 | int alert_on; /* OUTSIDE (default) or INSIDE */ | ||
| 50 | char *text; /* original unparsed text input */ | ||
| 51 | } range; | ||
| 52 | |||
| 53 | /* | ||
| 54 | * Perfdata type for storing perfdata output | ||
| 55 | */ | ||
| 56 | typedef struct { | ||
| 57 | char *label; | ||
| 58 | char *uom; | ||
| 59 | mp_perfdata_value value; | ||
| 60 | |||
| 61 | bool warn_present; | ||
| 62 | mp_range warn; | ||
| 63 | |||
| 64 | bool crit_present; | ||
| 65 | mp_range crit; | ||
| 66 | |||
| 67 | bool min_present; | ||
| 68 | mp_perfdata_value min; | ||
| 69 | |||
| 70 | bool max_present; | ||
| 71 | mp_perfdata_value max; | ||
| 72 | } mp_perfdata; | ||
| 73 | |||
| 74 | /* | ||
| 75 | * List of mp_perfdata values | ||
| 76 | */ | ||
| 77 | typedef struct pd_list_struct { | ||
| 78 | mp_perfdata data; | ||
| 79 | struct pd_list_struct *next; | ||
| 80 | } pd_list; | ||
| 81 | |||
| 82 | /* | ||
| 83 | * ============ | ||
| 84 | * Initializers | ||
| 85 | * ============ | ||
| 86 | */ | ||
| 87 | /* | ||
| 88 | * Initialize mp_perfdata value. Always use this to generate a new one | ||
| 89 | */ | ||
| 90 | mp_perfdata perfdata_init(void); | ||
| 91 | |||
| 92 | /* | ||
| 93 | * Initialize pd_list value. Always use this to generate a new one | ||
| 94 | */ | ||
| 95 | pd_list *pd_list_init(void); | ||
| 96 | |||
| 97 | /* | ||
| 98 | * Initialize a new mp_range value, with unset values (start and end are infinite | ||
| 99 | */ | ||
| 100 | mp_range mp_range_init(void); | ||
| 101 | |||
| 102 | /* | ||
| 103 | * Worker functions | ||
| 104 | */ | ||
| 105 | |||
| 106 | mp_range mp_range_set_start(mp_range, mp_perfdata_value); | ||
| 107 | mp_range mp_range_set_end(mp_range, mp_perfdata_value); | ||
| 108 | |||
| 109 | /* | ||
| 110 | * Parsing a range from a string | ||
| 111 | */ | ||
| 112 | |||
| 113 | typedef enum { | ||
| 114 | MP_PARSING_SUCCES = 0, | ||
| 115 | MP_PARSING_FAILURE, | ||
| 116 | MP_RANGE_PARSING_FAILURE, | ||
| 117 | MP_RANGE_PARSING_UNDERFLOW, | ||
| 118 | MP_RANGE_PARSING_OVERFLOW, | ||
| 119 | MP_RANGE_PARSING_INVALID_CHAR, | ||
| 120 | } mp_range_parser_error; | ||
| 121 | |||
| 122 | typedef struct mp_range_parsed { | ||
| 123 | mp_range_parser_error error; | ||
| 124 | mp_range range; | ||
| 125 | } mp_range_parsed; | ||
| 126 | |||
| 127 | mp_range_parsed mp_parse_range_string(const char * /*input*/); | ||
| 128 | |||
| 129 | /* | ||
| 130 | * Appends a mp_perfdata value to a pd_list | ||
| 131 | */ | ||
| 132 | void pd_list_append(pd_list[1], mp_perfdata); | ||
| 133 | |||
| 134 | #define mp_set_pd_value(P, V) \ | ||
| 135 | _Generic((V), \ | ||
| 136 | float: mp_set_pd_value_float, \ | ||
| 137 | double: mp_set_pd_value_double, \ | ||
| 138 | int: mp_set_pd_value_int, \ | ||
| 139 | unsigned int: mp_set_pd_value_u_int, \ | ||
| 140 | long: mp_set_pd_value_long, \ | ||
| 141 | unsigned long: mp_set_pd_value_u_long, \ | ||
| 142 | long long: mp_set_pd_value_long_long, \ | ||
| 143 | unsigned long long: mp_set_pd_value_u_long_long)(P, V) | ||
| 144 | |||
| 145 | mp_perfdata mp_set_pd_value_float(mp_perfdata, float); | ||
| 146 | mp_perfdata mp_set_pd_value_double(mp_perfdata, double); | ||
| 147 | mp_perfdata mp_set_pd_value_int(mp_perfdata, int); | ||
| 148 | mp_perfdata mp_set_pd_value_u_int(mp_perfdata, unsigned int); | ||
| 149 | mp_perfdata mp_set_pd_value_long(mp_perfdata, long); | ||
| 150 | mp_perfdata mp_set_pd_value_u_long(mp_perfdata, unsigned long); | ||
| 151 | mp_perfdata mp_set_pd_value_long_long(mp_perfdata, long long); | ||
| 152 | mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long); | ||
| 153 | |||
| 154 | #define mp_create_pd_value(V) \ | ||
| 155 | _Generic((V), \ | ||
| 156 | float: mp_create_pd_value_float, \ | ||
| 157 | double: mp_create_pd_value_double, \ | ||
| 158 | char: mp_create_pd_value_char, \ | ||
| 159 | unsigned char: mp_create_pd_value_u_char, \ | ||
| 160 | int: mp_create_pd_value_int, \ | ||
| 161 | unsigned int: mp_create_pd_value_u_int, \ | ||
| 162 | long: mp_create_pd_value_long, \ | ||
| 163 | unsigned long: mp_create_pd_value_u_long, \ | ||
| 164 | long long: mp_create_pd_value_long_long, \ | ||
| 165 | unsigned long long: mp_create_pd_value_u_long_long)(V) | ||
| 166 | |||
| 167 | mp_perfdata_value mp_create_pd_value_float(float); | ||
| 168 | mp_perfdata_value mp_create_pd_value_double(double); | ||
| 169 | mp_perfdata_value mp_create_pd_value_char(char); | ||
| 170 | mp_perfdata_value mp_create_pd_value_u_char(unsigned char); | ||
| 171 | mp_perfdata_value mp_create_pd_value_int(int); | ||
| 172 | mp_perfdata_value mp_create_pd_value_u_int(unsigned int); | ||
| 173 | mp_perfdata_value mp_create_pd_value_long(long); | ||
| 174 | mp_perfdata_value mp_create_pd_value_u_long(unsigned long); | ||
| 175 | mp_perfdata_value mp_create_pd_value_long_long(long long); | ||
| 176 | mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long); | ||
| 177 | |||
| 178 | mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value); | ||
| 179 | mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value); | ||
| 180 | |||
| 181 | double mp_get_pd_value(mp_perfdata_value value); | ||
| 182 | |||
| 183 | /* | ||
| 184 | * Free the memory used by a pd_list | ||
| 185 | */ | ||
| 186 | void pd_list_free(pd_list[1]); | ||
| 187 | |||
| 188 | int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value); | ||
| 189 | |||
| 190 | // ================ | ||
| 191 | // Helper functions | ||
| 192 | // ================ | ||
| 193 | |||
| 194 | mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right); | ||
| 195 | mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor); | ||
| 196 | |||
| 197 | // ================= | ||
| 198 | // String formatters | ||
| 199 | // ================= | ||
| 200 | /* | ||
| 201 | * Generate string from mp_perfdata value | ||
| 202 | */ | ||
| 203 | char *pd_to_string(mp_perfdata); | ||
| 204 | |||
| 205 | /* | ||
| 206 | * Generate string from perfdata_value value | ||
| 207 | */ | ||
| 208 | char *pd_value_to_string(mp_perfdata_value); | ||
| 209 | |||
| 210 | /* | ||
| 211 | * Generate string from pd_list value for the final output | ||
| 212 | */ | ||
| 213 | char *pd_list_to_string(pd_list); | ||
| 214 | |||
| 215 | /* | ||
| 216 | * Generate string from a mp_range value | ||
| 217 | */ | ||
| 218 | char *mp_range_to_string(mp_range); | ||
| 219 | char *fmt_range(range); | ||
diff --git a/lib/states.h b/lib/states.h new file mode 100644 index 00000000..4a170caa --- /dev/null +++ b/lib/states.h | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | #ifndef _MP_STATES_ | ||
| 2 | #define _MP_STATES_ | ||
| 3 | |||
| 4 | #include "../config.h" | ||
| 5 | #include <sys/param.h> | ||
| 6 | |||
| 7 | typedef enum state_enum { | ||
| 8 | STATE_OK, | ||
| 9 | STATE_WARNING, | ||
| 10 | STATE_CRITICAL, | ||
| 11 | STATE_UNKNOWN, | ||
| 12 | STATE_DEPENDENT | ||
| 13 | } mp_state_enum; | ||
| 14 | |||
| 15 | /* ************************************************************************** | ||
| 16 | * max_state(STATE_x, STATE_y) | ||
| 17 | * compares STATE_x to STATE_y and returns result based on the following | ||
| 18 | * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL | ||
| 19 | * | ||
| 20 | * Note that numerically the above does not hold | ||
| 21 | ****************************************************************************/ | ||
| 22 | |||
| 23 | static inline mp_state_enum max_state(mp_state_enum a, mp_state_enum b) { | ||
| 24 | if (a == STATE_CRITICAL || b == STATE_CRITICAL) { | ||
| 25 | return STATE_CRITICAL; | ||
| 26 | } | ||
| 27 | if (a == STATE_WARNING || b == STATE_WARNING) { | ||
| 28 | return STATE_WARNING; | ||
| 29 | } | ||
| 30 | if (a == STATE_OK || b == STATE_OK) { | ||
| 31 | return STATE_OK; | ||
| 32 | } | ||
| 33 | if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) { | ||
| 34 | return STATE_UNKNOWN; | ||
| 35 | } | ||
| 36 | if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) { | ||
| 37 | return STATE_DEPENDENT; | ||
| 38 | } | ||
| 39 | return MAX(a, b); | ||
| 40 | } | ||
| 41 | |||
| 42 | /* ************************************************************************** | ||
| 43 | * max_state_alt(STATE_x, STATE_y) | ||
| 44 | * compares STATE_x to STATE_y and returns result based on the following | ||
| 45 | * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL | ||
| 46 | * | ||
| 47 | * The main difference between max_state_alt and max_state it that it doesn't | ||
| 48 | * allow setting a default to UNKNOWN. It will instead prioritixe any valid | ||
| 49 | * non-OK state. | ||
| 50 | ****************************************************************************/ | ||
| 51 | |||
| 52 | static inline mp_state_enum max_state_alt(mp_state_enum a, mp_state_enum b) { | ||
| 53 | if (a == STATE_CRITICAL || b == STATE_CRITICAL) { | ||
| 54 | return STATE_CRITICAL; | ||
| 55 | } | ||
| 56 | if (a == STATE_WARNING || b == STATE_WARNING) { | ||
| 57 | return STATE_WARNING; | ||
| 58 | } | ||
| 59 | if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) { | ||
| 60 | return STATE_UNKNOWN; | ||
| 61 | } | ||
| 62 | if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) { | ||
| 63 | return STATE_DEPENDENT; | ||
| 64 | } | ||
| 65 | if (a == STATE_OK || b == STATE_OK) { | ||
| 66 | return STATE_OK; | ||
| 67 | } | ||
| 68 | return MAX(a, b); | ||
| 69 | } | ||
| 70 | |||
| 71 | #endif | ||
diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am index 31d79df6..7798a72e 100644 --- a/lib/tests/Makefile.am +++ b/lib/tests/Makefile.am | |||
| @@ -8,9 +8,9 @@ check_PROGRAMS = @EXTRA_TEST@ | |||
| 8 | AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ | 8 | AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ |
| 9 | -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins | 9 | -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins |
| 10 | 10 | ||
| 11 | EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 | 11 | EXTRA_PROGRAMS = test_utils test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output |
| 12 | 12 | ||
| 13 | np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t | 13 | np_test_scripts = test_base64.t test_cmd.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t |
| 14 | np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini | 14 | np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini |
| 15 | EXTRA_DIST = $(np_test_scripts) $(np_test_files) var | 15 | EXTRA_DIST = $(np_test_scripts) $(np_test_files) var |
| 16 | 16 | ||
| @@ -29,7 +29,7 @@ AM_CFLAGS = -g -I$(top_srcdir)/lib -I$(top_srcdir)/gl $(tap_cflags) | |||
| 29 | AM_LDFLAGS = $(tap_ldflags) -ltap | 29 | AM_LDFLAGS = $(tap_ldflags) -ltap |
| 30 | LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO) | 30 | LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO) |
| 31 | 31 | ||
| 32 | SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c | 32 | SOURCES = test_utils.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c |
| 33 | 33 | ||
| 34 | test: ${noinst_PROGRAMS} | 34 | test: ${noinst_PROGRAMS} |
| 35 | perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS) | 35 | perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS) |
diff --git a/lib/tests/test_base64.c b/lib/tests/test_base64.c index 94cb5aa9..798244da 100644 --- a/lib/tests/test_base64.c +++ b/lib/tests/test_base64.c | |||
| @@ -180,117 +180,168 @@ int main(int argc, char **argv) { | |||
| 180 | #endif | 180 | #endif |
| 181 | 181 | ||
| 182 | char random[1024] = { | 182 | char random[1024] = { |
| 183 | 0x0b, 0x30, 0x44, 0x62, 0x7c, 0x22, 0x1f, 0x0d, 0x05, 0x67, 0x2c, 0x2a, 0x39, 0x21, 0x46, 0x08, 0x50, 0x66, 0x34, 0x37, 0x0b, 0x45, | 183 | 0x0b, 0x30, 0x44, 0x62, 0x7c, 0x22, 0x1f, 0x0d, 0x05, 0x67, 0x2c, 0x2a, 0x39, 0x21, 0x46, |
| 184 | 0x4b, 0x38, 0x32, 0x06, 0x7a, 0x3e, 0x7f, 0x0c, 0x40, 0x18, 0x6b, 0x2d, 0x60, 0x4c, 0x60, 0x0c, 0x23, 0x43, 0x3b, 0x3e, 0x1b, 0x16, | 184 | 0x08, 0x50, 0x66, 0x34, 0x37, 0x0b, 0x45, 0x4b, 0x38, 0x32, 0x06, 0x7a, 0x3e, 0x7f, 0x0c, |
| 185 | 0x04, 0x46, 0x58, 0x3f, 0x40, 0x6a, 0x11, 0x05, 0x63, 0x71, 0x14, 0x35, 0x47, 0x79, 0x13, 0x6f, 0x6b, 0x27, 0x18, 0x5b, 0x48, 0x27, | 185 | 0x40, 0x18, 0x6b, 0x2d, 0x60, 0x4c, 0x60, 0x0c, 0x23, 0x43, 0x3b, 0x3e, 0x1b, 0x16, 0x04, |
| 186 | 0x3e, 0x6f, 0x15, 0x33, 0x4f, 0x3e, 0x5e, 0x51, 0x73, 0x68, 0x25, 0x0f, 0x06, 0x5b, 0x7c, 0x72, 0x75, 0x3e, 0x3f, 0x1b, 0x5c, 0x6d, | 186 | 0x46, 0x58, 0x3f, 0x40, 0x6a, 0x11, 0x05, 0x63, 0x71, 0x14, 0x35, 0x47, 0x79, 0x13, 0x6f, |
| 187 | 0x6a, 0x39, 0x7c, 0x63, 0x63, 0x60, 0x6c, 0x7a, 0x33, 0x76, 0x52, 0x13, 0x25, 0x33, 0x7d, 0x65, 0x23, 0x27, 0x11, 0x06, 0x06, 0x47, | 187 | 0x6b, 0x27, 0x18, 0x5b, 0x48, 0x27, 0x3e, 0x6f, 0x15, 0x33, 0x4f, 0x3e, 0x5e, 0x51, 0x73, |
| 188 | 0x71, 0x1e, 0x14, 0x74, 0x63, 0x70, 0x2d, 0x15, 0x27, 0x18, 0x51, 0x06, 0x05, 0x33, 0x11, 0x2c, 0x6b, 0x00, 0x2d, 0x77, 0x20, 0x48, | 188 | 0x68, 0x25, 0x0f, 0x06, 0x5b, 0x7c, 0x72, 0x75, 0x3e, 0x3f, 0x1b, 0x5c, 0x6d, 0x6a, 0x39, |
| 189 | 0x0d, 0x73, 0x51, 0x45, 0x25, 0x7f, 0x7f, 0x35, 0x26, 0x2e, 0x26, 0x53, 0x24, 0x68, 0x1e, 0x0e, 0x58, 0x3a, 0x59, 0x50, 0x56, 0x37, | 189 | 0x7c, 0x63, 0x63, 0x60, 0x6c, 0x7a, 0x33, 0x76, 0x52, 0x13, 0x25, 0x33, 0x7d, 0x65, 0x23, |
| 190 | 0x5f, 0x66, 0x01, 0x4c, 0x5a, 0x64, 0x32, 0x50, 0x7b, 0x6a, 0x20, 0x72, 0x2b, 0x1d, 0x7e, 0x43, 0x7b, 0x61, 0x42, 0x0b, 0x61, 0x73, | 190 | 0x27, 0x11, 0x06, 0x06, 0x47, 0x71, 0x1e, 0x14, 0x74, 0x63, 0x70, 0x2d, 0x15, 0x27, 0x18, |
| 191 | 0x24, 0x79, 0x3a, 0x6b, 0x4a, 0x79, 0x6e, 0x09, 0x0f, 0x27, 0x2d, 0x0c, 0x5e, 0x32, 0x4b, 0x0d, 0x79, 0x46, 0x39, 0x21, 0x0a, 0x26, | 191 | 0x51, 0x06, 0x05, 0x33, 0x11, 0x2c, 0x6b, 0x00, 0x2d, 0x77, 0x20, 0x48, 0x0d, 0x73, 0x51, |
| 192 | 0x5f, 0x3a, 0x00, 0x26, 0x3f, 0x13, 0x2e, 0x7e, 0x50, 0x2b, 0x67, 0x46, 0x72, 0x3f, 0x3b, 0x01, 0x46, 0x1b, 0x0b, 0x35, 0x49, 0x39, | 192 | 0x45, 0x25, 0x7f, 0x7f, 0x35, 0x26, 0x2e, 0x26, 0x53, 0x24, 0x68, 0x1e, 0x0e, 0x58, 0x3a, |
| 193 | 0x19, 0x70, 0x3d, 0x02, 0x41, 0x0e, 0x38, 0x05, 0x76, 0x65, 0x4f, 0x31, 0x6c, 0x5e, 0x17, 0x04, 0x15, 0x36, 0x26, 0x64, 0x34, 0x14, | 193 | 0x59, 0x50, 0x56, 0x37, 0x5f, 0x66, 0x01, 0x4c, 0x5a, 0x64, 0x32, 0x50, 0x7b, 0x6a, 0x20, |
| 194 | 0x17, 0x7c, 0x0e, 0x0b, 0x5b, 0x55, 0x53, 0x6b, 0x00, 0x42, 0x41, 0x4f, 0x02, 0x5c, 0x13, 0x0a, 0x2c, 0x2c, 0x3e, 0x10, 0x14, 0x33, | 194 | 0x72, 0x2b, 0x1d, 0x7e, 0x43, 0x7b, 0x61, 0x42, 0x0b, 0x61, 0x73, 0x24, 0x79, 0x3a, 0x6b, |
| 195 | 0x45, 0x7c, 0x7a, 0x5a, 0x31, 0x61, 0x39, 0x08, 0x22, 0x6a, 0x1e, 0x0f, 0x6f, 0x1b, 0x6c, 0x13, 0x5e, 0x79, 0x20, 0x79, 0x50, 0x62, | 195 | 0x4a, 0x79, 0x6e, 0x09, 0x0f, 0x27, 0x2d, 0x0c, 0x5e, 0x32, 0x4b, 0x0d, 0x79, 0x46, 0x39, |
| 196 | 0x06, 0x2c, 0x76, 0x17, 0x04, 0x2b, 0x2a, 0x75, 0x1f, 0x0c, 0x37, 0x4e, 0x0f, 0x7b, 0x2d, 0x34, 0x75, 0x60, 0x31, 0x74, 0x2e, 0x0a, | 196 | 0x21, 0x0a, 0x26, 0x5f, 0x3a, 0x00, 0x26, 0x3f, 0x13, 0x2e, 0x7e, 0x50, 0x2b, 0x67, 0x46, |
| 197 | 0x4a, 0x11, 0x6c, 0x49, 0x25, 0x01, 0x3a, 0x3d, 0x22, 0x1e, 0x6d, 0x18, 0x51, 0x78, 0x2d, 0x62, 0x31, 0x4c, 0x50, 0x40, 0x17, 0x4b, | 197 | 0x72, 0x3f, 0x3b, 0x01, 0x46, 0x1b, 0x0b, 0x35, 0x49, 0x39, 0x19, 0x70, 0x3d, 0x02, 0x41, |
| 198 | 0x6f, 0x22, 0x00, 0x7f, 0x61, 0x2a, 0x34, 0x3e, 0x00, 0x5f, 0x2f, 0x5f, 0x2f, 0x14, 0x2a, 0x55, 0x27, 0x1f, 0x46, 0x1f, 0x12, 0x46, | 198 | 0x0e, 0x38, 0x05, 0x76, 0x65, 0x4f, 0x31, 0x6c, 0x5e, 0x17, 0x04, 0x15, 0x36, 0x26, 0x64, |
| 199 | 0x5e, 0x1e, 0x0c, 0x7c, 0x38, 0x01, 0x61, 0x64, 0x76, 0x22, 0x6e, 0x08, 0x20, 0x38, 0x4f, 0x73, 0x72, 0x55, 0x12, 0x42, 0x19, 0x50, | 199 | 0x34, 0x14, 0x17, 0x7c, 0x0e, 0x0b, 0x5b, 0x55, 0x53, 0x6b, 0x00, 0x42, 0x41, 0x4f, 0x02, |
| 200 | 0x61, 0x43, 0x77, 0x7d, 0x41, 0x2e, 0x35, 0x4f, 0x3d, 0x31, 0x28, 0x58, 0x67, 0x1b, 0x03, 0x51, 0x20, 0x32, 0x1c, 0x08, 0x6e, 0x37, | 200 | 0x5c, 0x13, 0x0a, 0x2c, 0x2c, 0x3e, 0x10, 0x14, 0x33, 0x45, 0x7c, 0x7a, 0x5a, 0x31, 0x61, |
| 201 | 0x75, 0x37, 0x44, 0x4f, 0x68, 0x19, 0x07, 0x64, 0x14, 0x28, 0x25, 0x2b, 0x69, 0x35, 0x18, 0x27, 0x26, 0x14, 0x13, 0x70, 0x42, 0x19, | 201 | 0x39, 0x08, 0x22, 0x6a, 0x1e, 0x0f, 0x6f, 0x1b, 0x6c, 0x13, 0x5e, 0x79, 0x20, 0x79, 0x50, |
| 202 | 0x12, 0x75, 0x3e, 0x02, 0x5d, 0x7c, 0x13, 0x1f, 0x16, 0x53, 0x3b, 0x74, 0x48, 0x3c, 0x5e, 0x39, 0x6c, 0x1c, 0x1c, 0x74, 0x39, 0x1f, | 202 | 0x62, 0x06, 0x2c, 0x76, 0x17, 0x04, 0x2b, 0x2a, 0x75, 0x1f, 0x0c, 0x37, 0x4e, 0x0f, 0x7b, |
| 203 | 0x00, 0x1b, 0x06, 0x0a, 0x68, 0x3b, 0x52, 0x4f, 0x1e, 0x6e, 0x3c, 0x35, 0x0c, 0x38, 0x0e, 0x0b, 0x3b, 0x1a, 0x76, 0x23, 0x29, 0x53, | 203 | 0x2d, 0x34, 0x75, 0x60, 0x31, 0x74, 0x2e, 0x0a, 0x4a, 0x11, 0x6c, 0x49, 0x25, 0x01, 0x3a, |
| 204 | 0x1e, 0x5f, 0x41, 0x0c, 0x4b, 0x0a, 0x65, 0x28, 0x78, 0x67, 0x48, 0x59, 0x26, 0x6d, 0x31, 0x76, 0x23, 0x70, 0x61, 0x64, 0x3b, 0x38, | 204 | 0x3d, 0x22, 0x1e, 0x6d, 0x18, 0x51, 0x78, 0x2d, 0x62, 0x31, 0x4c, 0x50, 0x40, 0x17, 0x4b, |
| 205 | 0x79, 0x66, 0x74, 0x53, 0x2c, 0x64, 0x64, 0x54, 0x03, 0x54, 0x65, 0x44, 0x4c, 0x18, 0x4f, 0x48, 0x20, 0x4f, 0x72, 0x10, 0x3f, 0x0c, | 205 | 0x6f, 0x22, 0x00, 0x7f, 0x61, 0x2a, 0x34, 0x3e, 0x00, 0x5f, 0x2f, 0x5f, 0x2f, 0x14, 0x2a, |
| 206 | 0x52, 0x2d, 0x03, 0x14, 0x03, 0x51, 0x42, 0x10, 0x77, 0x6a, 0x34, 0x06, 0x32, 0x03, 0x72, 0x14, 0x7c, 0x08, 0x5d, 0x52, 0x1a, 0x62, | 206 | 0x55, 0x27, 0x1f, 0x46, 0x1f, 0x12, 0x46, 0x5e, 0x1e, 0x0c, 0x7c, 0x38, 0x01, 0x61, 0x64, |
| 207 | 0x7c, 0x3e, 0x30, 0x7e, 0x5f, 0x7f, 0x54, 0x0f, 0x44, 0x49, 0x5d, 0x5e, 0x10, 0x6a, 0x06, 0x2b, 0x06, 0x53, 0x10, 0x39, 0x37, 0x32, | 207 | 0x76, 0x22, 0x6e, 0x08, 0x20, 0x38, 0x4f, 0x73, 0x72, 0x55, 0x12, 0x42, 0x19, 0x50, 0x61, |
| 208 | 0x4a, 0x4e, 0x3d, 0x2b, 0x65, 0x38, 0x39, 0x07, 0x72, 0x54, 0x64, 0x4d, 0x56, 0x6a, 0x03, 0x22, 0x70, 0x7b, 0x5f, 0x60, 0x0b, 0x2a, | 208 | 0x43, 0x77, 0x7d, 0x41, 0x2e, 0x35, 0x4f, 0x3d, 0x31, 0x28, 0x58, 0x67, 0x1b, 0x03, 0x51, |
| 209 | 0x0b, 0x6b, 0x10, 0x64, 0x14, 0x05, 0x22, 0x00, 0x73, 0x40, 0x23, 0x5b, 0x51, 0x1f, 0x2b, 0x1a, 0x5d, 0x69, 0x7a, 0x46, 0x0c, 0x5f, | 209 | 0x20, 0x32, 0x1c, 0x08, 0x6e, 0x37, 0x75, 0x37, 0x44, 0x4f, 0x68, 0x19, 0x07, 0x64, 0x14, |
| 210 | 0x32, 0x4b, 0x4a, 0x28, 0x52, 0x79, 0x5b, 0x12, 0x42, 0x18, 0x00, 0x5d, 0x27, 0x31, 0x53, 0x3c, 0x4c, 0x36, 0x4e, 0x38, 0x3f, 0x72, | 210 | 0x28, 0x25, 0x2b, 0x69, 0x35, 0x18, 0x27, 0x26, 0x14, 0x13, 0x70, 0x42, 0x19, 0x12, 0x75, |
| 211 | 0x03, 0x71, 0x02, 0x5b, 0x36, 0x59, 0x7f, 0x75, 0x6e, 0x08, 0x54, 0x0d, 0x34, 0x1c, 0x34, 0x57, 0x5d, 0x69, 0x48, 0x00, 0x3b, 0x05, | 211 | 0x3e, 0x02, 0x5d, 0x7c, 0x13, 0x1f, 0x16, 0x53, 0x3b, 0x74, 0x48, 0x3c, 0x5e, 0x39, 0x6c, |
| 212 | 0x07, 0x6e, 0x27, 0x65, 0x6e, 0x40, 0x3d, 0x3a, 0x4f, 0x72, 0x5d, 0x39, 0x16, 0x0f, 0x63, 0x12, 0x12, 0x15, 0x3a, 0x70, 0x0d, 0x57, | 212 | 0x1c, 0x1c, 0x74, 0x39, 0x1f, 0x00, 0x1b, 0x06, 0x0a, 0x68, 0x3b, 0x52, 0x4f, 0x1e, 0x6e, |
| 213 | 0x18, 0x0d, 0x5e, 0x3d, 0x22, 0x68, 0x68, 0x7c, 0x6d, 0x4f, 0x0c, 0x7b, 0x09, 0x2d, 0x4a, 0x73, 0x20, 0x47, 0x07, 0x57, 0x75, 0x5d, | 213 | 0x3c, 0x35, 0x0c, 0x38, 0x0e, 0x0b, 0x3b, 0x1a, 0x76, 0x23, 0x29, 0x53, 0x1e, 0x5f, 0x41, |
| 214 | 0x53, 0x70, 0x34, 0x21, 0x40, 0x57, 0x51, 0x5e, 0x49, 0x44, 0x00, 0x54, 0x27, 0x04, 0x68, 0x7e, 0x59, 0x56, 0x58, 0x74, 0x14, 0x3c, | 214 | 0x0c, 0x4b, 0x0a, 0x65, 0x28, 0x78, 0x67, 0x48, 0x59, 0x26, 0x6d, 0x31, 0x76, 0x23, 0x70, |
| 215 | 0x16, 0x33, 0x41, 0x16, 0x4b, 0x2f, 0x49, 0x37, 0x0a, 0x54, 0x08, 0x08, 0x1f, 0x39, 0x67, 0x76, 0x28, 0x28, 0x07, 0x1d, 0x61, 0x47, | 215 | 0x61, 0x64, 0x3b, 0x38, 0x79, 0x66, 0x74, 0x53, 0x2c, 0x64, 0x64, 0x54, 0x03, 0x54, 0x65, |
| 216 | 0x51, 0x4d, 0x75, 0x26, 0x52, 0x47, 0x47, 0x0c, 0x57, 0x58, 0x74, 0x3e, 0x62, 0x6c, 0x58, 0x3a, 0x44, 0x1e, 0x16, 0x2e, 0x21, 0x1c, | 216 | 0x44, 0x4c, 0x18, 0x4f, 0x48, 0x20, 0x4f, 0x72, 0x10, 0x3f, 0x0c, 0x52, 0x2d, 0x03, 0x14, |
| 217 | 0x73, 0x45, 0x67, 0x74, 0x4f, 0x33, 0x66, 0x0e, 0x74, 0x66, 0x26, 0x1f, 0x2e, 0x38, 0x44, 0x40, 0x7e, 0x2a, 0x50, 0x52, 0x5e, 0x43, | 217 | 0x03, 0x51, 0x42, 0x10, 0x77, 0x6a, 0x34, 0x06, 0x32, 0x03, 0x72, 0x14, 0x7c, 0x08, 0x5d, |
| 218 | 0x01, 0x7a, 0x38, 0x49, 0x3c, 0x55, 0x4d, 0x5a, 0x44, 0x08, 0x26, 0x59, 0x4d, 0x45, 0x0b, 0x48, 0x0a, 0x33, 0x5e, 0x4a, 0x4d, 0x75, | 218 | 0x52, 0x1a, 0x62, 0x7c, 0x3e, 0x30, 0x7e, 0x5f, 0x7f, 0x54, 0x0f, 0x44, 0x49, 0x5d, 0x5e, |
| 219 | 0x16, 0x17, 0x63, 0x46, 0x01, 0x2a, 0x55, 0x7b, 0x0f, 0x02, 0x73, 0x6a, 0x4b, 0x7f, 0x75, 0x65, 0x3c, 0x4c, 0x33, 0x39, 0x6c, 0x74, | 219 | 0x10, 0x6a, 0x06, 0x2b, 0x06, 0x53, 0x10, 0x39, 0x37, 0x32, 0x4a, 0x4e, 0x3d, 0x2b, 0x65, |
| 220 | 0x05, 0x60, 0x0f, 0x7f, 0x2d, 0x41, 0x4d, 0x4d, 0x46, 0x71, 0x09, 0x6f, 0x4f, 0x60, 0x15, 0x0f, 0x46, 0x73, 0x63, 0x4c, 0x5e, 0x74, | 220 | 0x38, 0x39, 0x07, 0x72, 0x54, 0x64, 0x4d, 0x56, 0x6a, 0x03, 0x22, 0x70, 0x7b, 0x5f, 0x60, |
| 221 | 0x30, 0x0d, 0x28, 0x43, 0x08, 0x72, 0x32, 0x04, 0x2e, 0x31, 0x29, 0x27, 0x44, 0x6d, 0x13, 0x17, 0x48, 0x0f, 0x49, 0x52, 0x10, 0x13, | 221 | 0x0b, 0x2a, 0x0b, 0x6b, 0x10, 0x64, 0x14, 0x05, 0x22, 0x00, 0x73, 0x40, 0x23, 0x5b, 0x51, |
| 222 | 0x7f, 0x17, 0x16, 0x62, 0x79, 0x35, 0x78, 0x3e, 0x01, 0x7c, 0x2e, 0x0f, 0x76, 0x3e, 0x5e, 0x53, 0x6c, 0x5b, 0x5f, 0x7c, 0x19, 0x41, | 222 | 0x1f, 0x2b, 0x1a, 0x5d, 0x69, 0x7a, 0x46, 0x0c, 0x5f, 0x32, 0x4b, 0x4a, 0x28, 0x52, 0x79, |
| 223 | 0x02, 0x2f, 0x17, 0x64, 0x41, 0x75, 0x10, 0x04, 0x47, 0x7c, 0x3d, 0x4b, 0x52, 0x00, 0x10, 0x5d, 0x51, 0x4e, 0x7a, 0x27, 0x25, 0x55, | 223 | 0x5b, 0x12, 0x42, 0x18, 0x00, 0x5d, 0x27, 0x31, 0x53, 0x3c, 0x4c, 0x36, 0x4e, 0x38, 0x3f, |
| 224 | 0x40, 0x12, 0x35, 0x60, 0x05, 0x1b, 0x34, 0x2d, 0x04, 0x7a, 0x6a, 0x69, 0x02, 0x79, 0x03, 0x3a, 0x2f, 0x06, 0x0a, 0x79, 0x7b, 0x12, | 224 | 0x72, 0x03, 0x71, 0x02, 0x5b, 0x36, 0x59, 0x7f, 0x75, 0x6e, 0x08, 0x54, 0x0d, 0x34, 0x1c, |
| 225 | 0x5d, 0x7c, 0x52, 0x29, 0x47, 0x58, 0x12, 0x73, 0x3f, 0x27, 0x56, 0x05, 0x0c, 0x48, 0x32, 0x58, 0x6b, 0x57, 0x5c, 0x03, 0x64, 0x56, | 225 | 0x34, 0x57, 0x5d, 0x69, 0x48, 0x00, 0x3b, 0x05, 0x07, 0x6e, 0x27, 0x65, 0x6e, 0x40, 0x3d, |
| 226 | 0x11, 0x52, 0x7a, 0x30, 0x36, 0x29, 0x17, 0x3b, 0x68, 0x7a, 0x7c, 0x05, 0x6b, 0x6b, 0x13, 0x6a, 0x24, 0x5c, 0x68, 0x42, 0x18, 0x32, | 226 | 0x3a, 0x4f, 0x72, 0x5d, 0x39, 0x16, 0x0f, 0x63, 0x12, 0x12, 0x15, 0x3a, 0x70, 0x0d, 0x57, |
| 227 | 0x03, 0x73, 0x6e, 0x04, 0x21, 0x2e, 0x01, 0x04, 0x63, 0x7d, 0x44, 0x41, 0x12, 0x31, 0x0b, 0x15, 0x1f, 0x70, 0x00, 0x2e, 0x66, 0x14, | 227 | 0x18, 0x0d, 0x5e, 0x3d, 0x22, 0x68, 0x68, 0x7c, 0x6d, 0x4f, 0x0c, 0x7b, 0x09, 0x2d, 0x4a, |
| 228 | 0x3c, 0x7f, 0x2b, 0x00, 0x1f, 0x0c, 0x28, 0x59, 0x0a, 0x16, 0x49, 0x5a, 0x5c, 0x64, 0x65, 0x4b, 0x11, 0x29, 0x15, 0x36, 0x5a, 0x65, | 228 | 0x73, 0x20, 0x47, 0x07, 0x57, 0x75, 0x5d, 0x53, 0x70, 0x34, 0x21, 0x40, 0x57, 0x51, 0x5e, |
| 229 | 0x19, 0x4f, 0x60, 0x23, 0x3a, 0x3a, 0x13, 0x25, 0x02, 0x78, 0x4c, 0x54}; | 229 | 0x49, 0x44, 0x00, 0x54, 0x27, 0x04, 0x68, 0x7e, 0x59, 0x56, 0x58, 0x74, 0x14, 0x3c, 0x16, |
| 230 | 0x33, 0x41, 0x16, 0x4b, 0x2f, 0x49, 0x37, 0x0a, 0x54, 0x08, 0x08, 0x1f, 0x39, 0x67, 0x76, | ||
| 231 | 0x28, 0x28, 0x07, 0x1d, 0x61, 0x47, 0x51, 0x4d, 0x75, 0x26, 0x52, 0x47, 0x47, 0x0c, 0x57, | ||
| 232 | 0x58, 0x74, 0x3e, 0x62, 0x6c, 0x58, 0x3a, 0x44, 0x1e, 0x16, 0x2e, 0x21, 0x1c, 0x73, 0x45, | ||
| 233 | 0x67, 0x74, 0x4f, 0x33, 0x66, 0x0e, 0x74, 0x66, 0x26, 0x1f, 0x2e, 0x38, 0x44, 0x40, 0x7e, | ||
| 234 | 0x2a, 0x50, 0x52, 0x5e, 0x43, 0x01, 0x7a, 0x38, 0x49, 0x3c, 0x55, 0x4d, 0x5a, 0x44, 0x08, | ||
| 235 | 0x26, 0x59, 0x4d, 0x45, 0x0b, 0x48, 0x0a, 0x33, 0x5e, 0x4a, 0x4d, 0x75, 0x16, 0x17, 0x63, | ||
| 236 | 0x46, 0x01, 0x2a, 0x55, 0x7b, 0x0f, 0x02, 0x73, 0x6a, 0x4b, 0x7f, 0x75, 0x65, 0x3c, 0x4c, | ||
| 237 | 0x33, 0x39, 0x6c, 0x74, 0x05, 0x60, 0x0f, 0x7f, 0x2d, 0x41, 0x4d, 0x4d, 0x46, 0x71, 0x09, | ||
| 238 | 0x6f, 0x4f, 0x60, 0x15, 0x0f, 0x46, 0x73, 0x63, 0x4c, 0x5e, 0x74, 0x30, 0x0d, 0x28, 0x43, | ||
| 239 | 0x08, 0x72, 0x32, 0x04, 0x2e, 0x31, 0x29, 0x27, 0x44, 0x6d, 0x13, 0x17, 0x48, 0x0f, 0x49, | ||
| 240 | 0x52, 0x10, 0x13, 0x7f, 0x17, 0x16, 0x62, 0x79, 0x35, 0x78, 0x3e, 0x01, 0x7c, 0x2e, 0x0f, | ||
| 241 | 0x76, 0x3e, 0x5e, 0x53, 0x6c, 0x5b, 0x5f, 0x7c, 0x19, 0x41, 0x02, 0x2f, 0x17, 0x64, 0x41, | ||
| 242 | 0x75, 0x10, 0x04, 0x47, 0x7c, 0x3d, 0x4b, 0x52, 0x00, 0x10, 0x5d, 0x51, 0x4e, 0x7a, 0x27, | ||
| 243 | 0x25, 0x55, 0x40, 0x12, 0x35, 0x60, 0x05, 0x1b, 0x34, 0x2d, 0x04, 0x7a, 0x6a, 0x69, 0x02, | ||
| 244 | 0x79, 0x03, 0x3a, 0x2f, 0x06, 0x0a, 0x79, 0x7b, 0x12, 0x5d, 0x7c, 0x52, 0x29, 0x47, 0x58, | ||
| 245 | 0x12, 0x73, 0x3f, 0x27, 0x56, 0x05, 0x0c, 0x48, 0x32, 0x58, 0x6b, 0x57, 0x5c, 0x03, 0x64, | ||
| 246 | 0x56, 0x11, 0x52, 0x7a, 0x30, 0x36, 0x29, 0x17, 0x3b, 0x68, 0x7a, 0x7c, 0x05, 0x6b, 0x6b, | ||
| 247 | 0x13, 0x6a, 0x24, 0x5c, 0x68, 0x42, 0x18, 0x32, 0x03, 0x73, 0x6e, 0x04, 0x21, 0x2e, 0x01, | ||
| 248 | 0x04, 0x63, 0x7d, 0x44, 0x41, 0x12, 0x31, 0x0b, 0x15, 0x1f, 0x70, 0x00, 0x2e, 0x66, 0x14, | ||
| 249 | 0x3c, 0x7f, 0x2b, 0x00, 0x1f, 0x0c, 0x28, 0x59, 0x0a, 0x16, 0x49, 0x5a, 0x5c, 0x64, 0x65, | ||
| 250 | 0x4b, 0x11, 0x29, 0x15, 0x36, 0x5a, 0x65, 0x19, 0x4f, 0x60, 0x23, 0x3a, 0x3a, 0x13, 0x25, | ||
| 251 | 0x02, 0x78, 0x4c, 0x54}; | ||
| 230 | char b64_known[1369] = { | 252 | char b64_known[1369] = { |
| 231 | 0x43, 0x7a, 0x42, 0x45, 0x59, 0x6e, 0x77, 0x69, 0x48, 0x77, 0x30, 0x46, 0x5a, 0x79, 0x77, 0x71, 0x4f, 0x53, 0x46, 0x47, 0x43, 0x46, | 253 | 0x43, 0x7a, 0x42, 0x45, 0x59, 0x6e, 0x77, 0x69, 0x48, 0x77, 0x30, 0x46, 0x5a, 0x79, 0x77, |
| 232 | 0x42, 0x6d, 0x4e, 0x44, 0x63, 0x4c, 0x52, 0x55, 0x73, 0x34, 0x4d, 0x67, 0x5a, 0x36, 0x50, 0x6e, 0x38, 0x4d, 0x51, 0x42, 0x68, 0x72, | 254 | 0x71, 0x4f, 0x53, 0x46, 0x47, 0x43, 0x46, 0x42, 0x6d, 0x4e, 0x44, 0x63, 0x4c, 0x52, 0x55, |
| 233 | 0x4c, 0x57, 0x42, 0x4d, 0x59, 0x41, 0x77, 0x6a, 0x51, 0x7a, 0x73, 0x2b, 0x47, 0x78, 0x59, 0x45, 0x52, 0x6c, 0x67, 0x2f, 0x51, 0x47, | 255 | 0x73, 0x34, 0x4d, 0x67, 0x5a, 0x36, 0x50, 0x6e, 0x38, 0x4d, 0x51, 0x42, 0x68, 0x72, 0x4c, |
| 234 | 0x6f, 0x52, 0x42, 0x57, 0x4e, 0x78, 0x46, 0x44, 0x56, 0x48, 0x65, 0x52, 0x4e, 0x76, 0x61, 0x79, 0x63, 0x59, 0x57, 0x30, 0x67, 0x6e, | 256 | 0x57, 0x42, 0x4d, 0x59, 0x41, 0x77, 0x6a, 0x51, 0x7a, 0x73, 0x2b, 0x47, 0x78, 0x59, 0x45, |
| 235 | 0x50, 0x6d, 0x38, 0x56, 0x4d, 0x30, 0x38, 0x2b, 0x58, 0x6c, 0x46, 0x7a, 0x61, 0x43, 0x55, 0x50, 0x42, 0x6c, 0x74, 0x38, 0x63, 0x6e, | 257 | 0x52, 0x6c, 0x67, 0x2f, 0x51, 0x47, 0x6f, 0x52, 0x42, 0x57, 0x4e, 0x78, 0x46, 0x44, 0x56, |
| 236 | 0x55, 0x2b, 0x50, 0x78, 0x74, 0x63, 0x62, 0x57, 0x6f, 0x35, 0x66, 0x47, 0x4e, 0x6a, 0x59, 0x47, 0x78, 0x36, 0x4d, 0x33, 0x5a, 0x53, | 258 | 0x48, 0x65, 0x52, 0x4e, 0x76, 0x61, 0x79, 0x63, 0x59, 0x57, 0x30, 0x67, 0x6e, 0x50, 0x6d, |
| 237 | 0x45, 0x79, 0x55, 0x7a, 0x66, 0x57, 0x55, 0x6a, 0x4a, 0x78, 0x45, 0x47, 0x42, 0x6b, 0x64, 0x78, 0x48, 0x68, 0x52, 0x30, 0x59, 0x33, | 259 | 0x38, 0x56, 0x4d, 0x30, 0x38, 0x2b, 0x58, 0x6c, 0x46, 0x7a, 0x61, 0x43, 0x55, 0x50, 0x42, |
| 238 | 0x41, 0x74, 0x46, 0x53, 0x63, 0x59, 0x55, 0x51, 0x59, 0x46, 0x4d, 0x78, 0x45, 0x73, 0x61, 0x77, 0x41, 0x74, 0x64, 0x79, 0x42, 0x49, | 260 | 0x6c, 0x74, 0x38, 0x63, 0x6e, 0x55, 0x2b, 0x50, 0x78, 0x74, 0x63, 0x62, 0x57, 0x6f, 0x35, |
| 239 | 0x44, 0x58, 0x4e, 0x52, 0x52, 0x53, 0x56, 0x2f, 0x66, 0x7a, 0x55, 0x6d, 0x4c, 0x69, 0x5a, 0x54, 0x4a, 0x47, 0x67, 0x65, 0x44, 0x6c, | 261 | 0x66, 0x47, 0x4e, 0x6a, 0x59, 0x47, 0x78, 0x36, 0x4d, 0x33, 0x5a, 0x53, 0x45, 0x79, 0x55, |
| 240 | 0x67, 0x36, 0x57, 0x56, 0x42, 0x57, 0x4e, 0x31, 0x39, 0x6d, 0x41, 0x55, 0x78, 0x61, 0x5a, 0x44, 0x4a, 0x51, 0x65, 0x32, 0x6f, 0x67, | 262 | 0x7a, 0x66, 0x57, 0x55, 0x6a, 0x4a, 0x78, 0x45, 0x47, 0x42, 0x6b, 0x64, 0x78, 0x48, 0x68, |
| 241 | 0x63, 0x69, 0x73, 0x64, 0x66, 0x6b, 0x4e, 0x37, 0x59, 0x55, 0x49, 0x4c, 0x59, 0x58, 0x4d, 0x6b, 0x65, 0x54, 0x70, 0x72, 0x53, 0x6e, | 263 | 0x52, 0x30, 0x59, 0x33, 0x41, 0x74, 0x46, 0x53, 0x63, 0x59, 0x55, 0x51, 0x59, 0x46, 0x4d, |
| 242 | 0x6c, 0x75, 0x43, 0x51, 0x38, 0x6e, 0x4c, 0x51, 0x78, 0x65, 0x4d, 0x6b, 0x73, 0x4e, 0x65, 0x55, 0x59, 0x35, 0x49, 0x51, 0x6f, 0x6d, | 264 | 0x78, 0x45, 0x73, 0x61, 0x77, 0x41, 0x74, 0x64, 0x79, 0x42, 0x49, 0x44, 0x58, 0x4e, 0x52, |
| 243 | 0x58, 0x7a, 0x6f, 0x41, 0x4a, 0x6a, 0x38, 0x54, 0x4c, 0x6e, 0x35, 0x51, 0x4b, 0x32, 0x64, 0x47, 0x63, 0x6a, 0x38, 0x37, 0x41, 0x55, | 265 | 0x52, 0x53, 0x56, 0x2f, 0x66, 0x7a, 0x55, 0x6d, 0x4c, 0x69, 0x5a, 0x54, 0x4a, 0x47, 0x67, |
| 244 | 0x59, 0x62, 0x43, 0x7a, 0x56, 0x4a, 0x4f, 0x52, 0x6c, 0x77, 0x50, 0x51, 0x4a, 0x42, 0x44, 0x6a, 0x67, 0x46, 0x64, 0x6d, 0x56, 0x50, | 266 | 0x65, 0x44, 0x6c, 0x67, 0x36, 0x57, 0x56, 0x42, 0x57, 0x4e, 0x31, 0x39, 0x6d, 0x41, 0x55, |
| 245 | 0x4d, 0x57, 0x78, 0x65, 0x46, 0x77, 0x51, 0x56, 0x4e, 0x69, 0x5a, 0x6b, 0x4e, 0x42, 0x51, 0x58, 0x66, 0x41, 0x34, 0x4c, 0x57, 0x31, | 267 | 0x78, 0x61, 0x5a, 0x44, 0x4a, 0x51, 0x65, 0x32, 0x6f, 0x67, 0x63, 0x69, 0x73, 0x64, 0x66, |
| 246 | 0x56, 0x54, 0x61, 0x77, 0x42, 0x43, 0x51, 0x55, 0x38, 0x43, 0x58, 0x42, 0x4d, 0x4b, 0x4c, 0x43, 0x77, 0x2b, 0x45, 0x42, 0x51, 0x7a, | 268 | 0x6b, 0x4e, 0x37, 0x59, 0x55, 0x49, 0x4c, 0x59, 0x58, 0x4d, 0x6b, 0x65, 0x54, 0x70, 0x72, |
| 247 | 0x52, 0x58, 0x78, 0x36, 0x57, 0x6a, 0x46, 0x68, 0x4f, 0x51, 0x67, 0x69, 0x61, 0x68, 0x34, 0x50, 0x62, 0x78, 0x74, 0x73, 0x45, 0x31, | 269 | 0x53, 0x6e, 0x6c, 0x75, 0x43, 0x51, 0x38, 0x6e, 0x4c, 0x51, 0x78, 0x65, 0x4d, 0x6b, 0x73, |
| 248 | 0x35, 0x35, 0x49, 0x48, 0x6c, 0x51, 0x59, 0x67, 0x59, 0x73, 0x64, 0x68, 0x63, 0x45, 0x4b, 0x79, 0x70, 0x31, 0x48, 0x77, 0x77, 0x33, | 270 | 0x4e, 0x65, 0x55, 0x59, 0x35, 0x49, 0x51, 0x6f, 0x6d, 0x58, 0x7a, 0x6f, 0x41, 0x4a, 0x6a, |
| 249 | 0x54, 0x67, 0x39, 0x37, 0x4c, 0x54, 0x52, 0x31, 0x59, 0x44, 0x46, 0x30, 0x4c, 0x67, 0x70, 0x4b, 0x45, 0x57, 0x78, 0x4a, 0x4a, 0x51, | 271 | 0x38, 0x54, 0x4c, 0x6e, 0x35, 0x51, 0x4b, 0x32, 0x64, 0x47, 0x63, 0x6a, 0x38, 0x37, 0x41, |
| 250 | 0x45, 0x36, 0x50, 0x53, 0x49, 0x65, 0x62, 0x52, 0x68, 0x52, 0x65, 0x43, 0x31, 0x69, 0x4d, 0x55, 0x78, 0x51, 0x51, 0x42, 0x64, 0x4c, | 272 | 0x55, 0x59, 0x62, 0x43, 0x7a, 0x56, 0x4a, 0x4f, 0x52, 0x6c, 0x77, 0x50, 0x51, 0x4a, 0x42, |
| 251 | 0x62, 0x79, 0x49, 0x41, 0x66, 0x32, 0x45, 0x71, 0x4e, 0x44, 0x34, 0x41, 0x58, 0x79, 0x39, 0x66, 0x4c, 0x78, 0x51, 0x71, 0x56, 0x53, | 273 | 0x44, 0x6a, 0x67, 0x46, 0x64, 0x6d, 0x56, 0x50, 0x4d, 0x57, 0x78, 0x65, 0x46, 0x77, 0x51, |
| 252 | 0x63, 0x66, 0x52, 0x68, 0x38, 0x53, 0x52, 0x6c, 0x34, 0x65, 0x44, 0x48, 0x77, 0x34, 0x41, 0x57, 0x46, 0x6b, 0x64, 0x69, 0x4a, 0x75, | 274 | 0x56, 0x4e, 0x69, 0x5a, 0x6b, 0x4e, 0x42, 0x51, 0x58, 0x66, 0x41, 0x34, 0x4c, 0x57, 0x31, |
| 253 | 0x43, 0x43, 0x41, 0x34, 0x54, 0x33, 0x4e, 0x79, 0x56, 0x52, 0x4a, 0x43, 0x47, 0x56, 0x42, 0x68, 0x51, 0x33, 0x64, 0x39, 0x51, 0x53, | 275 | 0x56, 0x54, 0x61, 0x77, 0x42, 0x43, 0x51, 0x55, 0x38, 0x43, 0x58, 0x42, 0x4d, 0x4b, 0x4c, |
| 254 | 0x34, 0x31, 0x54, 0x7a, 0x30, 0x78, 0x4b, 0x46, 0x68, 0x6e, 0x47, 0x77, 0x4e, 0x52, 0x49, 0x44, 0x49, 0x63, 0x43, 0x47, 0x34, 0x33, | 276 | 0x43, 0x77, 0x2b, 0x45, 0x42, 0x51, 0x7a, 0x52, 0x58, 0x78, 0x36, 0x57, 0x6a, 0x46, 0x68, |
| 255 | 0x64, 0x54, 0x64, 0x45, 0x54, 0x32, 0x67, 0x5a, 0x42, 0x32, 0x51, 0x55, 0x4b, 0x43, 0x55, 0x72, 0x61, 0x54, 0x55, 0x59, 0x4a, 0x79, | 277 | 0x4f, 0x51, 0x67, 0x69, 0x61, 0x68, 0x34, 0x50, 0x62, 0x78, 0x74, 0x73, 0x45, 0x31, 0x35, |
| 256 | 0x59, 0x55, 0x45, 0x33, 0x42, 0x43, 0x47, 0x52, 0x4a, 0x31, 0x50, 0x67, 0x4a, 0x64, 0x66, 0x42, 0x4d, 0x66, 0x46, 0x6c, 0x4d, 0x37, | 278 | 0x35, 0x49, 0x48, 0x6c, 0x51, 0x59, 0x67, 0x59, 0x73, 0x64, 0x68, 0x63, 0x45, 0x4b, 0x79, |
| 257 | 0x64, 0x45, 0x67, 0x38, 0x58, 0x6a, 0x6c, 0x73, 0x48, 0x42, 0x78, 0x30, 0x4f, 0x52, 0x38, 0x41, 0x47, 0x77, 0x59, 0x4b, 0x61, 0x44, | 279 | 0x70, 0x31, 0x48, 0x77, 0x77, 0x33, 0x54, 0x67, 0x39, 0x37, 0x4c, 0x54, 0x52, 0x31, 0x59, |
| 258 | 0x74, 0x53, 0x54, 0x78, 0x35, 0x75, 0x50, 0x44, 0x55, 0x4d, 0x4f, 0x41, 0x34, 0x4c, 0x4f, 0x78, 0x70, 0x32, 0x49, 0x79, 0x6c, 0x54, | 280 | 0x44, 0x46, 0x30, 0x4c, 0x67, 0x70, 0x4b, 0x45, 0x57, 0x78, 0x4a, 0x4a, 0x51, 0x45, 0x36, |
| 259 | 0x48, 0x6c, 0x39, 0x42, 0x44, 0x45, 0x73, 0x4b, 0x5a, 0x53, 0x68, 0x34, 0x5a, 0x30, 0x68, 0x5a, 0x4a, 0x6d, 0x30, 0x78, 0x64, 0x69, | 281 | 0x50, 0x53, 0x49, 0x65, 0x62, 0x52, 0x68, 0x52, 0x65, 0x43, 0x31, 0x69, 0x4d, 0x55, 0x78, |
| 260 | 0x4e, 0x77, 0x59, 0x57, 0x51, 0x37, 0x4f, 0x48, 0x6c, 0x6d, 0x64, 0x46, 0x4d, 0x73, 0x5a, 0x47, 0x52, 0x55, 0x41, 0x31, 0x52, 0x6c, | 282 | 0x51, 0x51, 0x42, 0x64, 0x4c, 0x62, 0x79, 0x49, 0x41, 0x66, 0x32, 0x45, 0x71, 0x4e, 0x44, |
| 261 | 0x52, 0x45, 0x77, 0x59, 0x54, 0x30, 0x67, 0x67, 0x54, 0x33, 0x49, 0x51, 0x50, 0x77, 0x78, 0x53, 0x4c, 0x51, 0x4d, 0x55, 0x41, 0x31, | 283 | 0x34, 0x41, 0x58, 0x79, 0x39, 0x66, 0x4c, 0x78, 0x51, 0x71, 0x56, 0x53, 0x63, 0x66, 0x52, |
| 262 | 0x46, 0x43, 0x45, 0x48, 0x64, 0x71, 0x4e, 0x41, 0x59, 0x79, 0x41, 0x33, 0x49, 0x55, 0x66, 0x41, 0x68, 0x64, 0x55, 0x68, 0x70, 0x69, | 284 | 0x68, 0x38, 0x53, 0x52, 0x6c, 0x34, 0x65, 0x44, 0x48, 0x77, 0x34, 0x41, 0x57, 0x46, 0x6b, |
| 263 | 0x66, 0x44, 0x34, 0x77, 0x66, 0x6c, 0x39, 0x2f, 0x56, 0x41, 0x39, 0x45, 0x53, 0x56, 0x31, 0x65, 0x45, 0x47, 0x6f, 0x47, 0x4b, 0x77, | 285 | 0x64, 0x69, 0x4a, 0x75, 0x43, 0x43, 0x41, 0x34, 0x54, 0x33, 0x4e, 0x79, 0x56, 0x52, 0x4a, |
| 264 | 0x5a, 0x54, 0x45, 0x44, 0x6b, 0x33, 0x4d, 0x6b, 0x70, 0x4f, 0x50, 0x53, 0x74, 0x6c, 0x4f, 0x44, 0x6b, 0x48, 0x63, 0x6c, 0x52, 0x6b, | 286 | 0x43, 0x47, 0x56, 0x42, 0x68, 0x51, 0x33, 0x64, 0x39, 0x51, 0x53, 0x34, 0x31, 0x54, 0x7a, |
| 265 | 0x54, 0x56, 0x5a, 0x71, 0x41, 0x79, 0x4a, 0x77, 0x65, 0x31, 0x39, 0x67, 0x43, 0x79, 0x6f, 0x4c, 0x61, 0x78, 0x42, 0x6b, 0x46, 0x41, | 287 | 0x30, 0x78, 0x4b, 0x46, 0x68, 0x6e, 0x47, 0x77, 0x4e, 0x52, 0x49, 0x44, 0x49, 0x63, 0x43, |
| 266 | 0x55, 0x69, 0x41, 0x48, 0x4e, 0x41, 0x49, 0x31, 0x74, 0x52, 0x48, 0x79, 0x73, 0x61, 0x58, 0x57, 0x6c, 0x36, 0x52, 0x67, 0x78, 0x66, | 288 | 0x47, 0x34, 0x33, 0x64, 0x54, 0x64, 0x45, 0x54, 0x32, 0x67, 0x5a, 0x42, 0x32, 0x51, 0x55, |
| 267 | 0x4d, 0x6b, 0x74, 0x4b, 0x4b, 0x46, 0x4a, 0x35, 0x57, 0x78, 0x4a, 0x43, 0x47, 0x41, 0x42, 0x64, 0x4a, 0x7a, 0x46, 0x54, 0x50, 0x45, | 289 | 0x4b, 0x43, 0x55, 0x72, 0x61, 0x54, 0x55, 0x59, 0x4a, 0x79, 0x59, 0x55, 0x45, 0x33, 0x42, |
| 268 | 0x77, 0x32, 0x54, 0x6a, 0x67, 0x2f, 0x63, 0x67, 0x4e, 0x78, 0x41, 0x6c, 0x73, 0x32, 0x57, 0x58, 0x39, 0x31, 0x62, 0x67, 0x68, 0x55, | 290 | 0x43, 0x47, 0x52, 0x4a, 0x31, 0x50, 0x67, 0x4a, 0x64, 0x66, 0x42, 0x4d, 0x66, 0x46, 0x6c, |
| 269 | 0x44, 0x54, 0x51, 0x63, 0x4e, 0x46, 0x64, 0x64, 0x61, 0x55, 0x67, 0x41, 0x4f, 0x77, 0x55, 0x48, 0x62, 0x69, 0x64, 0x6c, 0x62, 0x6b, | 291 | 0x4d, 0x37, 0x64, 0x45, 0x67, 0x38, 0x58, 0x6a, 0x6c, 0x73, 0x48, 0x42, 0x78, 0x30, 0x4f, |
| 270 | 0x41, 0x39, 0x4f, 0x6b, 0x39, 0x79, 0x58, 0x54, 0x6b, 0x57, 0x44, 0x32, 0x4d, 0x53, 0x45, 0x68, 0x55, 0x36, 0x63, 0x41, 0x31, 0x58, | 292 | 0x52, 0x38, 0x41, 0x47, 0x77, 0x59, 0x4b, 0x61, 0x44, 0x74, 0x53, 0x54, 0x78, 0x35, 0x75, |
| 271 | 0x47, 0x41, 0x31, 0x65, 0x50, 0x53, 0x4a, 0x6f, 0x61, 0x48, 0x78, 0x74, 0x54, 0x77, 0x78, 0x37, 0x43, 0x53, 0x31, 0x4b, 0x63, 0x79, | 293 | 0x50, 0x44, 0x55, 0x4d, 0x4f, 0x41, 0x34, 0x4c, 0x4f, 0x78, 0x70, 0x32, 0x49, 0x79, 0x6c, |
| 272 | 0x42, 0x48, 0x42, 0x31, 0x64, 0x31, 0x58, 0x56, 0x4e, 0x77, 0x4e, 0x43, 0x46, 0x41, 0x56, 0x31, 0x46, 0x65, 0x53, 0x55, 0x51, 0x41, | 294 | 0x54, 0x48, 0x6c, 0x39, 0x42, 0x44, 0x45, 0x73, 0x4b, 0x5a, 0x53, 0x68, 0x34, 0x5a, 0x30, |
| 273 | 0x56, 0x43, 0x63, 0x45, 0x61, 0x48, 0x35, 0x5a, 0x56, 0x6c, 0x68, 0x30, 0x46, 0x44, 0x77, 0x57, 0x4d, 0x30, 0x45, 0x57, 0x53, 0x79, | 295 | 0x68, 0x5a, 0x4a, 0x6d, 0x30, 0x78, 0x64, 0x69, 0x4e, 0x77, 0x59, 0x57, 0x51, 0x37, 0x4f, |
| 274 | 0x39, 0x4a, 0x4e, 0x77, 0x70, 0x55, 0x43, 0x41, 0x67, 0x66, 0x4f, 0x57, 0x64, 0x32, 0x4b, 0x43, 0x67, 0x48, 0x48, 0x57, 0x46, 0x48, | 296 | 0x48, 0x6c, 0x6d, 0x64, 0x46, 0x4d, 0x73, 0x5a, 0x47, 0x52, 0x55, 0x41, 0x31, 0x52, 0x6c, |
| 275 | 0x55, 0x55, 0x31, 0x31, 0x4a, 0x6c, 0x4a, 0x48, 0x52, 0x77, 0x78, 0x58, 0x57, 0x48, 0x51, 0x2b, 0x59, 0x6d, 0x78, 0x59, 0x4f, 0x6b, | 297 | 0x52, 0x45, 0x77, 0x59, 0x54, 0x30, 0x67, 0x67, 0x54, 0x33, 0x49, 0x51, 0x50, 0x77, 0x78, |
| 276 | 0x51, 0x65, 0x46, 0x69, 0x34, 0x68, 0x48, 0x48, 0x4e, 0x46, 0x5a, 0x33, 0x52, 0x50, 0x4d, 0x32, 0x59, 0x4f, 0x64, 0x47, 0x59, 0x6d, | 298 | 0x53, 0x4c, 0x51, 0x4d, 0x55, 0x41, 0x31, 0x46, 0x43, 0x45, 0x48, 0x64, 0x71, 0x4e, 0x41, |
| 277 | 0x48, 0x79, 0x34, 0x34, 0x52, 0x45, 0x42, 0x2b, 0x4b, 0x6c, 0x42, 0x53, 0x58, 0x6b, 0x4d, 0x42, 0x65, 0x6a, 0x68, 0x4a, 0x50, 0x46, | 299 | 0x59, 0x79, 0x41, 0x33, 0x49, 0x55, 0x66, 0x41, 0x68, 0x64, 0x55, 0x68, 0x70, 0x69, 0x66, |
| 278 | 0x56, 0x4e, 0x57, 0x6b, 0x51, 0x49, 0x4a, 0x6c, 0x6c, 0x4e, 0x52, 0x51, 0x74, 0x49, 0x43, 0x6a, 0x4e, 0x65, 0x53, 0x6b, 0x31, 0x31, | 300 | 0x44, 0x34, 0x77, 0x66, 0x6c, 0x39, 0x2f, 0x56, 0x41, 0x39, 0x45, 0x53, 0x56, 0x31, 0x65, |
| 279 | 0x46, 0x68, 0x64, 0x6a, 0x52, 0x67, 0x45, 0x71, 0x56, 0x58, 0x73, 0x50, 0x41, 0x6e, 0x4e, 0x71, 0x53, 0x33, 0x39, 0x31, 0x5a, 0x54, | 301 | 0x45, 0x47, 0x6f, 0x47, 0x4b, 0x77, 0x5a, 0x54, 0x45, 0x44, 0x6b, 0x33, 0x4d, 0x6b, 0x70, |
| 280 | 0x78, 0x4d, 0x4d, 0x7a, 0x6c, 0x73, 0x64, 0x41, 0x56, 0x67, 0x44, 0x33, 0x38, 0x74, 0x51, 0x55, 0x31, 0x4e, 0x52, 0x6e, 0x45, 0x4a, | 302 | 0x4f, 0x50, 0x53, 0x74, 0x6c, 0x4f, 0x44, 0x6b, 0x48, 0x63, 0x6c, 0x52, 0x6b, 0x54, 0x56, |
| 281 | 0x62, 0x30, 0x39, 0x67, 0x46, 0x51, 0x39, 0x47, 0x63, 0x32, 0x4e, 0x4d, 0x58, 0x6e, 0x51, 0x77, 0x44, 0x53, 0x68, 0x44, 0x43, 0x48, | 303 | 0x5a, 0x71, 0x41, 0x79, 0x4a, 0x77, 0x65, 0x31, 0x39, 0x67, 0x43, 0x79, 0x6f, 0x4c, 0x61, |
| 282 | 0x49, 0x79, 0x42, 0x43, 0x34, 0x78, 0x4b, 0x53, 0x64, 0x45, 0x62, 0x52, 0x4d, 0x58, 0x53, 0x41, 0x39, 0x4a, 0x55, 0x68, 0x41, 0x54, | 304 | 0x78, 0x42, 0x6b, 0x46, 0x41, 0x55, 0x69, 0x41, 0x48, 0x4e, 0x41, 0x49, 0x31, 0x74, 0x52, |
| 283 | 0x66, 0x78, 0x63, 0x57, 0x59, 0x6e, 0x6b, 0x31, 0x65, 0x44, 0x34, 0x42, 0x66, 0x43, 0x34, 0x50, 0x64, 0x6a, 0x35, 0x65, 0x55, 0x32, | 305 | 0x48, 0x79, 0x73, 0x61, 0x58, 0x57, 0x6c, 0x36, 0x52, 0x67, 0x78, 0x66, 0x4d, 0x6b, 0x74, |
| 284 | 0x78, 0x62, 0x58, 0x33, 0x77, 0x5a, 0x51, 0x51, 0x49, 0x76, 0x46, 0x32, 0x52, 0x42, 0x64, 0x52, 0x41, 0x45, 0x52, 0x33, 0x77, 0x39, | 306 | 0x4b, 0x4b, 0x46, 0x4a, 0x35, 0x57, 0x78, 0x4a, 0x43, 0x47, 0x41, 0x42, 0x64, 0x4a, 0x7a, |
| 285 | 0x53, 0x31, 0x49, 0x41, 0x45, 0x46, 0x31, 0x52, 0x54, 0x6e, 0x6f, 0x6e, 0x4a, 0x56, 0x56, 0x41, 0x45, 0x6a, 0x56, 0x67, 0x42, 0x52, | 307 | 0x46, 0x54, 0x50, 0x45, 0x77, 0x32, 0x54, 0x6a, 0x67, 0x2f, 0x63, 0x67, 0x4e, 0x78, 0x41, |
| 286 | 0x73, 0x30, 0x4c, 0x51, 0x52, 0x36, 0x61, 0x6d, 0x6b, 0x43, 0x65, 0x51, 0x4d, 0x36, 0x4c, 0x77, 0x59, 0x4b, 0x65, 0x58, 0x73, 0x53, | 308 | 0x6c, 0x73, 0x32, 0x57, 0x58, 0x39, 0x31, 0x62, 0x67, 0x68, 0x55, 0x44, 0x54, 0x51, 0x63, |
| 287 | 0x58, 0x58, 0x78, 0x53, 0x4b, 0x55, 0x64, 0x59, 0x45, 0x6e, 0x4d, 0x2f, 0x4a, 0x31, 0x59, 0x46, 0x44, 0x45, 0x67, 0x79, 0x57, 0x47, | 309 | 0x4e, 0x46, 0x64, 0x64, 0x61, 0x55, 0x67, 0x41, 0x4f, 0x77, 0x55, 0x48, 0x62, 0x69, 0x64, |
| 288 | 0x74, 0x58, 0x58, 0x41, 0x4e, 0x6b, 0x56, 0x68, 0x46, 0x53, 0x65, 0x6a, 0x41, 0x32, 0x4b, 0x52, 0x63, 0x37, 0x61, 0x48, 0x70, 0x38, | 310 | 0x6c, 0x62, 0x6b, 0x41, 0x39, 0x4f, 0x6b, 0x39, 0x79, 0x58, 0x54, 0x6b, 0x57, 0x44, 0x32, |
| 289 | 0x42, 0x57, 0x74, 0x72, 0x45, 0x32, 0x6f, 0x6b, 0x58, 0x47, 0x68, 0x43, 0x47, 0x44, 0x49, 0x44, 0x63, 0x32, 0x34, 0x45, 0x49, 0x53, | 311 | 0x4d, 0x53, 0x45, 0x68, 0x55, 0x36, 0x63, 0x41, 0x31, 0x58, 0x47, 0x41, 0x31, 0x65, 0x50, |
| 290 | 0x34, 0x42, 0x42, 0x47, 0x4e, 0x39, 0x52, 0x45, 0x45, 0x53, 0x4d, 0x51, 0x73, 0x56, 0x48, 0x33, 0x41, 0x41, 0x4c, 0x6d, 0x59, 0x55, | 312 | 0x53, 0x4a, 0x6f, 0x61, 0x48, 0x78, 0x74, 0x54, 0x77, 0x78, 0x37, 0x43, 0x53, 0x31, 0x4b, |
| 291 | 0x50, 0x48, 0x38, 0x72, 0x41, 0x42, 0x38, 0x4d, 0x4b, 0x46, 0x6b, 0x4b, 0x46, 0x6b, 0x6c, 0x61, 0x58, 0x47, 0x52, 0x6c, 0x53, 0x78, | 313 | 0x63, 0x79, 0x42, 0x48, 0x42, 0x31, 0x64, 0x31, 0x58, 0x56, 0x4e, 0x77, 0x4e, 0x43, 0x46, |
| 292 | 0x45, 0x70, 0x46, 0x54, 0x5a, 0x61, 0x5a, 0x52, 0x6c, 0x50, 0x59, 0x43, 0x4d, 0x36, 0x4f, 0x68, 0x4d, 0x6c, 0x41, 0x6e, 0x68, 0x4d, | 314 | 0x41, 0x56, 0x31, 0x46, 0x65, 0x53, 0x55, 0x51, 0x41, 0x56, 0x43, 0x63, 0x45, 0x61, 0x48, |
| 293 | 0x56, 0x41, 0x3d, 0x3d, 0x00}; | 315 | 0x35, 0x5a, 0x56, 0x6c, 0x68, 0x30, 0x46, 0x44, 0x77, 0x57, 0x4d, 0x30, 0x45, 0x57, 0x53, |
| 316 | 0x79, 0x39, 0x4a, 0x4e, 0x77, 0x70, 0x55, 0x43, 0x41, 0x67, 0x66, 0x4f, 0x57, 0x64, 0x32, | ||
| 317 | 0x4b, 0x43, 0x67, 0x48, 0x48, 0x57, 0x46, 0x48, 0x55, 0x55, 0x31, 0x31, 0x4a, 0x6c, 0x4a, | ||
| 318 | 0x48, 0x52, 0x77, 0x78, 0x58, 0x57, 0x48, 0x51, 0x2b, 0x59, 0x6d, 0x78, 0x59, 0x4f, 0x6b, | ||
| 319 | 0x51, 0x65, 0x46, 0x69, 0x34, 0x68, 0x48, 0x48, 0x4e, 0x46, 0x5a, 0x33, 0x52, 0x50, 0x4d, | ||
| 320 | 0x32, 0x59, 0x4f, 0x64, 0x47, 0x59, 0x6d, 0x48, 0x79, 0x34, 0x34, 0x52, 0x45, 0x42, 0x2b, | ||
| 321 | 0x4b, 0x6c, 0x42, 0x53, 0x58, 0x6b, 0x4d, 0x42, 0x65, 0x6a, 0x68, 0x4a, 0x50, 0x46, 0x56, | ||
| 322 | 0x4e, 0x57, 0x6b, 0x51, 0x49, 0x4a, 0x6c, 0x6c, 0x4e, 0x52, 0x51, 0x74, 0x49, 0x43, 0x6a, | ||
| 323 | 0x4e, 0x65, 0x53, 0x6b, 0x31, 0x31, 0x46, 0x68, 0x64, 0x6a, 0x52, 0x67, 0x45, 0x71, 0x56, | ||
| 324 | 0x58, 0x73, 0x50, 0x41, 0x6e, 0x4e, 0x71, 0x53, 0x33, 0x39, 0x31, 0x5a, 0x54, 0x78, 0x4d, | ||
| 325 | 0x4d, 0x7a, 0x6c, 0x73, 0x64, 0x41, 0x56, 0x67, 0x44, 0x33, 0x38, 0x74, 0x51, 0x55, 0x31, | ||
| 326 | 0x4e, 0x52, 0x6e, 0x45, 0x4a, 0x62, 0x30, 0x39, 0x67, 0x46, 0x51, 0x39, 0x47, 0x63, 0x32, | ||
| 327 | 0x4e, 0x4d, 0x58, 0x6e, 0x51, 0x77, 0x44, 0x53, 0x68, 0x44, 0x43, 0x48, 0x49, 0x79, 0x42, | ||
| 328 | 0x43, 0x34, 0x78, 0x4b, 0x53, 0x64, 0x45, 0x62, 0x52, 0x4d, 0x58, 0x53, 0x41, 0x39, 0x4a, | ||
| 329 | 0x55, 0x68, 0x41, 0x54, 0x66, 0x78, 0x63, 0x57, 0x59, 0x6e, 0x6b, 0x31, 0x65, 0x44, 0x34, | ||
| 330 | 0x42, 0x66, 0x43, 0x34, 0x50, 0x64, 0x6a, 0x35, 0x65, 0x55, 0x32, 0x78, 0x62, 0x58, 0x33, | ||
| 331 | 0x77, 0x5a, 0x51, 0x51, 0x49, 0x76, 0x46, 0x32, 0x52, 0x42, 0x64, 0x52, 0x41, 0x45, 0x52, | ||
| 332 | 0x33, 0x77, 0x39, 0x53, 0x31, 0x49, 0x41, 0x45, 0x46, 0x31, 0x52, 0x54, 0x6e, 0x6f, 0x6e, | ||
| 333 | 0x4a, 0x56, 0x56, 0x41, 0x45, 0x6a, 0x56, 0x67, 0x42, 0x52, 0x73, 0x30, 0x4c, 0x51, 0x52, | ||
| 334 | 0x36, 0x61, 0x6d, 0x6b, 0x43, 0x65, 0x51, 0x4d, 0x36, 0x4c, 0x77, 0x59, 0x4b, 0x65, 0x58, | ||
| 335 | 0x73, 0x53, 0x58, 0x58, 0x78, 0x53, 0x4b, 0x55, 0x64, 0x59, 0x45, 0x6e, 0x4d, 0x2f, 0x4a, | ||
| 336 | 0x31, 0x59, 0x46, 0x44, 0x45, 0x67, 0x79, 0x57, 0x47, 0x74, 0x58, 0x58, 0x41, 0x4e, 0x6b, | ||
| 337 | 0x56, 0x68, 0x46, 0x53, 0x65, 0x6a, 0x41, 0x32, 0x4b, 0x52, 0x63, 0x37, 0x61, 0x48, 0x70, | ||
| 338 | 0x38, 0x42, 0x57, 0x74, 0x72, 0x45, 0x32, 0x6f, 0x6b, 0x58, 0x47, 0x68, 0x43, 0x47, 0x44, | ||
| 339 | 0x49, 0x44, 0x63, 0x32, 0x34, 0x45, 0x49, 0x53, 0x34, 0x42, 0x42, 0x47, 0x4e, 0x39, 0x52, | ||
| 340 | 0x45, 0x45, 0x53, 0x4d, 0x51, 0x73, 0x56, 0x48, 0x33, 0x41, 0x41, 0x4c, 0x6d, 0x59, 0x55, | ||
| 341 | 0x50, 0x48, 0x38, 0x72, 0x41, 0x42, 0x38, 0x4d, 0x4b, 0x46, 0x6b, 0x4b, 0x46, 0x6b, 0x6c, | ||
| 342 | 0x61, 0x58, 0x47, 0x52, 0x6c, 0x53, 0x78, 0x45, 0x70, 0x46, 0x54, 0x5a, 0x61, 0x5a, 0x52, | ||
| 343 | 0x6c, 0x50, 0x59, 0x43, 0x4d, 0x36, 0x4f, 0x68, 0x4d, 0x6c, 0x41, 0x6e, 0x68, 0x4d, 0x56, | ||
| 344 | 0x41, 0x3d, 0x3d, 0x00}; | ||
| 294 | char *b64_test; | 345 | char *b64_test; |
| 295 | 346 | ||
| 296 | plan_tests(1); | 347 | plan_tests(1); |
diff --git a/lib/tests/test_cmd.c b/lib/tests/test_cmd.c index c8867dfb..d51016cc 100644 --- a/lib/tests/test_cmd.c +++ b/lib/tests/test_cmd.c | |||
| @@ -38,36 +38,35 @@ char *get_command(char *const *line) { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | int main(int argc, char **argv) { | 40 | int main(int argc, char **argv) { |
| 41 | char **command_line = malloc(sizeof(char *) * COMMAND_LINE); | ||
| 42 | char *command = NULL; | ||
| 43 | char *perl; | ||
| 44 | output chld_out, chld_err; | ||
| 45 | int c; | ||
| 46 | int result = UNSET; | ||
| 47 | |||
| 48 | plan_tests(51); | 41 | plan_tests(51); |
| 49 | 42 | ||
| 50 | diag("Running plain echo command, set one"); | 43 | diag("Running plain echo command, set one"); |
| 51 | 44 | ||
| 52 | /* ensure everything is empty before we begin */ | 45 | /* ensure everything is empty before we begin */ |
| 46 | |||
| 47 | output chld_out; | ||
| 53 | memset(&chld_out, 0, sizeof(output)); | 48 | memset(&chld_out, 0, sizeof(output)); |
| 49 | output chld_err; | ||
| 54 | memset(&chld_err, 0, sizeof(output)); | 50 | memset(&chld_err, 0, sizeof(output)); |
| 55 | ok(chld_out.lines == 0, "(initialised) Checking stdout is reset"); | 51 | ok(chld_out.lines == 0, "(initialised) Checking stdout is reset"); |
| 56 | ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); | 52 | ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); |
| 53 | int result = UNSET; | ||
| 57 | ok(result == UNSET, "(initialised) Checking exit code is reset"); | 54 | ok(result == UNSET, "(initialised) Checking exit code is reset"); |
| 58 | 55 | ||
| 56 | char **command_line = malloc(sizeof(char *) * COMMAND_LINE); | ||
| 59 | command_line[0] = strdup("/bin/echo"); | 57 | command_line[0] = strdup("/bin/echo"); |
| 60 | command_line[1] = strdup("this"); | 58 | command_line[1] = strdup("this"); |
| 61 | command_line[2] = strdup("is"); | 59 | command_line[2] = strdup("is"); |
| 62 | command_line[3] = strdup("test"); | 60 | command_line[3] = strdup("test"); |
| 63 | command_line[4] = strdup("one"); | 61 | command_line[4] = strdup("one"); |
| 64 | 62 | ||
| 65 | command = get_command(command_line); | 63 | char *command = get_command(command_line); |
| 66 | 64 | ||
| 67 | result = cmd_run_array(command_line, &chld_out, &chld_err, 0); | 65 | result = cmd_run_array(command_line, &chld_out, &chld_err, 0); |
| 68 | ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); | 66 | ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); |
| 69 | ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); | 67 | ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); |
| 70 | ok(strcmp(chld_out.line[0], "this is test one") == 0, "(array) Check for expected stdout output"); | 68 | ok(strcmp(chld_out.line[0], "this is test one") == 0, |
| 69 | "(array) Check for expected stdout output"); | ||
| 71 | ok(result == 0, "(array) Checking exit code"); | 70 | ok(result == 0, "(array) Checking exit code"); |
| 72 | 71 | ||
| 73 | /* ensure everything is empty again */ | 72 | /* ensure everything is empty again */ |
| @@ -82,7 +81,8 @@ int main(int argc, char **argv) { | |||
| 82 | 81 | ||
| 83 | ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); | 82 | ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); |
| 84 | ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); | 83 | ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); |
| 85 | ok(strcmp(chld_out.line[0], "this is test one") == 0, "(string) Check for expected stdout output"); | 84 | ok(strcmp(chld_out.line[0], "this is test one") == 0, |
| 85 | "(string) Check for expected stdout output"); | ||
| 86 | ok(result == 0, "(string) Checking exit code"); | 86 | ok(result == 0, "(string) Checking exit code"); |
| 87 | 87 | ||
| 88 | diag("Running plain echo command, set two"); | 88 | diag("Running plain echo command, set two"); |
| @@ -104,7 +104,8 @@ int main(int argc, char **argv) { | |||
| 104 | result = cmd_run_array(command_line, &chld_out, &chld_err, 0); | 104 | result = cmd_run_array(command_line, &chld_out, &chld_err, 0); |
| 105 | ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); | 105 | ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); |
| 106 | ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); | 106 | ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); |
| 107 | ok(strcmp(chld_out.line[0], "this is test two") == 0, "(array) Check for expected stdout output"); | 107 | ok(strcmp(chld_out.line[0], "this is test two") == 0, |
| 108 | "(array) Check for expected stdout output"); | ||
| 108 | ok(result == 0, "(array) Checking exit code"); | 109 | ok(result == 0, "(array) Checking exit code"); |
| 109 | 110 | ||
| 110 | /* ensure everything is empty again */ | 111 | /* ensure everything is empty again */ |
| @@ -119,7 +120,8 @@ int main(int argc, char **argv) { | |||
| 119 | 120 | ||
| 120 | ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); | 121 | ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); |
| 121 | ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); | 122 | ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); |
| 122 | ok(strcmp(chld_out.line[0], "this is test one") == 0, "(string) Check for expected stdout output"); | 123 | ok(strcmp(chld_out.line[0], "this is test one") == 0, |
| 124 | "(string) Check for expected stdout output"); | ||
| 123 | ok(result == 0, "(string) Checking exit code"); | 125 | ok(result == 0, "(string) Checking exit code"); |
| 124 | 126 | ||
| 125 | /* ensure everything is empty again */ | 127 | /* ensure everything is empty again */ |
| @@ -130,7 +132,8 @@ int main(int argc, char **argv) { | |||
| 130 | ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); | 132 | ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); |
| 131 | ok(result == UNSET, "(initialised) Checking exit code is reset"); | 133 | ok(result == UNSET, "(initialised) Checking exit code is reset"); |
| 132 | 134 | ||
| 133 | /* Pass linefeeds via parameters through - those should be evaluated by echo to give multi line output */ | 135 | /* Pass linefeeds via parameters through - those should be evaluated by echo to give multi line |
| 136 | * output */ | ||
| 134 | command_line[0] = strdup("/bin/echo"); | 137 | command_line[0] = strdup("/bin/echo"); |
| 135 | command_line[1] = strdup("this is a test via echo\nline two\nit's line 3"); | 138 | command_line[1] = strdup("this is a test via echo\nline two\nit's line 3"); |
| 136 | command_line[2] = strdup("and (note space between '3' and 'and') $$ will not get evaluated"); | 139 | command_line[2] = strdup("and (note space between '3' and 'and') $$ will not get evaluated"); |
| @@ -138,9 +141,12 @@ int main(int argc, char **argv) { | |||
| 138 | result = cmd_run_array(command_line, &chld_out, &chld_err, 0); | 141 | result = cmd_run_array(command_line, &chld_out, &chld_err, 0); |
| 139 | ok(chld_out.lines == 3, "(array) Check for expected number of stdout lines"); | 142 | ok(chld_out.lines == 3, "(array) Check for expected number of stdout lines"); |
| 140 | ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); | 143 | ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); |
| 141 | ok(strcmp(chld_out.line[0], "this is a test via echo") == 0, "(array) Check line 1 for expected stdout output"); | 144 | ok(strcmp(chld_out.line[0], "this is a test via echo") == 0, |
| 142 | ok(strcmp(chld_out.line[1], "line two") == 0, "(array) Check line 2 for expected stdout output"); | 145 | "(array) Check line 1 for expected stdout output"); |
| 143 | ok(strcmp(chld_out.line[2], "it's line 3 and (note space between '3' and 'and') $$ will not get evaluated") == 0, | 146 | ok(strcmp(chld_out.line[1], "line two") == 0, |
| 147 | "(array) Check line 2 for expected stdout output"); | ||
| 148 | ok(strcmp(chld_out.line[2], | ||
| 149 | "it's line 3 and (note space between '3' and 'and') $$ will not get evaluated") == 0, | ||
| 144 | "(array) Check line 3 for expected stdout output"); | 150 | "(array) Check line 3 for expected stdout output"); |
| 145 | ok(result == 0, "(array) Checking exit code"); | 151 | ok(result == 0, "(array) Checking exit code"); |
| 146 | 152 | ||
| @@ -171,7 +177,8 @@ int main(int argc, char **argv) { | |||
| 171 | 177 | ||
| 172 | ok(chld_out.lines == 0, "/bin/sh returns no stdout when file is missing..."); | 178 | ok(chld_out.lines == 0, "/bin/sh returns no stdout when file is missing..."); |
| 173 | ok(chld_err.lines == 1, "...but does give an error line"); | 179 | ok(chld_err.lines == 1, "...but does give an error line"); |
| 174 | ok(strstr(chld_err.line[0], "non-existent-file") != NULL, "And missing filename is in error message"); | 180 | ok(strstr(chld_err.line[0], "non-existent-file") != NULL, |
| 181 | "And missing filename is in error message"); | ||
| 175 | ok(result != 0, "Get non-zero return code from /bin/sh"); | 182 | ok(result != 0, "Get non-zero return code from /bin/sh"); |
| 176 | 183 | ||
| 177 | /* ensure everything is empty again */ | 184 | /* ensure everything is empty again */ |
diff --git a/lib/tests/test_disk.t b/lib/tests/test_disk.t deleted file mode 100755 index da84dfdf..00000000 --- a/lib/tests/test_disk.t +++ /dev/null | |||
| @@ -1,6 +0,0 @@ | |||
| 1 | #!/usr/bin/perl | ||
| 2 | use Test::More; | ||
| 3 | if (! -e "./test_disk") { | ||
| 4 | plan skip_all => "./test_disk not compiled - please enable libtap library to test"; | ||
| 5 | } | ||
| 6 | exec "./test_disk"; | ||
diff --git a/lib/tests/test_generic_output.c b/lib/tests/test_generic_output.c new file mode 100644 index 00000000..e4a78bcd --- /dev/null +++ b/lib/tests/test_generic_output.c | |||
| @@ -0,0 +1,317 @@ | |||
| 1 | #include "../lib/output.h" | ||
| 2 | #include "../../tap/tap.h" | ||
| 3 | #include "./states.h" | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | |||
| 7 | void test_one_subcheck(void); | ||
| 8 | void test_two_subchecks(void); | ||
| 9 | |||
| 10 | void test_perfdata_formatting(void); | ||
| 11 | void test_perfdata_formatting2(void); | ||
| 12 | |||
| 13 | void test_deep_check_hierarchy(void); | ||
| 14 | void test_deep_check_hierarchy2(void); | ||
| 15 | |||
| 16 | void test_default_states1(void); | ||
| 17 | void test_default_states2(void); | ||
| 18 | |||
| 19 | int main(void) { | ||
| 20 | plan_tests(19); | ||
| 21 | |||
| 22 | diag("Simple test with one subcheck"); | ||
| 23 | test_one_subcheck(); | ||
| 24 | |||
| 25 | diag("Test with two subchecks"); | ||
| 26 | test_two_subchecks(); | ||
| 27 | |||
| 28 | diag("Test for performance data formatting"); | ||
| 29 | test_perfdata_formatting(); | ||
| 30 | |||
| 31 | diag("Another test for performance data formatting"); | ||
| 32 | test_perfdata_formatting2(); | ||
| 33 | |||
| 34 | diag("Test for deeper hierarchies"); | ||
| 35 | test_deep_check_hierarchy(); | ||
| 36 | |||
| 37 | diag("Another test for deeper hierarchies"); | ||
| 38 | test_deep_check_hierarchy2(); | ||
| 39 | |||
| 40 | diag("Testing the default state logic"); | ||
| 41 | test_default_states1(); | ||
| 42 | |||
| 43 | diag("Testing the default state logic #2"); | ||
| 44 | test_default_states2(); | ||
| 45 | |||
| 46 | return exit_status(); | ||
| 47 | } | ||
| 48 | |||
| 49 | void test_one_subcheck(void) { | ||
| 50 | mp_subcheck sc1 = mp_subcheck_init(); | ||
| 51 | |||
| 52 | sc1.output = "foobar"; | ||
| 53 | sc1 = mp_set_subcheck_state(sc1, STATE_WARNING); | ||
| 54 | |||
| 55 | mp_check check = mp_check_init(); | ||
| 56 | mp_add_subcheck_to_check(&check, sc1); | ||
| 57 | |||
| 58 | ok(mp_compute_check_state(check) == STATE_WARNING, "Main state should be warning"); | ||
| 59 | |||
| 60 | char *output = mp_fmt_output(check); | ||
| 61 | |||
| 62 | // diag("Formatted output"); | ||
| 63 | // diag(output); | ||
| 64 | |||
| 65 | char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n" | ||
| 66 | "\t\\_[WARNING] - foobar\n"; | ||
| 67 | |||
| 68 | // diag("Expected output"); | ||
| 69 | // diag(expected); | ||
| 70 | |||
| 71 | ok(strcmp(output, expected) == 0, "Simple output test"); | ||
| 72 | } | ||
| 73 | |||
| 74 | void test_perfdata_formatting2(void) { | ||
| 75 | mp_perfdata pd1 = perfdata_init(); | ||
| 76 | mp_perfdata pd2 = perfdata_init(); | ||
| 77 | |||
| 78 | pd1.label = "foo"; | ||
| 79 | pd2.label = "bar"; | ||
| 80 | |||
| 81 | pd1 = mp_set_pd_value(pd1, 23); | ||
| 82 | pd2 = mp_set_pd_value(pd2, 1LL); | ||
| 83 | |||
| 84 | pd_list *tmp = pd_list_init(); | ||
| 85 | |||
| 86 | pd_list_append(tmp, pd1); | ||
| 87 | pd_list_append(tmp, pd2); | ||
| 88 | |||
| 89 | char *result = pd_list_to_string(*tmp); | ||
| 90 | |||
| 91 | ok(strcmp(result, "foo=23;;; bar=1;;;") == 0, "Perfdata string formatting"); | ||
| 92 | } | ||
| 93 | |||
| 94 | void test_perfdata_formatting(void) { | ||
| 95 | mp_perfdata pd1 = perfdata_init(); | ||
| 96 | |||
| 97 | pd1.uom = "s"; | ||
| 98 | pd1.label = "foo"; | ||
| 99 | |||
| 100 | pd1 = mp_set_pd_value(pd1, 23); | ||
| 101 | |||
| 102 | char *pd_string = pd_to_string(pd1); | ||
| 103 | |||
| 104 | ok(strcmp(pd_string, "foo=23s;;;") == 0, "Perfdata string formatting"); | ||
| 105 | } | ||
| 106 | |||
| 107 | void test_two_subchecks(void) { | ||
| 108 | mp_subcheck sc1 = mp_subcheck_init(); | ||
| 109 | |||
| 110 | sc1.output = "foobar"; | ||
| 111 | sc1 = mp_set_subcheck_state(sc1, STATE_WARNING); | ||
| 112 | |||
| 113 | ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, | ||
| 114 | "Test subcheck state directly after setting it"); | ||
| 115 | |||
| 116 | mp_perfdata pd1 = perfdata_init(); | ||
| 117 | |||
| 118 | pd1 = mp_set_pd_value(pd1, 23); | ||
| 119 | |||
| 120 | pd1.uom = "s"; | ||
| 121 | pd1.label = "foo"; | ||
| 122 | |||
| 123 | mp_add_perfdata_to_subcheck(&sc1, pd1); | ||
| 124 | |||
| 125 | mp_subcheck sc2 = mp_subcheck_init(); | ||
| 126 | sc2.output = "baz"; | ||
| 127 | sc2 = mp_set_subcheck_state(sc2, STATE_OK); | ||
| 128 | |||
| 129 | ok(mp_compute_subcheck_state(sc2) == STATE_OK, "Test subcheck 2 state after setting it"); | ||
| 130 | |||
| 131 | mp_add_subcheck_to_subcheck(&sc1, sc2); | ||
| 132 | |||
| 133 | ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, | ||
| 134 | "Test subcheck state after adding a subcheck"); | ||
| 135 | |||
| 136 | mp_check check = mp_check_init(); | ||
| 137 | mp_add_subcheck_to_check(&check, sc1); | ||
| 138 | |||
| 139 | ok(mp_compute_check_state(check) == STATE_WARNING, "Test main check result"); | ||
| 140 | |||
| 141 | char *output = mp_fmt_output(check); | ||
| 142 | |||
| 143 | // diag("Formatted output. Length: %u", strlen(output)); | ||
| 144 | // diag(output); | ||
| 145 | |||
| 146 | ok(output != NULL, "Output should not be NULL"); | ||
| 147 | |||
| 148 | char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n" | ||
| 149 | "\t\\_[WARNING] - foobar\n" | ||
| 150 | "\t\t\\_[OK] - baz\n" | ||
| 151 | "|foo=23s;;; \n"; | ||
| 152 | |||
| 153 | // diag("Expected output. Length: %u", strlen(expected)); | ||
| 154 | // diag(expected); | ||
| 155 | |||
| 156 | ok(strcmp(output, expected) == 0, "Output is as expected"); | ||
| 157 | } | ||
| 158 | |||
| 159 | void test_deep_check_hierarchy(void) { | ||
| 160 | // level 4 | ||
| 161 | mp_subcheck sc4 = mp_subcheck_init(); | ||
| 162 | sc4.output = "level4"; | ||
| 163 | sc4 = mp_set_subcheck_state(sc4, STATE_OK); | ||
| 164 | |||
| 165 | // level 3 | ||
| 166 | mp_subcheck sc3 = mp_subcheck_init(); | ||
| 167 | sc3.output = "level3"; | ||
| 168 | sc3 = mp_set_subcheck_state(sc3, STATE_OK); | ||
| 169 | |||
| 170 | // level 2 | ||
| 171 | mp_subcheck sc2 = mp_subcheck_init(); | ||
| 172 | sc2.output = "baz"; | ||
| 173 | sc2 = mp_set_subcheck_state(sc2, STATE_OK); | ||
| 174 | |||
| 175 | // level 1 | ||
| 176 | mp_subcheck sc1 = mp_subcheck_init(); | ||
| 177 | |||
| 178 | sc1.output = "foobar"; | ||
| 179 | sc1 = mp_set_subcheck_state(sc1, STATE_WARNING); | ||
| 180 | |||
| 181 | mp_perfdata pd1 = perfdata_init(); | ||
| 182 | |||
| 183 | pd1.uom = "s"; | ||
| 184 | pd1.label = "foo"; | ||
| 185 | pd1 = mp_set_pd_value(pd1, 23); | ||
| 186 | |||
| 187 | mp_add_perfdata_to_subcheck(&sc1, pd1); | ||
| 188 | |||
| 189 | // main check | ||
| 190 | mp_check check = mp_check_init(); | ||
| 191 | |||
| 192 | mp_add_subcheck_to_subcheck(&sc3, sc4); | ||
| 193 | mp_add_subcheck_to_subcheck(&sc2, sc3); | ||
| 194 | mp_add_subcheck_to_subcheck(&sc1, sc2); | ||
| 195 | mp_add_subcheck_to_check(&check, sc1); | ||
| 196 | |||
| 197 | char *output = mp_fmt_output(check); | ||
| 198 | |||
| 199 | size_t output_length = strlen(output); | ||
| 200 | |||
| 201 | // diag("Formatted output of length %i", output_length); | ||
| 202 | // diag(output); | ||
| 203 | |||
| 204 | ok(output != NULL, "Output should not be NULL"); | ||
| 205 | |||
| 206 | char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n" | ||
| 207 | "\t\\_[WARNING] - foobar\n" | ||
| 208 | "\t\t\\_[OK] - baz\n" | ||
| 209 | "\t\t\t\\_[OK] - level3\n" | ||
| 210 | "\t\t\t\t\\_[OK] - level4\n" | ||
| 211 | "|foo=23s;;; \n"; | ||
| 212 | |||
| 213 | size_t expected_length = strlen(expected); | ||
| 214 | |||
| 215 | // diag("Expected output of length: %i", expected_length); | ||
| 216 | // diag(expected); | ||
| 217 | |||
| 218 | ok(output_length == expected_length, "Outputs are of equal length"); | ||
| 219 | ok(strcmp(output, expected) == 0, "Output is as expected"); | ||
| 220 | } | ||
| 221 | |||
| 222 | void test_deep_check_hierarchy2(void) { | ||
| 223 | // level 1 | ||
| 224 | mp_subcheck sc1 = mp_subcheck_init(); | ||
| 225 | |||
| 226 | sc1.output = "foobar"; | ||
| 227 | sc1 = mp_set_subcheck_state(sc1, STATE_WARNING); | ||
| 228 | |||
| 229 | mp_perfdata pd1 = perfdata_init(); | ||
| 230 | pd1.uom = "s"; | ||
| 231 | pd1.label = "foo"; | ||
| 232 | pd1 = mp_set_pd_value(pd1, 23); | ||
| 233 | |||
| 234 | mp_add_perfdata_to_subcheck(&sc1, pd1); | ||
| 235 | |||
| 236 | // level 2 | ||
| 237 | mp_subcheck sc2 = mp_subcheck_init(); | ||
| 238 | sc2.output = "baz"; | ||
| 239 | sc2 = mp_set_subcheck_state(sc2, STATE_OK); | ||
| 240 | |||
| 241 | mp_perfdata pd2 = perfdata_init(); | ||
| 242 | pd2.uom = "B"; | ||
| 243 | pd2.label = "baz"; | ||
| 244 | pd2 = mp_set_pd_value(pd2, 1024); | ||
| 245 | mp_add_perfdata_to_subcheck(&sc2, pd2); | ||
| 246 | |||
| 247 | // level 3 | ||
| 248 | mp_subcheck sc3 = mp_subcheck_init(); | ||
| 249 | sc3.output = "level3"; | ||
| 250 | sc3 = mp_set_subcheck_state(sc3, STATE_OK); | ||
| 251 | |||
| 252 | mp_perfdata pd3 = perfdata_init(); | ||
| 253 | pd3.label = "floatMe"; | ||
| 254 | pd3 = mp_set_pd_value(pd3, 1024.1024); | ||
| 255 | mp_add_perfdata_to_subcheck(&sc3, pd3); | ||
| 256 | |||
| 257 | // level 4 | ||
| 258 | mp_subcheck sc4 = mp_subcheck_init(); | ||
| 259 | sc4.output = "level4"; | ||
| 260 | sc4 = mp_set_subcheck_state(sc4, STATE_OK); | ||
| 261 | |||
| 262 | mp_check check = mp_check_init(); | ||
| 263 | |||
| 264 | mp_add_subcheck_to_subcheck(&sc3, sc4); | ||
| 265 | mp_add_subcheck_to_subcheck(&sc2, sc3); | ||
| 266 | mp_add_subcheck_to_subcheck(&sc1, sc2); | ||
| 267 | mp_add_subcheck_to_check(&check, sc1); | ||
| 268 | |||
| 269 | char *output = mp_fmt_output(check); | ||
| 270 | |||
| 271 | // diag("Formatted output of length: %i", strlen(output)); | ||
| 272 | // diag(output); | ||
| 273 | |||
| 274 | ok(output != NULL, "Output should not be NULL"); | ||
| 275 | |||
| 276 | char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n" | ||
| 277 | "\t\\_[WARNING] - foobar\n" | ||
| 278 | "\t\t\\_[OK] - baz\n" | ||
| 279 | "\t\t\t\\_[OK] - level3\n" | ||
| 280 | "\t\t\t\t\\_[OK] - level4\n" | ||
| 281 | "|foo=23s;;; baz=1024B;;; floatMe=1024.102400;;; \n"; | ||
| 282 | |||
| 283 | // diag("Expected output of length: %i", strlen(expected)); | ||
| 284 | // diag(expected); | ||
| 285 | |||
| 286 | ok(strcmp(output, expected) == 0, "Output is as expected"); | ||
| 287 | } | ||
| 288 | |||
| 289 | void test_default_states1(void) { | ||
| 290 | mp_subcheck sc = mp_subcheck_init(); | ||
| 291 | |||
| 292 | mp_state_enum state1 = mp_compute_subcheck_state(sc); | ||
| 293 | ok(state1 == STATE_UNKNOWN, "Default default state is Unknown"); | ||
| 294 | |||
| 295 | sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL); | ||
| 296 | |||
| 297 | mp_state_enum state2 = mp_compute_subcheck_state(sc); | ||
| 298 | ok(state2 == STATE_CRITICAL, "Default state is Critical"); | ||
| 299 | |||
| 300 | sc = mp_set_subcheck_state(sc, STATE_OK); | ||
| 301 | |||
| 302 | mp_state_enum state3 = mp_compute_subcheck_state(sc); | ||
| 303 | ok(state3 == STATE_OK, "Default state is Critical"); | ||
| 304 | } | ||
| 305 | |||
| 306 | void test_default_states2(void) { | ||
| 307 | mp_check check = mp_check_init(); | ||
| 308 | |||
| 309 | mp_subcheck sc = mp_subcheck_init(); | ||
| 310 | sc.output = "placeholder"; | ||
| 311 | sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL); | ||
| 312 | |||
| 313 | mp_add_subcheck_to_check(&check, sc); | ||
| 314 | |||
| 315 | mp_state_enum result_state = mp_compute_check_state(check); | ||
| 316 | ok(result_state == STATE_CRITICAL, "Derived state is the proper default state"); | ||
| 317 | } | ||
diff --git a/lib/tests/test_generic_output.t b/lib/tests/test_generic_output.t new file mode 100644 index 00000000..48c2ddf4 --- /dev/null +++ b/lib/tests/test_generic_output.t | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #!/usr/bin/perl | ||
| 2 | use Test::More; | ||
| 3 | if (! -e "./test_generic_output") { | ||
| 4 | plan skip_all => "./test_generic_output not compiled - please enable libtap library to test"; | ||
| 5 | } | ||
| 6 | exec "./test_generic_output"; | ||
diff --git a/lib/tests/test_ini1.c b/lib/tests/test_ini1.c index 246c1250..de983764 100644 --- a/lib/tests/test_ini1.c +++ b/lib/tests/test_ini1.c | |||
| @@ -42,27 +42,30 @@ char *list2str(np_arg_list *optlst) { | |||
| 42 | free(optltmp); | 42 | free(optltmp); |
| 43 | } | 43 | } |
| 44 | /* Strip last whitespace */ | 44 | /* Strip last whitespace */ |
| 45 | if (strlen(optstr) > 1) | 45 | if (strlen(optstr) > 1) { |
| 46 | optstr[strlen(optstr) - 1] = '\0'; | 46 | optstr[strlen(optstr) - 1] = '\0'; |
| 47 | } | ||
| 47 | 48 | ||
| 48 | return optstr; | 49 | return optstr; |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | int main(int argc, char **argv) { | 52 | int main(int argc, char **argv) { |
| 52 | char *optstr = NULL; | ||
| 53 | 53 | ||
| 54 | plan_tests(12); | 54 | plan_tests(12); |
| 55 | 55 | ||
| 56 | optstr = list2str(np_get_defaults("section@./config-tiny.ini", "check_disk")); | 56 | char *optstr = list2str(np_get_defaults("section@./config-tiny.ini", "check_disk")); |
| 57 | ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), "config-tiny.ini's section as expected"); | 57 | ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), |
| 58 | "config-tiny.ini's section as expected"); | ||
| 58 | my_free(optstr); | 59 | my_free(optstr); |
| 59 | 60 | ||
| 60 | optstr = list2str(np_get_defaults("@./config-tiny.ini", "section")); | 61 | optstr = list2str(np_get_defaults("@./config-tiny.ini", "section")); |
| 61 | ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), "Used default section name, without specific"); | 62 | ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), |
| 63 | "Used default section name, without specific"); | ||
| 62 | my_free(optstr); | 64 | my_free(optstr); |
| 63 | 65 | ||
| 64 | optstr = list2str(np_get_defaults("Section Two@./config-tiny.ini", "check_disk")); | 66 | optstr = list2str(np_get_defaults("Section Two@./config-tiny.ini", "check_disk")); |
| 65 | ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), "config-tiny.ini's Section Two as expected"); | 67 | ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), |
| 68 | "config-tiny.ini's Section Two as expected"); | ||
| 66 | my_free(optstr); | 69 | my_free(optstr); |
| 67 | 70 | ||
| 68 | optstr = list2str(np_get_defaults("/path/to/file.txt@./config-tiny.ini", "check_disk")); | 71 | optstr = list2str(np_get_defaults("/path/to/file.txt@./config-tiny.ini", "check_disk")); |
| @@ -70,15 +73,18 @@ int main(int argc, char **argv) { | |||
| 70 | my_free(optstr); | 73 | my_free(optstr); |
| 71 | 74 | ||
| 72 | optstr = list2str(np_get_defaults("section2@./config-tiny.ini", "check_disk")); | 75 | optstr = list2str(np_get_defaults("section2@./config-tiny.ini", "check_disk")); |
| 73 | ok(!strcmp(optstr, "--this=that"), "config-tiny.ini's section2 with whitespace before section name"); | 76 | ok(!strcmp(optstr, "--this=that"), |
| 77 | "config-tiny.ini's section2 with whitespace before section name"); | ||
| 74 | my_free(optstr); | 78 | my_free(optstr); |
| 75 | 79 | ||
| 76 | optstr = list2str(np_get_defaults("section3@./config-tiny.ini", "check_disk")); | 80 | optstr = list2str(np_get_defaults("section3@./config-tiny.ini", "check_disk")); |
| 77 | ok(!strcmp(optstr, "--this=that"), "config-tiny.ini's section3 with whitespace after section name"); | 81 | ok(!strcmp(optstr, "--this=that"), |
| 82 | "config-tiny.ini's section3 with whitespace after section name"); | ||
| 78 | my_free(optstr); | 83 | my_free(optstr); |
| 79 | 84 | ||
| 80 | optstr = list2str(np_get_defaults("check_mysql@./plugin.ini", "check_disk")); | 85 | optstr = list2str(np_get_defaults("check_mysql@./plugin.ini", "check_disk")); |
| 81 | ok(!strcmp(optstr, "--username=operator --password=secret"), "plugin.ini's check_mysql as expected"); | 86 | ok(!strcmp(optstr, "--username=operator --password=secret"), |
| 87 | "plugin.ini's check_mysql as expected"); | ||
| 82 | my_free(optstr); | 88 | my_free(optstr); |
| 83 | 89 | ||
| 84 | optstr = list2str(np_get_defaults("check_mysql2@./plugin.ini", "check_disk")); | 90 | optstr = list2str(np_get_defaults("check_mysql2@./plugin.ini", "check_disk")); |
| @@ -90,29 +96,39 @@ int main(int argc, char **argv) { | |||
| 90 | my_free(optstr); | 96 | my_free(optstr); |
| 91 | 97 | ||
| 92 | optstr = list2str(np_get_defaults("Section Two@./config-dos.ini", "check_disk")); | 98 | optstr = list2str(np_get_defaults("Section Two@./config-dos.ini", "check_disk")); |
| 93 | ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), "config-dos.ini's Section Two as expected"); | 99 | ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), |
| 100 | "config-dos.ini's Section Two as expected"); | ||
| 94 | my_free(optstr); | 101 | my_free(optstr); |
| 95 | 102 | ||
| 96 | optstr = list2str(np_get_defaults("section_twice@./plugin.ini", "check_disk")); | 103 | optstr = list2str(np_get_defaults("section_twice@./plugin.ini", "check_disk")); |
| 97 | ok(!strcmp(optstr, "--foo=bar --bar=foo"), "plugin.ini's section_twice defined twice in the file"); | 104 | ok(!strcmp(optstr, "--foo=bar --bar=foo"), |
| 105 | "plugin.ini's section_twice defined twice in the file"); | ||
| 98 | my_free(optstr); | 106 | my_free(optstr); |
| 99 | 107 | ||
| 100 | optstr = list2str(np_get_defaults("tcp_long_lines@plugins.ini", "check_tcp")); | 108 | optstr = list2str(np_get_defaults("tcp_long_lines@plugins.ini", "check_tcp")); |
| 101 | ok(!strcmp(optstr, "--escape --send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar " | 109 | ok(!strcmp(optstr, "--escape --send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 110 | "yadda Foo bar BAZ yadda yadda yadda Foo bar " | ||
| 102 | "BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 111 | "BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 103 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " | 112 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar " |
| 113 | "BAZ yadda yadda yadda Foo bar BAZ yadda " | ||
| 104 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo " | 114 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo " |
| 105 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 115 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " |
| 116 | "yadda yadda Foo bar BAZ yadda yadda " | ||
| 106 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " | 117 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " |
| 107 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda --expect=Foo bar BAZ yadda yadda " | 118 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 119 | "yadda --expect=Foo bar BAZ yadda yadda " | ||
| 108 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " | 120 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " |
| 109 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo " | 121 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 122 | "yadda Foo bar BAZ yadda yadda yadda Foo " | ||
| 110 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 123 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 111 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " | 124 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar " |
| 125 | "BAZ yadda yadda yadda Foo bar BAZ yadda " | ||
| 112 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo " | 126 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo " |
| 113 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 127 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " |
| 128 | "yadda yadda Foo bar BAZ yadda yadda " | ||
| 114 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " | 129 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " |
| 115 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo " | 130 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 131 | "yadda Foo bar BAZ yadda yadda yadda Foo " | ||
| 116 | "bar BAZ yadda yadda yadda --jail"), | 132 | "bar BAZ yadda yadda yadda --jail"), |
| 117 | "Long options"); | 133 | "Long options"); |
| 118 | my_free(optstr); | 134 | my_free(optstr); |
diff --git a/lib/tests/test_opts1.c b/lib/tests/test_opts1.c index 984183d3..fa95c4d4 100644 --- a/lib/tests/test_opts1.c +++ b/lib/tests/test_opts1.c | |||
| @@ -40,37 +40,40 @@ void my_free(int *argc, char **newargv, char **argv) { | |||
| 40 | #else | 40 | #else |
| 41 | void my_free(int *argc, char **newargv, char **argv) { | 41 | void my_free(int *argc, char **newargv, char **argv) { |
| 42 | /* Free stuff (and print while we're at it) */ | 42 | /* Free stuff (and print while we're at it) */ |
| 43 | int i, freeflag = 1; | 43 | bool freeflag = true; |
| 44 | printf(" Arg(%i): ", *argc + 1); | 44 | printf(" Arg(%i): ", *argc + 1); |
| 45 | printf("'%s' ", newargv[0]); | 45 | printf("'%s' ", newargv[0]); |
| 46 | for (i = 1; i < *argc; i++) { | 46 | |
| 47 | for (int i = 1; i < *argc; i++) { | ||
| 47 | printf("'%s' ", newargv[i]); | 48 | printf("'%s' ", newargv[i]); |
| 48 | /* Stop freeing when we get to the start of the original array */ | 49 | /* Stop freeing when we get to the start of the original array */ |
| 49 | if (freeflag) { | 50 | if (freeflag) { |
| 50 | if (newargv[i] == argv[1]) | 51 | if (newargv[i] == argv[1]) { |
| 51 | freeflag = 0; | 52 | freeflag = false; |
| 52 | else | 53 | } else { |
| 53 | free(newargv[i]); | 54 | free(newargv[i]); |
| 55 | } | ||
| 54 | } | 56 | } |
| 55 | } | 57 | } |
| 56 | printf("\n"); | 58 | printf("\n"); |
| 57 | /* Free only if it's a different array */ | 59 | /* Free only if it's a different array */ |
| 58 | if (newargv != argv) | 60 | if (newargv != argv) { |
| 59 | free(newargv); | 61 | free(newargv); |
| 62 | } | ||
| 60 | *argc = 0; | 63 | *argc = 0; |
| 61 | } | 64 | } |
| 62 | #endif | 65 | #endif |
| 63 | 66 | ||
| 64 | int array_diff(int i1, char **a1, int i2, char **a2) { | 67 | int array_diff(int i1, char **a1, int i2, char **a2) { |
| 65 | int i; | ||
| 66 | |||
| 67 | if (i1 != i2) { | 68 | if (i1 != i2) { |
| 68 | printf(" Argument count doesn't match!\n"); | 69 | printf(" Argument count doesn't match!\n"); |
| 69 | return 0; | 70 | return 0; |
| 70 | } | 71 | } |
| 71 | for (i = 0; i <= i1; i++) { | 72 | |
| 72 | if (a1[i] == NULL && a2[i] == NULL) | 73 | for (int i = 0; i <= i1; i++) { |
| 74 | if (a1[i] == NULL && a2[i] == NULL) { | ||
| 73 | continue; | 75 | continue; |
| 76 | } | ||
| 74 | if (a1[i] == NULL || a2[i] == NULL) { | 77 | if (a1[i] == NULL || a2[i] == NULL) { |
| 75 | printf(" Argument # %i null in one array!\n", i); | 78 | printf(" Argument # %i null in one array!\n", i); |
| 76 | return 0; | 79 | return 0; |
| @@ -84,11 +87,10 @@ int array_diff(int i1, char **a1, int i2, char **a2) { | |||
| 84 | } | 87 | } |
| 85 | 88 | ||
| 86 | int main(int argc, char **argv) { | 89 | int main(int argc, char **argv) { |
| 87 | char **argv_new = NULL; | ||
| 88 | int i, argc_test; | ||
| 89 | |||
| 90 | plan_tests(5); | 90 | plan_tests(5); |
| 91 | 91 | ||
| 92 | char **argv_new = NULL; | ||
| 93 | int argc_test; | ||
| 92 | { | 94 | { |
| 93 | char *argv_test[] = {"prog_name", (char *)NULL}; | 95 | char *argv_test[] = {"prog_name", (char *)NULL}; |
| 94 | argc_test = 1; | 96 | argc_test = 1; |
| @@ -110,27 +112,36 @@ int main(int argc, char **argv) { | |||
| 110 | { | 112 | { |
| 111 | char *argv_test[] = {"prog_name", "--extra-opts=@./config-opts.ini", (char *)NULL}; | 113 | char *argv_test[] = {"prog_name", "--extra-opts=@./config-opts.ini", (char *)NULL}; |
| 112 | argc_test = 2; | 114 | argc_test = 2; |
| 113 | char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", (char *)NULL}; | 115 | char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", |
| 116 | (char *)NULL}; | ||
| 114 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); | 117 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); |
| 115 | ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts using default section"); | 118 | ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts using default section"); |
| 116 | my_free(&argc_test, argv_new, argv_test); | 119 | my_free(&argc_test, argv_new, argv_test); |
| 117 | } | 120 | } |
| 118 | 121 | ||
| 119 | { | 122 | { |
| 120 | char *argv_test[] = {"prog_name", "--extra-opts=sect1@./config-opts.ini", "--extra-opts", "sect2@./config-opts.ini", (char *)NULL}; | 123 | char *argv_test[] = {"prog_name", "--extra-opts=sect1@./config-opts.ini", "--extra-opts", |
| 124 | "sect2@./config-opts.ini", (char *)NULL}; | ||
| 121 | argc_test = 4; | 125 | argc_test = 4; |
| 122 | char *argv_known[] = {"prog_name", "--one=two", "--something else=oops", "--this=that", (char *)NULL}; | 126 | char *argv_known[] = {"prog_name", "--one=two", "--something else=oops", "--this=that", |
| 127 | (char *)NULL}; | ||
| 123 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); | 128 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); |
| 124 | ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts specified twice"); | 129 | ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts specified twice"); |
| 125 | my_free(&argc_test, argv_new, argv_test); | 130 | my_free(&argc_test, argv_new, argv_test); |
| 126 | } | 131 | } |
| 127 | 132 | ||
| 128 | { | 133 | { |
| 129 | char *argv_test[] = {"prog_name", "--arg1=val1", "--extra-opts=@./config-opts.ini", "--extra-opts", "sect1@./config-opts.ini", | 134 | char *argv_test[] = {"prog_name", |
| 130 | "--arg2", (char *)NULL}; | 135 | "--arg1=val1", |
| 136 | "--extra-opts=@./config-opts.ini", | ||
| 137 | "--extra-opts", | ||
| 138 | "sect1@./config-opts.ini", | ||
| 139 | "--arg2", | ||
| 140 | (char *)NULL}; | ||
| 131 | argc_test = 6; | 141 | argc_test = 6; |
| 132 | char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", "--one=two", | 142 | char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", |
| 133 | "--arg1=val1", "--arg2", (char *)NULL}; | 143 | "--blank", "--one=two", "--arg1=val1", |
| 144 | "--arg2", (char *)NULL}; | ||
| 134 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); | 145 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); |
| 135 | ok(array_diff(argc_test, argv_new, 7, argv_known), "twice extra opts using two sections"); | 146 | ok(array_diff(argc_test, argv_new, 7, argv_known), "twice extra opts using two sections"); |
| 136 | my_free(&argc_test, argv_new, argv_test); | 147 | my_free(&argc_test, argv_new, argv_test); |
diff --git a/lib/tests/test_opts2.c b/lib/tests/test_opts2.c index 23496617..3dd1b039 100644 --- a/lib/tests/test_opts2.c +++ b/lib/tests/test_opts2.c | |||
| @@ -23,36 +23,39 @@ | |||
| 23 | 23 | ||
| 24 | void my_free(int *argc, char **newargv, char **argv) { | 24 | void my_free(int *argc, char **newargv, char **argv) { |
| 25 | /* Free stuff (and print while we're at it) */ | 25 | /* Free stuff (and print while we're at it) */ |
| 26 | int i, freeflag = 1; | 26 | bool freeflag = true; |
| 27 | |||
| 27 | printf(" Arg(%i): ", *argc + 1); | 28 | printf(" Arg(%i): ", *argc + 1); |
| 28 | printf("'%s' ", newargv[0]); | 29 | printf("'%s' ", newargv[0]); |
| 29 | for (i = 1; i < *argc; i++) { | 30 | for (int i = 1; i < *argc; i++) { |
| 30 | printf("'%s' ", newargv[i]); | 31 | printf("'%s' ", newargv[i]); |
| 31 | /* Stop freeing when we get to the start of the original array */ | 32 | /* Stop freeing when we get to the start of the original array */ |
| 32 | if (freeflag) { | 33 | if (freeflag) { |
| 33 | if (newargv[i] == argv[1]) | 34 | if (newargv[i] == argv[1]) { |
| 34 | freeflag = 0; | 35 | freeflag = false; |
| 35 | else | 36 | } else { |
| 36 | free(newargv[i]); | 37 | free(newargv[i]); |
| 38 | } | ||
| 37 | } | 39 | } |
| 38 | } | 40 | } |
| 39 | printf("\n"); | 41 | printf("\n"); |
| 40 | /* Free only if it's a different array */ | 42 | /* Free only if it's a different array */ |
| 41 | if (newargv != argv) | 43 | if (newargv != argv) { |
| 42 | free(newargv); | 44 | free(newargv); |
| 45 | } | ||
| 43 | *argc = 0; | 46 | *argc = 0; |
| 44 | } | 47 | } |
| 45 | 48 | ||
| 46 | int array_diff(int i1, char **a1, int i2, char **a2) { | 49 | int array_diff(int i1, char **a1, int i2, char **a2) { |
| 47 | int i; | ||
| 48 | |||
| 49 | if (i1 != i2) { | 50 | if (i1 != i2) { |
| 50 | printf(" Argument count doesn't match!\n"); | 51 | printf(" Argument count doesn't match!\n"); |
| 51 | return 0; | 52 | return 0; |
| 52 | } | 53 | } |
| 53 | for (i = 0; i <= i1; i++) { | 54 | |
| 54 | if (a1[i] == NULL && a2[i] == NULL) | 55 | for (int i = 0; i <= i1; i++) { |
| 56 | if (a1[i] == NULL && a2[i] == NULL) { | ||
| 55 | continue; | 57 | continue; |
| 58 | } | ||
| 56 | if (a1[i] == NULL || a2[i] == NULL) { | 59 | if (a1[i] == NULL || a2[i] == NULL) { |
| 57 | printf(" Argument # %i null in one array!\n", i); | 60 | printf(" Argument # %i null in one array!\n", i); |
| 58 | return 0; | 61 | return 0; |
| @@ -66,11 +69,10 @@ int array_diff(int i1, char **a1, int i2, char **a2) { | |||
| 66 | } | 69 | } |
| 67 | 70 | ||
| 68 | int main(int argc, char **argv) { | 71 | int main(int argc, char **argv) { |
| 69 | char **argv_new = NULL; | ||
| 70 | int i, argc_test; | ||
| 71 | |||
| 72 | plan_tests(5); | 72 | plan_tests(5); |
| 73 | 73 | ||
| 74 | char **argv_new = NULL; | ||
| 75 | int argc_test; | ||
| 74 | { | 76 | { |
| 75 | char *argv_test[] = {"prog_name", "arg1", "--extra-opts", "--arg3", "val2", (char *)NULL}; | 77 | char *argv_test[] = {"prog_name", "arg1", "--extra-opts", "--arg3", "val2", (char *)NULL}; |
| 76 | argc_test = 5; | 78 | argc_test = 5; |
| @@ -90,7 +92,8 @@ int main(int argc, char **argv) { | |||
| 90 | } | 92 | } |
| 91 | 93 | ||
| 92 | { | 94 | { |
| 93 | char *argv_test[] = {"prog_name", "arg1", "--extra-opts=section1", "--arg3", "val2", (char *)NULL}; | 95 | char *argv_test[] = {"prog_name", "arg1", "--extra-opts=section1", |
| 96 | "--arg3", "val2", (char *)NULL}; | ||
| 94 | argc_test = 5; | 97 | argc_test = 5; |
| 95 | char *argv_known[] = {"prog_name", "--foobar=baz", "arg1", "--arg3", "val2", (char *)NULL}; | 98 | char *argv_known[] = {"prog_name", "--foobar=baz", "arg1", "--arg3", "val2", (char *)NULL}; |
| 96 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); | 99 | argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); |
| @@ -108,30 +111,39 @@ int main(int argc, char **argv) { | |||
| 108 | } | 111 | } |
| 109 | 112 | ||
| 110 | { | 113 | { |
| 111 | char *argv_test[] = {"check_tcp", "--extra-opts", "--extra-opts=tcp_long_lines", (char *)NULL}; | 114 | char *argv_test[] = {"check_tcp", "--extra-opts", "--extra-opts=tcp_long_lines", |
| 115 | (char *)NULL}; | ||
| 112 | argc_test = 3; | 116 | argc_test = 3; |
| 113 | char *argv_known[] = { | 117 | char *argv_known[] = {"check_tcp", |
| 114 | "check_tcp", | 118 | "--timeout=10", |
| 115 | "--timeout=10", | 119 | "--escape", |
| 116 | "--escape", | 120 | "--send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda " |
| 117 | "--send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 121 | "Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 118 | "yadda Foo bar BAZ yadda " | 122 | "yadda Foo bar BAZ yadda " |
| 119 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 123 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 120 | "yadda Foo bar BAZ " | 124 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 121 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " | 125 | "yadda Foo bar BAZ " |
| 122 | "yadda yadda Foo bar " | 126 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " |
| 123 | "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda", | 127 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " |
| 124 | "--expect=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 128 | "yadda yadda Foo bar " |
| 125 | "yadda Foo bar BAZ yadda " | 129 | "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " |
| 126 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " | 130 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda", |
| 127 | "yadda Foo bar BAZ " | 131 | "--expect=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 128 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " | 132 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 129 | "yadda yadda Foo bar " | 133 | "yadda Foo bar BAZ yadda " |
| 130 | "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " | 134 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 131 | "yadda yadda yadda Foo " | 135 | "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " |
| 132 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda", | 136 | "yadda Foo bar BAZ " |
| 133 | "--jail", | 137 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " |
| 134 | (char *)NULL}; | 138 | "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " |
| 139 | "yadda yadda Foo bar " | ||
| 140 | "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " | ||
| 141 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " | ||
| 142 | "yadda yadda yadda Foo " | ||
| 143 | "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " | ||
| 144 | "yadda yadda yadda Foo bar BAZ yadda yadda yadda", | ||
| 145 | "--jail", | ||
| 146 | (char *)NULL}; | ||
| 135 | argv_new = np_extra_opts(&argc_test, argv_test, "check_tcp"); | 147 | argv_new = np_extra_opts(&argc_test, argv_test, "check_tcp"); |
| 136 | ok(array_diff(argc_test, argv_new, 6, argv_known), "Long lines test"); | 148 | ok(array_diff(argc_test, argv_new, 6, argv_known), "Long lines test"); |
| 137 | my_free(&argc_test, argv_new, argv_test); | 149 | my_free(&argc_test, argv_new, argv_test); |
diff --git a/lib/tests/test_tcp.c b/lib/tests/test_tcp.c index 1b3003e9..37c818c9 100644 --- a/lib/tests/test_tcp.c +++ b/lib/tests/test_tcp.c | |||
| @@ -21,30 +21,38 @@ | |||
| 21 | #include "tap.h" | 21 | #include "tap.h" |
| 22 | 22 | ||
| 23 | int main(void) { | 23 | int main(void) { |
| 24 | char **server_expect; | ||
| 25 | int server_expect_count = 3; | ||
| 26 | |||
| 27 | plan_tests(9); | 24 | plan_tests(9); |
| 28 | 25 | ||
| 26 | char **server_expect; | ||
| 27 | const int server_expect_count = 3; | ||
| 29 | server_expect = malloc(sizeof(char *) * server_expect_count); | 28 | server_expect = malloc(sizeof(char *) * server_expect_count); |
| 30 | 29 | ||
| 31 | server_expect[0] = strdup("AA"); | 30 | server_expect[0] = strdup("AA"); |
| 32 | server_expect[1] = strdup("bb"); | 31 | server_expect[1] = strdup("bb"); |
| 33 | server_expect[2] = strdup("CC"); | 32 | server_expect[2] = strdup("CC"); |
| 34 | 33 | ||
| 35 | ok(np_expect_match("AA bb CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_SUCCESS, | 34 | ok(np_expect_match("AA bb CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == |
| 35 | NP_MATCH_SUCCESS, | ||
| 36 | "Test matching any string at the beginning (first expect string)"); | 36 | "Test matching any string at the beginning (first expect string)"); |
| 37 | ok(np_expect_match("bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_SUCCESS, | 37 | ok(np_expect_match("bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == |
| 38 | NP_MATCH_SUCCESS, | ||
| 38 | "Test matching any string at the beginning (second expect string)"); | 39 | "Test matching any string at the beginning (second expect string)"); |
| 39 | ok(np_expect_match("b", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_RETRY, | 40 | ok(np_expect_match("b", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_RETRY, |
| 40 | "Test matching any string at the beginning (substring match)"); | 41 | "Test matching any string at the beginning (substring match)"); |
| 41 | ok(np_expect_match("XX bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_FAILURE, | 42 | ok(np_expect_match("XX bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == |
| 43 | NP_MATCH_FAILURE, | ||
| 42 | "Test with strings not matching at the beginning"); | 44 | "Test with strings not matching at the beginning"); |
| 43 | ok(np_expect_match("XX CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_FAILURE, "Test matching any string"); | 45 | ok(np_expect_match("XX CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == |
| 44 | ok(np_expect_match("XX", server_expect, server_expect_count, 0) == NP_MATCH_RETRY, "Test not matching any string"); | 46 | NP_MATCH_FAILURE, |
| 45 | ok(np_expect_match("XX AA bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_SUCCESS, | 47 | "Test matching any string"); |
| 48 | ok(np_expect_match("XX", server_expect, server_expect_count, 0) == NP_MATCH_RETRY, | ||
| 49 | "Test not matching any string"); | ||
| 50 | ok(np_expect_match("XX AA bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == | ||
| 51 | NP_MATCH_SUCCESS, | ||
| 46 | "Test matching all strings"); | 52 | "Test matching all strings"); |
| 47 | ok(np_expect_match("XX bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY, "Test not matching all strings"); | 53 | ok(np_expect_match("XX bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == |
| 54 | NP_MATCH_RETRY, | ||
| 55 | "Test not matching all strings"); | ||
| 48 | ok(np_expect_match("XX XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY, | 56 | ok(np_expect_match("XX XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY, |
| 49 | "Test not matching any string (testing all)"); | 57 | "Test not matching any string (testing all)"); |
| 50 | 58 | ||
diff --git a/lib/tests/test_utils.c b/lib/tests/test_utils.c index c3150f00..8040dec8 100644 --- a/lib/tests/test_utils.c +++ b/lib/tests/test_utils.c | |||
| @@ -28,17 +28,7 @@ | |||
| 28 | #include "utils_base.c" | 28 | #include "utils_base.c" |
| 29 | 29 | ||
| 30 | int main(int argc, char **argv) { | 30 | int 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(¤t_time); | ||
| 427 | np_state_write_string(0, "String to read"); | ||
| 428 | temp_state_data = np_state_read(); | ||
| 429 | /* Check time is set to current_time */ | ||
| 430 | ok(system("cmp var/generated var/statefile > /dev/null") != 0, "Generated file should be different this time"); | ||
| 431 | ok(this_monitoring_plugin->state->state_data->time - current_time <= 1, "Has time generated from current time"); | ||
| 432 | |||
| 433 | /* Don't know how to automatically test this. Need to be able to redefine die and catch the error */ | ||
| 434 | /* | ||
| 435 | temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write"; | ||
| 436 | np_state_write_string(0, "Bad file"); | ||
| 437 | */ | ||
| 438 | |||
| 439 | np_cleanup(); | ||
| 440 | |||
| 441 | ok(this_monitoring_plugin == NULL, "Free'd this_monitoring_plugin"); | ||
| 442 | |||
| 443 | ok(mp_suid() == false, "Test aren't suid"); | 299 | ok(mp_suid() == false, "Test aren't suid"); |
| 444 | 300 | ||
| 445 | /* base states with random case */ | 301 | /* base states with random case */ |
| 446 | char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL}; | 302 | char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL}; |
| 447 | 303 | ||
| 448 | for (i = 0; states[i] != NULL; i++) { | 304 | for (int i = 0; states[i] != NULL; i++) { |
| 449 | /* out of the random case states, create the lower and upper versions + numeric string one */ | 305 | /* out of the random case states, create the lower and upper versions + numeric string one |
| 306 | */ | ||
| 450 | char *statelower = strdup(states[i]); | 307 | char *statelower = strdup(states[i]); |
| 451 | char *stateupper = strdup(states[i]); | 308 | char *stateupper = strdup(states[i]); |
| 452 | char statenum[2]; | 309 | char statenum[2]; |
| 453 | char *temp_ptr; | 310 | for (char *temp_ptr = statelower; *temp_ptr; temp_ptr++) { |
| 454 | for (temp_ptr = statelower; *temp_ptr; temp_ptr++) { | 311 | *temp_ptr = (char)tolower(*temp_ptr); |
| 455 | *temp_ptr = tolower(*temp_ptr); | ||
| 456 | } | 312 | } |
| 457 | for (temp_ptr = stateupper; *temp_ptr; temp_ptr++) { | 313 | for (char *temp_ptr = stateupper; *temp_ptr; temp_ptr++) { |
| 458 | *temp_ptr = toupper(*temp_ptr); | 314 | *temp_ptr = (char)toupper(*temp_ptr); |
| 459 | } | 315 | } |
| 460 | snprintf(statenum, 2, "%i", i); | 316 | snprintf(statenum, 2, "%i", i); |
| 461 | 317 | ||
| 462 | /* Base test names, we'll append the state string */ | 318 | /* Base test names, we'll append the state string */ |
| 463 | char testname[64] = "Translate state string: "; | 319 | char testname[64] = "Translate state string: "; |
| 464 | int tlen = strlen(testname); | 320 | size_t tlen = strlen(testname); |
| 465 | 321 | ||
| 466 | strcpy(testname + tlen, states[i]); | 322 | strcpy(testname + tlen, states[i]); |
| 467 | ok(i == mp_translate_state(states[i]), testname); | 323 | ok(i == mp_translate_state(states[i]), testname); |
diff --git a/lib/thresholds.c b/lib/thresholds.c new file mode 100644 index 00000000..de2b9315 --- /dev/null +++ b/lib/thresholds.c | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | #include "./thresholds.h" | ||
| 2 | #include "./utils_base.h" | ||
| 3 | #include "perfdata.h" | ||
| 4 | |||
| 5 | #include <stddef.h> | ||
| 6 | |||
| 7 | mp_thresholds mp_thresholds_init() { | ||
| 8 | mp_thresholds tmp = { | ||
| 9 | .critical = {}, | ||
| 10 | .critical_is_set = false, | ||
| 11 | .warning = {}, | ||
| 12 | .warning_is_set = false, | ||
| 13 | }; | ||
| 14 | return tmp; | ||
| 15 | } | ||
| 16 | |||
| 17 | char *fmt_threshold_warning(const thresholds th) { | ||
| 18 | if (th.warning == NULL) { | ||
| 19 | return ""; | ||
| 20 | } | ||
| 21 | |||
| 22 | return fmt_range(*th.warning); | ||
| 23 | } | ||
| 24 | |||
| 25 | char *fmt_threshold_critical(const thresholds th) { | ||
| 26 | if (th.critical == NULL) { | ||
| 27 | return ""; | ||
| 28 | } | ||
| 29 | return fmt_range(*th.critical); | ||
| 30 | } | ||
| 31 | |||
| 32 | mp_perfdata mp_pd_set_thresholds(mp_perfdata perfdata, mp_thresholds threshold) { | ||
| 33 | if (threshold.critical_is_set) { | ||
| 34 | perfdata.crit = threshold.critical; | ||
| 35 | perfdata.crit_present = true; | ||
| 36 | } | ||
| 37 | |||
| 38 | if (threshold.warning_is_set) { | ||
| 39 | perfdata.warn = threshold.warning; | ||
| 40 | perfdata.warn_present = true; | ||
| 41 | } | ||
| 42 | |||
| 43 | return perfdata; | ||
| 44 | } | ||
| 45 | |||
| 46 | mp_state_enum mp_get_pd_status(mp_perfdata perfdata) { | ||
| 47 | if (perfdata.crit_present) { | ||
| 48 | if (mp_check_range(perfdata.value, perfdata.crit)) { | ||
| 49 | return STATE_CRITICAL; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | if (perfdata.warn_present) { | ||
| 53 | if (mp_check_range(perfdata.value, perfdata.warn)) { | ||
| 54 | return STATE_WARNING; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | return STATE_OK; | ||
| 59 | } | ||
| 60 | |||
| 61 | mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn) { | ||
| 62 | thlds.warning = warn; | ||
| 63 | thlds.warning_is_set = true; | ||
| 64 | return thlds; | ||
| 65 | } | ||
| 66 | |||
| 67 | mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit) { | ||
| 68 | thlds.critical = crit; | ||
| 69 | thlds.critical_is_set = true; | ||
| 70 | return thlds; | ||
| 71 | } | ||
diff --git a/lib/thresholds.h b/lib/thresholds.h new file mode 100644 index 00000000..f8647681 --- /dev/null +++ b/lib/thresholds.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "./perfdata.h" | ||
| 4 | #include "states.h" | ||
| 5 | |||
| 6 | /* | ||
| 7 | * Old threshold type using the old range type | ||
| 8 | */ | ||
| 9 | typedef struct { | ||
| 10 | range *warning; | ||
| 11 | range *critical; | ||
| 12 | } thresholds; | ||
| 13 | |||
| 14 | typedef struct { | ||
| 15 | bool warning_is_set; | ||
| 16 | mp_range warning; | ||
| 17 | bool critical_is_set; | ||
| 18 | mp_range critical; | ||
| 19 | } mp_thresholds; | ||
| 20 | |||
| 21 | mp_thresholds mp_thresholds_init(void); | ||
| 22 | |||
| 23 | mp_perfdata mp_pd_set_thresholds(mp_perfdata /* pd */, mp_thresholds /* th */); | ||
| 24 | |||
| 25 | mp_state_enum mp_get_pd_status(mp_perfdata /* pd */); | ||
| 26 | |||
| 27 | mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn); | ||
| 28 | mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit); | ||
| 29 | |||
| 30 | char *fmt_threshold_warning(thresholds th); | ||
| 31 | char *fmt_threshold_critical(thresholds th); | ||
diff --git a/lib/utils_base.c b/lib/utils_base.c index 90a4aaa5..28e6dc47 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | *****************************************************************************/ | 25 | *****************************************************************************/ |
| 26 | 26 | ||
| 27 | #include "../plugins/common.h" | 27 | #include "../plugins/common.h" |
| 28 | #include "states.h" | ||
| 28 | #include <stdarg.h> | 29 | #include <stdarg.h> |
| 29 | #include "utils_base.h" | 30 | #include "utils_base.h" |
| 30 | #include <ctype.h> | 31 | #include <ctype.h> |
| @@ -33,20 +34,20 @@ | |||
| 33 | #include <unistd.h> | 34 | #include <unistd.h> |
| 34 | #include <sys/types.h> | 35 | #include <sys/types.h> |
| 35 | 36 | ||
| 36 | #define np_free(ptr) \ | 37 | #define np_free(ptr) \ |
| 37 | { \ | 38 | { \ |
| 38 | if (ptr) { \ | 39 | if (ptr) { \ |
| 39 | free(ptr); \ | 40 | free(ptr); \ |
| 40 | ptr = NULL; \ | 41 | ptr = NULL; \ |
| 41 | } \ | 42 | } \ |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | monitoring_plugin *this_monitoring_plugin = NULL; | 45 | monitoring_plugin *this_monitoring_plugin = NULL; |
| 45 | 46 | ||
| 46 | int timeout_state = STATE_CRITICAL; | 47 | mp_state_enum timeout_state = STATE_CRITICAL; |
| 47 | unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; | 48 | unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; |
| 48 | 49 | ||
| 49 | bool _np_state_read_file(FILE *); | 50 | bool _np_state_read_file(FILE *state_file); |
| 50 | 51 | ||
| 51 | void np_init(char *plugin_name, int argc, char **argv) { | 52 | void np_init(char *plugin_name, int argc, char **argv) { |
| 52 | if (this_monitoring_plugin == NULL) { | 53 | if (this_monitoring_plugin == NULL) { |
| @@ -55,31 +56,25 @@ void np_init(char *plugin_name, int argc, char **argv) { | |||
| 55 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | 56 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); |
| 56 | } | 57 | } |
| 57 | this_monitoring_plugin->plugin_name = strdup(plugin_name); | 58 | this_monitoring_plugin->plugin_name = strdup(plugin_name); |
| 58 | if (this_monitoring_plugin->plugin_name == NULL) | 59 | if (this_monitoring_plugin->plugin_name == NULL) { |
| 59 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); | 60 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); |
| 61 | } | ||
| 60 | this_monitoring_plugin->argc = argc; | 62 | this_monitoring_plugin->argc = argc; |
| 61 | this_monitoring_plugin->argv = argv; | 63 | this_monitoring_plugin->argv = argv; |
| 62 | } | 64 | } |
| 63 | } | 65 | } |
| 64 | 66 | ||
| 65 | void np_set_args(int argc, char **argv) { | 67 | void np_set_args(int argc, char **argv) { |
| 66 | if (this_monitoring_plugin == NULL) | 68 | if (this_monitoring_plugin == NULL) { |
| 67 | die(STATE_UNKNOWN, _("This requires np_init to be called")); | 69 | die(STATE_UNKNOWN, _("This requires np_init to be called")); |
| 70 | } | ||
| 68 | 71 | ||
| 69 | this_monitoring_plugin->argc = argc; | 72 | this_monitoring_plugin->argc = argc; |
| 70 | this_monitoring_plugin->argv = argv; | 73 | this_monitoring_plugin->argv = argv; |
| 71 | } | 74 | } |
| 72 | 75 | ||
| 73 | void np_cleanup() { | 76 | void np_cleanup(void) { |
| 74 | if (this_monitoring_plugin != NULL) { | 77 | if (this_monitoring_plugin != NULL) { |
| 75 | if (this_monitoring_plugin->state != NULL) { | ||
| 76 | if (this_monitoring_plugin->state->state_data) { | ||
| 77 | np_free(this_monitoring_plugin->state->state_data->data); | ||
| 78 | np_free(this_monitoring_plugin->state->state_data); | ||
| 79 | } | ||
| 80 | np_free(this_monitoring_plugin->state->name); | ||
| 81 | np_free(this_monitoring_plugin->state); | ||
| 82 | } | ||
| 83 | np_free(this_monitoring_plugin->plugin_name); | 78 | np_free(this_monitoring_plugin->plugin_name); |
| 84 | np_free(this_monitoring_plugin); | 79 | np_free(this_monitoring_plugin); |
| 85 | } | 80 | } |
| @@ -151,7 +146,8 @@ range *parse_range_string(char *str) { | |||
| 151 | set_range_end(temp_range, end); | 146 | set_range_end(temp_range, end); |
| 152 | } | 147 | } |
| 153 | 148 | ||
| 154 | if (temp_range->start_infinity == true || temp_range->end_infinity == true || temp_range->start <= temp_range->end) { | 149 | if (temp_range->start_infinity || temp_range->end_infinity || |
| 150 | temp_range->start <= temp_range->end) { | ||
| 155 | return temp_range; | 151 | return temp_range; |
| 156 | } | 152 | } |
| 157 | free(temp_range); | 153 | free(temp_range); |
| @@ -162,8 +158,9 @@ range *parse_range_string(char *str) { | |||
| 162 | int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) { | 158 | int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) { |
| 163 | thresholds *temp_thresholds = NULL; | 159 | thresholds *temp_thresholds = NULL; |
| 164 | 160 | ||
| 165 | if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) | 161 | if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) { |
| 166 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | 162 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); |
| 163 | } | ||
| 167 | 164 | ||
| 168 | temp_thresholds->warning = NULL; | 165 | temp_thresholds->warning = NULL; |
| 169 | temp_thresholds->critical = NULL; | 166 | temp_thresholds->critical = NULL; |
| @@ -202,12 +199,14 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) { | |||
| 202 | printf("Threshold not set"); | 199 | printf("Threshold not set"); |
| 203 | } else { | 200 | } else { |
| 204 | if (my_threshold->warning) { | 201 | if (my_threshold->warning) { |
| 205 | printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end); | 202 | printf("Warning: start=%g end=%g; ", my_threshold->warning->start, |
| 203 | my_threshold->warning->end); | ||
| 206 | } else { | 204 | } else { |
| 207 | printf("Warning not set; "); | 205 | printf("Warning not set; "); |
| 208 | } | 206 | } |
| 209 | if (my_threshold->critical) { | 207 | if (my_threshold->critical) { |
| 210 | printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end); | 208 | printf("Critical: start=%g end=%g", my_threshold->critical->start, |
| 209 | my_threshold->critical->end); | ||
| 211 | } else { | 210 | } else { |
| 212 | printf("Critical not set"); | 211 | printf("Critical not set"); |
| 213 | } | 212 | } |
| @@ -215,6 +214,36 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) { | |||
| 215 | printf("\n"); | 214 | printf("\n"); |
| 216 | } | 215 | } |
| 217 | 216 | ||
| 217 | /* Returns true if alert should be raised based on the range, false otherwise */ | ||
| 218 | bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { | ||
| 219 | bool is_inside = false; | ||
| 220 | |||
| 221 | if (!my_range.end_infinity && !my_range.start_infinity) { | ||
| 222 | // range: .........|---inside---|........... | ||
| 223 | // value | ||
| 224 | is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) && | ||
| 225 | (cmp_perfdata_value(value, my_range.end) <= 0)); | ||
| 226 | } else if (!my_range.start_infinity && my_range.end_infinity) { | ||
| 227 | // range: .........|---inside--------- | ||
| 228 | // value | ||
| 229 | is_inside = (cmp_perfdata_value(value, my_range.start) >= 0); | ||
| 230 | } else if (my_range.start_infinity && !my_range.end_infinity) { | ||
| 231 | // range: -inside--------|.................... | ||
| 232 | // value | ||
| 233 | is_inside = (cmp_perfdata_value(value, my_range.end) == -1); | ||
| 234 | } else { | ||
| 235 | // range from -inf to inf, so always inside | ||
| 236 | is_inside = true; | ||
| 237 | } | ||
| 238 | |||
| 239 | if ((is_inside && my_range.alert_on_inside_range == INSIDE) || | ||
| 240 | (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) { | ||
| 241 | return true; | ||
| 242 | } | ||
| 243 | |||
| 244 | return false; | ||
| 245 | } | ||
| 246 | |||
| 218 | /* Returns true if alert should be raised based on the range */ | 247 | /* Returns true if alert should be raised based on the range */ |
| 219 | bool check_range(double value, range *my_range) { | 248 | bool check_range(double value, range *my_range) { |
| 220 | bool no = false; | 249 | bool no = false; |
| @@ -225,38 +254,38 @@ bool check_range(double value, range *my_range) { | |||
| 225 | yes = false; | 254 | yes = false; |
| 226 | } | 255 | } |
| 227 | 256 | ||
| 228 | if (my_range->end_infinity == false && my_range->start_infinity == false) { | 257 | if (!my_range->end_infinity && !my_range->start_infinity) { |
| 229 | if ((my_range->start <= value) && (value <= my_range->end)) { | 258 | if ((my_range->start <= value) && (value <= my_range->end)) { |
| 230 | return no; | 259 | return no; |
| 231 | } else { | ||
| 232 | return yes; | ||
| 233 | } | 260 | } |
| 234 | } else if (my_range->start_infinity == false && my_range->end_infinity == true) { | 261 | return yes; |
| 262 | } | ||
| 263 | |||
| 264 | if (!my_range->start_infinity && my_range->end_infinity) { | ||
| 235 | if (my_range->start <= value) { | 265 | if (my_range->start <= value) { |
| 236 | return no; | 266 | return no; |
| 237 | } else { | ||
| 238 | return yes; | ||
| 239 | } | 267 | } |
| 240 | } else if (my_range->start_infinity == true && my_range->end_infinity == false) { | 268 | return yes; |
| 269 | } | ||
| 270 | |||
| 271 | if (my_range->start_infinity && !my_range->end_infinity) { | ||
| 241 | if (value <= my_range->end) { | 272 | if (value <= my_range->end) { |
| 242 | return no; | 273 | return no; |
| 243 | } else { | ||
| 244 | return yes; | ||
| 245 | } | 274 | } |
| 246 | } else { | 275 | return yes; |
| 247 | return no; | ||
| 248 | } | 276 | } |
| 277 | return no; | ||
| 249 | } | 278 | } |
| 250 | 279 | ||
| 251 | /* Returns status */ | 280 | /* Returns status */ |
| 252 | int get_status(double value, thresholds *my_thresholds) { | 281 | mp_state_enum get_status(double value, thresholds *my_thresholds) { |
| 253 | if (my_thresholds->critical != NULL) { | 282 | if (my_thresholds->critical != NULL) { |
| 254 | if (check_range(value, my_thresholds->critical) == true) { | 283 | if (check_range(value, my_thresholds->critical)) { |
| 255 | return STATE_CRITICAL; | 284 | return STATE_CRITICAL; |
| 256 | } | 285 | } |
| 257 | } | 286 | } |
| 258 | if (my_thresholds->warning != NULL) { | 287 | if (my_thresholds->warning != NULL) { |
| 259 | if (check_range(value, my_thresholds->warning) == true) { | 288 | if (check_range(value, my_thresholds->warning)) { |
| 260 | return STATE_WARNING; | 289 | return STATE_WARNING; |
| 261 | } | 290 | } |
| 262 | } | 291 | } |
| @@ -265,31 +294,31 @@ int get_status(double value, thresholds *my_thresholds) { | |||
| 265 | 294 | ||
| 266 | char *np_escaped_string(const char *string) { | 295 | char *np_escaped_string(const char *string) { |
| 267 | char *data; | 296 | char *data; |
| 268 | int i, j = 0; | 297 | int write_index = 0; |
| 269 | data = strdup(string); | 298 | data = strdup(string); |
| 270 | for (i = 0; data[i]; i++) { | 299 | for (int i = 0; data[i]; i++) { |
| 271 | if (data[i] == '\\') { | 300 | if (data[i] == '\\') { |
| 272 | switch (data[++i]) { | 301 | switch (data[++i]) { |
| 273 | case 'n': | 302 | case 'n': |
| 274 | data[j++] = '\n'; | 303 | data[write_index++] = '\n'; |
| 275 | break; | 304 | break; |
| 276 | case 'r': | 305 | case 'r': |
| 277 | data[j++] = '\r'; | 306 | data[write_index++] = '\r'; |
| 278 | break; | 307 | break; |
| 279 | case 't': | 308 | case 't': |
| 280 | data[j++] = '\t'; | 309 | data[write_index++] = '\t'; |
| 281 | break; | 310 | break; |
| 282 | case '\\': | 311 | case '\\': |
| 283 | data[j++] = '\\'; | 312 | data[write_index++] = '\\'; |
| 284 | break; | 313 | break; |
| 285 | default: | 314 | default: |
| 286 | data[j++] = data[i]; | 315 | data[write_index++] = data[i]; |
| 287 | } | 316 | } |
| 288 | } else { | 317 | } else { |
| 289 | data[j++] = data[i]; | 318 | data[write_index++] = data[i]; |
| 290 | } | 319 | } |
| 291 | } | 320 | } |
| 292 | data[j] = '\0'; | 321 | data[write_index] = '\0'; |
| 293 | return data; | 322 | return data; |
| 294 | } | 323 | } |
| 295 | 324 | ||
| @@ -302,38 +331,43 @@ int np_check_if_root(void) { return (geteuid() == 0); } | |||
| 302 | * data strings. | 331 | * data strings. |
| 303 | */ | 332 | */ |
| 304 | char *np_extract_value(const char *varlist, const char *name, char sep) { | 333 | char *np_extract_value(const char *varlist, const char *name, char sep) { |
| 305 | char *tmp = NULL, *value = NULL; | 334 | char *tmp = NULL; |
| 306 | int i; | 335 | char *value = NULL; |
| 307 | 336 | ||
| 308 | while (1) { | 337 | while (true) { |
| 309 | /* Strip any leading space */ | 338 | /* Strip any leading space */ |
| 310 | for (; isspace(varlist[0]); varlist++) | 339 | for (; isspace(varlist[0]); varlist++) { |
| 311 | ; | 340 | ; |
| 341 | } | ||
| 312 | 342 | ||
| 313 | if (strncmp(name, varlist, strlen(name)) == 0) { | 343 | if (strncmp(name, varlist, strlen(name)) == 0) { |
| 314 | varlist += strlen(name); | 344 | varlist += strlen(name); |
| 315 | /* strip trailing spaces */ | 345 | /* strip trailing spaces */ |
| 316 | for (; isspace(varlist[0]); varlist++) | 346 | for (; isspace(varlist[0]); varlist++) { |
| 317 | ; | 347 | ; |
| 348 | } | ||
| 318 | 349 | ||
| 319 | if (varlist[0] == '=') { | 350 | if (varlist[0] == '=') { |
| 320 | /* We matched the key, go past the = sign */ | 351 | /* We matched the key, go past the = sign */ |
| 321 | varlist++; | 352 | varlist++; |
| 322 | /* strip leading spaces */ | 353 | /* strip leading spaces */ |
| 323 | for (; isspace(varlist[0]); varlist++) | 354 | for (; isspace(varlist[0]); varlist++) { |
| 324 | ; | 355 | ; |
| 356 | } | ||
| 325 | 357 | ||
| 326 | if ((tmp = index(varlist, sep))) { | 358 | if ((tmp = index(varlist, sep))) { |
| 327 | /* Value is delimited by a comma */ | 359 | /* Value is delimited by a comma */ |
| 328 | if (tmp - varlist == 0) | 360 | if (tmp - varlist == 0) { |
| 329 | continue; | 361 | continue; |
| 330 | value = (char *)calloc(1, tmp - varlist + 1); | 362 | } |
| 331 | strncpy(value, varlist, tmp - varlist); | 363 | value = (char *)calloc(1, (unsigned long)(tmp - varlist + 1)); |
| 364 | strncpy(value, varlist, (unsigned long)(tmp - varlist)); | ||
| 332 | value[tmp - varlist] = '\0'; | 365 | value[tmp - varlist] = '\0'; |
| 333 | } else { | 366 | } else { |
| 334 | /* Value is delimited by a \0 */ | 367 | /* Value is delimited by a \0 */ |
| 335 | if (strlen(varlist) == 0) | 368 | if (strlen(varlist) == 0) { |
| 336 | continue; | 369 | continue; |
| 370 | } | ||
| 337 | value = (char *)calloc(1, strlen(varlist) + 1); | 371 | value = (char *)calloc(1, strlen(varlist) + 1); |
| 338 | strncpy(value, varlist, strlen(varlist)); | 372 | strncpy(value, varlist, strlen(varlist)); |
| 339 | value[strlen(varlist)] = '\0'; | 373 | value[strlen(varlist)] = '\0'; |
| @@ -351,14 +385,16 @@ char *np_extract_value(const char *varlist, const char *name, char sep) { | |||
| 351 | } | 385 | } |
| 352 | 386 | ||
| 353 | /* Clean-up trailing spaces/newlines */ | 387 | /* Clean-up trailing spaces/newlines */ |
| 354 | if (value) | 388 | if (value) { |
| 355 | for (i = strlen(value) - 1; isspace(value[i]); i--) | 389 | for (unsigned long i = strlen(value) - 1; isspace(value[i]); i--) { |
| 356 | value[i] = '\0'; | 390 | value[i] = '\0'; |
| 391 | } | ||
| 392 | } | ||
| 357 | 393 | ||
| 358 | return value; | 394 | return value; |
| 359 | } | 395 | } |
| 360 | 396 | ||
| 361 | const char *state_text(int result) { | 397 | const char *state_text(mp_state_enum result) { |
| 362 | switch (result) { | 398 | switch (result) { |
| 363 | case STATE_OK: | 399 | case STATE_OK: |
| 364 | return "OK"; | 400 | return "OK"; |
| @@ -378,343 +414,17 @@ const char *state_text(int result) { | |||
| 378 | * return the corresponding STATE_ value or ERROR) | 414 | * return the corresponding STATE_ value or ERROR) |
| 379 | */ | 415 | */ |
| 380 | int mp_translate_state(char *state_text) { | 416 | int mp_translate_state(char *state_text) { |
| 381 | if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) | 417 | if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) { |
| 382 | return STATE_OK; | 418 | return STATE_OK; |
| 383 | if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) | ||
| 384 | return STATE_WARNING; | ||
| 385 | if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) | ||
| 386 | return STATE_CRITICAL; | ||
| 387 | if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) | ||
| 388 | return STATE_UNKNOWN; | ||
| 389 | return ERROR; | ||
| 390 | } | ||
| 391 | |||
| 392 | /* | ||
| 393 | * Returns a string to use as a keyname, based on an md5 hash of argv, thus | ||
| 394 | * hopefully a unique key per service/plugin invocation. Use the extra-opts | ||
| 395 | * parse of argv, so that uniqueness in parameters are reflected there. | ||
| 396 | */ | ||
| 397 | char *_np_state_generate_key() { | ||
| 398 | int i; | ||
| 399 | char **argv = this_monitoring_plugin->argv; | ||
| 400 | char keyname[41]; | ||
| 401 | char *p = NULL; | ||
| 402 | |||
| 403 | unsigned char result[256]; | ||
| 404 | |||
| 405 | #ifdef USE_OPENSSL | ||
| 406 | /* | ||
| 407 | * This code path is chosen if openssl is available (which should be the most common | ||
| 408 | * scenario). Alternatively, the gnulib implementation/ | ||
| 409 | * | ||
| 410 | */ | ||
| 411 | EVP_MD_CTX *ctx = EVP_MD_CTX_new(); | ||
| 412 | |||
| 413 | EVP_DigestInit(ctx, EVP_sha256()); | ||
| 414 | |||
| 415 | for (i = 0; i < this_monitoring_plugin->argc; i++) { | ||
| 416 | EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); | ||
| 417 | } | ||
| 418 | |||
| 419 | EVP_DigestFinal(ctx, result, NULL); | ||
| 420 | #else | ||
| 421 | |||
| 422 | struct sha256_ctx ctx; | ||
| 423 | |||
| 424 | for (i = 0; i < this_monitoring_plugin->argc; i++) { | ||
| 425 | sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); | ||
| 426 | } | ||
| 427 | |||
| 428 | sha256_finish_ctx(&ctx, result); | ||
| 429 | #endif // FOUNDOPENSSL | ||
| 430 | |||
| 431 | for (i = 0; i < 20; ++i) { | ||
| 432 | sprintf(&keyname[2 * i], "%02x", result[i]); | ||
| 433 | } | ||
| 434 | |||
| 435 | keyname[40] = '\0'; | ||
| 436 | |||
| 437 | p = strdup(keyname); | ||
| 438 | if (p == NULL) { | ||
| 439 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); | ||
| 440 | } | ||
| 441 | return p; | ||
| 442 | } | ||
| 443 | |||
| 444 | void _cleanup_state_data() { | ||
| 445 | if (this_monitoring_plugin->state->state_data != NULL) { | ||
| 446 | np_free(this_monitoring_plugin->state->state_data->data); | ||
| 447 | np_free(this_monitoring_plugin->state->state_data); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | /* | ||
| 452 | * Internal function. Returns either: | ||
| 453 | * envvar NAGIOS_PLUGIN_STATE_DIRECTORY | ||
| 454 | * statically compiled shared state directory | ||
| 455 | */ | ||
| 456 | char *_np_state_calculate_location_prefix() { | ||
| 457 | char *env_dir; | ||
| 458 | |||
| 459 | /* Do not allow passing MP_STATE_PATH in setuid plugins | ||
| 460 | * for security reasons */ | ||
| 461 | if (!mp_suid()) { | ||
| 462 | env_dir = getenv("MP_STATE_PATH"); | ||
| 463 | if (env_dir && env_dir[0] != '\0') | ||
| 464 | return env_dir; | ||
| 465 | /* This is the former ENV, for backward-compatibility */ | ||
| 466 | env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); | ||
| 467 | if (env_dir && env_dir[0] != '\0') | ||
| 468 | return env_dir; | ||
| 469 | } | ||
| 470 | |||
| 471 | return NP_STATE_DIR_PREFIX; | ||
| 472 | } | ||
| 473 | |||
| 474 | /* | ||
| 475 | * Initiatializer for state routines. | ||
| 476 | * Sets variables. Generates filename. Returns np_state_key. die with | ||
| 477 | * UNKNOWN if exception | ||
| 478 | */ | ||
| 479 | void np_enable_state(char *keyname, int expected_data_version) { | ||
| 480 | state_key *this_state = NULL; | ||
| 481 | char *temp_filename = NULL; | ||
| 482 | char *temp_keyname = NULL; | ||
| 483 | char *p = NULL; | ||
| 484 | int ret; | ||
| 485 | |||
| 486 | if (this_monitoring_plugin == NULL) | ||
| 487 | die(STATE_UNKNOWN, _("This requires np_init to be called")); | ||
| 488 | |||
| 489 | this_state = (state_key *)calloc(1, sizeof(state_key)); | ||
| 490 | if (this_state == NULL) | ||
| 491 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 492 | |||
| 493 | if (keyname == NULL) { | ||
| 494 | temp_keyname = _np_state_generate_key(); | ||
| 495 | } else { | ||
| 496 | temp_keyname = strdup(keyname); | ||
| 497 | if (temp_keyname == NULL) | ||
| 498 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); | ||
| 499 | } | 419 | } |
| 500 | /* Die if invalid characters used for keyname */ | 420 | if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) { |
| 501 | p = temp_keyname; | 421 | return STATE_WARNING; |
| 502 | while (*p != '\0') { | ||
| 503 | if (!(isalnum(*p) || *p == '_')) { | ||
| 504 | die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); | ||
| 505 | } | ||
| 506 | p++; | ||
| 507 | } | ||
| 508 | this_state->name = temp_keyname; | ||
| 509 | this_state->plugin_name = this_monitoring_plugin->plugin_name; | ||
| 510 | this_state->data_version = expected_data_version; | ||
| 511 | this_state->state_data = NULL; | ||
| 512 | |||
| 513 | /* Calculate filename */ | ||
| 514 | ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(), | ||
| 515 | this_monitoring_plugin->plugin_name, this_state->name); | ||
| 516 | if (ret < 0) | ||
| 517 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 518 | |||
| 519 | this_state->_filename = temp_filename; | ||
| 520 | |||
| 521 | this_monitoring_plugin->state = this_state; | ||
| 522 | } | ||
| 523 | |||
| 524 | /* | ||
| 525 | * Will return NULL if no data is available (first run). If key currently | ||
| 526 | * exists, read data. If state file format version is not expected, return | ||
| 527 | * as if no data. Get state data version number and compares to expected. | ||
| 528 | * If numerically lower, then return as no previous state. die with UNKNOWN | ||
| 529 | * if exceptional error. | ||
| 530 | */ | ||
| 531 | state_data *np_state_read() { | ||
| 532 | state_data *this_state_data = NULL; | ||
| 533 | FILE *statefile; | ||
| 534 | bool rc = false; | ||
| 535 | |||
| 536 | if (this_monitoring_plugin == NULL) | ||
| 537 | die(STATE_UNKNOWN, _("This requires np_init to be called")); | ||
| 538 | |||
| 539 | /* Open file. If this fails, no previous state found */ | ||
| 540 | statefile = fopen(this_monitoring_plugin->state->_filename, "r"); | ||
| 541 | if (statefile != NULL) { | ||
| 542 | |||
| 543 | this_state_data = (state_data *)calloc(1, sizeof(state_data)); | ||
| 544 | if (this_state_data == NULL) | ||
| 545 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 546 | |||
| 547 | this_state_data->data = NULL; | ||
| 548 | this_monitoring_plugin->state->state_data = this_state_data; | ||
| 549 | |||
| 550 | rc = _np_state_read_file(statefile); | ||
| 551 | |||
| 552 | fclose(statefile); | ||
| 553 | } | ||
| 554 | |||
| 555 | if (!rc) { | ||
| 556 | _cleanup_state_data(); | ||
| 557 | } | ||
| 558 | |||
| 559 | return this_monitoring_plugin->state->state_data; | ||
| 560 | } | ||
| 561 | |||
| 562 | /* | ||
| 563 | * Read the state file | ||
| 564 | */ | ||
| 565 | bool _np_state_read_file(FILE *f) { | ||
| 566 | bool status = false; | ||
| 567 | size_t pos; | ||
| 568 | char *line; | ||
| 569 | int i; | ||
| 570 | int failure = 0; | ||
| 571 | time_t current_time, data_time; | ||
| 572 | enum { | ||
| 573 | STATE_FILE_VERSION, | ||
| 574 | STATE_DATA_VERSION, | ||
| 575 | STATE_DATA_TIME, | ||
| 576 | STATE_DATA_TEXT, | ||
| 577 | STATE_DATA_END | ||
| 578 | } expected = STATE_FILE_VERSION; | ||
| 579 | |||
| 580 | time(¤t_time); | ||
| 581 | |||
| 582 | /* Note: This introduces a limit of 1024 bytes in the string data */ | ||
| 583 | line = (char *)calloc(1, 1024); | ||
| 584 | if (line == NULL) | ||
| 585 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 586 | |||
| 587 | while (!failure && (fgets(line, 1024, f)) != NULL) { | ||
| 588 | pos = strlen(line); | ||
| 589 | if (line[pos - 1] == '\n') { | ||
| 590 | line[pos - 1] = '\0'; | ||
| 591 | } | ||
| 592 | |||
| 593 | if (line[0] == '#') | ||
| 594 | continue; | ||
| 595 | |||
| 596 | switch (expected) { | ||
| 597 | case STATE_FILE_VERSION: | ||
| 598 | i = atoi(line); | ||
| 599 | if (i != NP_STATE_FORMAT_VERSION) | ||
| 600 | failure++; | ||
| 601 | else | ||
| 602 | expected = STATE_DATA_VERSION; | ||
| 603 | break; | ||
| 604 | case STATE_DATA_VERSION: | ||
| 605 | i = atoi(line); | ||
| 606 | if (i != this_monitoring_plugin->state->data_version) | ||
| 607 | failure++; | ||
| 608 | else | ||
| 609 | expected = STATE_DATA_TIME; | ||
| 610 | break; | ||
| 611 | case STATE_DATA_TIME: | ||
| 612 | /* If time > now, error */ | ||
| 613 | data_time = strtoul(line, NULL, 10); | ||
| 614 | if (data_time > current_time) | ||
| 615 | failure++; | ||
| 616 | else { | ||
| 617 | this_monitoring_plugin->state->state_data->time = data_time; | ||
| 618 | expected = STATE_DATA_TEXT; | ||
| 619 | } | ||
| 620 | break; | ||
| 621 | case STATE_DATA_TEXT: | ||
| 622 | this_monitoring_plugin->state->state_data->data = strdup(line); | ||
| 623 | if (this_monitoring_plugin->state->state_data->data == NULL) | ||
| 624 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); | ||
| 625 | expected = STATE_DATA_END; | ||
| 626 | status = true; | ||
| 627 | break; | ||
| 628 | case STATE_DATA_END:; | ||
| 629 | } | ||
| 630 | } | ||
| 631 | |||
| 632 | np_free(line); | ||
| 633 | return status; | ||
| 634 | } | ||
| 635 | |||
| 636 | /* | ||
| 637 | * If time=NULL, use current time. Create state file, with state format | ||
| 638 | * version, default text. Writes version, time, and data. Avoid locking | ||
| 639 | * problems - use mv to write and then swap. Possible loss of state data if | ||
| 640 | * two things writing to same key at same time. | ||
| 641 | * Will die with UNKNOWN if errors | ||
| 642 | */ | ||
| 643 | void np_state_write_string(time_t data_time, char *data_string) { | ||
| 644 | FILE *fp; | ||
| 645 | char *temp_file = NULL; | ||
| 646 | int fd = 0, result = 0; | ||
| 647 | time_t current_time; | ||
| 648 | char *directories = NULL; | ||
| 649 | char *p = NULL; | ||
| 650 | |||
| 651 | if (data_time == 0) | ||
| 652 | time(¤t_time); | ||
| 653 | else | ||
| 654 | current_time = data_time; | ||
| 655 | |||
| 656 | /* If file doesn't currently exist, create directories */ | ||
| 657 | if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) { | ||
| 658 | result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename); | ||
| 659 | if (result < 0) | ||
| 660 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 661 | |||
| 662 | for (p = directories + 1; *p; p++) { | ||
| 663 | if (*p == '/') { | ||
| 664 | *p = '\0'; | ||
| 665 | if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { | ||
| 666 | /* Can't free this! Otherwise error message is wrong! */ | ||
| 667 | /* np_free(directories); */ | ||
| 668 | die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); | ||
| 669 | } | ||
| 670 | *p = '/'; | ||
| 671 | } | ||
| 672 | } | ||
| 673 | np_free(directories); | ||
| 674 | } | ||
| 675 | |||
| 676 | result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename); | ||
| 677 | if (result < 0) | ||
| 678 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 679 | |||
| 680 | if ((fd = mkstemp(temp_file)) == -1) { | ||
| 681 | np_free(temp_file); | ||
| 682 | die(STATE_UNKNOWN, _("Cannot create temporary filename")); | ||
| 683 | } | ||
| 684 | |||
| 685 | fp = (FILE *)fdopen(fd, "w"); | ||
| 686 | if (fp == NULL) { | ||
| 687 | close(fd); | ||
| 688 | unlink(temp_file); | ||
| 689 | np_free(temp_file); | ||
| 690 | die(STATE_UNKNOWN, _("Unable to open temporary state file")); | ||
| 691 | } | 422 | } |
| 692 | 423 | if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) { | |
| 693 | fprintf(fp, "# NP State file\n"); | 424 | return STATE_CRITICAL; |
| 694 | fprintf(fp, "%d\n", NP_STATE_FORMAT_VERSION); | ||
| 695 | fprintf(fp, "%d\n", this_monitoring_plugin->state->data_version); | ||
| 696 | fprintf(fp, "%lu\n", current_time); | ||
| 697 | fprintf(fp, "%s\n", data_string); | ||
| 698 | |||
| 699 | fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP); | ||
| 700 | |||
| 701 | fflush(fp); | ||
| 702 | |||
| 703 | result = fclose(fp); | ||
| 704 | |||
| 705 | fsync(fd); | ||
| 706 | |||
| 707 | if (result != 0) { | ||
| 708 | unlink(temp_file); | ||
| 709 | np_free(temp_file); | ||
| 710 | die(STATE_UNKNOWN, _("Error writing temp file")); | ||
| 711 | } | 425 | } |
| 712 | 426 | if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) { | |
| 713 | if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) { | 427 | return STATE_UNKNOWN; |
| 714 | unlink(temp_file); | ||
| 715 | np_free(temp_file); | ||
| 716 | die(STATE_UNKNOWN, _("Cannot rename state temp file")); | ||
| 717 | } | 428 | } |
| 718 | 429 | return ERROR; | |
| 719 | np_free(temp_file); | ||
| 720 | } | 430 | } |
diff --git a/lib/utils_base.h b/lib/utils_base.h index a209cb6d..27884bf0 100644 --- a/lib/utils_base.h +++ b/lib/utils_base.h | |||
| @@ -2,6 +2,13 @@ | |||
| 2 | #define _UTILS_BASE_ | 2 | #define _UTILS_BASE_ |
| 3 | /* Header file for Monitoring Plugins utils_base.c */ | 3 | /* Header file for Monitoring Plugins utils_base.c */ |
| 4 | 4 | ||
| 5 | #include "../config.h" | ||
| 6 | #include <time.h> | ||
| 7 | |||
| 8 | #include "./perfdata.h" | ||
| 9 | #include "./thresholds.h" | ||
| 10 | #include "states.h" | ||
| 11 | |||
| 5 | #ifndef USE_OPENSSL | 12 | #ifndef USE_OPENSSL |
| 6 | # include "sha256.h" | 13 | # include "sha256.h" |
| 7 | #endif | 14 | #endif |
| @@ -19,39 +26,8 @@ | |||
| 19 | #define OUTSIDE 0 | 26 | #define OUTSIDE 0 |
| 20 | #define INSIDE 1 | 27 | #define INSIDE 1 |
| 21 | 28 | ||
| 22 | typedef struct range_struct { | ||
| 23 | double start; | ||
| 24 | bool start_infinity; | ||
| 25 | double end; | ||
| 26 | int end_infinity; | ||
| 27 | int alert_on; /* OUTSIDE (default) or INSIDE */ | ||
| 28 | char *text; /* original unparsed text input */ | ||
| 29 | } range; | ||
| 30 | |||
| 31 | typedef struct thresholds_struct { | ||
| 32 | range *warning; | ||
| 33 | range *critical; | ||
| 34 | } thresholds; | ||
| 35 | |||
| 36 | #define NP_STATE_FORMAT_VERSION 1 | ||
| 37 | |||
| 38 | typedef struct state_data_struct { | ||
| 39 | time_t time; | ||
| 40 | void *data; | ||
| 41 | int length; /* Of binary data */ | ||
| 42 | } state_data; | ||
| 43 | |||
| 44 | typedef struct state_key_struct { | ||
| 45 | char *name; | ||
| 46 | char *plugin_name; | ||
| 47 | int data_version; | ||
| 48 | char *_filename; | ||
| 49 | state_data *state_data; | ||
| 50 | } state_key; | ||
| 51 | |||
| 52 | typedef struct np_struct { | 29 | typedef struct np_struct { |
| 53 | char *plugin_name; | 30 | char *plugin_name; |
| 54 | state_key *state; | ||
| 55 | int argc; | 31 | int argc; |
| 56 | char **argv; | 32 | char **argv; |
| 57 | } monitoring_plugin; | 33 | } monitoring_plugin; |
| @@ -61,10 +37,11 @@ int _set_thresholds(thresholds **, char *, char *); | |||
| 61 | void set_thresholds(thresholds **, char *, char *); | 37 | void set_thresholds(thresholds **, char *, char *); |
| 62 | void print_thresholds(const char *, thresholds *); | 38 | void print_thresholds(const char *, thresholds *); |
| 63 | bool check_range(double, range *); | 39 | bool check_range(double, range *); |
| 64 | int get_status(double, thresholds *); | 40 | bool mp_check_range(mp_perfdata_value, mp_range); |
| 41 | mp_state_enum get_status(double, thresholds *); | ||
| 65 | 42 | ||
| 66 | /* Handle timeouts */ | 43 | /* Handle timeouts */ |
| 67 | extern int timeout_state; | 44 | extern mp_state_enum timeout_state; |
| 68 | extern unsigned int timeout_interval; | 45 | extern unsigned int timeout_interval; |
| 69 | 46 | ||
| 70 | /* All possible characters in a threshold range */ | 47 | /* All possible characters in a threshold range */ |
| @@ -106,13 +83,9 @@ char *np_extract_value(const char *, const char *, char); | |||
| 106 | */ | 83 | */ |
| 107 | int mp_translate_state(char *); | 84 | int mp_translate_state(char *); |
| 108 | 85 | ||
| 109 | void np_enable_state(char *, int); | ||
| 110 | state_data *np_state_read(); | ||
| 111 | void np_state_write_string(time_t, char *); | ||
| 112 | |||
| 113 | void np_init(char *, int argc, char **argv); | 86 | void np_init(char *, int argc, char **argv); |
| 114 | void np_set_args(int argc, char **argv); | 87 | void np_set_args(int argc, char **argv); |
| 115 | void np_cleanup(); | 88 | void np_cleanup(void); |
| 116 | const char *state_text(int); | 89 | const char *state_text(mp_state_enum); |
| 117 | 90 | ||
| 118 | #endif /* _UTILS_BASE_ */ | 91 | #endif /* _UTILS_BASE_ */ |
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index 18350ac0..42c81793 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c | |||
| @@ -40,7 +40,6 @@ | |||
| 40 | 40 | ||
| 41 | /** includes **/ | 41 | /** includes **/ |
| 42 | #include "common.h" | 42 | #include "common.h" |
| 43 | #include "utils.h" | ||
| 44 | #include "utils_cmd.h" | 43 | #include "utils_cmd.h" |
| 45 | /* This variable must be global, since there's no way the caller | 44 | /* This variable must be global, since there's no way the caller |
| 46 | * can forcibly slay a dead or ungainly running program otherwise. | 45 | * can forcibly slay a dead or ungainly running program otherwise. |
| @@ -57,21 +56,19 @@ static pid_t *_cmd_pids = NULL; | |||
| 57 | #include "./maxfd.h" | 56 | #include "./maxfd.h" |
| 58 | 57 | ||
| 59 | #include <fcntl.h> | 58 | #include <fcntl.h> |
| 59 | #include <stddef.h> | ||
| 60 | 60 | ||
| 61 | #ifdef HAVE_SYS_WAIT_H | 61 | #ifdef HAVE_SYS_WAIT_H |
| 62 | # include <sys/wait.h> | 62 | # include <sys/wait.h> |
| 63 | #endif | 63 | #endif |
| 64 | 64 | ||
| 65 | /* used in _cmd_open to pass the environment to commands */ | ||
| 66 | extern char **environ; | ||
| 67 | |||
| 68 | /** macros **/ | 65 | /** macros **/ |
| 69 | #ifndef WEXITSTATUS | 66 | #ifndef WEXITSTATUS |
| 70 | # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) | 67 | # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) |
| 71 | #endif | 68 | #endif |
| 72 | 69 | ||
| 73 | #ifndef WIFEXITED | 70 | #ifndef WIFEXITED |
| 74 | # define WIFEXITED(stat_val) (((stat_val)&255) == 0) | 71 | # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) |
| 75 | #endif | 72 | #endif |
| 76 | 73 | ||
| 77 | /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ | 74 | /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ |
| @@ -80,14 +77,13 @@ extern char **environ; | |||
| 80 | #endif | 77 | #endif |
| 81 | 78 | ||
| 82 | /** prototypes **/ | 79 | /** prototypes **/ |
| 83 | static int _cmd_open(char *const *, int *, int *) __attribute__((__nonnull__(1, 2, 3))); | 80 | static int _cmd_open(char *const *argv, int *pfd, int *pfderr) |
| 84 | 81 | __attribute__((__nonnull__(1, 2, 3))); | |
| 85 | static int _cmd_fetch_output(int, output *, int) __attribute__((__nonnull__(2))); | ||
| 86 | 82 | ||
| 87 | static int _cmd_close(int); | 83 | static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) |
| 84 | __attribute__((__nonnull__(2))); | ||
| 88 | 85 | ||
| 89 | /* prototype imported from utils.h */ | 86 | static int _cmd_close(int fileDescriptor); |
| 90 | extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 2, 3))); | ||
| 91 | 87 | ||
| 92 | /* this function is NOT async-safe. It is exported so multithreaded | 88 | /* this function is NOT async-safe. It is exported so multithreaded |
| 93 | * plugins (or other apps) can call it prior to running any commands | 89 | * plugins (or other apps) can call it prior to running any commands |
| @@ -103,26 +99,100 @@ void cmd_init(void) { | |||
| 103 | maxfd = MAXFD_LIMIT; | 99 | maxfd = MAXFD_LIMIT; |
| 104 | } | 100 | } |
| 105 | 101 | ||
| 106 | if (!_cmd_pids) | 102 | if (!_cmd_pids) { |
| 107 | _cmd_pids = calloc(maxfd, sizeof(pid_t)); | 103 | _cmd_pids = calloc(maxfd, sizeof(pid_t)); |
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | typedef struct { | ||
| 108 | int stdout_pipe_fd[2]; | ||
| 109 | int stderr_pipe_fd[2]; | ||
| 110 | int file_descriptor; | ||
| 111 | int error_code; | ||
| 112 | } int_cmd_open_result; | ||
| 113 | static int_cmd_open_result _cmd_open2(char *const *argv) { | ||
| 114 | #ifdef RLIMIT_CORE | ||
| 115 | struct rlimit limit; | ||
| 116 | #endif | ||
| 117 | |||
| 118 | if (!_cmd_pids) { | ||
| 119 | CMD_INIT; | ||
| 120 | } | ||
| 121 | |||
| 122 | setenv("LC_ALL", "C", 1); | ||
| 123 | |||
| 124 | int_cmd_open_result result = { | ||
| 125 | .error_code = 0, | ||
| 126 | .stdout_pipe_fd = {0, 0}, | ||
| 127 | .stderr_pipe_fd = {0, 0}, | ||
| 128 | }; | ||
| 129 | pid_t pid; | ||
| 130 | if (pipe(result.stdout_pipe_fd) < 0 || pipe(result.stderr_pipe_fd) < 0 || (pid = fork()) < 0) { | ||
| 131 | result.error_code = -1; | ||
| 132 | return result; /* errno set by the failing function */ | ||
| 133 | } | ||
| 134 | |||
| 135 | /* child runs exceve() and _exit. */ | ||
| 136 | if (pid == 0) { | ||
| 137 | #ifdef RLIMIT_CORE | ||
| 138 | /* the program we execve shouldn't leave core files */ | ||
| 139 | getrlimit(RLIMIT_CORE, &limit); | ||
| 140 | limit.rlim_cur = 0; | ||
| 141 | setrlimit(RLIMIT_CORE, &limit); | ||
| 142 | #endif | ||
| 143 | close(result.stdout_pipe_fd[0]); | ||
| 144 | if (result.stdout_pipe_fd[1] != STDOUT_FILENO) { | ||
| 145 | dup2(result.stdout_pipe_fd[1], STDOUT_FILENO); | ||
| 146 | close(result.stdout_pipe_fd[1]); | ||
| 147 | } | ||
| 148 | close(result.stderr_pipe_fd[0]); | ||
| 149 | if (result.stderr_pipe_fd[1] != STDERR_FILENO) { | ||
| 150 | dup2(result.stderr_pipe_fd[1], STDERR_FILENO); | ||
| 151 | close(result.stderr_pipe_fd[1]); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* close all descriptors in _cmd_pids[] | ||
| 155 | * This is executed in a separate address space (pure child), | ||
| 156 | * so we don't have to worry about async safety */ | ||
| 157 | long maxfd = mp_open_max(); | ||
| 158 | for (int i = 0; i < maxfd; i++) { | ||
| 159 | if (_cmd_pids[i] > 0) { | ||
| 160 | close(i); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | execve(argv[0], argv, environ); | ||
| 165 | _exit(STATE_UNKNOWN); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* parent picks up execution here */ | ||
| 169 | /* close children descriptors in our address space */ | ||
| 170 | close(result.stdout_pipe_fd[1]); | ||
| 171 | close(result.stderr_pipe_fd[1]); | ||
| 172 | |||
| 173 | /* tag our file's entry in the pid-list and return it */ | ||
| 174 | _cmd_pids[result.stdout_pipe_fd[0]] = pid; | ||
| 175 | |||
| 176 | result.file_descriptor = result.stdout_pipe_fd[0]; | ||
| 177 | return result; | ||
| 108 | } | 178 | } |
| 109 | 179 | ||
| 110 | /* Start running a command, array style */ | 180 | /* Start running a command, array style */ |
| 111 | static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { | 181 | static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { |
| 112 | pid_t pid; | ||
| 113 | #ifdef RLIMIT_CORE | 182 | #ifdef RLIMIT_CORE |
| 114 | struct rlimit limit; | 183 | struct rlimit limit; |
| 115 | #endif | 184 | #endif |
| 116 | 185 | ||
| 117 | int i = 0; | 186 | if (!_cmd_pids) { |
| 118 | |||
| 119 | if (!_cmd_pids) | ||
| 120 | CMD_INIT; | 187 | CMD_INIT; |
| 188 | } | ||
| 121 | 189 | ||
| 122 | setenv("LC_ALL", "C", 1); | 190 | setenv("LC_ALL", "C", 1); |
| 123 | 191 | ||
| 124 | if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) | 192 | pid_t pid; |
| 193 | if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) { | ||
| 125 | return -1; /* errno set by the failing function */ | 194 | return -1; /* errno set by the failing function */ |
| 195 | } | ||
| 126 | 196 | ||
| 127 | /* child runs exceve() and _exit. */ | 197 | /* child runs exceve() and _exit. */ |
| 128 | if (pid == 0) { | 198 | if (pid == 0) { |
| @@ -147,9 +217,11 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { | |||
| 147 | * This is executed in a separate address space (pure child), | 217 | * This is executed in a separate address space (pure child), |
| 148 | * so we don't have to worry about async safety */ | 218 | * so we don't have to worry about async safety */ |
| 149 | long maxfd = mp_open_max(); | 219 | long maxfd = mp_open_max(); |
| 150 | for (i = 0; i < maxfd; i++) | 220 | for (int i = 0; i < maxfd; i++) { |
| 151 | if (_cmd_pids[i] > 0) | 221 | if (_cmd_pids[i] > 0) { |
| 152 | close(i); | 222 | close(i); |
| 223 | } | ||
| 224 | } | ||
| 153 | 225 | ||
| 154 | execve(argv[0], argv, environ); | 226 | execve(argv[0], argv, environ); |
| 155 | _exit(STATE_UNKNOWN); | 227 | _exit(STATE_UNKNOWN); |
| @@ -166,88 +238,171 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { | |||
| 166 | return pfd[0]; | 238 | return pfd[0]; |
| 167 | } | 239 | } |
| 168 | 240 | ||
| 169 | static int _cmd_close(int fd) { | 241 | static int _cmd_close(int fileDescriptor) { |
| 170 | int status; | ||
| 171 | pid_t pid; | 242 | pid_t pid; |
| 172 | 243 | ||
| 173 | /* make sure the provided fd was opened */ | 244 | /* make sure the provided fd was opened */ |
| 174 | long maxfd = mp_open_max(); | 245 | long maxfd = mp_open_max(); |
| 175 | if (fd < 0 || fd > maxfd || !_cmd_pids || (pid = _cmd_pids[fd]) == 0) | 246 | if (fileDescriptor < 0 || fileDescriptor > maxfd || !_cmd_pids || |
| 247 | (pid = _cmd_pids[fileDescriptor]) == 0) { | ||
| 176 | return -1; | 248 | return -1; |
| 249 | } | ||
| 177 | 250 | ||
| 178 | _cmd_pids[fd] = 0; | 251 | _cmd_pids[fileDescriptor] = 0; |
| 179 | if (close(fd) == -1) | 252 | if (close(fileDescriptor) == -1) { |
| 180 | return -1; | 253 | return -1; |
| 254 | } | ||
| 181 | 255 | ||
| 182 | /* EINTR is ok (sort of), everything else is bad */ | 256 | /* EINTR is ok (sort of), everything else is bad */ |
| 183 | while (waitpid(pid, &status, 0) < 0) | 257 | int status; |
| 184 | if (errno != EINTR) | 258 | while (waitpid(pid, &status, 0) < 0) { |
| 259 | if (errno != EINTR) { | ||
| 185 | return -1; | 260 | return -1; |
| 261 | } | ||
| 262 | } | ||
| 186 | 263 | ||
| 187 | /* return child's termination status */ | 264 | /* return child's termination status */ |
| 188 | return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; | 265 | return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; |
| 189 | } | 266 | } |
| 190 | 267 | ||
| 191 | static int _cmd_fetch_output(int fd, output *op, int flags) { | 268 | typedef struct { |
| 192 | size_t len = 0, i = 0, lineno = 0; | 269 | int error_code; |
| 193 | size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ | 270 | output output_container; |
| 194 | char *buf = NULL; | 271 | } int_cmd_fetch_output2; |
| 195 | int ret; | 272 | static int_cmd_fetch_output2 _cmd_fetch_output2(int fileDescriptor, int flags) { |
| 196 | char tmpbuf[4096]; | 273 | char tmpbuf[4096]; |
| 197 | 274 | ||
| 198 | op->buf = NULL; | 275 | int_cmd_fetch_output2 result = { |
| 199 | op->buflen = 0; | 276 | .error_code = 0, |
| 200 | while ((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) { | 277 | .output_container = |
| 201 | len = (size_t)ret; | 278 | { |
| 202 | op->buf = realloc(op->buf, op->buflen + len + 1); | 279 | .buf = NULL, |
| 203 | memcpy(op->buf + op->buflen, tmpbuf, len); | 280 | .buflen = 0, |
| 204 | op->buflen += len; | 281 | .line = NULL, |
| 282 | .lines = 0, | ||
| 283 | }, | ||
| 284 | }; | ||
| 285 | ssize_t ret; | ||
| 286 | while ((ret = read(fileDescriptor, tmpbuf, sizeof(tmpbuf))) > 0) { | ||
| 287 | size_t len = (size_t)ret; | ||
| 288 | result.output_container.buf = | ||
| 289 | realloc(result.output_container.buf, result.output_container.buflen + len + 1); | ||
| 290 | memcpy(result.output_container.buf + result.output_container.buflen, tmpbuf, len); | ||
| 291 | result.output_container.buflen += len; | ||
| 292 | } | ||
| 293 | |||
| 294 | if (ret < 0) { | ||
| 295 | printf("read() returned %zd: %s\n", ret, strerror(errno)); | ||
| 296 | result.error_code = -1; | ||
| 297 | return result; | ||
| 298 | } | ||
| 299 | |||
| 300 | /* some plugins may want to keep output unbroken, and some commands | ||
| 301 | * will yield no output, so return here for those */ | ||
| 302 | if (flags & CMD_NO_ARRAYS || !result.output_container.buf || !result.output_container.buflen) { | ||
| 303 | return result; | ||
| 304 | } | ||
| 305 | |||
| 306 | /* and some may want both */ | ||
| 307 | char *buf = NULL; | ||
| 308 | if (flags & CMD_NO_ASSOC) { | ||
| 309 | buf = malloc(result.output_container.buflen); | ||
| 310 | memcpy(buf, result.output_container.buf, result.output_container.buflen); | ||
| 311 | } else { | ||
| 312 | buf = result.output_container.buf; | ||
| 313 | } | ||
| 314 | |||
| 315 | result.output_container.line = NULL; | ||
| 316 | size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ | ||
| 317 | size_t rsf = 6; | ||
| 318 | size_t lineno = 0; | ||
| 319 | for (size_t i = 0; i < result.output_container.buflen;) { | ||
| 320 | /* make sure we have enough memory */ | ||
| 321 | if (lineno >= ary_size) { | ||
| 322 | /* ary_size must never be zero */ | ||
| 323 | do { | ||
| 324 | ary_size = result.output_container.buflen >> --rsf; | ||
| 325 | } while (!ary_size); | ||
| 326 | |||
| 327 | result.output_container.line = | ||
| 328 | realloc(result.output_container.line, ary_size * sizeof(char *)); | ||
| 329 | } | ||
| 330 | |||
| 331 | /* set the pointer to the string */ | ||
| 332 | result.output_container.line[lineno] = &buf[i]; | ||
| 333 | |||
| 334 | /* hop to next newline or end of buffer */ | ||
| 335 | while (buf[i] != '\n' && i < result.output_container.buflen) { | ||
| 336 | i++; | ||
| 337 | } | ||
| 338 | buf[i] = '\0'; | ||
| 339 | |||
| 340 | lineno++; | ||
| 205 | i++; | 341 | i++; |
| 206 | } | 342 | } |
| 207 | 343 | ||
| 344 | result.output_container.lines = lineno; | ||
| 345 | |||
| 346 | return result; | ||
| 347 | } | ||
| 348 | |||
| 349 | static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) { | ||
| 350 | char tmpbuf[4096]; | ||
| 351 | cmd_output->buf = NULL; | ||
| 352 | cmd_output->buflen = 0; | ||
| 353 | ssize_t ret; | ||
| 354 | while ((ret = read(fileDescriptor, tmpbuf, sizeof(tmpbuf))) > 0) { | ||
| 355 | size_t len = (size_t)ret; | ||
| 356 | cmd_output->buf = realloc(cmd_output->buf, cmd_output->buflen + len + 1); | ||
| 357 | memcpy(cmd_output->buf + cmd_output->buflen, tmpbuf, len); | ||
| 358 | cmd_output->buflen += len; | ||
| 359 | } | ||
| 360 | |||
| 208 | if (ret < 0) { | 361 | if (ret < 0) { |
| 209 | printf("read() returned %d: %s\n", ret, strerror(errno)); | 362 | printf("read() returned %zd: %s\n", ret, strerror(errno)); |
| 210 | return ret; | 363 | return ret; |
| 211 | } | 364 | } |
| 212 | 365 | ||
| 213 | /* some plugins may want to keep output unbroken, and some commands | 366 | /* some plugins may want to keep output unbroken, and some commands |
| 214 | * will yield no output, so return here for those */ | 367 | * will yield no output, so return here for those */ |
| 215 | if (flags & CMD_NO_ARRAYS || !op->buf || !op->buflen) | 368 | if (flags & CMD_NO_ARRAYS || !cmd_output->buf || !cmd_output->buflen) { |
| 216 | return op->buflen; | 369 | return cmd_output->buflen; |
| 370 | } | ||
| 217 | 371 | ||
| 218 | /* and some may want both */ | 372 | /* and some may want both */ |
| 373 | char *buf = NULL; | ||
| 219 | if (flags & CMD_NO_ASSOC) { | 374 | if (flags & CMD_NO_ASSOC) { |
| 220 | buf = malloc(op->buflen); | 375 | buf = malloc(cmd_output->buflen); |
| 221 | memcpy(buf, op->buf, op->buflen); | 376 | memcpy(buf, cmd_output->buf, cmd_output->buflen); |
| 222 | } else | 377 | } else { |
| 223 | buf = op->buf; | 378 | buf = cmd_output->buf; |
| 224 | 379 | } | |
| 225 | op->line = NULL; | 380 | |
| 226 | op->lens = NULL; | 381 | cmd_output->line = NULL; |
| 227 | i = 0; | 382 | size_t i = 0; |
| 228 | while (i < op->buflen) { | 383 | size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ |
| 384 | size_t rsf = 6; | ||
| 385 | size_t lineno = 0; | ||
| 386 | while (i < cmd_output->buflen) { | ||
| 229 | /* make sure we have enough memory */ | 387 | /* make sure we have enough memory */ |
| 230 | if (lineno >= ary_size) { | 388 | if (lineno >= ary_size) { |
| 231 | /* ary_size must never be zero */ | 389 | /* ary_size must never be zero */ |
| 232 | do { | 390 | do { |
| 233 | ary_size = op->buflen >> --rsf; | 391 | ary_size = cmd_output->buflen >> --rsf; |
| 234 | } while (!ary_size); | 392 | } while (!ary_size); |
| 235 | 393 | ||
| 236 | op->line = realloc(op->line, ary_size * sizeof(char *)); | 394 | cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *)); |
| 237 | op->lens = realloc(op->lens, ary_size * sizeof(size_t)); | ||
| 238 | } | 395 | } |
| 239 | 396 | ||
| 240 | /* set the pointer to the string */ | 397 | /* set the pointer to the string */ |
| 241 | op->line[lineno] = &buf[i]; | 398 | cmd_output->line[lineno] = &buf[i]; |
| 242 | 399 | ||
| 243 | /* hop to next newline or end of buffer */ | 400 | /* hop to next newline or end of buffer */ |
| 244 | while (buf[i] != '\n' && i < op->buflen) | 401 | while (buf[i] != '\n' && i < cmd_output->buflen) { |
| 245 | i++; | 402 | i++; |
| 403 | } | ||
| 246 | buf[i] = '\0'; | 404 | buf[i] = '\0'; |
| 247 | 405 | ||
| 248 | /* calculate the string length using pointer difference */ | ||
| 249 | op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno]; | ||
| 250 | |||
| 251 | lineno++; | 406 | lineno++; |
| 252 | i++; | 407 | i++; |
| 253 | } | 408 | } |
| @@ -256,41 +411,42 @@ static int _cmd_fetch_output(int fd, output *op, int flags) { | |||
| 256 | } | 411 | } |
| 257 | 412 | ||
| 258 | int cmd_run(const char *cmdstring, output *out, output *err, int flags) { | 413 | int cmd_run(const char *cmdstring, output *out, output *err, int flags) { |
| 259 | int i = 0, argc; | 414 | if (cmdstring == NULL) { |
| 260 | size_t cmdlen; | ||
| 261 | char **argv = NULL; | ||
| 262 | char *cmd = NULL; | ||
| 263 | char *str = NULL; | ||
| 264 | |||
| 265 | if (cmdstring == NULL) | ||
| 266 | return -1; | 415 | return -1; |
| 416 | } | ||
| 267 | 417 | ||
| 268 | /* initialize the structs */ | 418 | /* initialize the structs */ |
| 269 | if (out) | 419 | if (out) { |
| 270 | memset(out, 0, sizeof(output)); | 420 | memset(out, 0, sizeof(output)); |
| 271 | if (err) | 421 | } |
| 422 | if (err) { | ||
| 272 | memset(err, 0, sizeof(output)); | 423 | memset(err, 0, sizeof(output)); |
| 424 | } | ||
| 273 | 425 | ||
| 274 | /* make copy of command string so strtok() doesn't silently modify it */ | 426 | /* make copy of command string so strtok() doesn't silently modify it */ |
| 275 | /* (the calling program may want to access it later) */ | 427 | /* (the calling program may want to access it later) */ |
| 276 | cmdlen = strlen(cmdstring); | 428 | size_t cmdlen = strlen(cmdstring); |
| 277 | if ((cmd = malloc(cmdlen + 1)) == NULL) | 429 | char *cmd = NULL; |
| 430 | if ((cmd = malloc(cmdlen + 1)) == NULL) { | ||
| 278 | return -1; | 431 | return -1; |
| 432 | } | ||
| 279 | memcpy(cmd, cmdstring, cmdlen); | 433 | memcpy(cmd, cmdstring, cmdlen); |
| 280 | cmd[cmdlen] = '\0'; | 434 | cmd[cmdlen] = '\0'; |
| 281 | 435 | ||
| 282 | /* This is not a shell, so we don't handle "???" */ | 436 | /* This is not a shell, so we don't handle "???" */ |
| 283 | if (strstr(cmdstring, "\"")) | 437 | if (strstr(cmdstring, "\"")) { |
| 284 | return -1; | 438 | return -1; |
| 439 | } | ||
| 285 | 440 | ||
| 286 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ | 441 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ |
| 287 | if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) | 442 | if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) { |
| 288 | return -1; | 443 | return -1; |
| 444 | } | ||
| 289 | 445 | ||
| 290 | /* each arg must be whitespace-separated, so args can be a maximum | 446 | /* each arg must be whitespace-separated, so args can be a maximum |
| 291 | * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ | 447 | * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ |
| 292 | argc = (cmdlen >> 1) + 2; | 448 | int argc = (cmdlen >> 1) + 2; |
| 293 | argv = calloc((size_t)argc, sizeof(char *)); | 449 | char **argv = calloc((size_t)argc, sizeof(char *)); |
| 294 | 450 | ||
| 295 | if (argv == NULL) { | 451 | if (argv == NULL) { |
| 296 | printf("%s\n", _("Could not malloc argv array in popen()")); | 452 | printf("%s\n", _("Could not malloc argv array in popen()")); |
| @@ -298,14 +454,16 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { | |||
| 298 | } | 454 | } |
| 299 | 455 | ||
| 300 | /* get command arguments (stupidly, but fairly quickly) */ | 456 | /* get command arguments (stupidly, but fairly quickly) */ |
| 457 | int i = 0; | ||
| 301 | while (cmd) { | 458 | while (cmd) { |
| 302 | str = cmd; | 459 | char *str = cmd; |
| 303 | str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ | 460 | str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ |
| 304 | 461 | ||
| 305 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ | 462 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ |
| 306 | str++; | 463 | str++; |
| 307 | if (!strstr(str, "'")) | 464 | if (!strstr(str, "'")) { |
| 308 | return -1; /* balanced? */ | 465 | return -1; /* balanced? */ |
| 466 | } | ||
| 309 | cmd = 1 + strstr(str, "'"); | 467 | cmd = 1 + strstr(str, "'"); |
| 310 | str[strcspn(str, "'")] = 0; | 468 | str[strcspn(str, "'")] = 0; |
| 311 | } else { | 469 | } else { |
| @@ -317,8 +475,9 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { | |||
| 317 | } | 475 | } |
| 318 | } | 476 | } |
| 319 | 477 | ||
| 320 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) | 478 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) { |
| 321 | cmd = NULL; | 479 | cmd = NULL; |
| 480 | } | ||
| 322 | 481 | ||
| 323 | argv[i++] = str; | 482 | argv[i++] = str; |
| 324 | } | 483 | } |
| @@ -326,54 +485,199 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { | |||
| 326 | return cmd_run_array(argv, out, err, flags); | 485 | return cmd_run_array(argv, out, err, flags); |
| 327 | } | 486 | } |
| 328 | 487 | ||
| 329 | int cmd_run_array(char *const *argv, output *out, output *err, int flags) { | 488 | cmd_run_result cmd_run2(const char *cmd_string, int flags) { |
| 330 | int fd, pfd_out[2], pfd_err[2]; | 489 | cmd_run_result result = { |
| 490 | .cmd_error_code = 0, | ||
| 491 | .error_code = 0, | ||
| 492 | .stderr = | ||
| 493 | { | ||
| 494 | .buf = NULL, | ||
| 495 | .buflen = 0, | ||
| 496 | .line = NULL, | ||
| 497 | .lines = 0, | ||
| 498 | }, | ||
| 499 | .stdout = | ||
| 500 | { | ||
| 501 | .buf = NULL, | ||
| 502 | .buflen = 0, | ||
| 503 | .line = NULL, | ||
| 504 | .lines = 0, | ||
| 505 | }, | ||
| 506 | }; | ||
| 507 | |||
| 508 | if (cmd_string == NULL) { | ||
| 509 | result.error_code = -1; | ||
| 510 | return result; | ||
| 511 | } | ||
| 512 | |||
| 513 | /* make copy of command string so strtok() doesn't silently modify it */ | ||
| 514 | /* (the calling program may want to access it later) */ | ||
| 515 | char *cmd = strdup(cmd_string); | ||
| 516 | if (cmd == NULL) { | ||
| 517 | result.error_code = -1; | ||
| 518 | return result; | ||
| 519 | } | ||
| 520 | |||
| 521 | /* This is not a shell, so we don't handle "???" */ | ||
| 522 | if (strstr(cmd, "\"")) { | ||
| 523 | result.error_code = -1; | ||
| 524 | return result; | ||
| 525 | } | ||
| 526 | |||
| 527 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ | ||
| 528 | if (strstr(cmd, " ' ") || strstr(cmd, "'''")) { | ||
| 529 | result.error_code = -1; | ||
| 530 | return result; | ||
| 531 | } | ||
| 532 | |||
| 533 | /* each arg must be whitespace-separated, so args can be a maximum | ||
| 534 | * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ | ||
| 535 | size_t cmdlen = strlen(cmd_string); | ||
| 536 | size_t argc = (cmdlen >> 1) + 2; | ||
| 537 | char **argv = calloc(argc, sizeof(char *)); | ||
| 538 | |||
| 539 | if (argv == NULL) { | ||
| 540 | printf("%s\n", _("Could not malloc argv array in popen()")); | ||
| 541 | result.error_code = -1; | ||
| 542 | return result; | ||
| 543 | } | ||
| 331 | 544 | ||
| 545 | /* get command arguments (stupidly, but fairly quickly) */ | ||
| 546 | for (int i = 0; cmd; i++) { | ||
| 547 | char *str = cmd; | ||
| 548 | str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ | ||
| 549 | |||
| 550 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ | ||
| 551 | str++; | ||
| 552 | if (!strstr(str, "'")) { | ||
| 553 | result.error_code = -1; | ||
| 554 | return result; /* balanced? */ | ||
| 555 | } | ||
| 556 | |||
| 557 | cmd = 1 + strstr(str, "'"); | ||
| 558 | str[strcspn(str, "'")] = 0; | ||
| 559 | } else { | ||
| 560 | if (strpbrk(str, " \t\r\n")) { | ||
| 561 | cmd = 1 + strpbrk(str, " \t\r\n"); | ||
| 562 | str[strcspn(str, " \t\r\n")] = 0; | ||
| 563 | } else { | ||
| 564 | cmd = NULL; | ||
| 565 | } | ||
| 566 | } | ||
| 567 | |||
| 568 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) { | ||
| 569 | cmd = NULL; | ||
| 570 | } | ||
| 571 | |||
| 572 | argv[i++] = str; | ||
| 573 | } | ||
| 574 | |||
| 575 | result = cmd_run_array2(argv, flags); | ||
| 576 | |||
| 577 | return result; | ||
| 578 | } | ||
| 579 | |||
| 580 | cmd_run_result cmd_run_array2(char *const *cmd, int flags) { | ||
| 581 | cmd_run_result result = { | ||
| 582 | .cmd_error_code = 0, | ||
| 583 | .error_code = 0, | ||
| 584 | .stderr = | ||
| 585 | { | ||
| 586 | .buf = NULL, | ||
| 587 | .buflen = 0, | ||
| 588 | .line = NULL, | ||
| 589 | .lines = 0, | ||
| 590 | }, | ||
| 591 | .stdout = | ||
| 592 | { | ||
| 593 | .buf = NULL, | ||
| 594 | .buflen = 0, | ||
| 595 | .line = NULL, | ||
| 596 | .lines = 0, | ||
| 597 | }, | ||
| 598 | }; | ||
| 599 | |||
| 600 | int_cmd_open_result cmd_open_result = _cmd_open2(cmd); | ||
| 601 | if (cmd_open_result.error_code != 0) { | ||
| 602 | // result.error_code = -1; | ||
| 603 | // return result; | ||
| 604 | // TODO properly handle this without dying | ||
| 605 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd[0]); | ||
| 606 | } | ||
| 607 | |||
| 608 | int file_descriptor = cmd_open_result.file_descriptor; | ||
| 609 | int pfd_out[2] = {cmd_open_result.stdout_pipe_fd[0], cmd_open_result.stdout_pipe_fd[1]}; | ||
| 610 | int pfd_err[2] = {cmd_open_result.stderr_pipe_fd[0], cmd_open_result.stderr_pipe_fd[1]}; | ||
| 611 | |||
| 612 | int_cmd_fetch_output2 tmp_stdout = _cmd_fetch_output2(pfd_out[0], flags); | ||
| 613 | result.stdout = tmp_stdout.output_container; | ||
| 614 | int_cmd_fetch_output2 tmp_stderr = _cmd_fetch_output2(pfd_err[0], flags); | ||
| 615 | result.stderr = tmp_stderr.output_container; | ||
| 616 | |||
| 617 | result.cmd_error_code = _cmd_close(file_descriptor); | ||
| 618 | return result; | ||
| 619 | } | ||
| 620 | |||
| 621 | int cmd_run_array(char *const *argv, output *out, output *err, int flags) { | ||
| 332 | /* initialize the structs */ | 622 | /* initialize the structs */ |
| 333 | if (out) | 623 | if (out) { |
| 334 | memset(out, 0, sizeof(output)); | 624 | memset(out, 0, sizeof(output)); |
| 335 | if (err) | 625 | } |
| 626 | if (err) { | ||
| 336 | memset(err, 0, sizeof(output)); | 627 | memset(err, 0, sizeof(output)); |
| 628 | } | ||
| 337 | 629 | ||
| 338 | if ((fd = _cmd_open(argv, pfd_out, pfd_err)) == -1) | 630 | int fd; |
| 631 | int pfd_out[2]; | ||
| 632 | int pfd_err[2]; | ||
| 633 | if ((fd = _cmd_open(argv, pfd_out, pfd_err)) == -1) { | ||
| 339 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), argv[0]); | 634 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), argv[0]); |
| 635 | } | ||
| 340 | 636 | ||
| 341 | if (out) | 637 | if (out) { |
| 342 | out->lines = _cmd_fetch_output(pfd_out[0], out, flags); | 638 | out->lines = _cmd_fetch_output(pfd_out[0], out, flags); |
| 343 | if (err) | 639 | } |
| 640 | if (err) { | ||
| 344 | err->lines = _cmd_fetch_output(pfd_err[0], err, flags); | 641 | err->lines = _cmd_fetch_output(pfd_err[0], err, flags); |
| 642 | } | ||
| 345 | 643 | ||
| 346 | return _cmd_close(fd); | 644 | return _cmd_close(fd); |
| 347 | } | 645 | } |
| 348 | 646 | ||
| 349 | int cmd_file_read(char *filename, output *out, int flags) { | 647 | int cmd_file_read(const char *filename, output *out, int flags) { |
| 350 | int fd; | 648 | int fd; |
| 351 | if (out) | 649 | if (out) { |
| 352 | memset(out, 0, sizeof(output)); | 650 | memset(out, 0, sizeof(output)); |
| 651 | } | ||
| 353 | 652 | ||
| 354 | if ((fd = open(filename, O_RDONLY)) == -1) { | 653 | if ((fd = open(filename, O_RDONLY)) == -1) { |
| 355 | die(STATE_UNKNOWN, _("Error opening %s: %s"), filename, strerror(errno)); | 654 | die(STATE_UNKNOWN, _("Error opening %s: %s"), filename, strerror(errno)); |
| 356 | } | 655 | } |
| 357 | 656 | ||
| 358 | if (out) | 657 | if (out) { |
| 359 | out->lines = _cmd_fetch_output(fd, out, flags); | 658 | out->lines = _cmd_fetch_output(fd, out, flags); |
| 659 | } | ||
| 360 | 660 | ||
| 361 | if (close(fd) == -1) | 661 | if (close(fd) == -1) { |
| 362 | die(STATE_UNKNOWN, _("Error closing %s: %s"), filename, strerror(errno)); | 662 | die(STATE_UNKNOWN, _("Error closing %s: %s"), filename, strerror(errno)); |
| 663 | } | ||
| 363 | 664 | ||
| 364 | return 0; | 665 | return 0; |
| 365 | } | 666 | } |
| 366 | 667 | ||
| 367 | void timeout_alarm_handler(int signo) { | 668 | void timeout_alarm_handler(int signo) { |
| 368 | if (signo == SIGALRM) { | 669 | if (signo == SIGALRM) { |
| 369 | printf(_("%s - Plugin timed out after %d seconds\n"), state_text(timeout_state), timeout_interval); | 670 | printf(_("%s - Plugin timed out after %d seconds\n"), state_text(timeout_state), |
| 671 | timeout_interval); | ||
| 370 | 672 | ||
| 371 | long maxfd = mp_open_max(); | 673 | long maxfd = mp_open_max(); |
| 372 | if (_cmd_pids) | 674 | if (_cmd_pids) { |
| 373 | for (long int i = 0; i < maxfd; i++) { | 675 | for (long int i = 0; i < maxfd; i++) { |
| 374 | if (_cmd_pids[i] != 0) | 676 | if (_cmd_pids[i] != 0) { |
| 375 | kill(_cmd_pids[i], SIGKILL); | 677 | kill(_cmd_pids[i], SIGKILL); |
| 678 | } | ||
| 376 | } | 679 | } |
| 680 | } | ||
| 377 | 681 | ||
| 378 | exit(timeout_state); | 682 | exit(timeout_state); |
| 379 | } | 683 | } |
diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h index d00069c9..d3a8f14f 100644 --- a/lib/utils_cmd.h +++ b/lib/utils_cmd.h | |||
| @@ -5,22 +5,30 @@ | |||
| 5 | * Header file for Monitoring Plugins utils_cmd.c | 5 | * Header file for Monitoring Plugins utils_cmd.c |
| 6 | * | 6 | * |
| 7 | */ | 7 | */ |
| 8 | #include "../config.h" | ||
| 9 | #include <stddef.h> | ||
| 8 | 10 | ||
| 9 | /** types **/ | 11 | /** types **/ |
| 10 | struct output { | 12 | typedef struct { |
| 11 | char *buf; /* output buffer */ | 13 | char *buf; /* output buffer */ |
| 12 | size_t buflen; /* output buffer content length */ | 14 | size_t buflen; /* output buffer content length */ |
| 13 | char **line; /* array of lines (points to buf) */ | 15 | char **line; /* array of lines (points to buf) */ |
| 14 | size_t *lens; /* string lengths */ | ||
| 15 | size_t lines; /* lines of output */ | 16 | size_t lines; /* lines of output */ |
| 16 | }; | 17 | } output; |
| 17 | |||
| 18 | typedef struct output output; | ||
| 19 | 18 | ||
| 20 | /** prototypes **/ | 19 | /** prototypes **/ |
| 21 | int cmd_run(const char *, output *, output *, int); | 20 | int cmd_run(const char *, output *, output *, int); |
| 22 | int cmd_run_array(char *const *, output *, output *, int); | 21 | int cmd_run_array(char *const *, output *, output *, int); |
| 23 | int cmd_file_read(char *, output *, int); | 22 | int cmd_file_read(const char *, output *, int); |
| 23 | |||
| 24 | typedef struct { | ||
| 25 | int error_code; | ||
| 26 | int cmd_error_code; | ||
| 27 | output stdout; | ||
| 28 | output stderr; | ||
| 29 | } cmd_run_result; | ||
| 30 | cmd_run_result cmd_run2(const char *cmd, int flags); | ||
| 31 | cmd_run_result cmd_run_array2(char * const *cmd, int flags); | ||
| 24 | 32 | ||
| 25 | /* only multi-threaded plugins need to bother with this */ | 33 | /* only multi-threaded plugins need to bother with this */ |
| 26 | void cmd_init(void); | 34 | void cmd_init(void); |
diff --git a/lib/utils_disk.c b/lib/utils_disk.c deleted file mode 100644 index 2b761f5e..00000000 --- a/lib/utils_disk.c +++ /dev/null | |||
| @@ -1,255 +0,0 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * Library for check_disk | ||
| 4 | * | ||
| 5 | * License: GPL | ||
| 6 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team | ||
| 7 | * | ||
| 8 | * Description: | ||
| 9 | * | ||
| 10 | * This file contains utilities for check_disk. These are tested by libtap | ||
| 11 | * | ||
| 12 | * | ||
| 13 | * This program is free software: you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation, either version 3 of the License, or | ||
| 16 | * (at your option) any later version. | ||
| 17 | * | ||
| 18 | * This program is distributed in the hope that it will be useful, | ||
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 21 | * GNU General Public License for more details. | ||
| 22 | * | ||
| 23 | * You should have received a copy of the GNU General Public License | ||
| 24 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 25 | * | ||
| 26 | * | ||
| 27 | *****************************************************************************/ | ||
| 28 | |||
| 29 | #include "common.h" | ||
| 30 | #include "utils_disk.h" | ||
| 31 | #include "gl/fsusage.h" | ||
| 32 | #include <string.h> | ||
| 33 | |||
| 34 | void np_add_name(struct name_list **list, const char *name) { | ||
| 35 | struct name_list *new_entry; | ||
| 36 | new_entry = (struct name_list *)malloc(sizeof *new_entry); | ||
| 37 | new_entry->name = (char *)name; | ||
| 38 | new_entry->next = *list; | ||
| 39 | *list = new_entry; | ||
| 40 | } | ||
| 41 | |||
| 42 | /* @brief Initialises a new regex at the begin of list via regcomp(3) | ||
| 43 | * | ||
| 44 | * @details if the regex fails to compile the error code of regcomp(3) is returned | ||
| 45 | * and list is not modified, otherwise list is modified to point to the new | ||
| 46 | * element | ||
| 47 | * @param list Pointer to a linked list of regex_list elements | ||
| 48 | * @param regex the string containing the regex which should be inserted into the list | ||
| 49 | * @param clags the cflags parameter for regcomp(3) | ||
| 50 | */ | ||
| 51 | int np_add_regex(struct regex_list **list, const char *regex, int cflags) { | ||
| 52 | struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry); | ||
| 53 | |||
| 54 | if (new_entry == NULL) { | ||
| 55 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 56 | } | ||
| 57 | |||
| 58 | int regcomp_result = regcomp(&new_entry->regex, regex, cflags); | ||
| 59 | |||
| 60 | if (!regcomp_result) { | ||
| 61 | // regcomp succeeded | ||
| 62 | new_entry->next = *list; | ||
| 63 | *list = new_entry; | ||
| 64 | |||
| 65 | return 0; | ||
| 66 | } else { | ||
| 67 | // regcomp failed | ||
| 68 | free(new_entry); | ||
| 69 | |||
| 70 | return regcomp_result; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Initialises a new parameter at the end of list */ | ||
| 75 | struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name) { | ||
| 76 | struct parameter_list *current = *list; | ||
| 77 | struct parameter_list *new_path; | ||
| 78 | new_path = (struct parameter_list *)malloc(sizeof *new_path); | ||
| 79 | new_path->name = (char *)malloc(strlen(name) + 1); | ||
| 80 | new_path->best_match = NULL; | ||
| 81 | new_path->name_next = NULL; | ||
| 82 | new_path->name_prev = NULL; | ||
| 83 | new_path->freespace_bytes = NULL; | ||
| 84 | new_path->freespace_units = NULL; | ||
| 85 | new_path->freespace_percent = NULL; | ||
| 86 | new_path->usedspace_bytes = NULL; | ||
| 87 | new_path->usedspace_units = NULL; | ||
| 88 | new_path->usedspace_percent = NULL; | ||
| 89 | new_path->usedinodes_percent = NULL; | ||
| 90 | new_path->freeinodes_percent = NULL; | ||
| 91 | new_path->group = NULL; | ||
| 92 | new_path->dfree_pct = -1; | ||
| 93 | new_path->dused_pct = -1; | ||
| 94 | new_path->total = 0; | ||
| 95 | new_path->available = 0; | ||
| 96 | new_path->available_to_root = 0; | ||
| 97 | new_path->used = 0; | ||
| 98 | new_path->dused_units = 0; | ||
| 99 | new_path->dfree_units = 0; | ||
| 100 | new_path->dtotal_units = 0; | ||
| 101 | new_path->inodes_total = 0; | ||
| 102 | new_path->inodes_free = 0; | ||
| 103 | new_path->inodes_free_to_root = 0; | ||
| 104 | new_path->inodes_used = 0; | ||
| 105 | new_path->dused_inodes_percent = 0; | ||
| 106 | new_path->dfree_inodes_percent = 0; | ||
| 107 | |||
| 108 | strcpy(new_path->name, name); | ||
| 109 | |||
| 110 | if (current == NULL) { | ||
| 111 | *list = new_path; | ||
| 112 | new_path->name_prev = NULL; | ||
| 113 | } else { | ||
| 114 | while (current->name_next) { | ||
| 115 | current = current->name_next; | ||
| 116 | } | ||
| 117 | current->name_next = new_path; | ||
| 118 | new_path->name_prev = current; | ||
| 119 | } | ||
| 120 | return new_path; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Delete a given parameter from list and return pointer to next element*/ | ||
| 124 | struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev) { | ||
| 125 | if (item == NULL) { | ||
| 126 | return NULL; | ||
| 127 | } | ||
| 128 | struct parameter_list *next; | ||
| 129 | |||
| 130 | if (item->name_next) | ||
| 131 | next = item->name_next; | ||
| 132 | else | ||
| 133 | next = NULL; | ||
| 134 | |||
| 135 | if (next) | ||
| 136 | next->name_prev = prev; | ||
| 137 | |||
| 138 | if (prev) | ||
| 139 | prev->name_next = next; | ||
| 140 | |||
| 141 | if (item->name) { | ||
| 142 | free(item->name); | ||
| 143 | } | ||
| 144 | free(item); | ||
| 145 | |||
| 146 | return next; | ||
| 147 | } | ||
| 148 | |||
| 149 | /* returns a pointer to the struct found in the list */ | ||
| 150 | struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name) { | ||
| 151 | struct parameter_list *temp_list; | ||
| 152 | for (temp_list = list; temp_list; temp_list = temp_list->name_next) { | ||
| 153 | if (!strcmp(temp_list->name, name)) | ||
| 154 | return temp_list; | ||
| 155 | } | ||
| 156 | |||
| 157 | return NULL; | ||
| 158 | } | ||
| 159 | |||
| 160 | void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact) { | ||
| 161 | struct parameter_list *d; | ||
| 162 | for (d = desired; d; d = d->name_next) { | ||
| 163 | if (!d->best_match) { | ||
| 164 | struct mount_entry *me; | ||
| 165 | size_t name_len = strlen(d->name); | ||
| 166 | size_t best_match_len = 0; | ||
| 167 | struct mount_entry *best_match = NULL; | ||
| 168 | struct fs_usage fsp; | ||
| 169 | |||
| 170 | /* set best match if path name exactly matches a mounted device name */ | ||
| 171 | for (me = mount_list; me; me = me->me_next) { | ||
| 172 | if (strcmp(me->me_devname, d->name) == 0) { | ||
| 173 | if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { | ||
| 174 | best_match = me; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | /* set best match by directory name if no match was found by devname */ | ||
| 180 | if (!best_match) { | ||
| 181 | for (me = mount_list; me; me = me->me_next) { | ||
| 182 | size_t len = strlen(me->me_mountdir); | ||
| 183 | if ((!exact && | ||
| 184 | (best_match_len <= len && len <= name_len && (len == 1 || strncmp(me->me_mountdir, d->name, len) == 0))) || | ||
| 185 | (exact && strcmp(me->me_mountdir, d->name) == 0)) { | ||
| 186 | if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { | ||
| 187 | best_match = me; | ||
| 188 | best_match_len = len; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | if (best_match) { | ||
| 195 | d->best_match = best_match; | ||
| 196 | } else { | ||
| 197 | d->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */ | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /* Returns true if name is in list */ | ||
| 204 | bool np_find_name(struct name_list *list, const char *name) { | ||
| 205 | const struct name_list *n; | ||
| 206 | |||
| 207 | if (list == NULL || name == NULL) { | ||
| 208 | return false; | ||
| 209 | } | ||
| 210 | for (n = list; n; n = n->next) { | ||
| 211 | if (!strcmp(name, n->name)) { | ||
| 212 | return true; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | return false; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* Returns true if name is in list */ | ||
| 219 | bool np_find_regmatch(struct regex_list *list, const char *name) { | ||
| 220 | int len; | ||
| 221 | regmatch_t m; | ||
| 222 | |||
| 223 | if (name == NULL) { | ||
| 224 | return false; | ||
| 225 | } | ||
| 226 | |||
| 227 | len = strlen(name); | ||
| 228 | |||
| 229 | for (; list; list = list->next) { | ||
| 230 | /* Emulate a full match as if surrounded with ^( )$ | ||
| 231 | by checking whether the match spans the whole name */ | ||
| 232 | if (!regexec(&list->regex, name, 1, &m, 0) && m.rm_so == 0 && m.rm_eo == len) { | ||
| 233 | return true; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | return false; | ||
| 238 | } | ||
| 239 | |||
| 240 | bool np_seen_name(struct name_list *list, const char *name) { | ||
| 241 | const struct name_list *s; | ||
| 242 | for (s = list; s; s = s->next) { | ||
| 243 | if (!strcmp(s->name, name)) { | ||
| 244 | return true; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | return false; | ||
| 248 | } | ||
| 249 | |||
| 250 | bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { | ||
| 251 | if (regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0 || regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0) { | ||
| 252 | return true; | ||
| 253 | } | ||
| 254 | return false; | ||
| 255 | } | ||
diff --git a/lib/utils_disk.h b/lib/utils_disk.h deleted file mode 100644 index c5e81dc1..00000000 --- a/lib/utils_disk.h +++ /dev/null | |||
| @@ -1,48 +0,0 @@ | |||
| 1 | /* Header file for utils_disk */ | ||
| 2 | |||
| 3 | #include "mountlist.h" | ||
| 4 | #include "utils_base.h" | ||
| 5 | #include "regex.h" | ||
| 6 | |||
| 7 | struct name_list { | ||
| 8 | char *name; | ||
| 9 | struct name_list *next; | ||
| 10 | }; | ||
| 11 | |||
| 12 | struct regex_list { | ||
| 13 | regex_t regex; | ||
| 14 | struct regex_list *next; | ||
| 15 | }; | ||
| 16 | |||
| 17 | struct parameter_list { | ||
| 18 | char *name; | ||
| 19 | thresholds *freespace_bytes; | ||
| 20 | thresholds *freespace_units; | ||
| 21 | thresholds *freespace_percent; | ||
| 22 | thresholds *usedspace_bytes; | ||
| 23 | thresholds *usedspace_units; | ||
| 24 | thresholds *usedspace_percent; | ||
| 25 | thresholds *usedinodes_percent; | ||
| 26 | thresholds *freeinodes_percent; | ||
| 27 | char *group; | ||
| 28 | struct mount_entry *best_match; | ||
| 29 | struct parameter_list *name_next; | ||
| 30 | struct parameter_list *name_prev; | ||
| 31 | uintmax_t total, available, available_to_root, used, inodes_free, inodes_free_to_root, inodes_used, inodes_total; | ||
| 32 | double dfree_pct, dused_pct; | ||
| 33 | uint64_t dused_units, dfree_units, dtotal_units; | ||
| 34 | double dused_inodes_percent, dfree_inodes_percent; | ||
| 35 | }; | ||
| 36 | |||
| 37 | void np_add_name(struct name_list **list, const char *name); | ||
| 38 | bool np_find_name(struct name_list *list, const char *name); | ||
| 39 | bool np_seen_name(struct name_list *list, const char *name); | ||
| 40 | int np_add_regex(struct regex_list **list, const char *regex, int cflags); | ||
| 41 | bool np_find_regmatch(struct regex_list *list, const char *name); | ||
| 42 | struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name); | ||
| 43 | struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name); | ||
| 44 | struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev); | ||
| 45 | |||
| 46 | int search_parameter_list(struct parameter_list *list, const char *name); | ||
| 47 | void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact); | ||
| 48 | bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re); | ||
diff --git a/lib/utils_tcp.c b/lib/utils_tcp.c index daae1d54..a82d5a3f 100644 --- a/lib/utils_tcp.c +++ b/lib/utils_tcp.c | |||
| @@ -26,28 +26,35 @@ | |||
| 26 | * | 26 | * |
| 27 | *****************************************************************************/ | 27 | *****************************************************************************/ |
| 28 | 28 | ||
| 29 | #include "common.h" | 29 | #include "../config.h" |
| 30 | #include "utils_tcp.h" | 30 | #include "utils_tcp.h" |
| 31 | #include <stdio.h> | ||
| 32 | #include <string.h> | ||
| 31 | 33 | ||
| 32 | #define VERBOSE(message) \ | 34 | #define VERBOSE(message) \ |
| 33 | do { \ | 35 | do { \ |
| 34 | if (flags & NP_MATCH_VERBOSE) \ | 36 | if (flags & NP_MATCH_VERBOSE) \ |
| 35 | puts(message); \ | 37 | puts(message); \ |
| 36 | } while (0) | 38 | } while (0) |
| 37 | 39 | ||
| 38 | enum np_match_result np_expect_match(char *status, char **server_expect, int expect_count, int flags) { | 40 | enum np_match_result np_expect_match(char *status, char **server_expect, int expect_count, |
| 39 | int i, match = 0, partial = 0; | 41 | int flags) { |
| 40 | 42 | int match = 0; | |
| 41 | for (i = 0; i < expect_count; i++) { | 43 | int partial = 0; |
| 42 | if (flags & NP_MATCH_VERBOSE) | 44 | for (int i = 0; i < expect_count; i++) { |
| 43 | printf("looking for [%s] %s [%s]\n", server_expect[i], (flags & NP_MATCH_EXACT) ? "in beginning of" : "anywhere in", status); | 45 | if (flags & NP_MATCH_VERBOSE) { |
| 46 | printf("looking for [%s] %s [%s]\n", server_expect[i], | ||
| 47 | (flags & NP_MATCH_EXACT) ? "in beginning of" : "anywhere in", status); | ||
| 48 | } | ||
| 44 | 49 | ||
| 45 | if (flags & NP_MATCH_EXACT) { | 50 | if (flags & NP_MATCH_EXACT) { |
| 46 | if (strncmp(status, server_expect[i], strlen(server_expect[i])) == 0) { | 51 | if (strncmp(status, server_expect[i], strlen(server_expect[i])) == 0) { |
| 47 | VERBOSE("found it"); | 52 | VERBOSE("found it"); |
| 48 | match++; | 53 | match++; |
| 49 | continue; | 54 | continue; |
| 50 | } else if (strncmp(status, server_expect[i], strlen(status)) == 0) { | 55 | } |
| 56 | |||
| 57 | if (strncmp(status, server_expect[i], strlen(status)) == 0) { | ||
| 51 | VERBOSE("found a substring"); | 58 | VERBOSE("found a substring"); |
| 52 | partial++; | 59 | partial++; |
| 53 | continue; | 60 | continue; |
| @@ -60,10 +67,12 @@ enum np_match_result np_expect_match(char *status, char **server_expect, int exp | |||
| 60 | VERBOSE("couldn't find it"); | 67 | VERBOSE("couldn't find it"); |
| 61 | } | 68 | } |
| 62 | 69 | ||
| 63 | if ((flags & NP_MATCH_ALL && match == expect_count) || (!(flags & NP_MATCH_ALL) && match >= 1)) | 70 | if ((flags & NP_MATCH_ALL && match == expect_count) || |
| 71 | (!(flags & NP_MATCH_ALL) && match >= 1)) { | ||
| 64 | return NP_MATCH_SUCCESS; | 72 | return NP_MATCH_SUCCESS; |
| 65 | else if (partial > 0 || !(flags & NP_MATCH_EXACT)) | 73 | } |
| 74 | if (partial > 0 || !(flags & NP_MATCH_EXACT)) { | ||
| 66 | return NP_MATCH_RETRY; | 75 | return NP_MATCH_RETRY; |
| 67 | else | 76 | } |
| 68 | return NP_MATCH_FAILURE; | 77 | return NP_MATCH_FAILURE; |
| 69 | } | 78 | } |
diff --git a/lib/utils_tcp.h b/lib/utils_tcp.h index d5999e9b..e5cdbb82 100644 --- a/lib/utils_tcp.h +++ b/lib/utils_tcp.h | |||
| @@ -11,9 +11,11 @@ | |||
| 11 | * server. | 11 | * server. |
| 12 | */ | 12 | */ |
| 13 | enum np_match_result { | 13 | enum np_match_result { |
| 14 | NP_MATCH_NONE, | ||
| 14 | NP_MATCH_FAILURE, | 15 | NP_MATCH_FAILURE, |
| 15 | NP_MATCH_SUCCESS, | 16 | NP_MATCH_SUCCESS, |
| 16 | NP_MATCH_RETRY | 17 | NP_MATCH_RETRY |
| 17 | }; | 18 | }; |
| 18 | 19 | ||
| 19 | enum np_match_result np_expect_match(char *status, char **server_expect, int server_expect_count, int flags); | 20 | enum np_match_result np_expect_match(char *status, char **server_expect, int server_expect_count, |
| 21 | int flags); | ||
diff --git a/lib/vendor/cJSON/cJSON.c b/lib/vendor/cJSON/cJSON.c new file mode 100644 index 00000000..12076e92 --- /dev/null +++ b/lib/vendor/cJSON/cJSON.c | |||
| @@ -0,0 +1,3165 @@ | |||
| 1 | /* | ||
| 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors | ||
| 3 | |||
| 4 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 5 | of this software and associated documentation files (the "Software"), to deal | ||
| 6 | in the Software without restriction, including without limitation the rights | ||
| 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 8 | copies of the Software, and to permit persons to whom the Software is | ||
| 9 | furnished to do so, subject to the following conditions: | ||
| 10 | |||
| 11 | The above copyright notice and this permission notice shall be included in | ||
| 12 | all copies or substantial portions of the Software. | ||
| 13 | |||
| 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 20 | THE SOFTWARE. | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* cJSON */ | ||
| 24 | /* JSON parser in C. */ | ||
| 25 | |||
| 26 | /* disable warnings about old C89 functions in MSVC */ | ||
| 27 | #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) | ||
| 28 | #define _CRT_SECURE_NO_DEPRECATE | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #ifdef __GNUC__ | ||
| 32 | #pragma GCC visibility push(default) | ||
| 33 | #endif | ||
| 34 | #if defined(_MSC_VER) | ||
| 35 | #pragma warning (push) | ||
| 36 | /* disable warning about single line comments in system headers */ | ||
| 37 | #pragma warning (disable : 4001) | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #include "../../../config.h" | ||
| 41 | #include <string.h> | ||
| 42 | #include <stdio.h> | ||
| 43 | #include <math.h> | ||
| 44 | #include <stdlib.h> | ||
| 45 | #include <limits.h> | ||
| 46 | #include <ctype.h> | ||
| 47 | #include <float.h> | ||
| 48 | |||
| 49 | #ifdef ENABLE_LOCALES | ||
| 50 | #include <locale.h> | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #if defined(_MSC_VER) | ||
| 54 | #pragma warning (pop) | ||
| 55 | #endif | ||
| 56 | #ifdef __GNUC__ | ||
| 57 | #pragma GCC visibility pop | ||
| 58 | #endif | ||
| 59 | |||
| 60 | #include "cJSON.h" | ||
| 61 | |||
| 62 | /* define our own boolean type */ | ||
| 63 | #ifdef true | ||
| 64 | #undef true | ||
| 65 | #endif | ||
| 66 | #define true ((cJSON_bool)1) | ||
| 67 | |||
| 68 | #ifdef false | ||
| 69 | #undef false | ||
| 70 | #endif | ||
| 71 | #define false ((cJSON_bool)0) | ||
| 72 | |||
| 73 | /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ | ||
| 74 | #ifndef isinf | ||
| 75 | #define isinf(d) (isnan((d - d)) && !isnan(d)) | ||
| 76 | #endif | ||
| 77 | #ifndef isnan | ||
| 78 | #define isnan(d) (d != d) | ||
| 79 | #endif | ||
| 80 | |||
| 81 | #ifndef NAN | ||
| 82 | #ifdef _WIN32 | ||
| 83 | #define NAN sqrt(-1.0) | ||
| 84 | #else | ||
| 85 | #define NAN 0.0/0.0 | ||
| 86 | #endif | ||
| 87 | #endif | ||
| 88 | |||
| 89 | typedef struct { | ||
| 90 | const unsigned char *json; | ||
| 91 | size_t position; | ||
| 92 | } error; | ||
| 93 | static error global_error = { NULL, 0 }; | ||
| 94 | |||
| 95 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) | ||
| 96 | { | ||
| 97 | return (const char*) (global_error.json + global_error.position); | ||
| 98 | } | ||
| 99 | |||
| 100 | CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) | ||
| 101 | { | ||
| 102 | if (!cJSON_IsString(item)) | ||
| 103 | { | ||
| 104 | return NULL; | ||
| 105 | } | ||
| 106 | |||
| 107 | return item->valuestring; | ||
| 108 | } | ||
| 109 | |||
| 110 | CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) | ||
| 111 | { | ||
| 112 | if (!cJSON_IsNumber(item)) | ||
| 113 | { | ||
| 114 | return (double) NAN; | ||
| 115 | } | ||
| 116 | |||
| 117 | return item->valuedouble; | ||
| 118 | } | ||
| 119 | |||
| 120 | /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ | ||
| 121 | #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) | ||
| 122 | #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. | ||
| 123 | #endif | ||
| 124 | |||
| 125 | CJSON_PUBLIC(const char*) cJSON_Version(void) | ||
| 126 | { | ||
| 127 | static char version[15]; | ||
| 128 | sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); | ||
| 129 | |||
| 130 | return version; | ||
| 131 | } | ||
| 132 | |||
| 133 | /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ | ||
| 134 | static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) | ||
| 135 | { | ||
| 136 | if ((string1 == NULL) || (string2 == NULL)) | ||
| 137 | { | ||
| 138 | return 1; | ||
| 139 | } | ||
| 140 | |||
| 141 | if (string1 == string2) | ||
| 142 | { | ||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) | ||
| 147 | { | ||
| 148 | if (*string1 == '\0') | ||
| 149 | { | ||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | return tolower(*string1) - tolower(*string2); | ||
| 155 | } | ||
| 156 | |||
| 157 | typedef struct internal_hooks | ||
| 158 | { | ||
| 159 | void *(CJSON_CDECL *allocate)(size_t size); | ||
| 160 | void (CJSON_CDECL *deallocate)(void *pointer); | ||
| 161 | void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); | ||
| 162 | } internal_hooks; | ||
| 163 | |||
| 164 | #if defined(_MSC_VER) | ||
| 165 | /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ | ||
| 166 | static void * CJSON_CDECL internal_malloc(size_t size) | ||
| 167 | { | ||
| 168 | return malloc(size); | ||
| 169 | } | ||
| 170 | static void CJSON_CDECL internal_free(void *pointer) | ||
| 171 | { | ||
| 172 | free(pointer); | ||
| 173 | } | ||
| 174 | static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) | ||
| 175 | { | ||
| 176 | return realloc(pointer, size); | ||
| 177 | } | ||
| 178 | #else | ||
| 179 | #define internal_malloc malloc | ||
| 180 | #define internal_free free | ||
| 181 | #define internal_realloc realloc | ||
| 182 | #endif | ||
| 183 | |||
| 184 | /* strlen of character literals resolved at compile time */ | ||
| 185 | #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) | ||
| 186 | |||
| 187 | static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; | ||
| 188 | |||
| 189 | static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) | ||
| 190 | { | ||
| 191 | size_t length = 0; | ||
| 192 | unsigned char *copy = NULL; | ||
| 193 | |||
| 194 | if (string == NULL) | ||
| 195 | { | ||
| 196 | return NULL; | ||
| 197 | } | ||
| 198 | |||
| 199 | length = strlen((const char*)string) + sizeof(""); | ||
| 200 | copy = (unsigned char*)hooks->allocate(length); | ||
| 201 | if (copy == NULL) | ||
| 202 | { | ||
| 203 | return NULL; | ||
| 204 | } | ||
| 205 | memcpy(copy, string, length); | ||
| 206 | |||
| 207 | return copy; | ||
| 208 | } | ||
| 209 | |||
| 210 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) | ||
| 211 | { | ||
| 212 | if (hooks == NULL) | ||
| 213 | { | ||
| 214 | /* Reset hooks */ | ||
| 215 | global_hooks.allocate = malloc; | ||
| 216 | global_hooks.deallocate = free; | ||
| 217 | global_hooks.reallocate = realloc; | ||
| 218 | return; | ||
| 219 | } | ||
| 220 | |||
| 221 | global_hooks.allocate = malloc; | ||
| 222 | if (hooks->malloc_fn != NULL) | ||
| 223 | { | ||
| 224 | global_hooks.allocate = hooks->malloc_fn; | ||
| 225 | } | ||
| 226 | |||
| 227 | global_hooks.deallocate = free; | ||
| 228 | if (hooks->free_fn != NULL) | ||
| 229 | { | ||
| 230 | global_hooks.deallocate = hooks->free_fn; | ||
| 231 | } | ||
| 232 | |||
| 233 | /* use realloc only if both free and malloc are used */ | ||
| 234 | global_hooks.reallocate = NULL; | ||
| 235 | if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) | ||
| 236 | { | ||
| 237 | global_hooks.reallocate = realloc; | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | /* Internal constructor. */ | ||
| 242 | static cJSON *cJSON_New_Item(const internal_hooks * const hooks) | ||
| 243 | { | ||
| 244 | cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); | ||
| 245 | if (node) | ||
| 246 | { | ||
| 247 | memset(node, '\0', sizeof(cJSON)); | ||
| 248 | } | ||
| 249 | |||
| 250 | return node; | ||
| 251 | } | ||
| 252 | |||
| 253 | /* Delete a cJSON structure. */ | ||
| 254 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) | ||
| 255 | { | ||
| 256 | cJSON *next = NULL; | ||
| 257 | while (item != NULL) | ||
| 258 | { | ||
| 259 | next = item->next; | ||
| 260 | if (!(item->type & cJSON_IsReference) && (item->child != NULL)) | ||
| 261 | { | ||
| 262 | cJSON_Delete(item->child); | ||
| 263 | } | ||
| 264 | if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) | ||
| 265 | { | ||
| 266 | global_hooks.deallocate(item->valuestring); | ||
| 267 | item->valuestring = NULL; | ||
| 268 | } | ||
| 269 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) | ||
| 270 | { | ||
| 271 | global_hooks.deallocate(item->string); | ||
| 272 | item->string = NULL; | ||
| 273 | } | ||
| 274 | global_hooks.deallocate(item); | ||
| 275 | item = next; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | /* get the decimal point character of the current locale */ | ||
| 280 | static unsigned char get_decimal_point(void) | ||
| 281 | { | ||
| 282 | #ifdef ENABLE_LOCALES | ||
| 283 | struct lconv *lconv = localeconv(); | ||
| 284 | return (unsigned char) lconv->decimal_point[0]; | ||
| 285 | #else | ||
| 286 | return '.'; | ||
| 287 | #endif | ||
| 288 | } | ||
| 289 | |||
| 290 | typedef struct | ||
| 291 | { | ||
| 292 | const unsigned char *content; | ||
| 293 | size_t length; | ||
| 294 | size_t offset; | ||
| 295 | size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ | ||
| 296 | internal_hooks hooks; | ||
| 297 | } parse_buffer; | ||
| 298 | |||
| 299 | /* check if the given size is left to read in a given parse buffer (starting with 1) */ | ||
| 300 | #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) | ||
| 301 | /* check if the buffer can be accessed at the given index (starting with 0) */ | ||
| 302 | #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) | ||
| 303 | #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) | ||
| 304 | /* get a pointer to the buffer at the position */ | ||
| 305 | #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) | ||
| 306 | |||
| 307 | /* Parse the input text to generate a number, and populate the result into item. */ | ||
| 308 | static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) | ||
| 309 | { | ||
| 310 | double number = 0; | ||
| 311 | unsigned char *after_end = NULL; | ||
| 312 | unsigned char number_c_string[64]; | ||
| 313 | unsigned char decimal_point = get_decimal_point(); | ||
| 314 | size_t i = 0; | ||
| 315 | |||
| 316 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) | ||
| 317 | { | ||
| 318 | return false; | ||
| 319 | } | ||
| 320 | |||
| 321 | /* copy the number into a temporary buffer and replace '.' with the decimal point | ||
| 322 | * of the current locale (for strtod) | ||
| 323 | * This also takes care of '\0' not necessarily being available for marking the end of the input */ | ||
| 324 | for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) | ||
| 325 | { | ||
| 326 | switch (buffer_at_offset(input_buffer)[i]) | ||
| 327 | { | ||
| 328 | case '0': | ||
| 329 | case '1': | ||
| 330 | case '2': | ||
| 331 | case '3': | ||
| 332 | case '4': | ||
| 333 | case '5': | ||
| 334 | case '6': | ||
| 335 | case '7': | ||
| 336 | case '8': | ||
| 337 | case '9': | ||
| 338 | case '+': | ||
| 339 | case '-': | ||
| 340 | case 'e': | ||
| 341 | case 'E': | ||
| 342 | number_c_string[i] = buffer_at_offset(input_buffer)[i]; | ||
| 343 | break; | ||
| 344 | |||
| 345 | case '.': | ||
| 346 | number_c_string[i] = decimal_point; | ||
| 347 | break; | ||
| 348 | |||
| 349 | default: | ||
| 350 | goto loop_end; | ||
| 351 | } | ||
| 352 | } | ||
| 353 | loop_end: | ||
| 354 | number_c_string[i] = '\0'; | ||
| 355 | |||
| 356 | number = strtod((const char*)number_c_string, (char**)&after_end); | ||
| 357 | if (number_c_string == after_end) | ||
| 358 | { | ||
| 359 | return false; /* parse_error */ | ||
| 360 | } | ||
| 361 | |||
| 362 | item->valuedouble = number; | ||
| 363 | |||
| 364 | /* use saturation in case of overflow */ | ||
| 365 | if (number >= INT_MAX) | ||
| 366 | { | ||
| 367 | item->valueint = INT_MAX; | ||
| 368 | } | ||
| 369 | else if (number <= (double)INT_MIN) | ||
| 370 | { | ||
| 371 | item->valueint = INT_MIN; | ||
| 372 | } | ||
| 373 | else | ||
| 374 | { | ||
| 375 | item->valueint = (int)number; | ||
| 376 | } | ||
| 377 | |||
| 378 | item->type = cJSON_Number; | ||
| 379 | |||
| 380 | input_buffer->offset += (size_t)(after_end - number_c_string); | ||
| 381 | return true; | ||
| 382 | } | ||
| 383 | |||
| 384 | /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ | ||
| 385 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) | ||
| 386 | { | ||
| 387 | if (number >= INT_MAX) | ||
| 388 | { | ||
| 389 | object->valueint = INT_MAX; | ||
| 390 | } | ||
| 391 | else if (number <= (double)INT_MIN) | ||
| 392 | { | ||
| 393 | object->valueint = INT_MIN; | ||
| 394 | } | ||
| 395 | else | ||
| 396 | { | ||
| 397 | object->valueint = (int)number; | ||
| 398 | } | ||
| 399 | |||
| 400 | return object->valuedouble = number; | ||
| 401 | } | ||
| 402 | |||
| 403 | /* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ | ||
| 404 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) | ||
| 405 | { | ||
| 406 | char *copy = NULL; | ||
| 407 | size_t v1_len; | ||
| 408 | size_t v2_len; | ||
| 409 | /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ | ||
| 410 | if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) | ||
| 411 | { | ||
| 412 | return NULL; | ||
| 413 | } | ||
| 414 | /* return NULL if the object is corrupted or valuestring is NULL */ | ||
| 415 | if (object->valuestring == NULL || valuestring == NULL) | ||
| 416 | { | ||
| 417 | return NULL; | ||
| 418 | } | ||
| 419 | |||
| 420 | v1_len = strlen(valuestring); | ||
| 421 | v2_len = strlen(object->valuestring); | ||
| 422 | |||
| 423 | if (v1_len <= v2_len) | ||
| 424 | { | ||
| 425 | /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ | ||
| 426 | if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring )) | ||
| 427 | { | ||
| 428 | return NULL; | ||
| 429 | } | ||
| 430 | strcpy(object->valuestring, valuestring); | ||
| 431 | return object->valuestring; | ||
| 432 | } | ||
| 433 | copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); | ||
| 434 | if (copy == NULL) | ||
| 435 | { | ||
| 436 | return NULL; | ||
| 437 | } | ||
| 438 | if (object->valuestring != NULL) | ||
| 439 | { | ||
| 440 | cJSON_free(object->valuestring); | ||
| 441 | } | ||
| 442 | object->valuestring = copy; | ||
| 443 | |||
| 444 | return copy; | ||
| 445 | } | ||
| 446 | |||
| 447 | typedef struct | ||
| 448 | { | ||
| 449 | unsigned char *buffer; | ||
| 450 | size_t length; | ||
| 451 | size_t offset; | ||
| 452 | size_t depth; /* current nesting depth (for formatted printing) */ | ||
| 453 | cJSON_bool noalloc; | ||
| 454 | cJSON_bool format; /* is this print a formatted print */ | ||
| 455 | internal_hooks hooks; | ||
| 456 | } printbuffer; | ||
| 457 | |||
| 458 | /* realloc printbuffer if necessary to have at least "needed" bytes more */ | ||
| 459 | static unsigned char* ensure(printbuffer * const p, size_t needed) | ||
| 460 | { | ||
| 461 | unsigned char *newbuffer = NULL; | ||
| 462 | size_t newsize = 0; | ||
| 463 | |||
| 464 | if ((p == NULL) || (p->buffer == NULL)) | ||
| 465 | { | ||
| 466 | return NULL; | ||
| 467 | } | ||
| 468 | |||
| 469 | if ((p->length > 0) && (p->offset >= p->length)) | ||
| 470 | { | ||
| 471 | /* make sure that offset is valid */ | ||
| 472 | return NULL; | ||
| 473 | } | ||
| 474 | |||
| 475 | if (needed > INT_MAX) | ||
| 476 | { | ||
| 477 | /* sizes bigger than INT_MAX are currently not supported */ | ||
| 478 | return NULL; | ||
| 479 | } | ||
| 480 | |||
| 481 | needed += p->offset + 1; | ||
| 482 | if (needed <= p->length) | ||
| 483 | { | ||
| 484 | return p->buffer + p->offset; | ||
| 485 | } | ||
| 486 | |||
| 487 | if (p->noalloc) { | ||
| 488 | return NULL; | ||
| 489 | } | ||
| 490 | |||
| 491 | /* calculate new buffer size */ | ||
| 492 | if (needed > (INT_MAX / 2)) | ||
| 493 | { | ||
| 494 | /* overflow of int, use INT_MAX if possible */ | ||
| 495 | if (needed <= INT_MAX) | ||
| 496 | { | ||
| 497 | newsize = INT_MAX; | ||
| 498 | } | ||
| 499 | else | ||
| 500 | { | ||
| 501 | return NULL; | ||
| 502 | } | ||
| 503 | } | ||
| 504 | else | ||
| 505 | { | ||
| 506 | newsize = needed * 2; | ||
| 507 | } | ||
| 508 | |||
| 509 | if (p->hooks.reallocate != NULL) | ||
| 510 | { | ||
| 511 | /* reallocate with realloc if available */ | ||
| 512 | newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); | ||
| 513 | if (newbuffer == NULL) | ||
| 514 | { | ||
| 515 | p->hooks.deallocate(p->buffer); | ||
| 516 | p->length = 0; | ||
| 517 | p->buffer = NULL; | ||
| 518 | |||
| 519 | return NULL; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | else | ||
| 523 | { | ||
| 524 | /* otherwise reallocate manually */ | ||
| 525 | newbuffer = (unsigned char*)p->hooks.allocate(newsize); | ||
| 526 | if (!newbuffer) | ||
| 527 | { | ||
| 528 | p->hooks.deallocate(p->buffer); | ||
| 529 | p->length = 0; | ||
| 530 | p->buffer = NULL; | ||
| 531 | |||
| 532 | return NULL; | ||
| 533 | } | ||
| 534 | |||
| 535 | memcpy(newbuffer, p->buffer, p->offset + 1); | ||
| 536 | p->hooks.deallocate(p->buffer); | ||
| 537 | } | ||
| 538 | p->length = newsize; | ||
| 539 | p->buffer = newbuffer; | ||
| 540 | |||
| 541 | return newbuffer + p->offset; | ||
| 542 | } | ||
| 543 | |||
| 544 | /* calculate the new length of the string in a printbuffer and update the offset */ | ||
| 545 | static void update_offset(printbuffer * const buffer) | ||
| 546 | { | ||
| 547 | const unsigned char *buffer_pointer = NULL; | ||
| 548 | if ((buffer == NULL) || (buffer->buffer == NULL)) | ||
| 549 | { | ||
| 550 | return; | ||
| 551 | } | ||
| 552 | buffer_pointer = buffer->buffer + buffer->offset; | ||
| 553 | |||
| 554 | buffer->offset += strlen((const char*)buffer_pointer); | ||
| 555 | } | ||
| 556 | |||
| 557 | /* securely comparison of floating-point variables */ | ||
| 558 | static cJSON_bool compare_double(double a, double b) | ||
| 559 | { | ||
| 560 | double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); | ||
| 561 | return (fabs(a - b) <= maxVal * DBL_EPSILON); | ||
| 562 | } | ||
| 563 | |||
| 564 | /* Render the number nicely from the given item into a string. */ | ||
| 565 | static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) | ||
| 566 | { | ||
| 567 | unsigned char *output_pointer = NULL; | ||
| 568 | double d = item->valuedouble; | ||
| 569 | int length = 0; | ||
| 570 | size_t i = 0; | ||
| 571 | unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ | ||
| 572 | unsigned char decimal_point = get_decimal_point(); | ||
| 573 | double test = 0.0; | ||
| 574 | |||
| 575 | if (output_buffer == NULL) | ||
| 576 | { | ||
| 577 | return false; | ||
| 578 | } | ||
| 579 | |||
| 580 | /* This checks for NaN and Infinity */ | ||
| 581 | if (isnan(d) || isinf(d)) | ||
| 582 | { | ||
| 583 | length = sprintf((char*)number_buffer, "null"); | ||
| 584 | } | ||
| 585 | else if(d == (double)item->valueint) | ||
| 586 | { | ||
| 587 | length = sprintf((char*)number_buffer, "%d", item->valueint); | ||
| 588 | } | ||
| 589 | else | ||
| 590 | { | ||
| 591 | /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ | ||
| 592 | length = sprintf((char*)number_buffer, "%1.15g", d); | ||
| 593 | |||
| 594 | /* Check whether the original double can be recovered */ | ||
| 595 | if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) | ||
| 596 | { | ||
| 597 | /* If not, print with 17 decimal places of precision */ | ||
| 598 | length = sprintf((char*)number_buffer, "%1.17g", d); | ||
| 599 | } | ||
| 600 | } | ||
| 601 | |||
| 602 | /* sprintf failed or buffer overrun occurred */ | ||
| 603 | if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) | ||
| 604 | { | ||
| 605 | return false; | ||
| 606 | } | ||
| 607 | |||
| 608 | /* reserve appropriate space in the output */ | ||
| 609 | output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); | ||
| 610 | if (output_pointer == NULL) | ||
| 611 | { | ||
| 612 | return false; | ||
| 613 | } | ||
| 614 | |||
| 615 | /* copy the printed number to the output and replace locale | ||
| 616 | * dependent decimal point with '.' */ | ||
| 617 | for (i = 0; i < ((size_t)length); i++) | ||
| 618 | { | ||
| 619 | if (number_buffer[i] == decimal_point) | ||
| 620 | { | ||
| 621 | output_pointer[i] = '.'; | ||
| 622 | continue; | ||
| 623 | } | ||
| 624 | |||
| 625 | output_pointer[i] = number_buffer[i]; | ||
| 626 | } | ||
| 627 | output_pointer[i] = '\0'; | ||
| 628 | |||
| 629 | output_buffer->offset += (size_t)length; | ||
| 630 | |||
| 631 | return true; | ||
| 632 | } | ||
| 633 | |||
| 634 | /* parse 4 digit hexadecimal number */ | ||
| 635 | static unsigned parse_hex4(const unsigned char * const input) | ||
| 636 | { | ||
| 637 | unsigned int h = 0; | ||
| 638 | size_t i = 0; | ||
| 639 | |||
| 640 | for (i = 0; i < 4; i++) | ||
| 641 | { | ||
| 642 | /* parse digit */ | ||
| 643 | if ((input[i] >= '0') && (input[i] <= '9')) | ||
| 644 | { | ||
| 645 | h += (unsigned int) input[i] - '0'; | ||
| 646 | } | ||
| 647 | else if ((input[i] >= 'A') && (input[i] <= 'F')) | ||
| 648 | { | ||
| 649 | h += (unsigned int) 10 + input[i] - 'A'; | ||
| 650 | } | ||
| 651 | else if ((input[i] >= 'a') && (input[i] <= 'f')) | ||
| 652 | { | ||
| 653 | h += (unsigned int) 10 + input[i] - 'a'; | ||
| 654 | } | ||
| 655 | else /* invalid */ | ||
| 656 | { | ||
| 657 | return 0; | ||
| 658 | } | ||
| 659 | |||
| 660 | if (i < 3) | ||
| 661 | { | ||
| 662 | /* shift left to make place for the next nibble */ | ||
| 663 | h = h << 4; | ||
| 664 | } | ||
| 665 | } | ||
| 666 | |||
| 667 | return h; | ||
| 668 | } | ||
| 669 | |||
| 670 | /* converts a UTF-16 literal to UTF-8 | ||
| 671 | * A literal can be one or two sequences of the form \uXXXX */ | ||
| 672 | static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) | ||
| 673 | { | ||
| 674 | long unsigned int codepoint = 0; | ||
| 675 | unsigned int first_code = 0; | ||
| 676 | const unsigned char *first_sequence = input_pointer; | ||
| 677 | unsigned char utf8_length = 0; | ||
| 678 | unsigned char utf8_position = 0; | ||
| 679 | unsigned char sequence_length = 0; | ||
| 680 | unsigned char first_byte_mark = 0; | ||
| 681 | |||
| 682 | if ((input_end - first_sequence) < 6) | ||
| 683 | { | ||
| 684 | /* input ends unexpectedly */ | ||
| 685 | goto fail; | ||
| 686 | } | ||
| 687 | |||
| 688 | /* get the first utf16 sequence */ | ||
| 689 | first_code = parse_hex4(first_sequence + 2); | ||
| 690 | |||
| 691 | /* check that the code is valid */ | ||
| 692 | if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) | ||
| 693 | { | ||
| 694 | goto fail; | ||
| 695 | } | ||
| 696 | |||
| 697 | /* UTF16 surrogate pair */ | ||
| 698 | if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) | ||
| 699 | { | ||
| 700 | const unsigned char *second_sequence = first_sequence + 6; | ||
| 701 | unsigned int second_code = 0; | ||
| 702 | sequence_length = 12; /* \uXXXX\uXXXX */ | ||
| 703 | |||
| 704 | if ((input_end - second_sequence) < 6) | ||
| 705 | { | ||
| 706 | /* input ends unexpectedly */ | ||
| 707 | goto fail; | ||
| 708 | } | ||
| 709 | |||
| 710 | if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) | ||
| 711 | { | ||
| 712 | /* missing second half of the surrogate pair */ | ||
| 713 | goto fail; | ||
| 714 | } | ||
| 715 | |||
| 716 | /* get the second utf16 sequence */ | ||
| 717 | second_code = parse_hex4(second_sequence + 2); | ||
| 718 | /* check that the code is valid */ | ||
| 719 | if ((second_code < 0xDC00) || (second_code > 0xDFFF)) | ||
| 720 | { | ||
| 721 | /* invalid second half of the surrogate pair */ | ||
| 722 | goto fail; | ||
| 723 | } | ||
| 724 | |||
| 725 | |||
| 726 | /* calculate the unicode codepoint from the surrogate pair */ | ||
| 727 | codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); | ||
| 728 | } | ||
| 729 | else | ||
| 730 | { | ||
| 731 | sequence_length = 6; /* \uXXXX */ | ||
| 732 | codepoint = first_code; | ||
| 733 | } | ||
| 734 | |||
| 735 | /* encode as UTF-8 | ||
| 736 | * takes at maximum 4 bytes to encode: | ||
| 737 | * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ | ||
| 738 | if (codepoint < 0x80) | ||
| 739 | { | ||
| 740 | /* normal ascii, encoding 0xxxxxxx */ | ||
| 741 | utf8_length = 1; | ||
| 742 | } | ||
| 743 | else if (codepoint < 0x800) | ||
| 744 | { | ||
| 745 | /* two bytes, encoding 110xxxxx 10xxxxxx */ | ||
| 746 | utf8_length = 2; | ||
| 747 | first_byte_mark = 0xC0; /* 11000000 */ | ||
| 748 | } | ||
| 749 | else if (codepoint < 0x10000) | ||
| 750 | { | ||
| 751 | /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ | ||
| 752 | utf8_length = 3; | ||
| 753 | first_byte_mark = 0xE0; /* 11100000 */ | ||
| 754 | } | ||
| 755 | else if (codepoint <= 0x10FFFF) | ||
| 756 | { | ||
| 757 | /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ | ||
| 758 | utf8_length = 4; | ||
| 759 | first_byte_mark = 0xF0; /* 11110000 */ | ||
| 760 | } | ||
| 761 | else | ||
| 762 | { | ||
| 763 | /* invalid unicode codepoint */ | ||
| 764 | goto fail; | ||
| 765 | } | ||
| 766 | |||
| 767 | /* encode as utf8 */ | ||
| 768 | for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) | ||
| 769 | { | ||
| 770 | /* 10xxxxxx */ | ||
| 771 | (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); | ||
| 772 | codepoint >>= 6; | ||
| 773 | } | ||
| 774 | /* encode first byte */ | ||
| 775 | if (utf8_length > 1) | ||
| 776 | { | ||
| 777 | (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); | ||
| 778 | } | ||
| 779 | else | ||
| 780 | { | ||
| 781 | (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); | ||
| 782 | } | ||
| 783 | |||
| 784 | *output_pointer += utf8_length; | ||
| 785 | |||
| 786 | return sequence_length; | ||
| 787 | |||
| 788 | fail: | ||
| 789 | return 0; | ||
| 790 | } | ||
| 791 | |||
| 792 | /* Parse the input text into an unescaped cinput, and populate item. */ | ||
| 793 | static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) | ||
| 794 | { | ||
| 795 | const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; | ||
| 796 | const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; | ||
| 797 | unsigned char *output_pointer = NULL; | ||
| 798 | unsigned char *output = NULL; | ||
| 799 | |||
| 800 | /* not a string */ | ||
| 801 | if (buffer_at_offset(input_buffer)[0] != '\"') | ||
| 802 | { | ||
| 803 | goto fail; | ||
| 804 | } | ||
| 805 | |||
| 806 | { | ||
| 807 | /* calculate approximate size of the output (overestimate) */ | ||
| 808 | size_t allocation_length = 0; | ||
| 809 | size_t skipped_bytes = 0; | ||
| 810 | while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) | ||
| 811 | { | ||
| 812 | /* is escape sequence */ | ||
| 813 | if (input_end[0] == '\\') | ||
| 814 | { | ||
| 815 | if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) | ||
| 816 | { | ||
| 817 | /* prevent buffer overflow when last input character is a backslash */ | ||
| 818 | goto fail; | ||
| 819 | } | ||
| 820 | skipped_bytes++; | ||
| 821 | input_end++; | ||
| 822 | } | ||
| 823 | input_end++; | ||
| 824 | } | ||
| 825 | if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) | ||
| 826 | { | ||
| 827 | goto fail; /* string ended unexpectedly */ | ||
| 828 | } | ||
| 829 | |||
| 830 | /* This is at most how much we need for the output */ | ||
| 831 | allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; | ||
| 832 | output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); | ||
| 833 | if (output == NULL) | ||
| 834 | { | ||
| 835 | goto fail; /* allocation failure */ | ||
| 836 | } | ||
| 837 | } | ||
| 838 | |||
| 839 | output_pointer = output; | ||
| 840 | /* loop through the string literal */ | ||
| 841 | while (input_pointer < input_end) | ||
| 842 | { | ||
| 843 | if (*input_pointer != '\\') | ||
| 844 | { | ||
| 845 | *output_pointer++ = *input_pointer++; | ||
| 846 | } | ||
| 847 | /* escape sequence */ | ||
| 848 | else | ||
| 849 | { | ||
| 850 | unsigned char sequence_length = 2; | ||
| 851 | if ((input_end - input_pointer) < 1) | ||
| 852 | { | ||
| 853 | goto fail; | ||
| 854 | } | ||
| 855 | |||
| 856 | switch (input_pointer[1]) | ||
| 857 | { | ||
| 858 | case 'b': | ||
| 859 | *output_pointer++ = '\b'; | ||
| 860 | break; | ||
| 861 | case 'f': | ||
| 862 | *output_pointer++ = '\f'; | ||
| 863 | break; | ||
| 864 | case 'n': | ||
| 865 | *output_pointer++ = '\n'; | ||
| 866 | break; | ||
| 867 | case 'r': | ||
| 868 | *output_pointer++ = '\r'; | ||
| 869 | break; | ||
| 870 | case 't': | ||
| 871 | *output_pointer++ = '\t'; | ||
| 872 | break; | ||
| 873 | case '\"': | ||
| 874 | case '\\': | ||
| 875 | case '/': | ||
| 876 | *output_pointer++ = input_pointer[1]; | ||
| 877 | break; | ||
| 878 | |||
| 879 | /* UTF-16 literal */ | ||
| 880 | case 'u': | ||
| 881 | sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); | ||
| 882 | if (sequence_length == 0) | ||
| 883 | { | ||
| 884 | /* failed to convert UTF16-literal to UTF-8 */ | ||
| 885 | goto fail; | ||
| 886 | } | ||
| 887 | break; | ||
| 888 | |||
| 889 | default: | ||
| 890 | goto fail; | ||
| 891 | } | ||
| 892 | input_pointer += sequence_length; | ||
| 893 | } | ||
| 894 | } | ||
| 895 | |||
| 896 | /* zero terminate the output */ | ||
| 897 | *output_pointer = '\0'; | ||
| 898 | |||
| 899 | item->type = cJSON_String; | ||
| 900 | item->valuestring = (char*)output; | ||
| 901 | |||
| 902 | input_buffer->offset = (size_t) (input_end - input_buffer->content); | ||
| 903 | input_buffer->offset++; | ||
| 904 | |||
| 905 | return true; | ||
| 906 | |||
| 907 | fail: | ||
| 908 | if (output != NULL) | ||
| 909 | { | ||
| 910 | input_buffer->hooks.deallocate(output); | ||
| 911 | output = NULL; | ||
| 912 | } | ||
| 913 | |||
| 914 | if (input_pointer != NULL) | ||
| 915 | { | ||
| 916 | input_buffer->offset = (size_t)(input_pointer - input_buffer->content); | ||
| 917 | } | ||
| 918 | |||
| 919 | return false; | ||
| 920 | } | ||
| 921 | |||
| 922 | /* Render the cstring provided to an escaped version that can be printed. */ | ||
| 923 | static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) | ||
| 924 | { | ||
| 925 | const unsigned char *input_pointer = NULL; | ||
| 926 | unsigned char *output = NULL; | ||
| 927 | unsigned char *output_pointer = NULL; | ||
| 928 | size_t output_length = 0; | ||
| 929 | /* numbers of additional characters needed for escaping */ | ||
| 930 | size_t escape_characters = 0; | ||
| 931 | |||
| 932 | if (output_buffer == NULL) | ||
| 933 | { | ||
| 934 | return false; | ||
| 935 | } | ||
| 936 | |||
| 937 | /* empty string */ | ||
| 938 | if (input == NULL) | ||
| 939 | { | ||
| 940 | output = ensure(output_buffer, sizeof("\"\"")); | ||
| 941 | if (output == NULL) | ||
| 942 | { | ||
| 943 | return false; | ||
| 944 | } | ||
| 945 | strcpy((char*)output, "\"\""); | ||
| 946 | |||
| 947 | return true; | ||
| 948 | } | ||
| 949 | |||
| 950 | /* set "flag" to 1 if something needs to be escaped */ | ||
| 951 | for (input_pointer = input; *input_pointer; input_pointer++) | ||
| 952 | { | ||
| 953 | switch (*input_pointer) | ||
| 954 | { | ||
| 955 | case '\"': | ||
| 956 | case '\\': | ||
| 957 | case '\b': | ||
| 958 | case '\f': | ||
| 959 | case '\n': | ||
| 960 | case '\r': | ||
| 961 | case '\t': | ||
| 962 | /* one character escape sequence */ | ||
| 963 | escape_characters++; | ||
| 964 | break; | ||
| 965 | default: | ||
| 966 | if (*input_pointer < 32) | ||
| 967 | { | ||
| 968 | /* UTF-16 escape sequence uXXXX */ | ||
| 969 | escape_characters += 5; | ||
| 970 | } | ||
| 971 | break; | ||
| 972 | } | ||
| 973 | } | ||
| 974 | output_length = (size_t)(input_pointer - input) + escape_characters; | ||
| 975 | |||
| 976 | output = ensure(output_buffer, output_length + sizeof("\"\"")); | ||
| 977 | if (output == NULL) | ||
| 978 | { | ||
| 979 | return false; | ||
| 980 | } | ||
| 981 | |||
| 982 | /* no characters have to be escaped */ | ||
| 983 | if (escape_characters == 0) | ||
| 984 | { | ||
| 985 | output[0] = '\"'; | ||
| 986 | memcpy(output + 1, input, output_length); | ||
| 987 | output[output_length + 1] = '\"'; | ||
| 988 | output[output_length + 2] = '\0'; | ||
| 989 | |||
| 990 | return true; | ||
| 991 | } | ||
| 992 | |||
| 993 | output[0] = '\"'; | ||
| 994 | output_pointer = output + 1; | ||
| 995 | /* copy the string */ | ||
| 996 | for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) | ||
| 997 | { | ||
| 998 | if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) | ||
| 999 | { | ||
| 1000 | /* normal character, copy */ | ||
| 1001 | *output_pointer = *input_pointer; | ||
| 1002 | } | ||
| 1003 | else | ||
| 1004 | { | ||
| 1005 | /* character needs to be escaped */ | ||
| 1006 | *output_pointer++ = '\\'; | ||
| 1007 | switch (*input_pointer) | ||
| 1008 | { | ||
| 1009 | case '\\': | ||
| 1010 | *output_pointer = '\\'; | ||
| 1011 | break; | ||
| 1012 | case '\"': | ||
| 1013 | *output_pointer = '\"'; | ||
| 1014 | break; | ||
| 1015 | case '\b': | ||
| 1016 | *output_pointer = 'b'; | ||
| 1017 | break; | ||
| 1018 | case '\f': | ||
| 1019 | *output_pointer = 'f'; | ||
| 1020 | break; | ||
| 1021 | case '\n': | ||
| 1022 | *output_pointer = 'n'; | ||
| 1023 | break; | ||
| 1024 | case '\r': | ||
| 1025 | *output_pointer = 'r'; | ||
| 1026 | break; | ||
| 1027 | case '\t': | ||
| 1028 | *output_pointer = 't'; | ||
| 1029 | break; | ||
| 1030 | default: | ||
| 1031 | /* escape and print as unicode codepoint */ | ||
| 1032 | sprintf((char*)output_pointer, "u%04x", *input_pointer); | ||
| 1033 | output_pointer += 4; | ||
| 1034 | break; | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | } | ||
| 1038 | output[output_length + 1] = '\"'; | ||
| 1039 | output[output_length + 2] = '\0'; | ||
| 1040 | |||
| 1041 | return true; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | /* Invoke print_string_ptr (which is useful) on an item. */ | ||
| 1045 | static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) | ||
| 1046 | { | ||
| 1047 | return print_string_ptr((unsigned char*)item->valuestring, p); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | /* Predeclare these prototypes. */ | ||
| 1051 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); | ||
| 1052 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); | ||
| 1053 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); | ||
| 1054 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); | ||
| 1055 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); | ||
| 1056 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); | ||
| 1057 | |||
| 1058 | /* Utility to jump whitespace and cr/lf */ | ||
| 1059 | static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) | ||
| 1060 | { | ||
| 1061 | if ((buffer == NULL) || (buffer->content == NULL)) | ||
| 1062 | { | ||
| 1063 | return NULL; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | if (cannot_access_at_index(buffer, 0)) | ||
| 1067 | { | ||
| 1068 | return buffer; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) | ||
| 1072 | { | ||
| 1073 | buffer->offset++; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | if (buffer->offset == buffer->length) | ||
| 1077 | { | ||
| 1078 | buffer->offset--; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | return buffer; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ | ||
| 1085 | static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) | ||
| 1086 | { | ||
| 1087 | if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) | ||
| 1088 | { | ||
| 1089 | return NULL; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) | ||
| 1093 | { | ||
| 1094 | buffer->offset += 3; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | return buffer; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) | ||
| 1101 | { | ||
| 1102 | size_t buffer_length; | ||
| 1103 | |||
| 1104 | if (NULL == value) | ||
| 1105 | { | ||
| 1106 | return NULL; | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | /* Adding null character size due to require_null_terminated. */ | ||
| 1110 | buffer_length = strlen(value) + sizeof(""); | ||
| 1111 | |||
| 1112 | return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | /* Parse an object - create a new root, and populate. */ | ||
| 1116 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) | ||
| 1117 | { | ||
| 1118 | parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; | ||
| 1119 | cJSON *item = NULL; | ||
| 1120 | |||
| 1121 | /* reset error position */ | ||
| 1122 | global_error.json = NULL; | ||
| 1123 | global_error.position = 0; | ||
| 1124 | |||
| 1125 | if (value == NULL || 0 == buffer_length) | ||
| 1126 | { | ||
| 1127 | goto fail; | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | buffer.content = (const unsigned char*)value; | ||
| 1131 | buffer.length = buffer_length; | ||
| 1132 | buffer.offset = 0; | ||
| 1133 | buffer.hooks = global_hooks; | ||
| 1134 | |||
| 1135 | item = cJSON_New_Item(&global_hooks); | ||
| 1136 | if (item == NULL) /* memory fail */ | ||
| 1137 | { | ||
| 1138 | goto fail; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) | ||
| 1142 | { | ||
| 1143 | /* parse failure. ep is set. */ | ||
| 1144 | goto fail; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ | ||
| 1148 | if (require_null_terminated) | ||
| 1149 | { | ||
| 1150 | buffer_skip_whitespace(&buffer); | ||
| 1151 | if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') | ||
| 1152 | { | ||
| 1153 | goto fail; | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | if (return_parse_end) | ||
| 1157 | { | ||
| 1158 | *return_parse_end = (const char*)buffer_at_offset(&buffer); | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | return item; | ||
| 1162 | |||
| 1163 | fail: | ||
| 1164 | if (item != NULL) | ||
| 1165 | { | ||
| 1166 | cJSON_Delete(item); | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | if (value != NULL) | ||
| 1170 | { | ||
| 1171 | error local_error; | ||
| 1172 | local_error.json = (const unsigned char*)value; | ||
| 1173 | local_error.position = 0; | ||
| 1174 | |||
| 1175 | if (buffer.offset < buffer.length) | ||
| 1176 | { | ||
| 1177 | local_error.position = buffer.offset; | ||
| 1178 | } | ||
| 1179 | else if (buffer.length > 0) | ||
| 1180 | { | ||
| 1181 | local_error.position = buffer.length - 1; | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | if (return_parse_end != NULL) | ||
| 1185 | { | ||
| 1186 | *return_parse_end = (const char*)local_error.json + local_error.position; | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | global_error = local_error; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | return NULL; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | /* Default options for cJSON_Parse */ | ||
| 1196 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) | ||
| 1197 | { | ||
| 1198 | return cJSON_ParseWithOpts(value, 0, 0); | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) | ||
| 1202 | { | ||
| 1203 | return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) | ||
| 1207 | |||
| 1208 | static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) | ||
| 1209 | { | ||
| 1210 | static const size_t default_buffer_size = 256; | ||
| 1211 | printbuffer buffer[1]; | ||
| 1212 | unsigned char *printed = NULL; | ||
| 1213 | |||
| 1214 | memset(buffer, 0, sizeof(buffer)); | ||
| 1215 | |||
| 1216 | /* create buffer */ | ||
| 1217 | buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); | ||
| 1218 | buffer->length = default_buffer_size; | ||
| 1219 | buffer->format = format; | ||
| 1220 | buffer->hooks = *hooks; | ||
| 1221 | if (buffer->buffer == NULL) | ||
| 1222 | { | ||
| 1223 | goto fail; | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | /* print the value */ | ||
| 1227 | if (!print_value(item, buffer)) | ||
| 1228 | { | ||
| 1229 | goto fail; | ||
| 1230 | } | ||
| 1231 | update_offset(buffer); | ||
| 1232 | |||
| 1233 | /* check if reallocate is available */ | ||
| 1234 | if (hooks->reallocate != NULL) | ||
| 1235 | { | ||
| 1236 | printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); | ||
| 1237 | if (printed == NULL) { | ||
| 1238 | goto fail; | ||
| 1239 | } | ||
| 1240 | buffer->buffer = NULL; | ||
| 1241 | } | ||
| 1242 | else /* otherwise copy the JSON over to a new buffer */ | ||
| 1243 | { | ||
| 1244 | printed = (unsigned char*) hooks->allocate(buffer->offset + 1); | ||
| 1245 | if (printed == NULL) | ||
| 1246 | { | ||
| 1247 | goto fail; | ||
| 1248 | } | ||
| 1249 | memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); | ||
| 1250 | printed[buffer->offset] = '\0'; /* just to be sure */ | ||
| 1251 | |||
| 1252 | /* free the buffer */ | ||
| 1253 | hooks->deallocate(buffer->buffer); | ||
| 1254 | buffer->buffer = NULL; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | return printed; | ||
| 1258 | |||
| 1259 | fail: | ||
| 1260 | if (buffer->buffer != NULL) | ||
| 1261 | { | ||
| 1262 | hooks->deallocate(buffer->buffer); | ||
| 1263 | buffer->buffer = NULL; | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | if (printed != NULL) | ||
| 1267 | { | ||
| 1268 | hooks->deallocate(printed); | ||
| 1269 | printed = NULL; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | return NULL; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | /* Render a cJSON item/entity/structure to text. */ | ||
| 1276 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) | ||
| 1277 | { | ||
| 1278 | return (char*)print(item, true, &global_hooks); | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) | ||
| 1282 | { | ||
| 1283 | return (char*)print(item, false, &global_hooks); | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) | ||
| 1287 | { | ||
| 1288 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; | ||
| 1289 | |||
| 1290 | if (prebuffer < 0) | ||
| 1291 | { | ||
| 1292 | return NULL; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); | ||
| 1296 | if (!p.buffer) | ||
| 1297 | { | ||
| 1298 | return NULL; | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | p.length = (size_t)prebuffer; | ||
| 1302 | p.offset = 0; | ||
| 1303 | p.noalloc = false; | ||
| 1304 | p.format = fmt; | ||
| 1305 | p.hooks = global_hooks; | ||
| 1306 | |||
| 1307 | if (!print_value(item, &p)) | ||
| 1308 | { | ||
| 1309 | global_hooks.deallocate(p.buffer); | ||
| 1310 | p.buffer = NULL; | ||
| 1311 | return NULL; | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | return (char*)p.buffer; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) | ||
| 1318 | { | ||
| 1319 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; | ||
| 1320 | |||
| 1321 | if ((length < 0) || (buffer == NULL)) | ||
| 1322 | { | ||
| 1323 | return false; | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | p.buffer = (unsigned char*)buffer; | ||
| 1327 | p.length = (size_t)length; | ||
| 1328 | p.offset = 0; | ||
| 1329 | p.noalloc = true; | ||
| 1330 | p.format = format; | ||
| 1331 | p.hooks = global_hooks; | ||
| 1332 | |||
| 1333 | return print_value(item, &p); | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | /* Parser core - when encountering text, process appropriately. */ | ||
| 1337 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) | ||
| 1338 | { | ||
| 1339 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) | ||
| 1340 | { | ||
| 1341 | return false; /* no input */ | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | /* parse the different types of values */ | ||
| 1345 | /* null */ | ||
| 1346 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) | ||
| 1347 | { | ||
| 1348 | item->type = cJSON_NULL; | ||
| 1349 | input_buffer->offset += 4; | ||
| 1350 | return true; | ||
| 1351 | } | ||
| 1352 | /* false */ | ||
| 1353 | if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) | ||
| 1354 | { | ||
| 1355 | item->type = cJSON_False; | ||
| 1356 | input_buffer->offset += 5; | ||
| 1357 | return true; | ||
| 1358 | } | ||
| 1359 | /* true */ | ||
| 1360 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) | ||
| 1361 | { | ||
| 1362 | item->type = cJSON_True; | ||
| 1363 | item->valueint = 1; | ||
| 1364 | input_buffer->offset += 4; | ||
| 1365 | return true; | ||
| 1366 | } | ||
| 1367 | /* string */ | ||
| 1368 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) | ||
| 1369 | { | ||
| 1370 | return parse_string(item, input_buffer); | ||
| 1371 | } | ||
| 1372 | /* number */ | ||
| 1373 | if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) | ||
| 1374 | { | ||
| 1375 | return parse_number(item, input_buffer); | ||
| 1376 | } | ||
| 1377 | /* array */ | ||
| 1378 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) | ||
| 1379 | { | ||
| 1380 | return parse_array(item, input_buffer); | ||
| 1381 | } | ||
| 1382 | /* object */ | ||
| 1383 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) | ||
| 1384 | { | ||
| 1385 | return parse_object(item, input_buffer); | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | return false; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | /* Render a value to text. */ | ||
| 1392 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) | ||
| 1393 | { | ||
| 1394 | unsigned char *output = NULL; | ||
| 1395 | |||
| 1396 | if ((item == NULL) || (output_buffer == NULL)) | ||
| 1397 | { | ||
| 1398 | return false; | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | switch ((item->type) & 0xFF) | ||
| 1402 | { | ||
| 1403 | case cJSON_NULL: | ||
| 1404 | output = ensure(output_buffer, 5); | ||
| 1405 | if (output == NULL) | ||
| 1406 | { | ||
| 1407 | return false; | ||
| 1408 | } | ||
| 1409 | strcpy((char*)output, "null"); | ||
| 1410 | return true; | ||
| 1411 | |||
| 1412 | case cJSON_False: | ||
| 1413 | output = ensure(output_buffer, 6); | ||
| 1414 | if (output == NULL) | ||
| 1415 | { | ||
| 1416 | return false; | ||
| 1417 | } | ||
| 1418 | strcpy((char*)output, "false"); | ||
| 1419 | return true; | ||
| 1420 | |||
| 1421 | case cJSON_True: | ||
| 1422 | output = ensure(output_buffer, 5); | ||
| 1423 | if (output == NULL) | ||
| 1424 | { | ||
| 1425 | return false; | ||
| 1426 | } | ||
| 1427 | strcpy((char*)output, "true"); | ||
| 1428 | return true; | ||
| 1429 | |||
| 1430 | case cJSON_Number: | ||
| 1431 | return print_number(item, output_buffer); | ||
| 1432 | |||
| 1433 | case cJSON_Raw: | ||
| 1434 | { | ||
| 1435 | size_t raw_length = 0; | ||
| 1436 | if (item->valuestring == NULL) | ||
| 1437 | { | ||
| 1438 | return false; | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | raw_length = strlen(item->valuestring) + sizeof(""); | ||
| 1442 | output = ensure(output_buffer, raw_length); | ||
| 1443 | if (output == NULL) | ||
| 1444 | { | ||
| 1445 | return false; | ||
| 1446 | } | ||
| 1447 | memcpy(output, item->valuestring, raw_length); | ||
| 1448 | return true; | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | case cJSON_String: | ||
| 1452 | return print_string(item, output_buffer); | ||
| 1453 | |||
| 1454 | case cJSON_Array: | ||
| 1455 | return print_array(item, output_buffer); | ||
| 1456 | |||
| 1457 | case cJSON_Object: | ||
| 1458 | return print_object(item, output_buffer); | ||
| 1459 | |||
| 1460 | default: | ||
| 1461 | return false; | ||
| 1462 | } | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | /* Build an array from input text. */ | ||
| 1466 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) | ||
| 1467 | { | ||
| 1468 | cJSON *head = NULL; /* head of the linked list */ | ||
| 1469 | cJSON *current_item = NULL; | ||
| 1470 | |||
| 1471 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) | ||
| 1472 | { | ||
| 1473 | return false; /* to deeply nested */ | ||
| 1474 | } | ||
| 1475 | input_buffer->depth++; | ||
| 1476 | |||
| 1477 | if (buffer_at_offset(input_buffer)[0] != '[') | ||
| 1478 | { | ||
| 1479 | /* not an array */ | ||
| 1480 | goto fail; | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | input_buffer->offset++; | ||
| 1484 | buffer_skip_whitespace(input_buffer); | ||
| 1485 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) | ||
| 1486 | { | ||
| 1487 | /* empty array */ | ||
| 1488 | goto success; | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | /* check if we skipped to the end of the buffer */ | ||
| 1492 | if (cannot_access_at_index(input_buffer, 0)) | ||
| 1493 | { | ||
| 1494 | input_buffer->offset--; | ||
| 1495 | goto fail; | ||
| 1496 | } | ||
| 1497 | |||
| 1498 | /* step back to character in front of the first element */ | ||
| 1499 | input_buffer->offset--; | ||
| 1500 | /* loop through the comma separated array elements */ | ||
| 1501 | do | ||
| 1502 | { | ||
| 1503 | /* allocate next item */ | ||
| 1504 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); | ||
| 1505 | if (new_item == NULL) | ||
| 1506 | { | ||
| 1507 | goto fail; /* allocation failure */ | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | /* attach next item to list */ | ||
| 1511 | if (head == NULL) | ||
| 1512 | { | ||
| 1513 | /* start the linked list */ | ||
| 1514 | current_item = head = new_item; | ||
| 1515 | } | ||
| 1516 | else | ||
| 1517 | { | ||
| 1518 | /* add to the end and advance */ | ||
| 1519 | current_item->next = new_item; | ||
| 1520 | new_item->prev = current_item; | ||
| 1521 | current_item = new_item; | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | /* parse next value */ | ||
| 1525 | input_buffer->offset++; | ||
| 1526 | buffer_skip_whitespace(input_buffer); | ||
| 1527 | if (!parse_value(current_item, input_buffer)) | ||
| 1528 | { | ||
| 1529 | goto fail; /* failed to parse value */ | ||
| 1530 | } | ||
| 1531 | buffer_skip_whitespace(input_buffer); | ||
| 1532 | } | ||
| 1533 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); | ||
| 1534 | |||
| 1535 | if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') | ||
| 1536 | { | ||
| 1537 | goto fail; /* expected end of array */ | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | success: | ||
| 1541 | input_buffer->depth--; | ||
| 1542 | |||
| 1543 | if (head != NULL) { | ||
| 1544 | head->prev = current_item; | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | item->type = cJSON_Array; | ||
| 1548 | item->child = head; | ||
| 1549 | |||
| 1550 | input_buffer->offset++; | ||
| 1551 | |||
| 1552 | return true; | ||
| 1553 | |||
| 1554 | fail: | ||
| 1555 | if (head != NULL) | ||
| 1556 | { | ||
| 1557 | cJSON_Delete(head); | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | return false; | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | /* Render an array to text */ | ||
| 1564 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) | ||
| 1565 | { | ||
| 1566 | unsigned char *output_pointer = NULL; | ||
| 1567 | size_t length = 0; | ||
| 1568 | cJSON *current_element = item->child; | ||
| 1569 | |||
| 1570 | if (output_buffer == NULL) | ||
| 1571 | { | ||
| 1572 | return false; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | /* Compose the output array. */ | ||
| 1576 | /* opening square bracket */ | ||
| 1577 | output_pointer = ensure(output_buffer, 1); | ||
| 1578 | if (output_pointer == NULL) | ||
| 1579 | { | ||
| 1580 | return false; | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | *output_pointer = '['; | ||
| 1584 | output_buffer->offset++; | ||
| 1585 | output_buffer->depth++; | ||
| 1586 | |||
| 1587 | while (current_element != NULL) | ||
| 1588 | { | ||
| 1589 | if (!print_value(current_element, output_buffer)) | ||
| 1590 | { | ||
| 1591 | return false; | ||
| 1592 | } | ||
| 1593 | update_offset(output_buffer); | ||
| 1594 | if (current_element->next) | ||
| 1595 | { | ||
| 1596 | length = (size_t) (output_buffer->format ? 2 : 1); | ||
| 1597 | output_pointer = ensure(output_buffer, length + 1); | ||
| 1598 | if (output_pointer == NULL) | ||
| 1599 | { | ||
| 1600 | return false; | ||
| 1601 | } | ||
| 1602 | *output_pointer++ = ','; | ||
| 1603 | if(output_buffer->format) | ||
| 1604 | { | ||
| 1605 | *output_pointer++ = ' '; | ||
| 1606 | } | ||
| 1607 | *output_pointer = '\0'; | ||
| 1608 | output_buffer->offset += length; | ||
| 1609 | } | ||
| 1610 | current_element = current_element->next; | ||
| 1611 | } | ||
| 1612 | |||
| 1613 | output_pointer = ensure(output_buffer, 2); | ||
| 1614 | if (output_pointer == NULL) | ||
| 1615 | { | ||
| 1616 | return false; | ||
| 1617 | } | ||
| 1618 | *output_pointer++ = ']'; | ||
| 1619 | *output_pointer = '\0'; | ||
| 1620 | output_buffer->depth--; | ||
| 1621 | |||
| 1622 | return true; | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | /* Build an object from the text. */ | ||
| 1626 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) | ||
| 1627 | { | ||
| 1628 | cJSON *head = NULL; /* linked list head */ | ||
| 1629 | cJSON *current_item = NULL; | ||
| 1630 | |||
| 1631 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) | ||
| 1632 | { | ||
| 1633 | return false; /* to deeply nested */ | ||
| 1634 | } | ||
| 1635 | input_buffer->depth++; | ||
| 1636 | |||
| 1637 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) | ||
| 1638 | { | ||
| 1639 | goto fail; /* not an object */ | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | input_buffer->offset++; | ||
| 1643 | buffer_skip_whitespace(input_buffer); | ||
| 1644 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) | ||
| 1645 | { | ||
| 1646 | goto success; /* empty object */ | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | /* check if we skipped to the end of the buffer */ | ||
| 1650 | if (cannot_access_at_index(input_buffer, 0)) | ||
| 1651 | { | ||
| 1652 | input_buffer->offset--; | ||
| 1653 | goto fail; | ||
| 1654 | } | ||
| 1655 | |||
| 1656 | /* step back to character in front of the first element */ | ||
| 1657 | input_buffer->offset--; | ||
| 1658 | /* loop through the comma separated array elements */ | ||
| 1659 | do | ||
| 1660 | { | ||
| 1661 | /* allocate next item */ | ||
| 1662 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); | ||
| 1663 | if (new_item == NULL) | ||
| 1664 | { | ||
| 1665 | goto fail; /* allocation failure */ | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | /* attach next item to list */ | ||
| 1669 | if (head == NULL) | ||
| 1670 | { | ||
| 1671 | /* start the linked list */ | ||
| 1672 | current_item = head = new_item; | ||
| 1673 | } | ||
| 1674 | else | ||
| 1675 | { | ||
| 1676 | /* add to the end and advance */ | ||
| 1677 | current_item->next = new_item; | ||
| 1678 | new_item->prev = current_item; | ||
| 1679 | current_item = new_item; | ||
| 1680 | } | ||
| 1681 | |||
| 1682 | if (cannot_access_at_index(input_buffer, 1)) | ||
| 1683 | { | ||
| 1684 | goto fail; /* nothing comes after the comma */ | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | /* parse the name of the child */ | ||
| 1688 | input_buffer->offset++; | ||
| 1689 | buffer_skip_whitespace(input_buffer); | ||
| 1690 | if (!parse_string(current_item, input_buffer)) | ||
| 1691 | { | ||
| 1692 | goto fail; /* failed to parse name */ | ||
| 1693 | } | ||
| 1694 | buffer_skip_whitespace(input_buffer); | ||
| 1695 | |||
| 1696 | /* swap valuestring and string, because we parsed the name */ | ||
| 1697 | current_item->string = current_item->valuestring; | ||
| 1698 | current_item->valuestring = NULL; | ||
| 1699 | |||
| 1700 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) | ||
| 1701 | { | ||
| 1702 | goto fail; /* invalid object */ | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | /* parse the value */ | ||
| 1706 | input_buffer->offset++; | ||
| 1707 | buffer_skip_whitespace(input_buffer); | ||
| 1708 | if (!parse_value(current_item, input_buffer)) | ||
| 1709 | { | ||
| 1710 | goto fail; /* failed to parse value */ | ||
| 1711 | } | ||
| 1712 | buffer_skip_whitespace(input_buffer); | ||
| 1713 | } | ||
| 1714 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); | ||
| 1715 | |||
| 1716 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) | ||
| 1717 | { | ||
| 1718 | goto fail; /* expected end of object */ | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | success: | ||
| 1722 | input_buffer->depth--; | ||
| 1723 | |||
| 1724 | if (head != NULL) { | ||
| 1725 | head->prev = current_item; | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | item->type = cJSON_Object; | ||
| 1729 | item->child = head; | ||
| 1730 | |||
| 1731 | input_buffer->offset++; | ||
| 1732 | return true; | ||
| 1733 | |||
| 1734 | fail: | ||
| 1735 | if (head != NULL) | ||
| 1736 | { | ||
| 1737 | cJSON_Delete(head); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | return false; | ||
| 1741 | } | ||
| 1742 | |||
| 1743 | /* Render an object to text. */ | ||
| 1744 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) | ||
| 1745 | { | ||
| 1746 | unsigned char *output_pointer = NULL; | ||
| 1747 | size_t length = 0; | ||
| 1748 | cJSON *current_item = item->child; | ||
| 1749 | |||
| 1750 | if (output_buffer == NULL) | ||
| 1751 | { | ||
| 1752 | return false; | ||
| 1753 | } | ||
| 1754 | |||
| 1755 | /* Compose the output: */ | ||
| 1756 | length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ | ||
| 1757 | output_pointer = ensure(output_buffer, length + 1); | ||
| 1758 | if (output_pointer == NULL) | ||
| 1759 | { | ||
| 1760 | return false; | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | *output_pointer++ = '{'; | ||
| 1764 | output_buffer->depth++; | ||
| 1765 | if (output_buffer->format) | ||
| 1766 | { | ||
| 1767 | *output_pointer++ = '\n'; | ||
| 1768 | } | ||
| 1769 | output_buffer->offset += length; | ||
| 1770 | |||
| 1771 | while (current_item) | ||
| 1772 | { | ||
| 1773 | if (output_buffer->format) | ||
| 1774 | { | ||
| 1775 | size_t i; | ||
| 1776 | output_pointer = ensure(output_buffer, output_buffer->depth); | ||
| 1777 | if (output_pointer == NULL) | ||
| 1778 | { | ||
| 1779 | return false; | ||
| 1780 | } | ||
| 1781 | for (i = 0; i < output_buffer->depth; i++) | ||
| 1782 | { | ||
| 1783 | *output_pointer++ = '\t'; | ||
| 1784 | } | ||
| 1785 | output_buffer->offset += output_buffer->depth; | ||
| 1786 | } | ||
| 1787 | |||
| 1788 | /* print key */ | ||
| 1789 | if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) | ||
| 1790 | { | ||
| 1791 | return false; | ||
| 1792 | } | ||
| 1793 | update_offset(output_buffer); | ||
| 1794 | |||
| 1795 | length = (size_t) (output_buffer->format ? 2 : 1); | ||
| 1796 | output_pointer = ensure(output_buffer, length); | ||
| 1797 | if (output_pointer == NULL) | ||
| 1798 | { | ||
| 1799 | return false; | ||
| 1800 | } | ||
| 1801 | *output_pointer++ = ':'; | ||
| 1802 | if (output_buffer->format) | ||
| 1803 | { | ||
| 1804 | *output_pointer++ = '\t'; | ||
| 1805 | } | ||
| 1806 | output_buffer->offset += length; | ||
| 1807 | |||
| 1808 | /* print value */ | ||
| 1809 | if (!print_value(current_item, output_buffer)) | ||
| 1810 | { | ||
| 1811 | return false; | ||
| 1812 | } | ||
| 1813 | update_offset(output_buffer); | ||
| 1814 | |||
| 1815 | /* print comma if not last */ | ||
| 1816 | length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); | ||
| 1817 | output_pointer = ensure(output_buffer, length + 1); | ||
| 1818 | if (output_pointer == NULL) | ||
| 1819 | { | ||
| 1820 | return false; | ||
| 1821 | } | ||
| 1822 | if (current_item->next) | ||
| 1823 | { | ||
| 1824 | *output_pointer++ = ','; | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | if (output_buffer->format) | ||
| 1828 | { | ||
| 1829 | *output_pointer++ = '\n'; | ||
| 1830 | } | ||
| 1831 | *output_pointer = '\0'; | ||
| 1832 | output_buffer->offset += length; | ||
| 1833 | |||
| 1834 | current_item = current_item->next; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); | ||
| 1838 | if (output_pointer == NULL) | ||
| 1839 | { | ||
| 1840 | return false; | ||
| 1841 | } | ||
| 1842 | if (output_buffer->format) | ||
| 1843 | { | ||
| 1844 | size_t i; | ||
| 1845 | for (i = 0; i < (output_buffer->depth - 1); i++) | ||
| 1846 | { | ||
| 1847 | *output_pointer++ = '\t'; | ||
| 1848 | } | ||
| 1849 | } | ||
| 1850 | *output_pointer++ = '}'; | ||
| 1851 | *output_pointer = '\0'; | ||
| 1852 | output_buffer->depth--; | ||
| 1853 | |||
| 1854 | return true; | ||
| 1855 | } | ||
| 1856 | |||
| 1857 | /* Get Array size/item / object item. */ | ||
| 1858 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) | ||
| 1859 | { | ||
| 1860 | cJSON *child = NULL; | ||
| 1861 | size_t size = 0; | ||
| 1862 | |||
| 1863 | if (array == NULL) | ||
| 1864 | { | ||
| 1865 | return 0; | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | child = array->child; | ||
| 1869 | |||
| 1870 | while(child != NULL) | ||
| 1871 | { | ||
| 1872 | size++; | ||
| 1873 | child = child->next; | ||
| 1874 | } | ||
| 1875 | |||
| 1876 | /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ | ||
| 1877 | |||
| 1878 | return (int)size; | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | static cJSON* get_array_item(const cJSON *array, size_t index) | ||
| 1882 | { | ||
| 1883 | cJSON *current_child = NULL; | ||
| 1884 | |||
| 1885 | if (array == NULL) | ||
| 1886 | { | ||
| 1887 | return NULL; | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | current_child = array->child; | ||
| 1891 | while ((current_child != NULL) && (index > 0)) | ||
| 1892 | { | ||
| 1893 | index--; | ||
| 1894 | current_child = current_child->next; | ||
| 1895 | } | ||
| 1896 | |||
| 1897 | return current_child; | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) | ||
| 1901 | { | ||
| 1902 | if (index < 0) | ||
| 1903 | { | ||
| 1904 | return NULL; | ||
| 1905 | } | ||
| 1906 | |||
| 1907 | return get_array_item(array, (size_t)index); | ||
| 1908 | } | ||
| 1909 | |||
| 1910 | static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) | ||
| 1911 | { | ||
| 1912 | cJSON *current_element = NULL; | ||
| 1913 | |||
| 1914 | if ((object == NULL) || (name == NULL)) | ||
| 1915 | { | ||
| 1916 | return NULL; | ||
| 1917 | } | ||
| 1918 | |||
| 1919 | current_element = object->child; | ||
| 1920 | if (case_sensitive) | ||
| 1921 | { | ||
| 1922 | while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) | ||
| 1923 | { | ||
| 1924 | current_element = current_element->next; | ||
| 1925 | } | ||
| 1926 | } | ||
| 1927 | else | ||
| 1928 | { | ||
| 1929 | while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) | ||
| 1930 | { | ||
| 1931 | current_element = current_element->next; | ||
| 1932 | } | ||
| 1933 | } | ||
| 1934 | |||
| 1935 | if ((current_element == NULL) || (current_element->string == NULL)) { | ||
| 1936 | return NULL; | ||
| 1937 | } | ||
| 1938 | |||
| 1939 | return current_element; | ||
| 1940 | } | ||
| 1941 | |||
| 1942 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) | ||
| 1943 | { | ||
| 1944 | return get_object_item(object, string, false); | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) | ||
| 1948 | { | ||
| 1949 | return get_object_item(object, string, true); | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) | ||
| 1953 | { | ||
| 1954 | return cJSON_GetObjectItem(object, string) ? 1 : 0; | ||
| 1955 | } | ||
| 1956 | |||
| 1957 | /* Utility for array list handling. */ | ||
| 1958 | static void suffix_object(cJSON *prev, cJSON *item) | ||
| 1959 | { | ||
| 1960 | prev->next = item; | ||
| 1961 | item->prev = prev; | ||
| 1962 | } | ||
| 1963 | |||
| 1964 | /* Utility for handling references. */ | ||
| 1965 | static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) | ||
| 1966 | { | ||
| 1967 | cJSON *reference = NULL; | ||
| 1968 | if (item == NULL) | ||
| 1969 | { | ||
| 1970 | return NULL; | ||
| 1971 | } | ||
| 1972 | |||
| 1973 | reference = cJSON_New_Item(hooks); | ||
| 1974 | if (reference == NULL) | ||
| 1975 | { | ||
| 1976 | return NULL; | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | memcpy(reference, item, sizeof(cJSON)); | ||
| 1980 | reference->string = NULL; | ||
| 1981 | reference->type |= cJSON_IsReference; | ||
| 1982 | reference->next = reference->prev = NULL; | ||
| 1983 | return reference; | ||
| 1984 | } | ||
| 1985 | |||
| 1986 | static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) | ||
| 1987 | { | ||
| 1988 | cJSON *child = NULL; | ||
| 1989 | |||
| 1990 | if ((item == NULL) || (array == NULL) || (array == item)) | ||
| 1991 | { | ||
| 1992 | return false; | ||
| 1993 | } | ||
| 1994 | |||
| 1995 | child = array->child; | ||
| 1996 | /* | ||
| 1997 | * To find the last item in array quickly, we use prev in array | ||
| 1998 | */ | ||
| 1999 | if (child == NULL) | ||
| 2000 | { | ||
| 2001 | /* list is empty, start new one */ | ||
| 2002 | array->child = item; | ||
| 2003 | item->prev = item; | ||
| 2004 | item->next = NULL; | ||
| 2005 | } | ||
| 2006 | else | ||
| 2007 | { | ||
| 2008 | /* append to the end */ | ||
| 2009 | if (child->prev) | ||
| 2010 | { | ||
| 2011 | suffix_object(child->prev, item); | ||
| 2012 | array->child->prev = item; | ||
| 2013 | } | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | return true; | ||
| 2017 | } | ||
| 2018 | |||
| 2019 | /* Add item to array/object. */ | ||
| 2020 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) | ||
| 2021 | { | ||
| 2022 | return add_item_to_array(array, item); | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) | ||
| 2026 | #pragma GCC diagnostic push | ||
| 2027 | #endif | ||
| 2028 | #ifdef __GNUC__ | ||
| 2029 | #pragma GCC diagnostic ignored "-Wcast-qual" | ||
| 2030 | #endif | ||
| 2031 | /* helper function to cast away const */ | ||
| 2032 | static void* cast_away_const(const void* string) | ||
| 2033 | { | ||
| 2034 | return (void*)string; | ||
| 2035 | } | ||
| 2036 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) | ||
| 2037 | #pragma GCC diagnostic pop | ||
| 2038 | #endif | ||
| 2039 | |||
| 2040 | |||
| 2041 | static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) | ||
| 2042 | { | ||
| 2043 | char *new_key = NULL; | ||
| 2044 | int new_type = cJSON_Invalid; | ||
| 2045 | |||
| 2046 | if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) | ||
| 2047 | { | ||
| 2048 | return false; | ||
| 2049 | } | ||
| 2050 | |||
| 2051 | if (constant_key) | ||
| 2052 | { | ||
| 2053 | new_key = (char*)cast_away_const(string); | ||
| 2054 | new_type = item->type | cJSON_StringIsConst; | ||
| 2055 | } | ||
| 2056 | else | ||
| 2057 | { | ||
| 2058 | new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); | ||
| 2059 | if (new_key == NULL) | ||
| 2060 | { | ||
| 2061 | return false; | ||
| 2062 | } | ||
| 2063 | |||
| 2064 | new_type = item->type & ~cJSON_StringIsConst; | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) | ||
| 2068 | { | ||
| 2069 | hooks->deallocate(item->string); | ||
| 2070 | } | ||
| 2071 | |||
| 2072 | item->string = new_key; | ||
| 2073 | item->type = new_type; | ||
| 2074 | |||
| 2075 | return add_item_to_array(object, item); | ||
| 2076 | } | ||
| 2077 | |||
| 2078 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) | ||
| 2079 | { | ||
| 2080 | return add_item_to_object(object, string, item, &global_hooks, false); | ||
| 2081 | } | ||
| 2082 | |||
| 2083 | /* Add an item to an object with constant string as key */ | ||
| 2084 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) | ||
| 2085 | { | ||
| 2086 | return add_item_to_object(object, string, item, &global_hooks, true); | ||
| 2087 | } | ||
| 2088 | |||
| 2089 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) | ||
| 2090 | { | ||
| 2091 | if (array == NULL) | ||
| 2092 | { | ||
| 2093 | return false; | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | return add_item_to_array(array, create_reference(item, &global_hooks)); | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) | ||
| 2100 | { | ||
| 2101 | if ((object == NULL) || (string == NULL)) | ||
| 2102 | { | ||
| 2103 | return false; | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); | ||
| 2107 | } | ||
| 2108 | |||
| 2109 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) | ||
| 2110 | { | ||
| 2111 | cJSON *null = cJSON_CreateNull(); | ||
| 2112 | if (add_item_to_object(object, name, null, &global_hooks, false)) | ||
| 2113 | { | ||
| 2114 | return null; | ||
| 2115 | } | ||
| 2116 | |||
| 2117 | cJSON_Delete(null); | ||
| 2118 | return NULL; | ||
| 2119 | } | ||
| 2120 | |||
| 2121 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) | ||
| 2122 | { | ||
| 2123 | cJSON *true_item = cJSON_CreateTrue(); | ||
| 2124 | if (add_item_to_object(object, name, true_item, &global_hooks, false)) | ||
| 2125 | { | ||
| 2126 | return true_item; | ||
| 2127 | } | ||
| 2128 | |||
| 2129 | cJSON_Delete(true_item); | ||
| 2130 | return NULL; | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) | ||
| 2134 | { | ||
| 2135 | cJSON *false_item = cJSON_CreateFalse(); | ||
| 2136 | if (add_item_to_object(object, name, false_item, &global_hooks, false)) | ||
| 2137 | { | ||
| 2138 | return false_item; | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | cJSON_Delete(false_item); | ||
| 2142 | return NULL; | ||
| 2143 | } | ||
| 2144 | |||
| 2145 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) | ||
| 2146 | { | ||
| 2147 | cJSON *bool_item = cJSON_CreateBool(boolean); | ||
| 2148 | if (add_item_to_object(object, name, bool_item, &global_hooks, false)) | ||
| 2149 | { | ||
| 2150 | return bool_item; | ||
| 2151 | } | ||
| 2152 | |||
| 2153 | cJSON_Delete(bool_item); | ||
| 2154 | return NULL; | ||
| 2155 | } | ||
| 2156 | |||
| 2157 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) | ||
| 2158 | { | ||
| 2159 | cJSON *number_item = cJSON_CreateNumber(number); | ||
| 2160 | if (add_item_to_object(object, name, number_item, &global_hooks, false)) | ||
| 2161 | { | ||
| 2162 | return number_item; | ||
| 2163 | } | ||
| 2164 | |||
| 2165 | cJSON_Delete(number_item); | ||
| 2166 | return NULL; | ||
| 2167 | } | ||
| 2168 | |||
| 2169 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) | ||
| 2170 | { | ||
| 2171 | cJSON *string_item = cJSON_CreateString(string); | ||
| 2172 | if (add_item_to_object(object, name, string_item, &global_hooks, false)) | ||
| 2173 | { | ||
| 2174 | return string_item; | ||
| 2175 | } | ||
| 2176 | |||
| 2177 | cJSON_Delete(string_item); | ||
| 2178 | return NULL; | ||
| 2179 | } | ||
| 2180 | |||
| 2181 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) | ||
| 2182 | { | ||
| 2183 | cJSON *raw_item = cJSON_CreateRaw(raw); | ||
| 2184 | if (add_item_to_object(object, name, raw_item, &global_hooks, false)) | ||
| 2185 | { | ||
| 2186 | return raw_item; | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | cJSON_Delete(raw_item); | ||
| 2190 | return NULL; | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) | ||
| 2194 | { | ||
| 2195 | cJSON *object_item = cJSON_CreateObject(); | ||
| 2196 | if (add_item_to_object(object, name, object_item, &global_hooks, false)) | ||
| 2197 | { | ||
| 2198 | return object_item; | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | cJSON_Delete(object_item); | ||
| 2202 | return NULL; | ||
| 2203 | } | ||
| 2204 | |||
| 2205 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) | ||
| 2206 | { | ||
| 2207 | cJSON *array = cJSON_CreateArray(); | ||
| 2208 | if (add_item_to_object(object, name, array, &global_hooks, false)) | ||
| 2209 | { | ||
| 2210 | return array; | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | cJSON_Delete(array); | ||
| 2214 | return NULL; | ||
| 2215 | } | ||
| 2216 | |||
| 2217 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) | ||
| 2218 | { | ||
| 2219 | if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) | ||
| 2220 | { | ||
| 2221 | return NULL; | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | if (item != parent->child) | ||
| 2225 | { | ||
| 2226 | /* not the first element */ | ||
| 2227 | item->prev->next = item->next; | ||
| 2228 | } | ||
| 2229 | if (item->next != NULL) | ||
| 2230 | { | ||
| 2231 | /* not the last element */ | ||
| 2232 | item->next->prev = item->prev; | ||
| 2233 | } | ||
| 2234 | |||
| 2235 | if (item == parent->child) | ||
| 2236 | { | ||
| 2237 | /* first element */ | ||
| 2238 | parent->child = item->next; | ||
| 2239 | } | ||
| 2240 | else if (item->next == NULL) | ||
| 2241 | { | ||
| 2242 | /* last element */ | ||
| 2243 | parent->child->prev = item->prev; | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | /* make sure the detached item doesn't point anywhere anymore */ | ||
| 2247 | item->prev = NULL; | ||
| 2248 | item->next = NULL; | ||
| 2249 | |||
| 2250 | return item; | ||
| 2251 | } | ||
| 2252 | |||
| 2253 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) | ||
| 2254 | { | ||
| 2255 | if (which < 0) | ||
| 2256 | { | ||
| 2257 | return NULL; | ||
| 2258 | } | ||
| 2259 | |||
| 2260 | return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); | ||
| 2261 | } | ||
| 2262 | |||
| 2263 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) | ||
| 2264 | { | ||
| 2265 | cJSON_Delete(cJSON_DetachItemFromArray(array, which)); | ||
| 2266 | } | ||
| 2267 | |||
| 2268 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) | ||
| 2269 | { | ||
| 2270 | cJSON *to_detach = cJSON_GetObjectItem(object, string); | ||
| 2271 | |||
| 2272 | return cJSON_DetachItemViaPointer(object, to_detach); | ||
| 2273 | } | ||
| 2274 | |||
| 2275 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) | ||
| 2276 | { | ||
| 2277 | cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); | ||
| 2278 | |||
| 2279 | return cJSON_DetachItemViaPointer(object, to_detach); | ||
| 2280 | } | ||
| 2281 | |||
| 2282 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) | ||
| 2283 | { | ||
| 2284 | cJSON_Delete(cJSON_DetachItemFromObject(object, string)); | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) | ||
| 2288 | { | ||
| 2289 | cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | /* Replace array/object items with new ones. */ | ||
| 2293 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) | ||
| 2294 | { | ||
| 2295 | cJSON *after_inserted = NULL; | ||
| 2296 | |||
| 2297 | if (which < 0 || newitem == NULL) | ||
| 2298 | { | ||
| 2299 | return false; | ||
| 2300 | } | ||
| 2301 | |||
| 2302 | after_inserted = get_array_item(array, (size_t)which); | ||
| 2303 | if (after_inserted == NULL) | ||
| 2304 | { | ||
| 2305 | return add_item_to_array(array, newitem); | ||
| 2306 | } | ||
| 2307 | |||
| 2308 | if (after_inserted != array->child && after_inserted->prev == NULL) { | ||
| 2309 | /* return false if after_inserted is a corrupted array item */ | ||
| 2310 | return false; | ||
| 2311 | } | ||
| 2312 | |||
| 2313 | newitem->next = after_inserted; | ||
| 2314 | newitem->prev = after_inserted->prev; | ||
| 2315 | after_inserted->prev = newitem; | ||
| 2316 | if (after_inserted == array->child) | ||
| 2317 | { | ||
| 2318 | array->child = newitem; | ||
| 2319 | } | ||
| 2320 | else | ||
| 2321 | { | ||
| 2322 | newitem->prev->next = newitem; | ||
| 2323 | } | ||
| 2324 | return true; | ||
| 2325 | } | ||
| 2326 | |||
| 2327 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) | ||
| 2328 | { | ||
| 2329 | if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) | ||
| 2330 | { | ||
| 2331 | return false; | ||
| 2332 | } | ||
| 2333 | |||
| 2334 | if (replacement == item) | ||
| 2335 | { | ||
| 2336 | return true; | ||
| 2337 | } | ||
| 2338 | |||
| 2339 | replacement->next = item->next; | ||
| 2340 | replacement->prev = item->prev; | ||
| 2341 | |||
| 2342 | if (replacement->next != NULL) | ||
| 2343 | { | ||
| 2344 | replacement->next->prev = replacement; | ||
| 2345 | } | ||
| 2346 | if (parent->child == item) | ||
| 2347 | { | ||
| 2348 | if (parent->child->prev == parent->child) | ||
| 2349 | { | ||
| 2350 | replacement->prev = replacement; | ||
| 2351 | } | ||
| 2352 | parent->child = replacement; | ||
| 2353 | } | ||
| 2354 | else | ||
| 2355 | { /* | ||
| 2356 | * To find the last item in array quickly, we use prev in array. | ||
| 2357 | * We can't modify the last item's next pointer where this item was the parent's child | ||
| 2358 | */ | ||
| 2359 | if (replacement->prev != NULL) | ||
| 2360 | { | ||
| 2361 | replacement->prev->next = replacement; | ||
| 2362 | } | ||
| 2363 | if (replacement->next == NULL) | ||
| 2364 | { | ||
| 2365 | parent->child->prev = replacement; | ||
| 2366 | } | ||
| 2367 | } | ||
| 2368 | |||
| 2369 | item->next = NULL; | ||
| 2370 | item->prev = NULL; | ||
| 2371 | cJSON_Delete(item); | ||
| 2372 | |||
| 2373 | return true; | ||
| 2374 | } | ||
| 2375 | |||
| 2376 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) | ||
| 2377 | { | ||
| 2378 | if (which < 0) | ||
| 2379 | { | ||
| 2380 | return false; | ||
| 2381 | } | ||
| 2382 | |||
| 2383 | return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) | ||
| 2387 | { | ||
| 2388 | if ((replacement == NULL) || (string == NULL)) | ||
| 2389 | { | ||
| 2390 | return false; | ||
| 2391 | } | ||
| 2392 | |||
| 2393 | /* replace the name in the replacement */ | ||
| 2394 | if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) | ||
| 2395 | { | ||
| 2396 | cJSON_free(replacement->string); | ||
| 2397 | } | ||
| 2398 | replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); | ||
| 2399 | if (replacement->string == NULL) | ||
| 2400 | { | ||
| 2401 | return false; | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | replacement->type &= ~cJSON_StringIsConst; | ||
| 2405 | |||
| 2406 | return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); | ||
| 2407 | } | ||
| 2408 | |||
| 2409 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) | ||
| 2410 | { | ||
| 2411 | return replace_item_in_object(object, string, newitem, false); | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) | ||
| 2415 | { | ||
| 2416 | return replace_item_in_object(object, string, newitem, true); | ||
| 2417 | } | ||
| 2418 | |||
| 2419 | /* Create basic types: */ | ||
| 2420 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) | ||
| 2421 | { | ||
| 2422 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2423 | if(item) | ||
| 2424 | { | ||
| 2425 | item->type = cJSON_NULL; | ||
| 2426 | } | ||
| 2427 | |||
| 2428 | return item; | ||
| 2429 | } | ||
| 2430 | |||
| 2431 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) | ||
| 2432 | { | ||
| 2433 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2434 | if(item) | ||
| 2435 | { | ||
| 2436 | item->type = cJSON_True; | ||
| 2437 | } | ||
| 2438 | |||
| 2439 | return item; | ||
| 2440 | } | ||
| 2441 | |||
| 2442 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) | ||
| 2443 | { | ||
| 2444 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2445 | if(item) | ||
| 2446 | { | ||
| 2447 | item->type = cJSON_False; | ||
| 2448 | } | ||
| 2449 | |||
| 2450 | return item; | ||
| 2451 | } | ||
| 2452 | |||
| 2453 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) | ||
| 2454 | { | ||
| 2455 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2456 | if(item) | ||
| 2457 | { | ||
| 2458 | item->type = boolean ? cJSON_True : cJSON_False; | ||
| 2459 | } | ||
| 2460 | |||
| 2461 | return item; | ||
| 2462 | } | ||
| 2463 | |||
| 2464 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) | ||
| 2465 | { | ||
| 2466 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2467 | if(item) | ||
| 2468 | { | ||
| 2469 | item->type = cJSON_Number; | ||
| 2470 | item->valuedouble = num; | ||
| 2471 | |||
| 2472 | /* use saturation in case of overflow */ | ||
| 2473 | if (num >= INT_MAX) | ||
| 2474 | { | ||
| 2475 | item->valueint = INT_MAX; | ||
| 2476 | } | ||
| 2477 | else if (num <= (double)INT_MIN) | ||
| 2478 | { | ||
| 2479 | item->valueint = INT_MIN; | ||
| 2480 | } | ||
| 2481 | else | ||
| 2482 | { | ||
| 2483 | item->valueint = (int)num; | ||
| 2484 | } | ||
| 2485 | } | ||
| 2486 | |||
| 2487 | return item; | ||
| 2488 | } | ||
| 2489 | |||
| 2490 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) | ||
| 2491 | { | ||
| 2492 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2493 | if(item) | ||
| 2494 | { | ||
| 2495 | item->type = cJSON_String; | ||
| 2496 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); | ||
| 2497 | if(!item->valuestring) | ||
| 2498 | { | ||
| 2499 | cJSON_Delete(item); | ||
| 2500 | return NULL; | ||
| 2501 | } | ||
| 2502 | } | ||
| 2503 | |||
| 2504 | return item; | ||
| 2505 | } | ||
| 2506 | |||
| 2507 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) | ||
| 2508 | { | ||
| 2509 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2510 | if (item != NULL) | ||
| 2511 | { | ||
| 2512 | item->type = cJSON_String | cJSON_IsReference; | ||
| 2513 | item->valuestring = (char*)cast_away_const(string); | ||
| 2514 | } | ||
| 2515 | |||
| 2516 | return item; | ||
| 2517 | } | ||
| 2518 | |||
| 2519 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) | ||
| 2520 | { | ||
| 2521 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2522 | if (item != NULL) { | ||
| 2523 | item->type = cJSON_Object | cJSON_IsReference; | ||
| 2524 | item->child = (cJSON*)cast_away_const(child); | ||
| 2525 | } | ||
| 2526 | |||
| 2527 | return item; | ||
| 2528 | } | ||
| 2529 | |||
| 2530 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { | ||
| 2531 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2532 | if (item != NULL) { | ||
| 2533 | item->type = cJSON_Array | cJSON_IsReference; | ||
| 2534 | item->child = (cJSON*)cast_away_const(child); | ||
| 2535 | } | ||
| 2536 | |||
| 2537 | return item; | ||
| 2538 | } | ||
| 2539 | |||
| 2540 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) | ||
| 2541 | { | ||
| 2542 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2543 | if(item) | ||
| 2544 | { | ||
| 2545 | item->type = cJSON_Raw; | ||
| 2546 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); | ||
| 2547 | if(!item->valuestring) | ||
| 2548 | { | ||
| 2549 | cJSON_Delete(item); | ||
| 2550 | return NULL; | ||
| 2551 | } | ||
| 2552 | } | ||
| 2553 | |||
| 2554 | return item; | ||
| 2555 | } | ||
| 2556 | |||
| 2557 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) | ||
| 2558 | { | ||
| 2559 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2560 | if(item) | ||
| 2561 | { | ||
| 2562 | item->type=cJSON_Array; | ||
| 2563 | } | ||
| 2564 | |||
| 2565 | return item; | ||
| 2566 | } | ||
| 2567 | |||
| 2568 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) | ||
| 2569 | { | ||
| 2570 | cJSON *item = cJSON_New_Item(&global_hooks); | ||
| 2571 | if (item) | ||
| 2572 | { | ||
| 2573 | item->type = cJSON_Object; | ||
| 2574 | } | ||
| 2575 | |||
| 2576 | return item; | ||
| 2577 | } | ||
| 2578 | |||
| 2579 | /* Create Arrays: */ | ||
| 2580 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) | ||
| 2581 | { | ||
| 2582 | size_t i = 0; | ||
| 2583 | cJSON *n = NULL; | ||
| 2584 | cJSON *p = NULL; | ||
| 2585 | cJSON *a = NULL; | ||
| 2586 | |||
| 2587 | if ((count < 0) || (numbers == NULL)) | ||
| 2588 | { | ||
| 2589 | return NULL; | ||
| 2590 | } | ||
| 2591 | |||
| 2592 | a = cJSON_CreateArray(); | ||
| 2593 | |||
| 2594 | for(i = 0; a && (i < (size_t)count); i++) | ||
| 2595 | { | ||
| 2596 | n = cJSON_CreateNumber(numbers[i]); | ||
| 2597 | if (!n) | ||
| 2598 | { | ||
| 2599 | cJSON_Delete(a); | ||
| 2600 | return NULL; | ||
| 2601 | } | ||
| 2602 | if(!i) | ||
| 2603 | { | ||
| 2604 | a->child = n; | ||
| 2605 | } | ||
| 2606 | else | ||
| 2607 | { | ||
| 2608 | suffix_object(p, n); | ||
| 2609 | } | ||
| 2610 | p = n; | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | if (a && a->child) { | ||
| 2614 | a->child->prev = n; | ||
| 2615 | } | ||
| 2616 | |||
| 2617 | return a; | ||
| 2618 | } | ||
| 2619 | |||
| 2620 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) | ||
| 2621 | { | ||
| 2622 | size_t i = 0; | ||
| 2623 | cJSON *n = NULL; | ||
| 2624 | cJSON *p = NULL; | ||
| 2625 | cJSON *a = NULL; | ||
| 2626 | |||
| 2627 | if ((count < 0) || (numbers == NULL)) | ||
| 2628 | { | ||
| 2629 | return NULL; | ||
| 2630 | } | ||
| 2631 | |||
| 2632 | a = cJSON_CreateArray(); | ||
| 2633 | |||
| 2634 | for(i = 0; a && (i < (size_t)count); i++) | ||
| 2635 | { | ||
| 2636 | n = cJSON_CreateNumber((double)numbers[i]); | ||
| 2637 | if(!n) | ||
| 2638 | { | ||
| 2639 | cJSON_Delete(a); | ||
| 2640 | return NULL; | ||
| 2641 | } | ||
| 2642 | if(!i) | ||
| 2643 | { | ||
| 2644 | a->child = n; | ||
| 2645 | } | ||
| 2646 | else | ||
| 2647 | { | ||
| 2648 | suffix_object(p, n); | ||
| 2649 | } | ||
| 2650 | p = n; | ||
| 2651 | } | ||
| 2652 | |||
| 2653 | if (a && a->child) { | ||
| 2654 | a->child->prev = n; | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | return a; | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) | ||
| 2661 | { | ||
| 2662 | size_t i = 0; | ||
| 2663 | cJSON *n = NULL; | ||
| 2664 | cJSON *p = NULL; | ||
| 2665 | cJSON *a = NULL; | ||
| 2666 | |||
| 2667 | if ((count < 0) || (numbers == NULL)) | ||
| 2668 | { | ||
| 2669 | return NULL; | ||
| 2670 | } | ||
| 2671 | |||
| 2672 | a = cJSON_CreateArray(); | ||
| 2673 | |||
| 2674 | for(i = 0; a && (i < (size_t)count); i++) | ||
| 2675 | { | ||
| 2676 | n = cJSON_CreateNumber(numbers[i]); | ||
| 2677 | if(!n) | ||
| 2678 | { | ||
| 2679 | cJSON_Delete(a); | ||
| 2680 | return NULL; | ||
| 2681 | } | ||
| 2682 | if(!i) | ||
| 2683 | { | ||
| 2684 | a->child = n; | ||
| 2685 | } | ||
| 2686 | else | ||
| 2687 | { | ||
| 2688 | suffix_object(p, n); | ||
| 2689 | } | ||
| 2690 | p = n; | ||
| 2691 | } | ||
| 2692 | |||
| 2693 | if (a && a->child) { | ||
| 2694 | a->child->prev = n; | ||
| 2695 | } | ||
| 2696 | |||
| 2697 | return a; | ||
| 2698 | } | ||
| 2699 | |||
| 2700 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) | ||
| 2701 | { | ||
| 2702 | size_t i = 0; | ||
| 2703 | cJSON *n = NULL; | ||
| 2704 | cJSON *p = NULL; | ||
| 2705 | cJSON *a = NULL; | ||
| 2706 | |||
| 2707 | if ((count < 0) || (strings == NULL)) | ||
| 2708 | { | ||
| 2709 | return NULL; | ||
| 2710 | } | ||
| 2711 | |||
| 2712 | a = cJSON_CreateArray(); | ||
| 2713 | |||
| 2714 | for (i = 0; a && (i < (size_t)count); i++) | ||
| 2715 | { | ||
| 2716 | n = cJSON_CreateString(strings[i]); | ||
| 2717 | if(!n) | ||
| 2718 | { | ||
| 2719 | cJSON_Delete(a); | ||
| 2720 | return NULL; | ||
| 2721 | } | ||
| 2722 | if(!i) | ||
| 2723 | { | ||
| 2724 | a->child = n; | ||
| 2725 | } | ||
| 2726 | else | ||
| 2727 | { | ||
| 2728 | suffix_object(p,n); | ||
| 2729 | } | ||
| 2730 | p = n; | ||
| 2731 | } | ||
| 2732 | |||
| 2733 | if (a && a->child) { | ||
| 2734 | a->child->prev = n; | ||
| 2735 | } | ||
| 2736 | |||
| 2737 | return a; | ||
| 2738 | } | ||
| 2739 | |||
| 2740 | /* Duplication */ | ||
| 2741 | cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); | ||
| 2742 | |||
| 2743 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) | ||
| 2744 | { | ||
| 2745 | return cJSON_Duplicate_rec(item, 0, recurse ); | ||
| 2746 | } | ||
| 2747 | |||
| 2748 | cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) | ||
| 2749 | { | ||
| 2750 | cJSON *newitem = NULL; | ||
| 2751 | cJSON *child = NULL; | ||
| 2752 | cJSON *next = NULL; | ||
| 2753 | cJSON *newchild = NULL; | ||
| 2754 | |||
| 2755 | /* Bail on bad ptr */ | ||
| 2756 | if (!item) | ||
| 2757 | { | ||
| 2758 | goto fail; | ||
| 2759 | } | ||
| 2760 | /* Create new item */ | ||
| 2761 | newitem = cJSON_New_Item(&global_hooks); | ||
| 2762 | if (!newitem) | ||
| 2763 | { | ||
| 2764 | goto fail; | ||
| 2765 | } | ||
| 2766 | /* Copy over all vars */ | ||
| 2767 | newitem->type = item->type & (~cJSON_IsReference); | ||
| 2768 | newitem->valueint = item->valueint; | ||
| 2769 | newitem->valuedouble = item->valuedouble; | ||
| 2770 | if (item->valuestring) | ||
| 2771 | { | ||
| 2772 | newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); | ||
| 2773 | if (!newitem->valuestring) | ||
| 2774 | { | ||
| 2775 | goto fail; | ||
| 2776 | } | ||
| 2777 | } | ||
| 2778 | if (item->string) | ||
| 2779 | { | ||
| 2780 | newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); | ||
| 2781 | if (!newitem->string) | ||
| 2782 | { | ||
| 2783 | goto fail; | ||
| 2784 | } | ||
| 2785 | } | ||
| 2786 | /* If non-recursive, then we're done! */ | ||
| 2787 | if (!recurse) | ||
| 2788 | { | ||
| 2789 | return newitem; | ||
| 2790 | } | ||
| 2791 | /* Walk the ->next chain for the child. */ | ||
| 2792 | child = item->child; | ||
| 2793 | while (child != NULL) | ||
| 2794 | { | ||
| 2795 | if(depth >= CJSON_CIRCULAR_LIMIT) { | ||
| 2796 | goto fail; | ||
| 2797 | } | ||
| 2798 | newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */ | ||
| 2799 | if (!newchild) | ||
| 2800 | { | ||
| 2801 | goto fail; | ||
| 2802 | } | ||
| 2803 | if (next != NULL) | ||
| 2804 | { | ||
| 2805 | /* If newitem->child already set, then crosswire ->prev and ->next and move on */ | ||
| 2806 | next->next = newchild; | ||
| 2807 | newchild->prev = next; | ||
| 2808 | next = newchild; | ||
| 2809 | } | ||
| 2810 | else | ||
| 2811 | { | ||
| 2812 | /* Set newitem->child and move to it */ | ||
| 2813 | newitem->child = newchild; | ||
| 2814 | next = newchild; | ||
| 2815 | } | ||
| 2816 | child = child->next; | ||
| 2817 | } | ||
| 2818 | if (newitem && newitem->child) | ||
| 2819 | { | ||
| 2820 | newitem->child->prev = newchild; | ||
| 2821 | } | ||
| 2822 | |||
| 2823 | return newitem; | ||
| 2824 | |||
| 2825 | fail: | ||
| 2826 | if (newitem != NULL) | ||
| 2827 | { | ||
| 2828 | cJSON_Delete(newitem); | ||
| 2829 | } | ||
| 2830 | |||
| 2831 | return NULL; | ||
| 2832 | } | ||
| 2833 | |||
| 2834 | static void skip_oneline_comment(char **input) | ||
| 2835 | { | ||
| 2836 | *input += static_strlen("//"); | ||
| 2837 | |||
| 2838 | for (; (*input)[0] != '\0'; ++(*input)) | ||
| 2839 | { | ||
| 2840 | if ((*input)[0] == '\n') { | ||
| 2841 | *input += static_strlen("\n"); | ||
| 2842 | return; | ||
| 2843 | } | ||
| 2844 | } | ||
| 2845 | } | ||
| 2846 | |||
| 2847 | static void skip_multiline_comment(char **input) | ||
| 2848 | { | ||
| 2849 | *input += static_strlen("/*"); | ||
| 2850 | |||
| 2851 | for (; (*input)[0] != '\0'; ++(*input)) | ||
| 2852 | { | ||
| 2853 | if (((*input)[0] == '*') && ((*input)[1] == '/')) | ||
| 2854 | { | ||
| 2855 | *input += static_strlen("*/"); | ||
| 2856 | return; | ||
| 2857 | } | ||
| 2858 | } | ||
| 2859 | } | ||
| 2860 | |||
| 2861 | static void minify_string(char **input, char **output) { | ||
| 2862 | (*output)[0] = (*input)[0]; | ||
| 2863 | *input += static_strlen("\""); | ||
| 2864 | *output += static_strlen("\""); | ||
| 2865 | |||
| 2866 | |||
| 2867 | for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { | ||
| 2868 | (*output)[0] = (*input)[0]; | ||
| 2869 | |||
| 2870 | if ((*input)[0] == '\"') { | ||
| 2871 | (*output)[0] = '\"'; | ||
| 2872 | *input += static_strlen("\""); | ||
| 2873 | *output += static_strlen("\""); | ||
| 2874 | return; | ||
| 2875 | } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { | ||
| 2876 | (*output)[1] = (*input)[1]; | ||
| 2877 | *input += static_strlen("\""); | ||
| 2878 | *output += static_strlen("\""); | ||
| 2879 | } | ||
| 2880 | } | ||
| 2881 | } | ||
| 2882 | |||
| 2883 | CJSON_PUBLIC(void) cJSON_Minify(char *json) | ||
| 2884 | { | ||
| 2885 | char *into = json; | ||
| 2886 | |||
| 2887 | if (json == NULL) | ||
| 2888 | { | ||
| 2889 | return; | ||
| 2890 | } | ||
| 2891 | |||
| 2892 | while (json[0] != '\0') | ||
| 2893 | { | ||
| 2894 | switch (json[0]) | ||
| 2895 | { | ||
| 2896 | case ' ': | ||
| 2897 | case '\t': | ||
| 2898 | case '\r': | ||
| 2899 | case '\n': | ||
| 2900 | json++; | ||
| 2901 | break; | ||
| 2902 | |||
| 2903 | case '/': | ||
| 2904 | if (json[1] == '/') | ||
| 2905 | { | ||
| 2906 | skip_oneline_comment(&json); | ||
| 2907 | } | ||
| 2908 | else if (json[1] == '*') | ||
| 2909 | { | ||
| 2910 | skip_multiline_comment(&json); | ||
| 2911 | } else { | ||
| 2912 | json++; | ||
| 2913 | } | ||
| 2914 | break; | ||
| 2915 | |||
| 2916 | case '\"': | ||
| 2917 | minify_string(&json, (char**)&into); | ||
| 2918 | break; | ||
| 2919 | |||
| 2920 | default: | ||
| 2921 | into[0] = json[0]; | ||
| 2922 | json++; | ||
| 2923 | into++; | ||
| 2924 | } | ||
| 2925 | } | ||
| 2926 | |||
| 2927 | /* and null-terminate. */ | ||
| 2928 | *into = '\0'; | ||
| 2929 | } | ||
| 2930 | |||
| 2931 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) | ||
| 2932 | { | ||
| 2933 | if (item == NULL) | ||
| 2934 | { | ||
| 2935 | return false; | ||
| 2936 | } | ||
| 2937 | |||
| 2938 | return (item->type & 0xFF) == cJSON_Invalid; | ||
| 2939 | } | ||
| 2940 | |||
| 2941 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) | ||
| 2942 | { | ||
| 2943 | if (item == NULL) | ||
| 2944 | { | ||
| 2945 | return false; | ||
| 2946 | } | ||
| 2947 | |||
| 2948 | return (item->type & 0xFF) == cJSON_False; | ||
| 2949 | } | ||
| 2950 | |||
| 2951 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) | ||
| 2952 | { | ||
| 2953 | if (item == NULL) | ||
| 2954 | { | ||
| 2955 | return false; | ||
| 2956 | } | ||
| 2957 | |||
| 2958 | return (item->type & 0xff) == cJSON_True; | ||
| 2959 | } | ||
| 2960 | |||
| 2961 | |||
| 2962 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) | ||
| 2963 | { | ||
| 2964 | if (item == NULL) | ||
| 2965 | { | ||
| 2966 | return false; | ||
| 2967 | } | ||
| 2968 | |||
| 2969 | return (item->type & (cJSON_True | cJSON_False)) != 0; | ||
| 2970 | } | ||
| 2971 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) | ||
| 2972 | { | ||
| 2973 | if (item == NULL) | ||
| 2974 | { | ||
| 2975 | return false; | ||
| 2976 | } | ||
| 2977 | |||
| 2978 | return (item->type & 0xFF) == cJSON_NULL; | ||
| 2979 | } | ||
| 2980 | |||
| 2981 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) | ||
| 2982 | { | ||
| 2983 | if (item == NULL) | ||
| 2984 | { | ||
| 2985 | return false; | ||
| 2986 | } | ||
| 2987 | |||
| 2988 | return (item->type & 0xFF) == cJSON_Number; | ||
| 2989 | } | ||
| 2990 | |||
| 2991 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) | ||
| 2992 | { | ||
| 2993 | if (item == NULL) | ||
| 2994 | { | ||
| 2995 | return false; | ||
| 2996 | } | ||
| 2997 | |||
| 2998 | return (item->type & 0xFF) == cJSON_String; | ||
| 2999 | } | ||
| 3000 | |||
| 3001 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) | ||
| 3002 | { | ||
| 3003 | if (item == NULL) | ||
| 3004 | { | ||
| 3005 | return false; | ||
| 3006 | } | ||
| 3007 | |||
| 3008 | return (item->type & 0xFF) == cJSON_Array; | ||
| 3009 | } | ||
| 3010 | |||
| 3011 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) | ||
| 3012 | { | ||
| 3013 | if (item == NULL) | ||
| 3014 | { | ||
| 3015 | return false; | ||
| 3016 | } | ||
| 3017 | |||
| 3018 | return (item->type & 0xFF) == cJSON_Object; | ||
| 3019 | } | ||
| 3020 | |||
| 3021 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) | ||
| 3022 | { | ||
| 3023 | if (item == NULL) | ||
| 3024 | { | ||
| 3025 | return false; | ||
| 3026 | } | ||
| 3027 | |||
| 3028 | return (item->type & 0xFF) == cJSON_Raw; | ||
| 3029 | } | ||
| 3030 | |||
| 3031 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) | ||
| 3032 | { | ||
| 3033 | if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) | ||
| 3034 | { | ||
| 3035 | return false; | ||
| 3036 | } | ||
| 3037 | |||
| 3038 | /* check if type is valid */ | ||
| 3039 | switch (a->type & 0xFF) | ||
| 3040 | { | ||
| 3041 | case cJSON_False: | ||
| 3042 | case cJSON_True: | ||
| 3043 | case cJSON_NULL: | ||
| 3044 | case cJSON_Number: | ||
| 3045 | case cJSON_String: | ||
| 3046 | case cJSON_Raw: | ||
| 3047 | case cJSON_Array: | ||
| 3048 | case cJSON_Object: | ||
| 3049 | break; | ||
| 3050 | |||
| 3051 | default: | ||
| 3052 | return false; | ||
| 3053 | } | ||
| 3054 | |||
| 3055 | /* identical objects are equal */ | ||
| 3056 | if (a == b) | ||
| 3057 | { | ||
| 3058 | return true; | ||
| 3059 | } | ||
| 3060 | |||
| 3061 | switch (a->type & 0xFF) | ||
| 3062 | { | ||
| 3063 | /* in these cases and equal type is enough */ | ||
| 3064 | case cJSON_False: | ||
| 3065 | case cJSON_True: | ||
| 3066 | case cJSON_NULL: | ||
| 3067 | return true; | ||
| 3068 | |||
| 3069 | case cJSON_Number: | ||
| 3070 | if (compare_double(a->valuedouble, b->valuedouble)) | ||
| 3071 | { | ||
| 3072 | return true; | ||
| 3073 | } | ||
| 3074 | return false; | ||
| 3075 | |||
| 3076 | case cJSON_String: | ||
| 3077 | case cJSON_Raw: | ||
| 3078 | if ((a->valuestring == NULL) || (b->valuestring == NULL)) | ||
| 3079 | { | ||
| 3080 | return false; | ||
| 3081 | } | ||
| 3082 | if (strcmp(a->valuestring, b->valuestring) == 0) | ||
| 3083 | { | ||
| 3084 | return true; | ||
| 3085 | } | ||
| 3086 | |||
| 3087 | return false; | ||
| 3088 | |||
| 3089 | case cJSON_Array: | ||
| 3090 | { | ||
| 3091 | cJSON *a_element = a->child; | ||
| 3092 | cJSON *b_element = b->child; | ||
| 3093 | |||
| 3094 | for (; (a_element != NULL) && (b_element != NULL);) | ||
| 3095 | { | ||
| 3096 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) | ||
| 3097 | { | ||
| 3098 | return false; | ||
| 3099 | } | ||
| 3100 | |||
| 3101 | a_element = a_element->next; | ||
| 3102 | b_element = b_element->next; | ||
| 3103 | } | ||
| 3104 | |||
| 3105 | /* one of the arrays is longer than the other */ | ||
| 3106 | if (a_element != b_element) { | ||
| 3107 | return false; | ||
| 3108 | } | ||
| 3109 | |||
| 3110 | return true; | ||
| 3111 | } | ||
| 3112 | |||
| 3113 | case cJSON_Object: | ||
| 3114 | { | ||
| 3115 | cJSON *a_element = NULL; | ||
| 3116 | cJSON *b_element = NULL; | ||
| 3117 | cJSON_ArrayForEach(a_element, a) | ||
| 3118 | { | ||
| 3119 | /* TODO This has O(n^2) runtime, which is horrible! */ | ||
| 3120 | b_element = get_object_item(b, a_element->string, case_sensitive); | ||
| 3121 | if (b_element == NULL) | ||
| 3122 | { | ||
| 3123 | return false; | ||
| 3124 | } | ||
| 3125 | |||
| 3126 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) | ||
| 3127 | { | ||
| 3128 | return false; | ||
| 3129 | } | ||
| 3130 | } | ||
| 3131 | |||
| 3132 | /* doing this twice, once on a and b to prevent true comparison if a subset of b | ||
| 3133 | * TODO: Do this the proper way, this is just a fix for now */ | ||
| 3134 | cJSON_ArrayForEach(b_element, b) | ||
| 3135 | { | ||
| 3136 | a_element = get_object_item(a, b_element->string, case_sensitive); | ||
| 3137 | if (a_element == NULL) | ||
| 3138 | { | ||
| 3139 | return false; | ||
| 3140 | } | ||
| 3141 | |||
| 3142 | if (!cJSON_Compare(b_element, a_element, case_sensitive)) | ||
| 3143 | { | ||
| 3144 | return false; | ||
| 3145 | } | ||
| 3146 | } | ||
| 3147 | |||
| 3148 | return true; | ||
| 3149 | } | ||
| 3150 | |||
| 3151 | default: | ||
| 3152 | return false; | ||
| 3153 | } | ||
| 3154 | } | ||
| 3155 | |||
| 3156 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size) | ||
| 3157 | { | ||
| 3158 | return global_hooks.allocate(size); | ||
| 3159 | } | ||
| 3160 | |||
| 3161 | CJSON_PUBLIC(void) cJSON_free(void *object) | ||
| 3162 | { | ||
| 3163 | global_hooks.deallocate(object); | ||
| 3164 | object = NULL; | ||
| 3165 | } | ||
diff --git a/lib/vendor/cJSON/cJSON.h b/lib/vendor/cJSON/cJSON.h new file mode 100644 index 00000000..37520bbc --- /dev/null +++ b/lib/vendor/cJSON/cJSON.h | |||
| @@ -0,0 +1,306 @@ | |||
| 1 | /* | ||
| 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors | ||
| 3 | |||
| 4 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 5 | of this software and associated documentation files (the "Software"), to deal | ||
| 6 | in the Software without restriction, including without limitation the rights | ||
| 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 8 | copies of the Software, and to permit persons to whom the Software is | ||
| 9 | furnished to do so, subject to the following conditions: | ||
| 10 | |||
| 11 | The above copyright notice and this permission notice shall be included in | ||
| 12 | all copies or substantial portions of the Software. | ||
| 13 | |||
| 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 20 | THE SOFTWARE. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef cJSON__h | ||
| 24 | #define cJSON__h | ||
| 25 | |||
| 26 | #ifdef __cplusplus | ||
| 27 | extern "C" | ||
| 28 | { | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) | ||
| 32 | #define __WINDOWS__ | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #ifdef __WINDOWS__ | ||
| 36 | |||
| 37 | /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: | ||
| 38 | |||
| 39 | CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols | ||
| 40 | CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) | ||
| 41 | CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol | ||
| 42 | |||
| 43 | For *nix builds that support visibility attribute, you can define similar behavior by | ||
| 44 | |||
| 45 | setting default visibility to hidden by adding | ||
| 46 | -fvisibility=hidden (for gcc) | ||
| 47 | or | ||
| 48 | -xldscope=hidden (for sun cc) | ||
| 49 | to CFLAGS | ||
| 50 | |||
| 51 | then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does | ||
| 52 | |||
| 53 | */ | ||
| 54 | |||
| 55 | #define CJSON_CDECL __cdecl | ||
| 56 | #define CJSON_STDCALL __stdcall | ||
| 57 | |||
| 58 | /* export symbols by default, this is necessary for copy pasting the C and header file */ | ||
| 59 | #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) | ||
| 60 | #define CJSON_EXPORT_SYMBOLS | ||
| 61 | #endif | ||
| 62 | |||
| 63 | #if defined(CJSON_HIDE_SYMBOLS) | ||
| 64 | #define CJSON_PUBLIC(type) type CJSON_STDCALL | ||
| 65 | #elif defined(CJSON_EXPORT_SYMBOLS) | ||
| 66 | #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL | ||
| 67 | #elif defined(CJSON_IMPORT_SYMBOLS) | ||
| 68 | #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL | ||
| 69 | #endif | ||
| 70 | #else /* !__WINDOWS__ */ | ||
| 71 | #define CJSON_CDECL | ||
| 72 | #define CJSON_STDCALL | ||
| 73 | |||
| 74 | #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) | ||
| 75 | #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type | ||
| 76 | #else | ||
| 77 | #define CJSON_PUBLIC(type) type | ||
| 78 | #endif | ||
| 79 | #endif | ||
| 80 | |||
| 81 | /* project version */ | ||
| 82 | #define CJSON_VERSION_MAJOR 1 | ||
| 83 | #define CJSON_VERSION_MINOR 7 | ||
| 84 | #define CJSON_VERSION_PATCH 18 | ||
| 85 | |||
| 86 | #include <stddef.h> | ||
| 87 | |||
| 88 | /* cJSON Types: */ | ||
| 89 | #define cJSON_Invalid (0) | ||
| 90 | #define cJSON_False (1 << 0) | ||
| 91 | #define cJSON_True (1 << 1) | ||
| 92 | #define cJSON_NULL (1 << 2) | ||
| 93 | #define cJSON_Number (1 << 3) | ||
| 94 | #define cJSON_String (1 << 4) | ||
| 95 | #define cJSON_Array (1 << 5) | ||
| 96 | #define cJSON_Object (1 << 6) | ||
| 97 | #define cJSON_Raw (1 << 7) /* raw json */ | ||
| 98 | |||
| 99 | #define cJSON_IsReference 256 | ||
| 100 | #define cJSON_StringIsConst 512 | ||
| 101 | |||
| 102 | /* The cJSON structure: */ | ||
| 103 | typedef struct cJSON | ||
| 104 | { | ||
| 105 | /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ | ||
| 106 | struct cJSON *next; | ||
| 107 | struct cJSON *prev; | ||
| 108 | /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ | ||
| 109 | struct cJSON *child; | ||
| 110 | |||
| 111 | /* The type of the item, as above. */ | ||
| 112 | int type; | ||
| 113 | |||
| 114 | /* The item's string, if type==cJSON_String and type == cJSON_Raw */ | ||
| 115 | char *valuestring; | ||
| 116 | /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ | ||
| 117 | int valueint; | ||
| 118 | /* The item's number, if type==cJSON_Number */ | ||
| 119 | double valuedouble; | ||
| 120 | |||
| 121 | /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ | ||
| 122 | char *string; | ||
| 123 | } cJSON; | ||
| 124 | |||
| 125 | typedef struct cJSON_Hooks | ||
| 126 | { | ||
| 127 | /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ | ||
| 128 | void *(CJSON_CDECL *malloc_fn)(size_t sz); | ||
| 129 | void (CJSON_CDECL *free_fn)(void *ptr); | ||
| 130 | } cJSON_Hooks; | ||
| 131 | |||
| 132 | typedef int cJSON_bool; | ||
| 133 | |||
| 134 | /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. | ||
| 135 | * This is to prevent stack overflows. */ | ||
| 136 | #ifndef CJSON_NESTING_LIMIT | ||
| 137 | #define CJSON_NESTING_LIMIT 1000 | ||
| 138 | #endif | ||
| 139 | |||
| 140 | /* Limits the length of circular references can be before cJSON rejects to parse them. | ||
| 141 | * This is to prevent stack overflows. */ | ||
| 142 | #ifndef CJSON_CIRCULAR_LIMIT | ||
| 143 | #define CJSON_CIRCULAR_LIMIT 10000 | ||
| 144 | #endif | ||
| 145 | |||
| 146 | /* returns the version of cJSON as a string */ | ||
| 147 | CJSON_PUBLIC(const char*) cJSON_Version(void); | ||
| 148 | |||
| 149 | /* Supply malloc, realloc and free functions to cJSON */ | ||
| 150 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); | ||
| 151 | |||
| 152 | /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ | ||
| 153 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ | ||
| 154 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); | ||
| 155 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); | ||
| 156 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ | ||
| 157 | /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ | ||
| 158 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); | ||
| 159 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); | ||
| 160 | |||
| 161 | /* Render a cJSON entity to text for transfer/storage. */ | ||
| 162 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); | ||
| 163 | /* Render a cJSON entity to text for transfer/storage without any formatting. */ | ||
| 164 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); | ||
| 165 | /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ | ||
| 166 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); | ||
| 167 | /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ | ||
| 168 | /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ | ||
| 169 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); | ||
| 170 | /* Delete a cJSON entity and all subentities. */ | ||
| 171 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); | ||
| 172 | |||
| 173 | /* Returns the number of items in an array (or object). */ | ||
| 174 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); | ||
| 175 | /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ | ||
| 176 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); | ||
| 177 | /* Get item "string" from object. Case insensitive. */ | ||
| 178 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); | ||
| 179 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); | ||
| 180 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); | ||
| 181 | /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ | ||
| 182 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); | ||
| 183 | |||
| 184 | /* Check item type and return its value */ | ||
| 185 | CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); | ||
| 186 | CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); | ||
| 187 | |||
| 188 | /* These functions check the type of an item */ | ||
| 189 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); | ||
| 190 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); | ||
| 191 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); | ||
| 192 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); | ||
| 193 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); | ||
| 194 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); | ||
| 195 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); | ||
| 196 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); | ||
| 197 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); | ||
| 198 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); | ||
| 199 | |||
| 200 | /* These calls create a cJSON item of the appropriate type. */ | ||
| 201 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); | ||
| 202 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); | ||
| 203 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); | ||
| 204 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); | ||
| 205 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); | ||
| 206 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); | ||
| 207 | /* raw json */ | ||
| 208 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); | ||
| 209 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); | ||
| 210 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); | ||
| 211 | |||
| 212 | /* Create a string where valuestring references a string so | ||
| 213 | * it will not be freed by cJSON_Delete */ | ||
| 214 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); | ||
| 215 | /* Create an object/array that only references it's elements so | ||
| 216 | * they will not be freed by cJSON_Delete */ | ||
| 217 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); | ||
| 218 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); | ||
| 219 | |||
| 220 | /* These utilities create an Array of count items. | ||
| 221 | * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ | ||
| 222 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); | ||
| 223 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); | ||
| 224 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); | ||
| 225 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); | ||
| 226 | |||
| 227 | /* Append item to the specified array/object. */ | ||
| 228 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); | ||
| 229 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); | ||
| 230 | /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. | ||
| 231 | * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before | ||
| 232 | * writing to `item->string` */ | ||
| 233 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); | ||
| 234 | /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ | ||
| 235 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); | ||
| 236 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); | ||
| 237 | |||
| 238 | /* Remove/Detach items from Arrays/Objects. */ | ||
| 239 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); | ||
| 240 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); | ||
| 241 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); | ||
| 242 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); | ||
| 243 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); | ||
| 244 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); | ||
| 245 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); | ||
| 246 | |||
| 247 | /* Update array items. */ | ||
| 248 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ | ||
| 249 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); | ||
| 250 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); | ||
| 251 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); | ||
| 252 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); | ||
| 253 | |||
| 254 | /* Duplicate a cJSON item */ | ||
| 255 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); | ||
| 256 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will | ||
| 257 | * need to be released. With recurse!=0, it will duplicate any children connected to the item. | ||
| 258 | * The item->next and ->prev pointers are always zero on return from Duplicate. */ | ||
| 259 | /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. | ||
| 260 | * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ | ||
| 261 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); | ||
| 262 | |||
| 263 | /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. | ||
| 264 | * The input pointer json cannot point to a read-only address area, such as a string constant, | ||
| 265 | * but should point to a readable and writable address area. */ | ||
| 266 | CJSON_PUBLIC(void) cJSON_Minify(char *json); | ||
| 267 | |||
| 268 | /* Helper functions for creating and adding items to an object at the same time. | ||
| 269 | * They return the added item or NULL on failure. */ | ||
| 270 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); | ||
| 271 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); | ||
| 272 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); | ||
| 273 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); | ||
| 274 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); | ||
| 275 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); | ||
| 276 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); | ||
| 277 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); | ||
| 278 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); | ||
| 279 | |||
| 280 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ | ||
| 281 | #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) | ||
| 282 | /* helper for the cJSON_SetNumberValue macro */ | ||
| 283 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); | ||
| 284 | #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) | ||
| 285 | /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ | ||
| 286 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); | ||
| 287 | |||
| 288 | /* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ | ||
| 289 | #define cJSON_SetBoolValue(object, boolValue) ( \ | ||
| 290 | (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ | ||
| 291 | (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ | ||
| 292 | cJSON_Invalid\ | ||
| 293 | ) | ||
| 294 | |||
| 295 | /* Macro for iterating over an array or object */ | ||
| 296 | #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) | ||
| 297 | |||
| 298 | /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ | ||
| 299 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size); | ||
| 300 | CJSON_PUBLIC(void) cJSON_free(void *object); | ||
| 301 | |||
| 302 | #ifdef __cplusplus | ||
| 303 | } | ||
| 304 | #endif | ||
| 305 | |||
| 306 | #endif | ||
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 | ||
| 25 | EXTRA_PROGRAMS = pst3 | 25 | EXTRA_PROGRAMS = pst3 |
| 26 | 26 | ||
| 27 | EXTRA_DIST = t pst3.c | 27 | EXTRA_DIST = t pst3.c \ |
| 28 | check_icmp.d \ | ||
| 29 | check_dhcp.d | ||
| 28 | 30 | ||
| 29 | BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a | 31 | BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a |
| 30 | NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) | 32 | NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) |
| @@ -82,6 +84,7 @@ install-exec-local: $(noinst_PROGRAMS) | |||
| 82 | # the actual targets | 84 | # the actual targets |
| 83 | check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) | 85 | check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) |
| 84 | check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) | 86 | check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) |
| 87 | check_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 |
| 87 | pst3_CFLAGS = @PST3CFLAGS@ | 90 | pst3_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! |
| 90 | pst3_CPPFLAGS = | 93 | pst3_CPPFLAGS = |
| 91 | 94 | ||
| 92 | check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) | 95 | check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) |
| 93 | check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) | 96 | check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) |
| 94 | 97 | ||
| 95 | clean-local: | 98 | clean-local: |
diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c index 6802232e..9a96547f 100644 --- a/plugins-root/check_dhcp.c +++ b/plugins-root/check_dhcp.c | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) | 6 | * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) |
| 7 | * Copyright (c) 2001-2023 Monitoring Plugins Development Team | 7 | * Copyright (c) 2001-2025 Monitoring Plugins Development Team |
| 8 | * | 8 | * |
| 9 | * Description: | 9 | * Description: |
| 10 | * | 10 | * |
| @@ -34,13 +34,17 @@ | |||
| 34 | *****************************************************************************/ | 34 | *****************************************************************************/ |
| 35 | 35 | ||
| 36 | const char *progname = "check_dhcp"; | 36 | const char *progname = "check_dhcp"; |
| 37 | const char *copyright = "2001-2024"; | 37 | const char *copyright = "2001-2025"; |
| 38 | const char *email = "devel@monitoring-plugins.org"; | 38 | const char *email = "devel@monitoring-plugins.org"; |
| 39 | 39 | ||
| 40 | #include "common.h" | 40 | #include "../plugins/common.h" |
| 41 | #include "netutils.h" | 41 | #include "../plugins/utils.h" |
| 42 | #include "utils.h" | 42 | #include "./check_dhcp.d/config.h" |
| 43 | #include "../lib/output.h" | ||
| 44 | #include "../lib/utils_base.h" | ||
| 43 | 45 | ||
| 46 | #include "states.h" | ||
| 47 | #include <stdint.h> | ||
| 44 | #include <ctype.h> | 48 | #include <ctype.h> |
| 45 | #include <stdio.h> | 49 | #include <stdio.h> |
| 46 | #include <stdlib.h> | 50 | #include <stdlib.h> |
| @@ -111,8 +115,9 @@ static long mac_addr_dlpi(const char *, int, u_char *); | |||
| 111 | 115 | ||
| 112 | /**** Common definitions ****/ | 116 | /**** Common definitions ****/ |
| 113 | 117 | ||
| 114 | #define OK 0 | 118 | #define OK 0 |
| 115 | #define ERROR -1 | 119 | #define ERROR -1 |
| 120 | #define MAC_ADDR_LEN 6 | ||
| 116 | 121 | ||
| 117 | /**** DHCP definitions ****/ | 122 | /**** DHCP definitions ****/ |
| 118 | 123 | ||
| @@ -122,17 +127,17 @@ static long mac_addr_dlpi(const char *, int, u_char *); | |||
| 122 | #define MAX_DHCP_OPTIONS_LENGTH 312 | 127 | #define MAX_DHCP_OPTIONS_LENGTH 312 |
| 123 | 128 | ||
| 124 | typedef struct dhcp_packet_struct { | 129 | typedef struct dhcp_packet_struct { |
| 125 | uint8_t op; /* packet type */ | 130 | uint8_t op; /* packet type */ |
| 126 | uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ | 131 | uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ |
| 127 | uint8_t hlen; /* length of hardware address (of this machine) */ | 132 | uint8_t hlen; /* length of hardware address (of this machine) */ |
| 128 | uint8_t hops; /* hops */ | 133 | uint8_t hops; /* hops */ |
| 129 | uint32_t xid; /* random transaction id number - chosen by this machine */ | 134 | uint32_t xid; /* random transaction id number - chosen by this machine */ |
| 130 | uint16_t secs; /* seconds used in timing */ | 135 | uint16_t secs; /* seconds used in timing */ |
| 131 | uint16_t flags; /* flags */ | 136 | uint16_t flags; /* flags */ |
| 132 | struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ | 137 | struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ |
| 133 | struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ | 138 | struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ |
| 134 | struct in_addr siaddr; /* IP address of next server */ | 139 | struct in_addr siaddr; /* IP address of next server */ |
| 135 | struct in_addr giaddr; /* IP address of DHCP relay */ | 140 | struct in_addr giaddr; /* IP address of DHCP relay */ |
| 136 | unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */ | 141 | unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */ |
| 137 | char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */ | 142 | char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */ |
| 138 | char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */ | 143 | char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */ |
| @@ -149,12 +154,6 @@ typedef struct dhcp_offer_struct { | |||
| 149 | struct dhcp_offer_struct *next; | 154 | struct dhcp_offer_struct *next; |
| 150 | } dhcp_offer; | 155 | } dhcp_offer; |
| 151 | 156 | ||
| 152 | typedef struct requested_server_struct { | ||
| 153 | struct in_addr server_address; | ||
| 154 | bool answered; | ||
| 155 | struct requested_server_struct *next; | ||
| 156 | } requested_server; | ||
| 157 | |||
| 158 | #define BOOTREQUEST 1 | 157 | #define BOOTREQUEST 1 |
| 159 | #define BOOTREPLY 2 | 158 | #define BOOTREPLY 2 |
| 160 | 159 | ||
| @@ -186,65 +185,69 @@ typedef struct requested_server_struct { | |||
| 186 | #define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ | 185 | #define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ |
| 187 | #define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ | 186 | #define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ |
| 188 | 187 | ||
| 189 | static bool unicast = false; /* unicast mode: mimic a DHCP relay */ | ||
| 190 | static bool exclusive = false; /* exclusive mode aka "rogue DHCP server detection" */ | ||
| 191 | static struct in_addr my_ip; /* our address (required for relay) */ | ||
| 192 | static struct in_addr dhcp_ip; /* server to query (if in unicast mode) */ | ||
| 193 | static unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = ""; | ||
| 194 | static unsigned char *user_specified_mac = NULL; | ||
| 195 | |||
| 196 | static char network_interface_name[IFNAMSIZ] = "eth0"; | ||
| 197 | |||
| 198 | static uint32_t packet_xid = 0; | ||
| 199 | |||
| 200 | static uint32_t dhcp_lease_time = 0; | ||
| 201 | static uint32_t dhcp_renewal_time = 0; | ||
| 202 | static uint32_t dhcp_rebinding_time = 0; | ||
| 203 | |||
| 204 | static int dhcpoffer_timeout = 2; | ||
| 205 | |||
| 206 | static dhcp_offer *dhcp_offer_list = NULL; | ||
| 207 | static requested_server *requested_server_list = NULL; | ||
| 208 | |||
| 209 | static int valid_responses = 0; /* number of valid DHCPOFFERs we received */ | ||
| 210 | static int requested_servers = 0; | ||
| 211 | static int requested_responses = 0; | ||
| 212 | |||
| 213 | static bool request_specific_address = false; | ||
| 214 | static bool received_requested_address = false; | ||
| 215 | static int verbose = 0; | 188 | static int verbose = 0; |
| 216 | static struct in_addr requested_address; | ||
| 217 | 189 | ||
| 218 | static int process_arguments(int, char **); | 190 | typedef struct process_arguments_wrapper { |
| 219 | static int call_getopt(int, char **); | 191 | int error; |
| 220 | static int validate_arguments(int); | 192 | check_dhcp_config config; |
| 193 | } process_arguments_wrapper; | ||
| 194 | |||
| 195 | static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 221 | void print_usage(void); | 196 | void print_usage(void); |
| 222 | static void print_help(void); | 197 | static void print_help(void); |
| 223 | 198 | ||
| 224 | static void resolve_host(const char *in, struct in_addr *out); | 199 | static void resolve_host(const char * /*in*/, struct in_addr * /*out*/); |
| 225 | static unsigned char *mac_aton(const char *); | 200 | static unsigned char *mac_aton(const char * /*string*/); |
| 226 | static void print_hardware_address(const unsigned char *); | 201 | static void print_hardware_address(const unsigned char * /*address*/); |
| 227 | static int get_hardware_address(int, char *); | 202 | static int get_hardware_address(int /*sock*/, char * /*interface_name*/, |
| 228 | static int get_ip_address(int, char *); | 203 | unsigned char *client_hardware_address); |
| 229 | 204 | ||
| 230 | static int send_dhcp_discover(int); | 205 | typedef struct get_ip_address_wrapper { |
| 231 | static int get_dhcp_offer(int); | 206 | int error; |
| 232 | 207 | struct in_addr my_ip; | |
| 233 | static int get_results(void); | 208 | } get_ip_address_wrapper; |
| 234 | 209 | static get_ip_address_wrapper get_ip_address(int /*sock*/, char * /*interface_name*/); | |
| 235 | static int add_dhcp_offer(struct in_addr, dhcp_packet *); | 210 | |
| 236 | static int free_dhcp_offer_list(void); | 211 | typedef struct send_dhcp_discover_wrapper { |
| 237 | static int free_requested_server_list(void); | 212 | int error; |
| 238 | 213 | uint32_t packet_xid; | |
| 239 | static int create_dhcp_socket(void); | 214 | } send_dhcp_discover_wrapper; |
| 240 | static int close_dhcp_socket(int); | 215 | static send_dhcp_discover_wrapper |
| 241 | static int send_dhcp_packet(void *, int, int, struct sockaddr_in *); | 216 | send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip, |
| 242 | static int receive_dhcp_packet(void *, int, int, int, struct sockaddr_in *); | 217 | struct in_addr requested_address, bool request_specific_address, |
| 218 | struct in_addr my_ip, unsigned char *client_hardware_address); | ||
| 219 | typedef struct get_dhcp_offer_wrapper { | ||
| 220 | int error; | ||
| 221 | int valid_responses; | ||
| 222 | dhcp_offer *dhcp_offer_list; | ||
| 223 | } get_dhcp_offer_wrapper; | ||
| 224 | static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout, | ||
| 225 | uint32_t packet_xid, dhcp_offer *dhcp_offer_list, | ||
| 226 | const unsigned char *client_hardware_address); | ||
| 227 | |||
| 228 | static mp_subcheck get_results(bool exclusive, int requested_servers, | ||
| 229 | struct in_addr requested_address, bool request_specific_address, | ||
| 230 | requested_server *requested_server_list, int valid_responses, | ||
| 231 | dhcp_offer *dhcp_offer_list); | ||
| 232 | |||
| 233 | typedef struct add_dhcp_offer_wrapper { | ||
| 234 | int error; | ||
| 235 | dhcp_offer *dhcp_offer_list; | ||
| 236 | } add_dhcp_offer_wrapper; | ||
| 237 | static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/, | ||
| 238 | dhcp_packet * /*offer_packet*/, | ||
| 239 | dhcp_offer *dhcp_offer_list); | ||
| 240 | static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list); | ||
| 241 | static int free_requested_server_list(requested_server *requested_server_list); | ||
| 242 | |||
| 243 | static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name); | ||
| 244 | static int close_dhcp_socket(int /*sock*/); | ||
| 245 | static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, | ||
| 246 | struct sockaddr_in * /*dest*/); | ||
| 247 | static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, | ||
| 248 | int /*timeout*/, struct sockaddr_in * /*address*/); | ||
| 243 | 249 | ||
| 244 | int main(int argc, char **argv) { | 250 | int main(int argc, char **argv) { |
| 245 | int dhcp_socket; | ||
| 246 | int result = STATE_UNKNOWN; | ||
| 247 | |||
| 248 | setlocale(LC_ALL, ""); | 251 | setlocale(LC_ALL, ""); |
| 249 | bindtextdomain(PACKAGE, LOCALEDIR); | 252 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 250 | textdomain(PACKAGE); | 253 | textdomain(PACKAGE); |
| @@ -252,43 +255,84 @@ int main(int argc, char **argv) { | |||
| 252 | /* Parse extra opts if any */ | 255 | /* Parse extra opts if any */ |
| 253 | argv = np_extra_opts(&argc, argv, progname); | 256 | argv = np_extra_opts(&argc, argv, progname); |
| 254 | 257 | ||
| 255 | if (process_arguments(argc, argv) != OK) { | 258 | process_arguments_wrapper tmp = process_arguments(argc, argv); |
| 259 | |||
| 260 | if (tmp.error != OK) { | ||
| 256 | usage4(_("Could not parse arguments")); | 261 | usage4(_("Could not parse arguments")); |
| 257 | } | 262 | } |
| 258 | 263 | ||
| 264 | check_dhcp_config config = tmp.config; | ||
| 265 | if (config.output_format_is_set) { | ||
| 266 | mp_set_format(config.output_format); | ||
| 267 | } | ||
| 268 | |||
| 259 | /* create socket for DHCP communications */ | 269 | /* create socket for DHCP communications */ |
| 260 | dhcp_socket = create_dhcp_socket(); | 270 | int dhcp_socket = create_dhcp_socket(config.unicast_mode, config.network_interface_name); |
| 261 | 271 | ||
| 262 | /* get hardware address of client machine */ | 272 | /* get hardware address of client machine */ |
| 263 | if (user_specified_mac != NULL) | 273 | unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = ""; |
| 264 | memcpy(client_hardware_address, user_specified_mac, 6); | 274 | if (config.user_specified_mac != NULL) { |
| 265 | else | 275 | memcpy(client_hardware_address, config.user_specified_mac, MAC_ADDR_LEN); |
| 266 | get_hardware_address(dhcp_socket, network_interface_name); | 276 | } else { |
| 277 | get_hardware_address(dhcp_socket, config.network_interface_name, client_hardware_address); | ||
| 278 | } | ||
| 267 | 279 | ||
| 268 | if (unicast) /* get IP address of client machine */ | 280 | struct in_addr my_ip = {0}; |
| 269 | get_ip_address(dhcp_socket, network_interface_name); | 281 | |
| 282 | if (config.unicast_mode) { /* get IP address of client machine */ | ||
| 283 | get_ip_address_wrapper tmp_get_ip = | ||
| 284 | get_ip_address(dhcp_socket, config.network_interface_name); | ||
| 285 | if (tmp_get_ip.error == OK) { | ||
| 286 | my_ip = tmp_get_ip.my_ip; | ||
| 287 | } else { | ||
| 288 | // TODO failed to get own IP | ||
| 289 | die(STATE_UNKNOWN, "Failed to retrieve my own IP address in unicast mode"); | ||
| 290 | } | ||
| 291 | } | ||
| 270 | 292 | ||
| 271 | /* send DHCPDISCOVER packet */ | 293 | /* send DHCPDISCOVER packet */ |
| 272 | send_dhcp_discover(dhcp_socket); | 294 | send_dhcp_discover_wrapper disco_res = send_dhcp_discover( |
| 295 | dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address, | ||
| 296 | config.request_specific_address, my_ip, client_hardware_address); | ||
| 297 | |||
| 298 | if (disco_res.error != OK) { | ||
| 299 | // DO something? | ||
| 300 | die(STATE_UNKNOWN, "Failed to send DHCP discover"); | ||
| 301 | } | ||
| 273 | 302 | ||
| 274 | /* wait for a DHCPOFFER packet */ | 303 | /* wait for a DHCPOFFER packet */ |
| 275 | get_dhcp_offer(dhcp_socket); | 304 | get_dhcp_offer_wrapper offer_res = get_dhcp_offer( |
| 305 | dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address); | ||
| 306 | |||
| 307 | int valid_responses = 0; | ||
| 308 | dhcp_offer *dhcp_offer_list = NULL; | ||
| 309 | if (offer_res.error == OK) { | ||
| 310 | valid_responses = offer_res.valid_responses; | ||
| 311 | dhcp_offer_list = offer_res.dhcp_offer_list; | ||
| 312 | } else { | ||
| 313 | die(STATE_UNKNOWN, "Failed to get DHCP offers"); | ||
| 314 | } | ||
| 276 | 315 | ||
| 277 | /* close socket we created */ | 316 | /* close socket we created */ |
| 278 | close_dhcp_socket(dhcp_socket); | 317 | close_dhcp_socket(dhcp_socket); |
| 279 | 318 | ||
| 280 | /* determine state/plugin output to return */ | 319 | mp_check overall = mp_check_init(); |
| 281 | result = get_results(); | ||
| 282 | 320 | ||
| 321 | /* determine state/plugin output to return */ | ||
| 322 | mp_subcheck sc_res = | ||
| 323 | get_results(config.exclusive_mode, config.num_of_requested_servers, | ||
| 324 | config.requested_address, config.request_specific_address, | ||
| 325 | config.requested_server_list, valid_responses, dhcp_offer_list); | ||
| 326 | mp_add_subcheck_to_check(&overall, sc_res); | ||
| 283 | /* free allocated memory */ | 327 | /* free allocated memory */ |
| 284 | free_dhcp_offer_list(); | 328 | free_dhcp_offer_list(dhcp_offer_list); |
| 285 | free_requested_server_list(); | 329 | free_requested_server_list(config.requested_server_list); |
| 286 | 330 | ||
| 287 | return result; | 331 | mp_exit(overall); |
| 288 | } | 332 | } |
| 289 | 333 | ||
| 290 | /* determines hardware address on client machine */ | 334 | /* determines hardware address on client machine */ |
| 291 | static int get_hardware_address(int sock, char *interface_name) { | 335 | int get_hardware_address(int sock, char *interface_name, unsigned char *client_hardware_address) { |
| 292 | 336 | ||
| 293 | #if defined(__linux__) | 337 | #if defined(__linux__) |
| 294 | struct ifreq ifr; | 338 | struct ifreq ifr; |
| @@ -302,7 +346,7 @@ static int get_hardware_address(int sock, char *interface_name) { | |||
| 302 | exit(STATE_UNKNOWN); | 346 | exit(STATE_UNKNOWN); |
| 303 | } | 347 | } |
| 304 | 348 | ||
| 305 | memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); | 349 | memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN); |
| 306 | 350 | ||
| 307 | #elif defined(__bsd__) | 351 | #elif defined(__bsd__) |
| 308 | /* King 2004 see ACKNOWLEDGEMENTS */ | 352 | /* King 2004 see ACKNOWLEDGEMENTS */ |
| @@ -326,17 +370,20 @@ static int get_hardware_address(int sock, char *interface_name) { | |||
| 326 | } | 370 | } |
| 327 | 371 | ||
| 328 | if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { | 372 | if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { |
| 329 | printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), interface_name, strerror(errno)); | 373 | printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), |
| 374 | interface_name, strerror(errno)); | ||
| 330 | exit(STATE_UNKNOWN); | 375 | exit(STATE_UNKNOWN); |
| 331 | } | 376 | } |
| 332 | 377 | ||
| 333 | if ((buf = malloc(len)) == NULL) { | 378 | if ((buf = malloc(len)) == NULL) { |
| 334 | printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), interface_name, strerror(errno)); | 379 | printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), |
| 380 | interface_name, strerror(errno)); | ||
| 335 | exit(4); | 381 | exit(4); |
| 336 | } | 382 | } |
| 337 | 383 | ||
| 338 | if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { | 384 | if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { |
| 339 | printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), interface_name, strerror(errno)); | 385 | printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), |
| 386 | interface_name, strerror(errno)); | ||
| 340 | exit(STATE_UNKNOWN); | 387 | exit(STATE_UNKNOWN); |
| 341 | } | 388 | } |
| 342 | 389 | ||
| @@ -358,20 +405,25 @@ static int get_hardware_address(int sock, char *interface_name) { | |||
| 358 | int i; | 405 | int i; |
| 359 | p = interface_name + strlen(interface_name) - 1; | 406 | p = interface_name + strlen(interface_name) - 1; |
| 360 | for (i = strlen(interface_name) - 1; i > 0; p--) { | 407 | for (i = strlen(interface_name) - 1; i > 0; p--) { |
| 361 | if (isalpha(*p)) | 408 | if (isalpha(*p)) { |
| 362 | break; | 409 | break; |
| 410 | } | ||
| 363 | } | 411 | } |
| 364 | p++; | 412 | p++; |
| 365 | if (p != interface_name) { | 413 | if (p != interface_name) { |
| 366 | unit = atoi(p); | 414 | unit = atoi(p); |
| 367 | strncat(dev, interface_name, 6); | 415 | strncat(dev, interface_name, 6); |
| 368 | } else { | 416 | } else { |
| 369 | printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n"), interface_name); | 417 | printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg " |
| 418 | "lnc0.\n"), | ||
| 419 | interface_name); | ||
| 370 | exit(STATE_UNKNOWN); | 420 | exit(STATE_UNKNOWN); |
| 371 | } | 421 | } |
| 372 | stat = mac_addr_dlpi(dev, unit, client_hardware_address); | 422 | stat = mac_addr_dlpi(dev, unit, client_hardware_address); |
| 373 | if (stat != 0) { | 423 | if (stat != 0) { |
| 374 | printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); | 424 | printf( |
| 425 | _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), | ||
| 426 | dev, unit); | ||
| 375 | exit(STATE_UNKNOWN); | 427 | exit(STATE_UNKNOWN); |
| 376 | } | 428 | } |
| 377 | 429 | ||
| @@ -383,7 +435,9 @@ static int get_hardware_address(int sock, char *interface_name) { | |||
| 383 | 435 | ||
| 384 | stat = mac_addr_dlpi(dev, unit, client_hardware_address); | 436 | stat = mac_addr_dlpi(dev, unit, client_hardware_address); |
| 385 | if (stat != 0) { | 437 | if (stat != 0) { |
| 386 | printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); | 438 | printf( |
| 439 | _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), | ||
| 440 | dev, unit); | ||
| 387 | exit(STATE_UNKNOWN); | 441 | exit(STATE_UNKNOWN); |
| 388 | } | 442 | } |
| 389 | /* Kompf 2000-2003 */ | 443 | /* Kompf 2000-2003 */ |
| @@ -393,14 +447,15 @@ static int get_hardware_address(int sock, char *interface_name) { | |||
| 393 | exit(STATE_UNKNOWN); | 447 | exit(STATE_UNKNOWN); |
| 394 | #endif | 448 | #endif |
| 395 | 449 | ||
| 396 | if (verbose) | 450 | if (verbose) { |
| 397 | print_hardware_address(client_hardware_address); | 451 | print_hardware_address(client_hardware_address); |
| 452 | } | ||
| 398 | 453 | ||
| 399 | return OK; | 454 | return OK; |
| 400 | } | 455 | } |
| 401 | 456 | ||
| 402 | /* determines IP address of the client interface */ | 457 | /* determines IP address of the client interface */ |
| 403 | static int get_ip_address(int sock, char *interface_name) { | 458 | get_ip_address_wrapper get_ip_address(int sock, char *interface_name) { |
| 404 | #if defined(SIOCGIFADDR) | 459 | #if defined(SIOCGIFADDR) |
| 405 | struct ifreq ifr; | 460 | struct ifreq ifr; |
| 406 | 461 | ||
| @@ -412,28 +467,30 @@ static int get_ip_address(int sock, char *interface_name) { | |||
| 412 | exit(STATE_UNKNOWN); | 467 | exit(STATE_UNKNOWN); |
| 413 | } | 468 | } |
| 414 | 469 | ||
| 415 | my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; | ||
| 416 | |||
| 417 | #else | 470 | #else |
| 418 | printf(_("Error: Cannot get interface IP address on this platform.\n")); | 471 | printf(_("Error: Cannot get interface IP address on this platform.\n")); |
| 419 | exit(STATE_UNKNOWN); | 472 | exit(STATE_UNKNOWN); |
| 420 | #endif | 473 | #endif |
| 421 | 474 | ||
| 422 | if (verbose) | 475 | get_ip_address_wrapper result = { |
| 423 | printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); | 476 | .error = OK, |
| 477 | .my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, | ||
| 478 | }; | ||
| 424 | 479 | ||
| 425 | return OK; | 480 | if (verbose) { |
| 481 | printf(_("Pretending to be relay client %s\n"), inet_ntoa(result.my_ip)); | ||
| 482 | } | ||
| 483 | |||
| 484 | return result; | ||
| 426 | } | 485 | } |
| 427 | 486 | ||
| 428 | /* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ | 487 | /* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ |
| 429 | static int send_dhcp_discover(int sock) { | 488 | static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip, |
| 430 | dhcp_packet discover_packet; | 489 | struct in_addr requested_address, |
| 431 | struct sockaddr_in sockaddr_broadcast; | 490 | bool request_specific_address, |
| 432 | unsigned short opts; | 491 | struct in_addr my_ip, |
| 433 | 492 | unsigned char *client_hardware_address) { | |
| 434 | /* clear the packet data structure */ | 493 | dhcp_packet discover_packet = {0}; |
| 435 | bzero(&discover_packet, sizeof(discover_packet)); | ||
| 436 | |||
| 437 | /* boot request flag (backward compatible with BOOTP servers) */ | 494 | /* boot request flag (backward compatible with BOOTP servers) */ |
| 438 | discover_packet.op = BOOTREQUEST; | 495 | discover_packet.op = BOOTREQUEST; |
| 439 | 496 | ||
| @@ -443,12 +500,15 @@ static int send_dhcp_discover(int sock) { | |||
| 443 | /* length of our hardware address */ | 500 | /* length of our hardware address */ |
| 444 | discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; | 501 | discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; |
| 445 | 502 | ||
| 503 | send_dhcp_discover_wrapper result = { | ||
| 504 | .error = OK, | ||
| 505 | }; | ||
| 446 | /* | 506 | /* |
| 447 | * transaction ID is supposed to be random. | 507 | * transaction ID is supposed to be random. |
| 448 | */ | 508 | */ |
| 449 | srand(time(NULL) ^ getpid()); | 509 | srand(time(NULL) ^ getpid()); |
| 450 | packet_xid = random(); | 510 | result.packet_xid = random(); |
| 451 | discover_packet.xid = htonl(packet_xid); | 511 | discover_packet.xid = htonl(result.packet_xid); |
| 452 | 512 | ||
| 453 | /*discover_packet.secs=htons(65535);*/ | 513 | /*discover_packet.secs=htons(65535);*/ |
| 454 | discover_packet.secs = 0xFF; | 514 | discover_packet.secs = 0xFF; |
| @@ -468,10 +528,11 @@ static int send_dhcp_discover(int sock) { | |||
| 468 | discover_packet.options[2] = '\x53'; | 528 | discover_packet.options[2] = '\x53'; |
| 469 | discover_packet.options[3] = '\x63'; | 529 | discover_packet.options[3] = '\x63'; |
| 470 | 530 | ||
| 471 | opts = 4; | 531 | unsigned short opts = 4; |
| 472 | /* DHCP message type is embedded in options field */ | 532 | /* DHCP message type is embedded in options field */ |
| 473 | discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ | 533 | discover_packet.options[opts++] = |
| 474 | discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ | 534 | DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ |
| 535 | discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ | ||
| 475 | discover_packet.options[opts++] = DHCPDISCOVER; | 536 | discover_packet.options[opts++] = DHCPDISCOVER; |
| 476 | 537 | ||
| 477 | /* the IP address we're requesting */ | 538 | /* the IP address we're requesting */ |
| @@ -484,21 +545,25 @@ static int send_dhcp_discover(int sock) { | |||
| 484 | discover_packet.options[opts++] = (char)DHCP_OPTION_END; | 545 | discover_packet.options[opts++] = (char)DHCP_OPTION_END; |
| 485 | 546 | ||
| 486 | /* unicast fields */ | 547 | /* unicast fields */ |
| 487 | if (unicast) | 548 | if (unicast) { |
| 488 | discover_packet.giaddr.s_addr = my_ip.s_addr; | 549 | discover_packet.giaddr.s_addr = my_ip.s_addr; |
| 550 | } | ||
| 489 | 551 | ||
| 490 | /* see RFC 1542, 4.1.1 */ | 552 | /* see RFC 1542, 4.1.1 */ |
| 491 | discover_packet.hops = unicast ? 1 : 0; | 553 | discover_packet.hops = unicast ? 1 : 0; |
| 492 | 554 | ||
| 493 | /* send the DHCPDISCOVER packet to broadcast address */ | 555 | /* send the DHCPDISCOVER packet to broadcast address */ |
| 494 | sockaddr_broadcast.sin_family = AF_INET; | 556 | struct sockaddr_in sockaddr_broadcast = { |
| 495 | sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); | 557 | .sin_family = AF_INET, |
| 496 | sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST; | 558 | .sin_port = htons(DHCP_SERVER_PORT), |
| 497 | bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); | 559 | .sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST, |
| 560 | }; | ||
| 498 | 561 | ||
| 499 | if (verbose) { | 562 | if (verbose) { |
| 500 | printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); | 563 | printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), |
| 501 | printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid), ntohl(discover_packet.xid)); | 564 | ntohs(sockaddr_broadcast.sin_port)); |
| 565 | printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid), | ||
| 566 | ntohl(discover_packet.xid)); | ||
| 502 | printf("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr)); | 567 | printf("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr)); |
| 503 | printf("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr)); | 568 | printf("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr)); |
| 504 | printf("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr)); | 569 | printf("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr)); |
| @@ -508,56 +573,58 @@ static int send_dhcp_discover(int sock) { | |||
| 508 | /* send the DHCPDISCOVER packet out */ | 573 | /* send the DHCPDISCOVER packet out */ |
| 509 | send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); | 574 | send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); |
| 510 | 575 | ||
| 511 | if (verbose) | 576 | if (verbose) { |
| 512 | printf("\n\n"); | 577 | printf("\n\n"); |
| 578 | } | ||
| 513 | 579 | ||
| 514 | return OK; | 580 | return result; |
| 515 | } | 581 | } |
| 516 | 582 | ||
| 517 | /* waits for a DHCPOFFER message from one or more DHCP servers */ | 583 | /* waits for a DHCPOFFER message from one or more DHCP servers */ |
| 518 | static int get_dhcp_offer(int sock) { | 584 | get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid, |
| 519 | dhcp_packet offer_packet; | 585 | dhcp_offer *dhcp_offer_list, |
| 520 | struct sockaddr_in source; | 586 | const unsigned char *client_hardware_address) { |
| 521 | struct sockaddr_in via; | ||
| 522 | int result = OK; | ||
| 523 | int responses = 0; | ||
| 524 | int x; | ||
| 525 | time_t start_time; | 587 | time_t start_time; |
| 526 | time_t current_time; | ||
| 527 | |||
| 528 | time(&start_time); | 588 | time(&start_time); |
| 529 | 589 | ||
| 590 | int result = OK; | ||
| 591 | int responses = 0; | ||
| 592 | int valid_responses = 0; | ||
| 530 | /* receive as many responses as we can */ | 593 | /* receive as many responses as we can */ |
| 531 | for (responses = 0, valid_responses = 0;;) { | 594 | for (;;) { |
| 532 | 595 | time_t current_time; | |
| 533 | time(¤t_time); | 596 | time(¤t_time); |
| 534 | if ((current_time - start_time) >= dhcpoffer_timeout) | 597 | if ((current_time - start_time) >= dhcpoffer_timeout) { |
| 535 | break; | 598 | break; |
| 599 | } | ||
| 536 | 600 | ||
| 537 | if (verbose) | 601 | if (verbose) { |
| 538 | printf("\n\n"); | 602 | printf("\n\n"); |
| 603 | } | ||
| 539 | 604 | ||
| 540 | bzero(&source, sizeof(source)); | 605 | struct sockaddr_in source = {0}; |
| 541 | bzero(&via, sizeof(via)); | 606 | dhcp_packet offer_packet = {0}; |
| 542 | bzero(&offer_packet, sizeof(offer_packet)); | ||
| 543 | 607 | ||
| 544 | result = OK; | 608 | result = OK; |
| 545 | result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); | 609 | result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, |
| 610 | &source); | ||
| 546 | 611 | ||
| 547 | if (result != OK) { | 612 | if (result != OK) { |
| 548 | if (verbose) | 613 | if (verbose) { |
| 549 | printf(_("Result=ERROR\n")); | 614 | printf(_("Result=ERROR\n")); |
| 615 | } | ||
| 550 | 616 | ||
| 551 | continue; | 617 | continue; |
| 552 | } else { | 618 | } |
| 553 | if (verbose) | 619 | if (verbose) { |
| 554 | printf(_("Result=OK\n")); | 620 | printf(_("Result=OK\n")); |
| 555 | |||
| 556 | responses++; | ||
| 557 | } | 621 | } |
| 558 | 622 | ||
| 623 | responses++; | ||
| 624 | |||
| 559 | /* The "source" is either a server or a relay. */ | 625 | /* The "source" is either a server or a relay. */ |
| 560 | /* Save a copy of "source" into "via" even if it's via itself */ | 626 | /* Save a copy of "source" into "via" even if it's via itself */ |
| 627 | struct sockaddr_in via = {0}; | ||
| 561 | memcpy(&via, &source, sizeof(source)); | 628 | memcpy(&via, &source, sizeof(source)); |
| 562 | 629 | ||
| 563 | if (verbose) { | 630 | if (verbose) { |
| @@ -568,30 +635,38 @@ static int get_dhcp_offer(int sock) { | |||
| 568 | 635 | ||
| 569 | /* check packet xid to see if its the same as the one we used in the discover packet */ | 636 | /* check packet xid to see if its the same as the one we used in the discover packet */ |
| 570 | if (ntohl(offer_packet.xid) != packet_xid) { | 637 | if (ntohl(offer_packet.xid) != packet_xid) { |
| 571 | if (verbose) | 638 | if (verbose) { |
| 572 | printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), packet_xid); | 639 | printf( |
| 640 | _("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), | ||
| 641 | ntohl(offer_packet.xid), packet_xid); | ||
| 642 | } | ||
| 573 | 643 | ||
| 574 | continue; | 644 | continue; |
| 575 | } | 645 | } |
| 576 | 646 | ||
| 577 | /* check hardware address */ | 647 | /* check hardware address */ |
| 578 | result = OK; | 648 | result = OK; |
| 579 | if (verbose) | 649 | if (verbose) { |
| 580 | printf("DHCPOFFER chaddr: "); | 650 | printf("DHCPOFFER chaddr: "); |
| 651 | } | ||
| 581 | 652 | ||
| 582 | for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { | 653 | for (int i = 0; i < ETHERNET_HARDWARE_ADDRESS_LENGTH; i++) { |
| 583 | if (verbose) | 654 | if (verbose) { |
| 584 | printf("%02X", (unsigned char)offer_packet.chaddr[x]); | 655 | printf("%02X", offer_packet.chaddr[i]); |
| 656 | } | ||
| 585 | 657 | ||
| 586 | if (offer_packet.chaddr[x] != client_hardware_address[x]) | 658 | if (offer_packet.chaddr[i] != client_hardware_address[i]) { |
| 587 | result = ERROR; | 659 | result = ERROR; |
| 660 | } | ||
| 588 | } | 661 | } |
| 589 | if (verbose) | 662 | if (verbose) { |
| 590 | printf("\n"); | 663 | printf("\n"); |
| 664 | } | ||
| 591 | 665 | ||
| 592 | if (result == ERROR) { | 666 | if (result == ERROR) { |
| 593 | if (verbose) | 667 | if (verbose) { |
| 594 | printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n")); | 668 | printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n")); |
| 669 | } | ||
| 595 | 670 | ||
| 596 | continue; | 671 | continue; |
| 597 | } | 672 | } |
| @@ -603,7 +678,13 @@ static int get_dhcp_offer(int sock) { | |||
| 603 | printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); | 678 | printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); |
| 604 | } | 679 | } |
| 605 | 680 | ||
| 606 | add_dhcp_offer(source.sin_addr, &offer_packet); | 681 | add_dhcp_offer_wrapper add_res = |
| 682 | add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list); | ||
| 683 | if (add_res.error != OK) { | ||
| 684 | // TODO | ||
| 685 | } else { | ||
| 686 | dhcp_offer_list = add_res.dhcp_offer_list; | ||
| 687 | } | ||
| 607 | 688 | ||
| 608 | valid_responses++; | 689 | valid_responses++; |
| 609 | } | 690 | } |
| @@ -613,104 +694,104 @@ static int get_dhcp_offer(int sock) { | |||
| 613 | printf(_("Valid responses for this machine: %d\n"), valid_responses); | 694 | printf(_("Valid responses for this machine: %d\n"), valid_responses); |
| 614 | } | 695 | } |
| 615 | 696 | ||
| 616 | return OK; | 697 | get_dhcp_offer_wrapper ret_val = { |
| 698 | .error = OK, | ||
| 699 | .valid_responses = valid_responses, | ||
| 700 | .dhcp_offer_list = dhcp_offer_list, | ||
| 701 | }; | ||
| 702 | return ret_val; | ||
| 617 | } | 703 | } |
| 618 | 704 | ||
| 619 | /* sends a DHCP packet */ | 705 | /* sends a DHCP packet */ |
| 620 | static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { | 706 | int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { |
| 621 | int result; | 707 | int result = |
| 622 | 708 | sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest)); | |
| 623 | result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest)); | ||
| 624 | 709 | ||
| 625 | if (verbose) | 710 | if (verbose) { |
| 626 | printf(_("send_dhcp_packet result: %d\n"), result); | 711 | printf(_("send_dhcp_packet result: %d\n"), result); |
| 712 | } | ||
| 627 | 713 | ||
| 628 | if (result < 0) | 714 | if (result < 0) { |
| 629 | return ERROR; | 715 | return ERROR; |
| 716 | } | ||
| 630 | 717 | ||
| 631 | return OK; | 718 | return OK; |
| 632 | } | 719 | } |
| 633 | 720 | ||
| 634 | /* receives a DHCP packet */ | 721 | /* receives a DHCP packet */ |
| 635 | static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { | 722 | int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, |
| 636 | struct timeval tv; | 723 | struct sockaddr_in *address) { |
| 637 | fd_set readfds; | ||
| 638 | fd_set oobfds; | ||
| 639 | int recv_result; | ||
| 640 | socklen_t address_size; | ||
| 641 | struct sockaddr_in source_address; | ||
| 642 | int nfound; | ||
| 643 | |||
| 644 | /* wait for data to arrive (up time timeout) */ | 724 | /* wait for data to arrive (up time timeout) */ |
| 645 | tv.tv_sec = timeout; | 725 | struct timeval timeout_val = { |
| 646 | tv.tv_usec = 0; | 726 | .tv_sec = timeout, |
| 727 | .tv_usec = 0, | ||
| 728 | }; | ||
| 729 | fd_set readfds; | ||
| 647 | FD_ZERO(&readfds); | 730 | FD_ZERO(&readfds); |
| 731 | fd_set oobfds; | ||
| 648 | FD_ZERO(&oobfds); | 732 | FD_ZERO(&oobfds); |
| 649 | FD_SET(sock, &readfds); | 733 | FD_SET(sock, &readfds); |
| 650 | FD_SET(sock, &oobfds); | 734 | FD_SET(sock, &oobfds); |
| 651 | nfound = select(sock + 1, &readfds, NULL, &oobfds, &tv); | 735 | int nfound = select(sock + 1, &readfds, NULL, &oobfds, &timeout_val); |
| 652 | 736 | ||
| 653 | /* make sure some data has arrived */ | 737 | /* make sure some data has arrived */ |
| 654 | if (!FD_ISSET(sock, &readfds)) { | 738 | if (!FD_ISSET(sock, &readfds)) { |
| 655 | if (verbose) | 739 | if (verbose) { |
| 656 | printf(_("No (more) data received (nfound: %d)\n"), nfound); | 740 | printf(_("No (more) data received (nfound: %d)\n"), nfound); |
| 741 | } | ||
| 657 | return ERROR; | 742 | return ERROR; |
| 658 | } | 743 | } |
| 659 | 744 | ||
| 660 | else { | 745 | struct sockaddr_in source_address = {0}; |
| 661 | bzero(&source_address, sizeof(source_address)); | 746 | socklen_t address_size = sizeof(source_address); |
| 662 | address_size = sizeof(source_address); | 747 | int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, |
| 663 | recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); | 748 | (struct sockaddr *)&source_address, &address_size); |
| 664 | if (verbose) | 749 | if (verbose) { |
| 665 | printf("recv_result: %d\n", recv_result); | 750 | printf("recv_result: %d\n", recv_result); |
| 666 | 751 | } | |
| 667 | if (recv_result == -1) { | ||
| 668 | if (verbose) { | ||
| 669 | printf(_("recvfrom() failed, ")); | ||
| 670 | printf("errno: (%d) -> %s\n", errno, strerror(errno)); | ||
| 671 | } | ||
| 672 | return ERROR; | ||
| 673 | } else { | ||
| 674 | if (verbose) { | ||
| 675 | printf(_("receive_dhcp_packet() result: %d\n"), recv_result); | ||
| 676 | printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr)); | ||
| 677 | } | ||
| 678 | 752 | ||
| 679 | memcpy(address, &source_address, sizeof(source_address)); | 753 | if (recv_result == -1) { |
| 680 | return OK; | 754 | if (verbose) { |
| 755 | printf(_("recvfrom() failed, ")); | ||
| 756 | printf("errno: (%d) -> %s\n", errno, strerror(errno)); | ||
| 681 | } | 757 | } |
| 758 | return ERROR; | ||
| 759 | } | ||
| 760 | if (verbose) { | ||
| 761 | printf(_("receive_dhcp_packet() result: %d\n"), recv_result); | ||
| 762 | printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr)); | ||
| 682 | } | 763 | } |
| 683 | 764 | ||
| 765 | memcpy(address, &source_address, sizeof(source_address)); | ||
| 684 | return OK; | 766 | return OK; |
| 685 | } | 767 | } |
| 686 | 768 | ||
| 687 | /* creates a socket for DHCP communication */ | 769 | /* creates a socket for DHCP communication */ |
| 688 | static int create_dhcp_socket(void) { | 770 | int create_dhcp_socket(bool unicast, char *network_interface_name) { |
| 689 | struct sockaddr_in myname; | ||
| 690 | struct ifreq interface; | ||
| 691 | int sock; | ||
| 692 | int flag = 1; | ||
| 693 | |||
| 694 | /* Set up the address we're going to bind to. */ | 771 | /* Set up the address we're going to bind to. */ |
| 695 | bzero(&myname, sizeof(myname)); | ||
| 696 | myname.sin_family = AF_INET; | ||
| 697 | /* listen to DHCP server port if we're in unicast mode */ | 772 | /* listen to DHCP server port if we're in unicast mode */ |
| 698 | myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT); | 773 | struct sockaddr_in myname = { |
| 699 | myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY; | 774 | .sin_family = AF_INET, |
| 700 | bzero(&myname.sin_zero, sizeof(myname.sin_zero)); | 775 | .sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT), |
| 776 | // TODO previously the next line was trying to use our own IP, we was not set | ||
| 777 | // until some point later, so it was removed. Recheck whether it is actually | ||
| 778 | // necessary/useful | ||
| 779 | .sin_addr.s_addr = INADDR_ANY, | ||
| 780 | }; | ||
| 701 | 781 | ||
| 702 | /* create a socket for DHCP communications */ | 782 | /* create a socket for DHCP communications */ |
| 703 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | 783 | int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| 704 | if (sock < 0) { | 784 | if (sock < 0) { |
| 705 | printf(_("Error: Could not create socket!\n")); | 785 | printf(_("Error: Could not create socket!\n")); |
| 706 | exit(STATE_UNKNOWN); | 786 | exit(STATE_UNKNOWN); |
| 707 | } | 787 | } |
| 708 | 788 | ||
| 709 | if (verbose) | 789 | if (verbose) { |
| 710 | printf("DHCP socket: %d\n", sock); | 790 | printf("DHCP socket: %d\n", sock); |
| 791 | } | ||
| 711 | 792 | ||
| 712 | /* set the reuse address flag so we don't get errors when restarting */ | 793 | /* set the reuse address flag so we don't get errors when restarting */ |
| 713 | flag = 1; | 794 | int flag = 1; |
| 714 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { | 795 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { |
| 715 | printf(_("Error: Could not set reuse address option on DHCP socket!\n")); | 796 | printf(_("Error: Could not set reuse address option on DHCP socket!\n")); |
| 716 | exit(STATE_UNKNOWN); | 797 | exit(STATE_UNKNOWN); |
| @@ -722,12 +803,14 @@ static int create_dhcp_socket(void) { | |||
| 722 | exit(STATE_UNKNOWN); | 803 | exit(STATE_UNKNOWN); |
| 723 | } | 804 | } |
| 724 | 805 | ||
| 806 | struct ifreq interface; | ||
| 725 | /* bind socket to interface */ | 807 | /* bind socket to interface */ |
| 726 | #if defined(__linux__) | 808 | #if defined(__linux__) |
| 727 | strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); | 809 | strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); |
| 728 | interface.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0'; | 810 | interface.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0'; |
| 729 | if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) { | 811 | if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) { |
| 730 | printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"), network_interface_name); | 812 | printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"), |
| 813 | network_interface_name); | ||
| 731 | exit(STATE_UNKNOWN); | 814 | exit(STATE_UNKNOWN); |
| 732 | } | 815 | } |
| 733 | 816 | ||
| @@ -738,7 +821,8 @@ static int create_dhcp_socket(void) { | |||
| 738 | 821 | ||
| 739 | /* bind the socket */ | 822 | /* bind the socket */ |
| 740 | if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) { | 823 | if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) { |
| 741 | printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"), DHCP_CLIENT_PORT); | 824 | printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"), |
| 825 | DHCP_CLIENT_PORT); | ||
| 742 | exit(STATE_UNKNOWN); | 826 | exit(STATE_UNKNOWN); |
| 743 | } | 827 | } |
| 744 | 828 | ||
| @@ -746,105 +830,121 @@ static int create_dhcp_socket(void) { | |||
| 746 | } | 830 | } |
| 747 | 831 | ||
| 748 | /* closes DHCP socket */ | 832 | /* closes DHCP socket */ |
| 749 | static int close_dhcp_socket(int sock) { | 833 | int close_dhcp_socket(int sock) { |
| 750 | |||
| 751 | close(sock); | 834 | close(sock); |
| 752 | |||
| 753 | return OK; | 835 | return OK; |
| 754 | } | 836 | } |
| 755 | 837 | ||
| 756 | /* adds a requested server address to list in memory */ | 838 | /* adds a requested server address to list in memory */ |
| 757 | static int add_requested_server(struct in_addr server_address) { | 839 | int add_requested_server(struct in_addr server_address, int *requested_servers, |
| 758 | requested_server *new_server; | 840 | requested_server **requested_server_list) { |
| 759 | 841 | requested_server *new_server = (requested_server *)malloc(sizeof(requested_server)); | |
| 760 | new_server = (requested_server *)malloc(sizeof(requested_server)); | 842 | if (new_server == NULL) { |
| 761 | if (new_server == NULL) | ||
| 762 | return ERROR; | 843 | return ERROR; |
| 844 | } | ||
| 763 | 845 | ||
| 764 | new_server->server_address = server_address; | 846 | new_server->server_address = server_address; |
| 765 | new_server->answered = false; | 847 | new_server->answered = false; |
| 766 | 848 | ||
| 767 | new_server->next = requested_server_list; | 849 | new_server->next = *requested_server_list; |
| 768 | requested_server_list = new_server; | 850 | *requested_server_list = new_server; |
| 769 | 851 | ||
| 770 | requested_servers++; | 852 | *requested_servers += 1; |
| 771 | 853 | ||
| 772 | if (verbose) | 854 | if (verbose) { |
| 773 | printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); | 855 | printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); |
| 856 | } | ||
| 774 | 857 | ||
| 775 | return OK; | 858 | return OK; |
| 776 | } | 859 | } |
| 777 | 860 | ||
| 778 | /* adds a DHCP OFFER to list in memory */ | 861 | /* adds a DHCP OFFER to list in memory */ |
| 779 | static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { | 862 | add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet, |
| 863 | dhcp_offer *dhcp_offer_list) { | ||
| 864 | if (offer_packet == NULL) { | ||
| 865 | add_dhcp_offer_wrapper tmp = { | ||
| 866 | .error = ERROR, | ||
| 867 | }; | ||
| 868 | return tmp; | ||
| 869 | } | ||
| 870 | |||
| 871 | uint32_t dhcp_lease_time = 0; | ||
| 872 | uint32_t dhcp_renewal_time = 0; | ||
| 873 | uint32_t dhcp_rebinding_time = 0; | ||
| 780 | dhcp_offer *new_offer; | 874 | dhcp_offer *new_offer; |
| 781 | int x; | ||
| 782 | unsigned option_type; | ||
| 783 | unsigned option_length; | ||
| 784 | struct in_addr serv_ident = {0}; | 875 | struct in_addr serv_ident = {0}; |
| 785 | |||
| 786 | if (offer_packet == NULL) | ||
| 787 | return ERROR; | ||
| 788 | |||
| 789 | /* process all DHCP options present in the packet */ | 876 | /* process all DHCP options present in the packet */ |
| 790 | for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { | 877 | for (int dchp_opt_idx = 4; dchp_opt_idx < MAX_DHCP_OPTIONS_LENGTH - 1;) { |
| 791 | 878 | ||
| 792 | if ((int)offer_packet->options[x] == -1) | 879 | if ((int)offer_packet->options[dchp_opt_idx] == -1) { |
| 793 | break; | 880 | break; |
| 881 | } | ||
| 794 | 882 | ||
| 795 | /* get option type */ | 883 | /* get option type */ |
| 796 | option_type = offer_packet->options[x++]; | 884 | unsigned option_type = offer_packet->options[dchp_opt_idx++]; |
| 797 | 885 | ||
| 798 | /* get option length */ | 886 | /* get option length */ |
| 799 | option_length = offer_packet->options[x++]; | 887 | unsigned option_length = offer_packet->options[dchp_opt_idx++]; |
| 800 | 888 | ||
| 801 | if (verbose) | 889 | if (verbose) { |
| 802 | printf("Option: %d (0x%02X)\n", option_type, option_length); | 890 | printf("Option: %d (0x%02X)\n", option_type, option_length); |
| 891 | } | ||
| 803 | 892 | ||
| 804 | /* get option data */ | 893 | /* get option data */ |
| 805 | switch (option_type) { | 894 | switch (option_type) { |
| 806 | case DHCP_OPTION_LEASE_TIME: | 895 | case DHCP_OPTION_LEASE_TIME: |
| 807 | memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); | 896 | memcpy(&dhcp_lease_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_lease_time)); |
| 808 | dhcp_lease_time = ntohl(dhcp_lease_time); | 897 | dhcp_lease_time = ntohl(dhcp_lease_time); |
| 809 | break; | 898 | break; |
| 810 | case DHCP_OPTION_RENEWAL_TIME: | 899 | case DHCP_OPTION_RENEWAL_TIME: |
| 811 | memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); | 900 | memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx], |
| 901 | sizeof(dhcp_renewal_time)); | ||
| 812 | dhcp_renewal_time = ntohl(dhcp_renewal_time); | 902 | dhcp_renewal_time = ntohl(dhcp_renewal_time); |
| 813 | break; | 903 | break; |
| 814 | case DHCP_OPTION_REBINDING_TIME: | 904 | case DHCP_OPTION_REBINDING_TIME: |
| 815 | memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); | 905 | memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx], |
| 906 | sizeof(dhcp_rebinding_time)); | ||
| 816 | dhcp_rebinding_time = ntohl(dhcp_rebinding_time); | 907 | dhcp_rebinding_time = ntohl(dhcp_rebinding_time); |
| 817 | break; | 908 | break; |
| 818 | case DHCP_OPTION_SERVER_IDENTIFIER: | 909 | case DHCP_OPTION_SERVER_IDENTIFIER: |
| 819 | memcpy(&serv_ident.s_addr, &offer_packet->options[x], sizeof(serv_ident.s_addr)); | 910 | memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx], |
| 911 | sizeof(serv_ident.s_addr)); | ||
| 820 | break; | 912 | break; |
| 821 | } | 913 | } |
| 822 | 914 | ||
| 823 | /* skip option data we're ignoring */ | 915 | /* skip option data we're ignoring */ |
| 824 | if (option_type == 0) /* "pad" option, see RFC 2132 (3.1) */ | 916 | if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */ |
| 825 | x += 1; | 917 | dchp_opt_idx += 1; |
| 826 | else | 918 | } else { |
| 827 | x += option_length; | 919 | dchp_opt_idx += option_length; |
| 920 | } | ||
| 828 | } | 921 | } |
| 829 | 922 | ||
| 830 | if (verbose) { | 923 | if (verbose) { |
| 831 | if (dhcp_lease_time == DHCP_INFINITE_TIME) | 924 | if (dhcp_lease_time == DHCP_INFINITE_TIME) { |
| 832 | printf(_("Lease Time: Infinite\n")); | 925 | printf(_("Lease Time: Infinite\n")); |
| 833 | else | 926 | } else { |
| 834 | printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time); | 927 | printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time); |
| 835 | if (dhcp_renewal_time == DHCP_INFINITE_TIME) | 928 | } |
| 929 | if (dhcp_renewal_time == DHCP_INFINITE_TIME) { | ||
| 836 | printf(_("Renewal Time: Infinite\n")); | 930 | printf(_("Renewal Time: Infinite\n")); |
| 837 | else | 931 | } else { |
| 838 | printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time); | 932 | printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time); |
| 839 | if (dhcp_rebinding_time == DHCP_INFINITE_TIME) | 933 | } |
| 934 | if (dhcp_rebinding_time == DHCP_INFINITE_TIME) { | ||
| 840 | printf(_("Rebinding Time: Infinite\n")); | 935 | printf(_("Rebinding Time: Infinite\n")); |
| 936 | } | ||
| 841 | printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time); | 937 | printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time); |
| 842 | } | 938 | } |
| 843 | 939 | ||
| 844 | new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); | 940 | new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); |
| 845 | 941 | ||
| 846 | if (new_offer == NULL) | 942 | if (new_offer == NULL) { |
| 847 | return ERROR; | 943 | add_dhcp_offer_wrapper tmp = { |
| 944 | .error = ERROR, | ||
| 945 | }; | ||
| 946 | return tmp; | ||
| 947 | } | ||
| 848 | 948 | ||
| 849 | /* | 949 | /* |
| 850 | * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the | 950 | * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the |
| @@ -874,15 +974,18 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { | |||
| 874 | new_offer->next = dhcp_offer_list; | 974 | new_offer->next = dhcp_offer_list; |
| 875 | dhcp_offer_list = new_offer; | 975 | dhcp_offer_list = new_offer; |
| 876 | 976 | ||
| 877 | return OK; | 977 | add_dhcp_offer_wrapper result = { |
| 978 | .error = OK, | ||
| 979 | .dhcp_offer_list = dhcp_offer_list, | ||
| 980 | }; | ||
| 981 | |||
| 982 | return result; | ||
| 878 | } | 983 | } |
| 879 | 984 | ||
| 880 | /* frees memory allocated to DHCP OFFER list */ | 985 | /* frees memory allocated to DHCP OFFER list */ |
| 881 | static int free_dhcp_offer_list(void) { | 986 | int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) { |
| 882 | dhcp_offer *this_offer; | ||
| 883 | dhcp_offer *next_offer; | 987 | dhcp_offer *next_offer; |
| 884 | 988 | for (dhcp_offer *this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) { | |
| 885 | for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) { | ||
| 886 | next_offer = this_offer->next; | 989 | next_offer = this_offer->next; |
| 887 | free(this_offer); | 990 | free(this_offer); |
| 888 | } | 991 | } |
| @@ -891,11 +994,10 @@ static int free_dhcp_offer_list(void) { | |||
| 891 | } | 994 | } |
| 892 | 995 | ||
| 893 | /* frees memory allocated to requested server list */ | 996 | /* frees memory allocated to requested server list */ |
| 894 | static int free_requested_server_list(void) { | 997 | int free_requested_server_list(requested_server *requested_server_list) { |
| 895 | requested_server *this_server; | ||
| 896 | requested_server *next_server; | 998 | requested_server *next_server; |
| 897 | 999 | for (requested_server *this_server = requested_server_list; this_server != NULL; | |
| 898 | for (this_server = requested_server_list; this_server != NULL; this_server = next_server) { | 1000 | this_server = next_server) { |
| 899 | next_server = this_server->next; | 1001 | next_server = this_server->next; |
| 900 | free(this_server); | 1002 | free(this_server); |
| 901 | } | 1003 | } |
| @@ -904,39 +1006,68 @@ static int free_requested_server_list(void) { | |||
| 904 | } | 1006 | } |
| 905 | 1007 | ||
| 906 | /* gets state and plugin output to return */ | 1008 | /* gets state and plugin output to return */ |
| 907 | static int get_results(void) { | 1009 | mp_subcheck get_results(bool exclusive, const int requested_servers, |
| 908 | dhcp_offer *temp_offer, *undesired_offer = NULL; | 1010 | const struct in_addr requested_address, bool request_specific_address, |
| 909 | requested_server *temp_server; | 1011 | requested_server *requested_server_list, int valid_responses, |
| 910 | int result; | 1012 | dhcp_offer *dhcp_offer_list) { |
| 911 | uint32_t max_lease_time = 0; | 1013 | mp_subcheck sc_dhcp_results = mp_subcheck_init(); |
| 1014 | sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK); | ||
| 912 | 1015 | ||
| 913 | received_requested_address = false; | 1016 | /* we didn't receive any DHCPOFFERs */ |
| 914 | 1017 | if (dhcp_offer_list == NULL) { | |
| 915 | /* checks responses from requested servers */ | 1018 | sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL); |
| 916 | requested_responses = 0; | 1019 | xasprintf(&sc_dhcp_results.output, "%s", "No DHCPOFFERs were received"); |
| 917 | if (requested_servers > 0) { | 1020 | return sc_dhcp_results; |
| 1021 | } | ||
| 918 | 1022 | ||
| 919 | for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { | 1023 | if (valid_responses == 0) { |
| 1024 | // No valid responses at all, so early exit here | ||
| 1025 | sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL); | ||
| 1026 | xasprintf(&sc_dhcp_results.output, "No valid responses received"); | ||
| 1027 | return sc_dhcp_results; | ||
| 1028 | } | ||
| 920 | 1029 | ||
| 921 | for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { | 1030 | if (valid_responses == 1) { |
| 1031 | xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFER", valid_responses); | ||
| 1032 | } else { | ||
| 1033 | xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFERs", valid_responses); | ||
| 1034 | } | ||
| 922 | 1035 | ||
| 1036 | bool received_requested_address = false; | ||
| 1037 | dhcp_offer *undesired_offer = NULL; | ||
| 1038 | uint32_t max_lease_time = 0; | ||
| 1039 | /* checks responses from requested servers */ | ||
| 1040 | int requested_responses = 0; | ||
| 1041 | if (requested_servers > 0) { | ||
| 1042 | for (requested_server *temp_server = requested_server_list; temp_server != NULL; | ||
| 1043 | temp_server = temp_server->next) { | ||
| 1044 | for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; | ||
| 1045 | temp_offer = temp_offer->next) { | ||
| 923 | /* get max lease time we were offered */ | 1046 | /* get max lease time we were offered */ |
| 924 | if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) | 1047 | if (temp_offer->lease_time > max_lease_time || |
| 1048 | temp_offer->lease_time == DHCP_INFINITE_TIME) { | ||
| 925 | max_lease_time = temp_offer->lease_time; | 1049 | max_lease_time = temp_offer->lease_time; |
| 1050 | } | ||
| 926 | 1051 | ||
| 927 | /* see if we got the address we requested */ | 1052 | /* see if we got the address we requested */ |
| 928 | if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) | 1053 | if (!memcmp(&requested_address, &temp_offer->offered_address, |
| 1054 | sizeof(requested_address))) { | ||
| 929 | received_requested_address = true; | 1055 | received_requested_address = true; |
| 1056 | } | ||
| 930 | 1057 | ||
| 931 | /* see if the servers we wanted a response from talked to us or not */ | 1058 | /* see if the servers we wanted a response from, talked to us or not */ |
| 932 | if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { | 1059 | if (!memcmp(&temp_offer->server_address, &temp_server->server_address, |
| 1060 | sizeof(temp_server->server_address))) { | ||
| 933 | if (verbose) { | 1061 | if (verbose) { |
| 934 | printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); | 1062 | printf(_("DHCP Server Match: Offerer=%s"), |
| 1063 | inet_ntoa(temp_offer->server_address)); | ||
| 935 | printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); | 1064 | printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); |
| 936 | if (temp_server->answered) | 1065 | if (temp_server->answered) { |
| 937 | printf(_(" (duplicate)")); | 1066 | printf(_(" (duplicate)")); |
| 1067 | } | ||
| 938 | printf(_("\n")); | 1068 | printf(_("\n")); |
| 939 | } | 1069 | } |
| 1070 | |||
| 940 | if (!temp_server->answered) { | 1071 | if (!temp_server->answered) { |
| 941 | requested_responses++; | 1072 | requested_responses++; |
| 942 | temp_server->answered = true; | 1073 | temp_server->answered = true; |
| @@ -947,160 +1078,188 @@ static int get_results(void) { | |||
| 947 | } | 1078 | } |
| 948 | 1079 | ||
| 949 | /* exclusive mode: check for undesired offers */ | 1080 | /* exclusive mode: check for undesired offers */ |
| 950 | for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { | 1081 | for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; |
| 1082 | temp_offer = temp_offer->next) { | ||
| 951 | if (!temp_offer->desired) { | 1083 | if (!temp_offer->desired) { |
| 952 | undesired_offer = temp_offer; /* Checks only for the first undesired offer */ | 1084 | undesired_offer = temp_offer; /* Checks only for the first undesired offer */ |
| 953 | break; /* no further checks needed */ | 1085 | break; /* no further checks needed */ |
| 954 | } | 1086 | } |
| 955 | } | 1087 | } |
| 956 | } | ||
| 957 | 1088 | ||
| 958 | /* else check and see if we got our requested address from any server */ | 1089 | mp_subcheck sc_rqust_srvs = mp_subcheck_init(); |
| 959 | else { | 1090 | xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded", |
| 1091 | requested_responses, requested_servers); | ||
| 960 | 1092 | ||
| 961 | for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { | 1093 | if (requested_responses == requested_servers) { |
| 1094 | sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK); | ||
| 1095 | } else if (requested_responses == 0) { | ||
| 1096 | sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_CRITICAL); | ||
| 1097 | } else if (requested_responses < requested_servers) { | ||
| 1098 | sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING); | ||
| 1099 | } else { | ||
| 1100 | // We received more(!) responses than we asked for? | ||
| 1101 | // This case shouldn't happen, but is here for completion | ||
| 1102 | sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING); | ||
| 1103 | } | ||
| 1104 | mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqust_srvs); | ||
| 962 | 1105 | ||
| 1106 | } else { | ||
| 1107 | /* else check and see if we got our requested address from any server */ | ||
| 1108 | for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; | ||
| 1109 | temp_offer = temp_offer->next) { | ||
| 963 | /* get max lease time we were offered */ | 1110 | /* get max lease time we were offered */ |
| 964 | if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) | 1111 | if (temp_offer->lease_time > max_lease_time || |
| 1112 | temp_offer->lease_time == DHCP_INFINITE_TIME) { | ||
| 965 | max_lease_time = temp_offer->lease_time; | 1113 | max_lease_time = temp_offer->lease_time; |
| 1114 | } | ||
| 966 | 1115 | ||
| 967 | /* see if we got the address we requested */ | 1116 | /* see if we got the address we requested */ |
| 968 | if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) | 1117 | if (!memcmp(&requested_address, &temp_offer->offered_address, |
| 1118 | sizeof(requested_address))) { | ||
| 969 | received_requested_address = true; | 1119 | received_requested_address = true; |
| 1120 | } | ||
| 970 | } | 1121 | } |
| 971 | } | 1122 | } |
| 972 | 1123 | ||
| 973 | result = STATE_OK; | 1124 | if (max_lease_time == DHCP_INFINITE_TIME) { |
| 974 | if (valid_responses == 0) | 1125 | xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output); |
| 975 | result = STATE_CRITICAL; | 1126 | } else { |
| 976 | else if (requested_servers > 0 && requested_responses == 0) | 1127 | xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds", |
| 977 | result = STATE_CRITICAL; | 1128 | sc_dhcp_results.output, max_lease_time); |
| 978 | else if (requested_responses < requested_servers) | ||
| 979 | result = STATE_WARNING; | ||
| 980 | else if (request_specific_address && !received_requested_address) | ||
| 981 | result = STATE_WARNING; | ||
| 982 | |||
| 983 | if (exclusive && undesired_offer) | ||
| 984 | result = STATE_CRITICAL; | ||
| 985 | |||
| 986 | if (result == 0) /* garrett honeycutt 2005 */ | ||
| 987 | printf("OK: "); | ||
| 988 | else if (result == 1) | ||
| 989 | printf("WARNING: "); | ||
| 990 | else if (result == 2) | ||
| 991 | printf("CRITICAL: "); | ||
| 992 | else if (result == 3) | ||
| 993 | printf("UNKNOWN: "); | ||
| 994 | |||
| 995 | /* we didn't receive any DHCPOFFERs */ | ||
| 996 | if (dhcp_offer_list == NULL) { | ||
| 997 | printf(_("No DHCPOFFERs were received.\n")); | ||
| 998 | return result; | ||
| 999 | } | 1129 | } |
| 1000 | 1130 | ||
| 1001 | printf(_("Received %d DHCPOFFER(s)"), valid_responses); | 1131 | if (exclusive) { |
| 1132 | mp_subcheck sc_rogue_server = mp_subcheck_init(); | ||
| 1002 | 1133 | ||
| 1003 | if (exclusive && undesired_offer) { | 1134 | if (undesired_offer != NULL) { |
| 1004 | printf(_(", Rogue DHCP Server detected! Server %s"), inet_ntoa(undesired_offer->server_address)); | 1135 | // We wanted to get a DHCPOFFER exclusively from one machine, but another one |
| 1005 | printf(_(" offered %s \n"), inet_ntoa(undesired_offer->offered_address)); | 1136 | // sent one (too) |
| 1006 | return result; | 1137 | sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_CRITICAL); |
| 1007 | } | ||
| 1008 | 1138 | ||
| 1009 | if (requested_servers > 0) | 1139 | // Get the addresses for printout |
| 1010 | printf(_(", %s%d of %d requested servers responded"), ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, | 1140 | // 1.address of the sending server |
| 1011 | requested_servers); | 1141 | char server_address[INET_ADDRSTRLEN]; |
| 1142 | const char *server_address_transformed = inet_ntop( | ||
| 1143 | AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address)); | ||
| 1012 | 1144 | ||
| 1013 | if (request_specific_address) | 1145 | if (server_address != server_address_transformed) { |
| 1014 | printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); | 1146 | die(STATE_UNKNOWN, "inet_ntop failed"); |
| 1147 | } | ||
| 1015 | 1148 | ||
| 1016 | printf(_(", max lease time = ")); | 1149 | // 2.address offered |
| 1017 | if (max_lease_time == DHCP_INFINITE_TIME) | 1150 | char offered_address[INET_ADDRSTRLEN]; |
| 1018 | printf(_("Infinity")); | 1151 | const char *offered_address_transformed = |
| 1019 | else | 1152 | inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address, |
| 1020 | printf("%lu sec", (unsigned long)max_lease_time); | 1153 | sizeof(offered_address)); |
| 1021 | 1154 | ||
| 1022 | printf(".\n"); | 1155 | if (offered_address != offered_address_transformed) { |
| 1156 | die(STATE_UNKNOWN, "inet_ntop failed"); | ||
| 1157 | } | ||
| 1023 | 1158 | ||
| 1024 | return result; | 1159 | xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s", |
| 1160 | server_address, offered_address); | ||
| 1161 | } else { | ||
| 1162 | sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK); | ||
| 1163 | xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected"); | ||
| 1164 | } | ||
| 1165 | mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rogue_server); | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | if (request_specific_address) { | ||
| 1169 | mp_subcheck sc_rqustd_addr = mp_subcheck_init(); | ||
| 1170 | |||
| 1171 | if (received_requested_address) { | ||
| 1172 | sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK); | ||
| 1173 | xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered", | ||
| 1174 | inet_ntoa(requested_address)); | ||
| 1175 | } else { | ||
| 1176 | sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING); | ||
| 1177 | xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered", | ||
| 1178 | inet_ntoa(requested_address)); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr); | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | return sc_dhcp_results; | ||
| 1025 | } | 1185 | } |
| 1026 | 1186 | ||
| 1027 | /* process command-line arguments */ | 1187 | /* process command-line arguments */ |
| 1028 | static int process_arguments(int argc, char **argv) { | 1188 | process_arguments_wrapper process_arguments(int argc, char **argv) { |
| 1029 | if (argc < 1) | 1189 | if (argc < 1) { |
| 1030 | return ERROR; | 1190 | process_arguments_wrapper tmp = { |
| 1191 | .error = ERROR, | ||
| 1192 | }; | ||
| 1193 | return tmp; | ||
| 1194 | } | ||
| 1031 | 1195 | ||
| 1032 | call_getopt(argc, argv); | 1196 | enum { |
| 1033 | return validate_arguments(argc); | 1197 | output_format_index = CHAR_MAX + 1, |
| 1034 | } | 1198 | }; |
| 1035 | 1199 | ||
| 1036 | static int call_getopt(int argc, char **argv) { | ||
| 1037 | extern int optind; | ||
| 1038 | int option_index = 0; | 1200 | int option_index = 0; |
| 1039 | static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, | 1201 | static struct option long_options[] = { |
| 1040 | {"requestedip", required_argument, 0, 'r'}, | 1202 | {"serverip", required_argument, 0, 's'}, |
| 1041 | {"timeout", required_argument, 0, 't'}, | 1203 | {"requestedip", required_argument, 0, 'r'}, |
| 1042 | {"interface", required_argument, 0, 'i'}, | 1204 | {"timeout", required_argument, 0, 't'}, |
| 1043 | {"mac", required_argument, 0, 'm'}, | 1205 | {"interface", required_argument, 0, 'i'}, |
| 1044 | {"unicast", no_argument, 0, 'u'}, | 1206 | {"mac", required_argument, 0, 'm'}, |
| 1045 | {"exclusive", no_argument, 0, 'x'}, | 1207 | {"unicast", no_argument, 0, 'u'}, |
| 1046 | {"verbose", no_argument, 0, 'v'}, | 1208 | {"exclusive", no_argument, 0, 'x'}, |
| 1047 | {"version", no_argument, 0, 'V'}, | 1209 | {"verbose", no_argument, 0, 'v'}, |
| 1048 | {"help", no_argument, 0, 'h'}, | 1210 | {"version", no_argument, 0, 'V'}, |
| 1049 | {0, 0, 0, 0}}; | 1211 | {"help", no_argument, 0, 'h'}, |
| 1050 | 1212 | {"output-format", required_argument, 0, output_format_index}, | |
| 1051 | int c = 0; | 1213 | {0, 0, 0, 0}}; |
| 1214 | |||
| 1215 | check_dhcp_config config = check_dhcp_config_init(); | ||
| 1216 | int option_char = 0; | ||
| 1052 | while (true) { | 1217 | while (true) { |
| 1053 | c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); | 1218 | option_char = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); |
| 1054 | 1219 | ||
| 1055 | if (c == -1 || c == EOF || c == 1) | 1220 | if (option_char == -1 || option_char == EOF || option_char == 1) { |
| 1056 | break; | 1221 | break; |
| 1222 | } | ||
| 1057 | 1223 | ||
| 1058 | switch (c) { | 1224 | switch (option_char) { |
| 1059 | |||
| 1060 | case 's': /* DHCP server address */ | 1225 | case 's': /* DHCP server address */ |
| 1061 | resolve_host(optarg, &dhcp_ip); | 1226 | resolve_host(optarg, &config.dhcp_ip); |
| 1062 | add_requested_server(dhcp_ip); | 1227 | add_requested_server(config.dhcp_ip, &config.num_of_requested_servers, |
| 1228 | &config.requested_server_list); | ||
| 1063 | break; | 1229 | break; |
| 1064 | 1230 | ||
| 1065 | case 'r': /* address we are requested from DHCP servers */ | 1231 | case 'r': /* address we are requested from DHCP servers */ |
| 1066 | resolve_host(optarg, &requested_address); | 1232 | resolve_host(optarg, &config.requested_address); |
| 1067 | request_specific_address = true; | 1233 | config.request_specific_address = true; |
| 1068 | break; | 1234 | break; |
| 1069 | 1235 | ||
| 1070 | case 't': /* timeout */ | 1236 | case 't': /* timeout */ |
| 1071 | 1237 | if (atoi(optarg) > 0) { | |
| 1072 | /* | 1238 | config.dhcpoffer_timeout = atoi(optarg); |
| 1073 | if(is_intnonneg(optarg)) | 1239 | } |
| 1074 | */ | ||
| 1075 | if (atoi(optarg) > 0) | ||
| 1076 | dhcpoffer_timeout = atoi(optarg); | ||
| 1077 | /* | ||
| 1078 | else | ||
| 1079 | usage("Time interval must be a nonnegative integer\n"); | ||
| 1080 | */ | ||
| 1081 | break; | 1240 | break; |
| 1082 | 1241 | ||
| 1083 | case 'm': /* MAC address */ | 1242 | case 'm': /* MAC address */ |
| 1084 | 1243 | if ((config.user_specified_mac = mac_aton(optarg)) == NULL) { | |
| 1085 | if ((user_specified_mac = mac_aton(optarg)) == NULL) | ||
| 1086 | usage("Cannot parse MAC address.\n"); | 1244 | usage("Cannot parse MAC address.\n"); |
| 1087 | if (verbose) | 1245 | } |
| 1088 | print_hardware_address(user_specified_mac); | 1246 | if (verbose) { |
| 1089 | 1247 | print_hardware_address(config.user_specified_mac); | |
| 1248 | } | ||
| 1090 | break; | 1249 | break; |
| 1091 | 1250 | ||
| 1092 | case 'i': /* interface name */ | 1251 | case 'i': /* interface name */ |
| 1093 | 1252 | strncpy(config.network_interface_name, optarg, | |
| 1094 | strncpy(network_interface_name, optarg, sizeof(network_interface_name) - 1); | 1253 | sizeof(config.network_interface_name) - 1); |
| 1095 | network_interface_name[sizeof(network_interface_name) - 1] = '\x0'; | 1254 | config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0'; |
| 1096 | |||
| 1097 | break; | 1255 | break; |
| 1098 | 1256 | ||
| 1099 | case 'u': /* unicast testing */ | 1257 | case 'u': /* unicast testing */ |
| 1100 | unicast = true; | 1258 | config.unicast_mode = true; |
| 1101 | break; | 1259 | break; |
| 1260 | |||
| 1102 | case 'x': /* exclusive testing aka "rogue DHCP server detection" */ | 1261 | case 'x': /* exclusive testing aka "rogue DHCP server detection" */ |
| 1103 | exclusive = true; | 1262 | config.exclusive_mode = true; |
| 1104 | break; | 1263 | break; |
| 1105 | 1264 | ||
| 1106 | case 'V': /* version */ | 1265 | case 'V': /* version */ |
| @@ -1114,6 +1273,18 @@ static int call_getopt(int argc, char **argv) { | |||
| 1114 | case 'v': /* verbose */ | 1273 | case 'v': /* verbose */ |
| 1115 | verbose = 1; | 1274 | verbose = 1; |
| 1116 | break; | 1275 | break; |
| 1276 | case output_format_index: { | ||
| 1277 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 1278 | if (!parser.parsing_success) { | ||
| 1279 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 1280 | printf("Invalid output format: %s\n", optarg); | ||
| 1281 | exit(STATE_UNKNOWN); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | config.output_format_is_set = true; | ||
| 1285 | config.output_format = parser.output_format; | ||
| 1286 | break; | ||
| 1287 | } | ||
| 1117 | case '?': /* help */ | 1288 | case '?': /* help */ |
| 1118 | usage5(); | 1289 | usage5(); |
| 1119 | break; | 1290 | break; |
| @@ -1122,15 +1293,16 @@ static int call_getopt(int argc, char **argv) { | |||
| 1122 | break; | 1293 | break; |
| 1123 | } | 1294 | } |
| 1124 | } | 1295 | } |
| 1125 | return optind; | ||
| 1126 | } | ||
| 1127 | 1296 | ||
| 1128 | static int validate_arguments(int argc) { | 1297 | if (argc - optind > 0) { |
| 1129 | |||
| 1130 | if (argc - optind > 0) | ||
| 1131 | usage(_("Got unexpected non-option argument")); | 1298 | usage(_("Got unexpected non-option argument")); |
| 1299 | } | ||
| 1132 | 1300 | ||
| 1133 | return OK; | 1301 | process_arguments_wrapper result = { |
| 1302 | .config = config, | ||
| 1303 | .error = OK, | ||
| 1304 | }; | ||
| 1305 | return result; | ||
| 1134 | } | 1306 | } |
| 1135 | 1307 | ||
| 1136 | #if defined(__sun__) || defined(__solaris__) || defined(__hpux__) | 1308 | #if defined(__sun__) || defined(__solaris__) || defined(__hpux__) |
| @@ -1180,7 +1352,8 @@ static int put_ctrl(int fd, int len, int pri) { | |||
| 1180 | 1352 | ||
| 1181 | ctl.len = len; | 1353 | ctl.len = len; |
| 1182 | if (putmsg(fd, &ctl, 0, pri) < 0) { | 1354 | if (putmsg(fd, &ctl, 0, pri) < 0) { |
| 1183 | printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), strerror(errno)); | 1355 | printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), |
| 1356 | strerror(errno)); | ||
| 1184 | exit(STATE_UNKNOWN); | 1357 | exit(STATE_UNKNOWN); |
| 1185 | } | 1358 | } |
| 1186 | 1359 | ||
| @@ -1193,7 +1366,8 @@ static int put_both(int fd, int clen, int dlen, int pri) { | |||
| 1193 | ctl.len = clen; | 1366 | ctl.len = clen; |
| 1194 | dat.len = dlen; | 1367 | dat.len = dlen; |
| 1195 | if (putmsg(fd, &ctl, &dat, pri) < 0) { | 1368 | if (putmsg(fd, &ctl, &dat, pri) < 0) { |
| 1196 | printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), strerror(errno)); | 1369 | printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), |
| 1370 | strerror(errno)); | ||
| 1197 | exit(STATE_UNKNOWN); | 1371 | exit(STATE_UNKNOWN); |
| 1198 | } | 1372 | } |
| 1199 | 1373 | ||
| @@ -1205,7 +1379,8 @@ static int dl_open(const char *dev, int unit, int *fd) { | |||
| 1205 | dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area; | 1379 | dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area; |
| 1206 | 1380 | ||
| 1207 | if ((*fd = open(dev, O_RDWR)) == -1) { | 1381 | if ((*fd = open(dev, O_RDWR)) == -1) { |
| 1208 | printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), dev, strerror(errno)); | 1382 | printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), |
| 1383 | dev, strerror(errno)); | ||
| 1209 | exit(STATE_UNKNOWN); | 1384 | exit(STATE_UNKNOWN); |
| 1210 | } | 1385 | } |
| 1211 | attach_req->dl_primitive = DL_ATTACH_REQ; | 1386 | attach_req->dl_primitive = DL_ATTACH_REQ; |
| @@ -1229,7 +1404,8 @@ static int dl_bind(int fd, int sap, u_char *addr) { | |||
| 1229 | put_ctrl(fd, sizeof(dl_bind_req_t), 0); | 1404 | put_ctrl(fd, sizeof(dl_bind_req_t), 0); |
| 1230 | get_msg(fd); | 1405 | get_msg(fd); |
| 1231 | if (GOT_ERR == check_ctrl(DL_BIND_ACK)) { | 1406 | if (GOT_ERR == check_ctrl(DL_BIND_ACK)) { |
| 1232 | printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), strerror(errno)); | 1407 | printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), |
| 1408 | strerror(errno)); | ||
| 1233 | exit(STATE_UNKNOWN); | 1409 | exit(STATE_UNKNOWN); |
| 1234 | } | 1410 | } |
| 1235 | bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length); | 1411 | bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length); |
| @@ -1249,7 +1425,7 @@ static int dl_bind(int fd, int sap, u_char *addr) { | |||
| 1249 | * | 1425 | * |
| 1250 | ***********************************************************************/ | 1426 | ***********************************************************************/ |
| 1251 | 1427 | ||
| 1252 | static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { | 1428 | long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { |
| 1253 | int fd; | 1429 | int fd; |
| 1254 | u_char mac_addr[25]; | 1430 | u_char mac_addr[25]; |
| 1255 | 1431 | ||
| @@ -1268,51 +1444,53 @@ static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { | |||
| 1268 | #endif | 1444 | #endif |
| 1269 | 1445 | ||
| 1270 | /* resolve host name or die (TODO: move this to netutils.c!) */ | 1446 | /* resolve host name or die (TODO: move this to netutils.c!) */ |
| 1271 | static void resolve_host(const char *in, struct in_addr *out) { | 1447 | void resolve_host(const char *name, struct in_addr *out) { |
| 1272 | struct addrinfo hints, *ai; | 1448 | struct addrinfo hints = { |
| 1449 | .ai_family = PF_INET, | ||
| 1450 | }; | ||
| 1451 | struct addrinfo *addr_info; | ||
| 1273 | 1452 | ||
| 1274 | memset(&hints, 0, sizeof(hints)); | 1453 | if (getaddrinfo(name, NULL, &hints, &addr_info) != 0) { |
| 1275 | hints.ai_family = PF_INET; | ||
| 1276 | if (getaddrinfo(in, NULL, &hints, &ai) != 0) | ||
| 1277 | usage_va(_("Invalid hostname/address - %s"), optarg); | 1454 | usage_va(_("Invalid hostname/address - %s"), optarg); |
| 1455 | } | ||
| 1278 | 1456 | ||
| 1279 | memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); | 1457 | memcpy(out, &((struct sockaddr_in *)addr_info->ai_addr)->sin_addr, sizeof(*out)); |
| 1280 | freeaddrinfo(ai); | 1458 | freeaddrinfo(addr_info); |
| 1281 | } | 1459 | } |
| 1282 | 1460 | ||
| 1283 | /* parse MAC address string, return 6 bytes (unterminated) or NULL */ | 1461 | /* parse MAC address string, return 6 bytes (unterminated) or NULL */ |
| 1284 | static unsigned char *mac_aton(const char *string) { | 1462 | unsigned char *mac_aton(const char *string) { |
| 1285 | static unsigned char result[6]; | 1463 | static unsigned char result[MAC_ADDR_LEN]; |
| 1286 | char tmp[3]; | 1464 | char tmp[3]; |
| 1287 | unsigned i, j; | 1465 | unsigned byte_counter = 0; |
| 1288 | 1466 | ||
| 1289 | for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { | 1467 | for (int i = 0; string[i] != '\0' && byte_counter < sizeof(result); i++) { |
| 1290 | /* ignore ':' and any other non-hex character */ | 1468 | /* ignore ':' and any other non-hex character */ |
| 1291 | if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) | 1469 | if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) { |
| 1292 | continue; | 1470 | continue; |
| 1471 | } | ||
| 1293 | tmp[0] = string[i]; | 1472 | tmp[0] = string[i]; |
| 1294 | tmp[1] = string[i + 1]; | 1473 | tmp[1] = string[i + 1]; |
| 1295 | tmp[2] = '\0'; | 1474 | tmp[2] = '\0'; |
| 1296 | result[j] = strtol(tmp, (char **)NULL, 16); | 1475 | result[byte_counter] = strtol(tmp, (char **)NULL, 16); |
| 1297 | i++; | 1476 | i++; |
| 1298 | j++; | 1477 | byte_counter++; |
| 1299 | } | 1478 | } |
| 1300 | 1479 | ||
| 1301 | return (j == 6) ? result : NULL; | 1480 | return (byte_counter == MAC_ADDR_LEN) ? result : NULL; |
| 1302 | } | 1481 | } |
| 1303 | 1482 | ||
| 1304 | static void print_hardware_address(const unsigned char *address) { | 1483 | void print_hardware_address(const unsigned char *address) { |
| 1305 | int i; | ||
| 1306 | 1484 | ||
| 1307 | printf(_("Hardware address: ")); | 1485 | printf(_("Hardware address: ")); |
| 1308 | for (i = 0; i < 5; i++) | 1486 | for (int addr_idx = 0; addr_idx < MAC_ADDR_LEN; addr_idx++) { |
| 1309 | printf("%2.2x:", address[i]); | 1487 | printf("%2.2x:", address[addr_idx]); |
| 1310 | printf("%2.2x", address[i]); | 1488 | } |
| 1311 | putchar('\n'); | 1489 | putchar('\n'); |
| 1312 | } | 1490 | } |
| 1313 | 1491 | ||
| 1314 | /* print usage help */ | 1492 | /* print usage help */ |
| 1315 | static void print_help(void) { | 1493 | void print_help(void) { |
| 1316 | 1494 | ||
| 1317 | print_revision(progname, NP_VERSION); | 1495 | print_revision(progname, NP_VERSION); |
| 1318 | 1496 | ||
| @@ -1328,6 +1506,7 @@ static void print_help(void) { | |||
| 1328 | printf(UT_HELP_VRSN); | 1506 | printf(UT_HELP_VRSN); |
| 1329 | printf(UT_EXTRA_OPTS); | 1507 | printf(UT_EXTRA_OPTS); |
| 1330 | 1508 | ||
| 1509 | printf(UT_OUTPUT_FORMAT); | ||
| 1331 | printf(UT_VERBOSE); | 1510 | printf(UT_VERBOSE); |
| 1332 | 1511 | ||
| 1333 | printf(" %s\n", "-s, --serverip=IPADDRESS"); | 1512 | printf(" %s\n", "-s, --serverip=IPADDRESS"); |
| @@ -1343,10 +1522,10 @@ static void print_help(void) { | |||
| 1343 | printf(" %s\n", "-u, --unicast"); | 1522 | printf(" %s\n", "-u, --unicast"); |
| 1344 | printf(" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s")); | 1523 | printf(" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s")); |
| 1345 | printf(" %s\n", "-x, --exclusive"); | 1524 | printf(" %s\n", "-x, --exclusive"); |
| 1346 | printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); | 1525 | printf(" %s\n", |
| 1526 | _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); | ||
| 1347 | 1527 | ||
| 1348 | printf(UT_SUPPORT); | 1528 | printf(UT_SUPPORT); |
| 1349 | return; | ||
| 1350 | } | 1529 | } |
| 1351 | 1530 | ||
| 1352 | void print_usage(void) { | 1531 | void print_usage(void) { |
| @@ -1354,6 +1533,4 @@ void print_usage(void) { | |||
| 1354 | printf("%s\n", _("Usage:")); | 1533 | printf("%s\n", _("Usage:")); |
| 1355 | printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); | 1534 | printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); |
| 1356 | printf(" [-i interface] [-m mac]\n"); | 1535 | printf(" [-i interface] [-m mac]\n"); |
| 1357 | |||
| 1358 | return; | ||
| 1359 | } | 1536 | } |
diff --git a/plugins-root/check_dhcp.d/config.h b/plugins-root/check_dhcp.d/config.h new file mode 100644 index 00000000..f189068b --- /dev/null +++ b/plugins-root/check_dhcp.d/config.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "../lib/states.h" | ||
| 5 | #include <stdbool.h> | ||
| 6 | #include <netinet/in.h> | ||
| 7 | #include "net/if.h" | ||
| 8 | #include "output.h" | ||
| 9 | |||
| 10 | typedef struct requested_server_struct { | ||
| 11 | struct in_addr server_address; | ||
| 12 | bool answered; | ||
| 13 | struct requested_server_struct *next; | ||
| 14 | } requested_server; | ||
| 15 | |||
| 16 | typedef 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 | |||
| 33 | check_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 | ||
| 99 | typedef unsigned short range_t; /* type for get_range() -- unimplemented */ | ||
| 100 | |||
| 101 | typedef 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 */ | ||
| 135 | typedef 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 */ | ||
| 144 | typedef 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 | |||
| 149 | typedef union ip_hdr { | 114 | typedef 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 | |||
| 179 | enum enum_threshold_mode { | 126 | enum 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 | ||
| 187 | typedef enum enum_threshold_mode threshold_mode; | 134 | typedef 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 **/ |
| 209 | void print_help(void); | 137 | void print_help(); |
| 210 | void print_usage(void); | 138 | void print_usage(void); |
| 211 | static u_int get_timevar(const char *); | 139 | |
| 212 | static u_int get_timevaldiff(struct timeval *, struct timeval *); | 140 | /* Time related */ |
| 213 | static in_addr_t get_ip_address(const char *); | 141 | typedef struct { |
| 214 | static int wait_for_reply(int, u_int); | 142 | int error_code; |
| 215 | static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *); | 143 | time_t time_range; |
| 216 | static int send_icmp_ping(int, struct rta_host *); | 144 | } get_timevar_wrapper; |
| 217 | static int get_threshold(char *str, threshold *th); | 145 | static get_timevar_wrapper get_timevar(const char *str); |
| 218 | static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode); | 146 | static time_t get_timevaldiff(struct timeval earlier, struct timeval later); |
| 219 | static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); | 147 | static time_t get_timevaldiff_to_now(struct timeval earlier); |
| 220 | static void run_checks(void); | 148 | |
| 221 | static void set_source_ip(char *); | 149 | static in_addr_t get_ip_address(const char *ifname); |
| 222 | static int add_target(char *); | 150 | static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family); |
| 223 | static int add_target_ip(char *, struct sockaddr_storage *); | 151 | |
| 224 | static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); | 152 | /* Receiving data */ |
| 225 | static void parse_address(struct sockaddr_storage *, char *, int); | 153 | static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval, |
| 226 | static unsigned short icmp_checksum(uint16_t *, size_t); | 154 | unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id, |
| 227 | static void finish(int); | 155 | ping_target **table, unsigned short packets, |
| 228 | static void crash(const char *, ...); | 156 | unsigned short number_of_targets, check_icmp_state *program_state); |
| 229 | 157 | ||
| 230 | /** external **/ | 158 | typedef struct { |
| 231 | extern int optind; | 159 | sa_family_t recv_proto; |
| 232 | extern char *optarg; | 160 | ssize_t received; |
| 233 | extern char **environ; | 161 | } recvfrom_wto_wrapper; |
| 162 | static 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); | ||
| 165 | static 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 */ | ||
| 171 | static 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 */ | ||
| 176 | typedef struct { | ||
| 177 | int errorcode; | ||
| 178 | check_icmp_threshold threshold; | ||
| 179 | } get_threshold_wrapper; | ||
| 180 | static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold); | ||
| 181 | |||
| 182 | typedef struct { | ||
| 183 | int errorcode; | ||
| 184 | check_icmp_threshold warn; | ||
| 185 | check_icmp_threshold crit; | ||
| 186 | } get_threshold2_wrapper; | ||
| 187 | static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn, | ||
| 188 | check_icmp_threshold crit, threshold_mode mode); | ||
| 189 | |||
| 190 | typedef struct { | ||
| 191 | int errorcode; | ||
| 192 | check_icmp_threshold result; | ||
| 193 | } parse_threshold2_helper_wrapper; | ||
| 194 | static 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 */ | ||
| 200 | static 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); | ||
| 205 | mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, | ||
| 206 | check_icmp_threshold warn, check_icmp_threshold crit); | ||
| 207 | |||
| 208 | typedef struct { | ||
| 209 | int targets_ok; | ||
| 210 | int targets_warn; | ||
| 211 | mp_subcheck sc_host; | ||
| 212 | } evaluate_host_wrapper; | ||
| 213 | evaluate_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 */ | ||
| 218 | typedef struct { | ||
| 219 | int error_code; | ||
| 220 | check_icmp_target_container host; | ||
| 221 | bool has_v4; | ||
| 222 | bool has_v6; | ||
| 223 | } add_host_wrapper; | ||
| 224 | static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode, | ||
| 225 | sa_family_t enforced_proto); | ||
| 226 | |||
| 227 | typedef 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; | ||
| 234 | static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode, | ||
| 235 | sa_family_t enforced_proto); | ||
| 236 | |||
| 237 | typedef struct { | ||
| 238 | int error_code; | ||
| 239 | ping_target *target; | ||
| 240 | } add_target_ip_wrapper; | ||
| 241 | static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address); | ||
| 242 | |||
| 243 | static void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size); | ||
| 244 | |||
| 245 | static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size); | ||
| 246 | |||
| 247 | /* End of run function */ | ||
| 248 | static 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 */ | ||
| 255 | static void crash(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | ||
| 234 | 256 | ||
| 235 | /** global variables **/ | 257 | /** global variables **/ |
| 236 | static struct rta_host **table, *cursor, *list; | 258 | static int debug = 0; |
| 237 | 259 | ||
| 238 | static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; | 260 | extern unsigned int timeout; |
| 239 | static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; | 261 | |
| 240 | 262 | /** the working code **/ | |
| 241 | static int mode, protocols, sockets, debug = 0, timeout = 10; | 263 | static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) { |
| 242 | static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; | 264 | return targets - targets_down; |
| 243 | static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; | 265 | } |
| 244 | 266 | static inline unsigned int icmp_pkts_en_route(unsigned int icmp_sent, unsigned int icmp_recv, | |
| 245 | static 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); |
| 247 | static unsigned short targets_down = 0, targets = 0, packets = 0; | 269 | } |
| 248 | #define targets_alive (targets - targets_down) | 270 | |
| 249 | static unsigned int retry_interval, pkt_interval, target_interval; | 271 | // Create configuration from cli parameters |
| 250 | static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK; | 272 | typedef struct { |
| 251 | static pid_t pid; | 273 | int errorcode; |
| 252 | static struct timezone tz; | 274 | check_icmp_config config; |
| 253 | static struct timeval prog_start; | 275 | } check_icmp_config_wrapper; |
| 254 | static unsigned long long max_completion_time = 0; | 276 | check_icmp_config_wrapper process_arguments(int argc, char **argv) { |
| 255 | static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ | 277 | /* get calling name the old-fashioned way for portability instead |
| 256 | static int min_hosts_alive = -1; | 278 | * of relying on the glibc-ism __progname */ |
| 257 | static float pkt_backoff_factor = 1.5; | 279 | char *ptr = strrchr(argv[0], '/'); |
| 258 | static float target_backoff_factor = 1.5; | 280 | if (ptr) { |
| 259 | static bool rta_mode = false; | 281 | progname = &ptr[1]; |
| 260 | static bool pl_mode = false; | 282 | } else { |
| 261 | static bool jitter_mode = false; | 283 | progname = argv[0]; |
| 262 | static bool score_mode = false; | 284 | } |
| 263 | static bool mos_mode = false; | 285 | |
| 264 | static 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 **/ |
| 267 | static void crash(const char *fmt, ...) { | 612 | static 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 | ||
| 388 | static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { | 733 | static 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 | ||
| 457 | void parse_address(struct sockaddr_storage *addr, char *address, int size) { | 807 | void 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 | ||
| 468 | int main(int argc, char **argv) { | 820 | int 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 | ||
| 882 | static 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 | ||
| 998 | static 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 | */ |
| 942 | static int wait_for_reply(int sock, u_int t) { | 1085 | static 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 */ |
| 1140 | static int send_icmp_ping(int sock, struct rta_host *host) { | 1273 | static 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(¤t_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, ¤t_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 | ||
| 1244 | static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { | 1391 | static 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 | ||
| 1315 | static 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 | ||
| 1501 | static 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 | ||
| 1680 | static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { | 1567 | static 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 | ||
| 1702 | static int add_target_ip(char *arg, struct sockaddr_storage *in) { | 1580 | static 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 | |||
| 1587 | static 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 */ |
| 1788 | static int add_target(char *arg) { | 1642 | static 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 | ||
| 1865 | static void set_source_ip(char *arg) { | 1766 | static 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. */ |
| 1879 | static in_addr_t get_ip_address(const char *ifname) { | 1780 | static 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 | */ |
| 1909 | static u_int get_timevar(const char *str) { | 1810 | static 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) */ | 1885 | static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) { |
| 1972 | static 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 | */ |
| 2018 | static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) { | 1944 | static 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 | ||
| 2048 | static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) { | 1993 | static 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 | ||
| 2083 | unsigned short icmp_checksum(uint16_t *p, size_t n) { | 2035 | unsigned 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 | ||
| 2105 | void print_help(void) { | 2057 | void 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 | ||
| 2193 | void print_usage(void) { | 2140 | void 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 | |||
| 2145 | static 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 | |||
| 2177 | mp_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 | |||
| 2445 | evaluate_host_wrapper evaluate_host(check_icmp_target_container host, | ||
| 2446 | check_icmp_mode_switches modes, check_icmp_threshold warn, | ||
| 2447 | check_icmp_threshold crit) { | ||
| 2448 | evaluate_host_wrapper result = { | ||
| 2449 | .targets_warn = 0, | ||
| 2450 | .targets_ok = 0, | ||
| 2451 | .sc_host = mp_subcheck_init(), | ||
| 2452 | }; | ||
| 2453 | result.sc_host = mp_set_subcheck_default_state(result.sc_host, STATE_OK); | ||
| 2454 | |||
| 2455 | result.sc_host.output = strdup(host.name); | ||
| 2456 | |||
| 2457 | ping_target *target = host.target_list; | ||
| 2458 | for (unsigned int i = 0; i < host.number_of_targets; i++) { | ||
| 2459 | mp_subcheck sc_target = evaluate_target(*target, modes, warn, crit); | ||
| 2460 | |||
| 2461 | mp_state_enum target_state = mp_compute_subcheck_state(sc_target); | ||
| 2462 | |||
| 2463 | if (target_state == STATE_WARNING) { | ||
| 2464 | result.targets_warn++; | ||
| 2465 | } else if (target_state == STATE_OK) { | ||
| 2466 | result.targets_ok++; | ||
| 2467 | } | ||
| 2468 | mp_add_subcheck_to_subcheck(&result.sc_host, sc_target); | ||
| 2469 | |||
| 2470 | target = target->next; | ||
| 2471 | } | ||
| 2472 | |||
| 2473 | return result; | ||
| 2196 | } | 2474 | } |
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c new file mode 100644 index 00000000..1b96a392 --- /dev/null +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | #include "./config.h" | ||
| 2 | #include <math.h> | ||
| 3 | #include <netinet/in.h> | ||
| 4 | #include <sys/socket.h> | ||
| 5 | #include "./check_icmp_helpers.h" | ||
| 6 | #include "../../plugins/netutils.h" | ||
| 7 | |||
| 8 | // timeout as a global variable to make it available to the timeout handler | ||
| 9 | unsigned int timeout = DEFAULT_TIMEOUT; | ||
| 10 | |||
| 11 | check_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 | |||
| 59 | ping_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 | |||
| 71 | check_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 | |||
| 77 | ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) { | ||
| 78 | ping_target_create_wrapper result = { | ||
| 79 | .errorcode = 0, | ||
| 80 | }; | ||
| 81 | |||
| 82 | struct sockaddr_storage *tmp_addr = &address; | ||
| 83 | |||
| 84 | /* disregard obviously stupid addresses | ||
| 85 | * (I didn't find an ipv6 equivalent to INADDR_NONE) */ | ||
| 86 | if (((tmp_addr->ss_family == AF_INET && | ||
| 87 | (((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_NONE || | ||
| 88 | ((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_ANY))) || | ||
| 89 | (tmp_addr->ss_family == AF_INET6 && | ||
| 90 | (((struct sockaddr_in6 *)tmp_addr)->sin6_addr.s6_addr == in6addr_any.s6_addr))) { | ||
| 91 | result.errorcode = 1; | ||
| 92 | return result; | ||
| 93 | } | ||
| 94 | |||
| 95 | /* add the fresh ip */ | ||
| 96 | ping_target target = ping_target_init(); | ||
| 97 | |||
| 98 | /* fill out the sockaddr_storage struct */ | ||
| 99 | target.address = address; | ||
| 100 | |||
| 101 | result.host = target; | ||
| 102 | |||
| 103 | return result; | ||
| 104 | } | ||
| 105 | |||
| 106 | check_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 | |||
| 115 | unsigned 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 | |||
| 12 | typedef 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 | |||
| 38 | ping_target ping_target_init(); | ||
| 39 | |||
| 40 | typedef struct { | ||
| 41 | char *name; | ||
| 42 | ping_target *target_list; | ||
| 43 | unsigned int number_of_targets; | ||
| 44 | } check_icmp_target_container; | ||
| 45 | |||
| 46 | check_icmp_target_container check_icmp_target_container_init(); | ||
| 47 | |||
| 48 | typedef 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 | |||
| 55 | check_icmp_state check_icmp_state_init(); | ||
| 56 | |||
| 57 | typedef struct { | ||
| 58 | int errorcode; | ||
| 59 | ping_target host; | ||
| 60 | } ping_target_create_wrapper; | ||
| 61 | |||
| 62 | typedef struct { | ||
| 63 | int socket4; | ||
| 64 | int socket6; | ||
| 65 | } check_icmp_socket_set; | ||
| 66 | |||
| 67 | ping_target_create_wrapper ping_target_create(struct sockaddr_storage address); | ||
| 68 | unsigned int ping_target_list_append(ping_target *list, ping_target *elem); | ||
diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h new file mode 100644 index 00000000..be388d0a --- /dev/null +++ b/plugins-root/check_icmp.d/config.h | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "../../lib/states.h" | ||
| 5 | #include <stddef.h> | ||
| 6 | #include <netinet/in_systm.h> | ||
| 7 | #include <netinet/in.h> | ||
| 8 | #include <netinet/ip.h> | ||
| 9 | #include <netinet/ip6.h> | ||
| 10 | #include <netinet/ip_icmp.h> | ||
| 11 | #include <netinet/icmp6.h> | ||
| 12 | #include <arpa/inet.h> | ||
| 13 | #include <stdint.h> | ||
| 14 | #include "./check_icmp_helpers.h" | ||
| 15 | #include "output.h" | ||
| 16 | |||
| 17 | /* threshold structure. all values are maximum allowed, exclusive */ | ||
| 18 | typedef 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 | */ | ||
| 38 | typedef enum { | ||
| 39 | MODE_RTA, | ||
| 40 | MODE_HOSTCHECK, | ||
| 41 | MODE_ALL, | ||
| 42 | MODE_ICMP, | ||
| 43 | } check_icmp_execution_mode; | ||
| 44 | |||
| 45 | typedef 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 | |||
| 54 | typedef 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 | |||
| 84 | check_icmp_config check_icmp_config_init(); | ||
| 85 | |||
| 86 | /* the data structure */ | ||
| 87 | typedef struct icmp_ping_data { | ||
| 88 | struct timeval stime; /* timestamp (saved in protocol struct as well) */ | ||
| 89 | unsigned short ping_id; | ||
| 90 | } icmp_ping_data; | ||
| 91 | |||
| 92 | #define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */ | ||
| 93 | #define IP_HDR_SIZE 20 | ||
| 94 | #define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN) | ||
| 95 | #define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data) | ||
| 96 | #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44) | ||
| 97 | |||
| 98 | /* 80 msec packet interval by default */ | ||
| 99 | // DEPRECATED, remove when removing the option | ||
| 100 | #define DEFAULT_PKT_INTERVAL 80000 | ||
| 101 | |||
| 102 | #define DEFAULT_TARGET_INTERVAL 0 | ||
| 103 | |||
| 104 | #define DEFAULT_WARN_RTA 200000 | ||
| 105 | #define DEFAULT_CRIT_RTA 500000 | ||
| 106 | #define DEFAULT_WARN_PL 40 | ||
| 107 | #define DEFAULT_CRIT_PL 80 | ||
| 108 | |||
| 109 | #define DEFAULT_TIMEOUT 10 | ||
| 110 | #define DEFAULT_TTL 64 | ||
| 111 | |||
| 112 | #define DEFAULT_NUMBER_OF_PACKETS 5 | ||
| 113 | |||
| 114 | #define PACKET_BACKOFF_FACTOR 1.5 | ||
| 115 | #define TARGET_BACKOFF_FACTOR 1.5 | ||
diff --git a/plugins-root/pst3.c b/plugins-root/pst3.c index 1f69f3a6..1bfe3d35 100644 --- a/plugins-root/pst3.c +++ b/plugins-root/pst3.c | |||
| @@ -1,45 +1,45 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * pst3 | 3 | * pst3 |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2008 Monitoring Plugins Development Team | 6 | * Copyright (c) 2008 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the pst3 executable. This is a replacement ps command | 10 | * This file contains the pst3 executable. This is a replacement ps command |
| 11 | * for Solaris to get output which provides a long argument listing, which | 11 | * for Solaris to get output which provides a long argument listing, which |
| 12 | * is not possible with the standard ps command (due to truncation). /usr/ucb/ps | 12 | * is not possible with the standard ps command (due to truncation). /usr/ucb/ps |
| 13 | * also has issues where some fields run into each other. | 13 | * also has issues where some fields run into each other. |
| 14 | * | 14 | * |
| 15 | * This executable works by reading process address structures, so needs | 15 | * This executable works by reading process address structures, so needs |
| 16 | * to be executed as root | 16 | * to be executed as root |
| 17 | * | 17 | * |
| 18 | * Originally written by R.W.Ingraham | 18 | * Originally written by R.W.Ingraham |
| 19 | * Rewritten by Duncan Ferguson (Altinity Ltd, June 2008) | 19 | * Rewritten by Duncan Ferguson (Altinity Ltd, June 2008) |
| 20 | * The rewrite was necessary as /dev/kmem is not available within | 20 | * The rewrite was necessary as /dev/kmem is not available within |
| 21 | * non-global zones on Solaris 10 | 21 | * non-global zones on Solaris 10 |
| 22 | * | 22 | * |
| 23 | * Details for rewrite came from | 23 | * Details for rewrite came from |
| 24 | * source of /usr/ucb/ps on Solaris: | 24 | * source of /usr/ucb/ps on Solaris: |
| 25 | * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff | 25 | * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff |
| 26 | * usenet group posting | 26 | * usenet group posting |
| 27 | * http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F | 27 | * http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F |
| 28 | * | 28 | * |
| 29 | * This program is free software: you can redistribute it and/or modify | 29 | * This program is free software: you can redistribute it and/or modify |
| 30 | * it under the terms of the GNU General Public License as published by | 30 | * it under the terms of the GNU General Public License as published by |
| 31 | * the Free Software Foundation, either version 3 of the License, or | 31 | * the Free Software Foundation, either version 3 of the License, or |
| 32 | * (at your option) any later version. | 32 | * (at your option) any later version. |
| 33 | * | 33 | * |
| 34 | * This program is distributed in the hope that it will be useful, | 34 | * This program is distributed in the hope that it will be useful, |
| 35 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 35 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 36 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 36 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 37 | * GNU General Public License for more details. | 37 | * GNU General Public License for more details. |
| 38 | * | 38 | * |
| 39 | * You should have received a copy of the GNU General Public License | 39 | * You should have received a copy of the GNU General Public License |
| 40 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 40 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 41 | * | 41 | * |
| 42 | *****************************************************************************/ | 42 | *****************************************************************************/ |
| 43 | 43 | ||
| 44 | #include <stdio.h> | 44 | #include <stdio.h> |
| 45 | #include <stdlib.h> | 45 | #include <stdlib.h> |
| @@ -55,14 +55,14 @@ | |||
| 55 | * Constants | 55 | * Constants |
| 56 | */ | 56 | */ |
| 57 | 57 | ||
| 58 | #define PROC_DIR "/proc" | 58 | #define PROC_DIR "/proc" |
| 59 | #define ARGS 30 | 59 | #define ARGS 30 |
| 60 | 60 | ||
| 61 | /* | 61 | /* |
| 62 | * Globals | 62 | * Globals |
| 63 | */ | 63 | */ |
| 64 | 64 | ||
| 65 | static char * szProg; | 65 | static char *szProg; |
| 66 | 66 | ||
| 67 | /* | 67 | /* |
| 68 | * Prototypes | 68 | * Prototypes |
| @@ -71,192 +71,179 @@ void usage(); | |||
| 71 | 71 | ||
| 72 | /*----------------------------------------------------------------------------*/ | 72 | /*----------------------------------------------------------------------------*/ |
| 73 | 73 | ||
| 74 | int main (int argc, char **argv) | 74 | int main(int argc, char **argv) { |
| 75 | { | 75 | DIR *procdir; |
| 76 | DIR *procdir; | 76 | struct dirent *proc; |
| 77 | struct dirent *proc; | 77 | char ps_name[ARGS]; |
| 78 | char ps_name[ARGS]; | 78 | char as_name[ARGS]; |
| 79 | char as_name[ARGS]; | 79 | psinfo_t psinfo; |
| 80 | psinfo_t psinfo; | 80 | |
| 81 | 81 | /* Set our program name global */ | |
| 82 | /* Set our program name global */ | 82 | if ((szProg = strrchr(argv[0], '/')) != NULL) { |
| 83 | if ((szProg = strrchr(argv[0], '/')) != NULL) | 83 | szProg++; |
| 84 | szProg++; | 84 | } else { |
| 85 | else | 85 | szProg = argv[0]; |
| 86 | szProg = argv[0]; | 86 | } |
| 87 | 87 | ||
| 88 | /* if given any parameters, print out help */ | 88 | /* if given any parameters, print out help */ |
| 89 | if(argc > 1) { | 89 | if (argc > 1) { |
| 90 | (void)usage(); | 90 | (void)usage(); |
| 91 | exit(1); | 91 | exit(1); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | /* Make sure that our euid is root */ | 94 | /* Make sure that our euid is root */ |
| 95 | if (geteuid() != 0) | 95 | if (geteuid() != 0) { |
| 96 | { | 96 | fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg); |
| 97 | fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg); | 97 | exit(1); |
| 98 | exit(1); | 98 | } |
| 99 | } | 99 | |
| 100 | 100 | if ((procdir = opendir(PROC_DIR)) == NULL) { | |
| 101 | if ((procdir = opendir(PROC_DIR)) == NULL) { | 101 | fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR); |
| 102 | fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR); | 102 | exit(1); |
| 103 | exit(1); | 103 | } |
| 104 | } | 104 | |
| 105 | 105 | /* Display column headings */ | |
| 106 | /* Display column headings */ | 106 | printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", 'S', "UID", "PID", "PPID", "VSZ", "RSS", "%CPU", |
| 107 | printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", | 107 | "COMMAND", "ARGS"); |
| 108 | 'S', | 108 | |
| 109 | "UID", | 109 | /* Zip through all of the process entries */ |
| 110 | "PID", | 110 | while ((proc = readdir(procdir))) { |
| 111 | "PPID", | 111 | int ps_fd; |
| 112 | "VSZ", | 112 | int as_fd; |
| 113 | "RSS", | 113 | off_t argoff; |
| 114 | "%CPU", | 114 | int i; |
| 115 | "COMMAND", | 115 | char *args; |
| 116 | "ARGS" | 116 | char *procname; |
| 117 | ); | 117 | char *ptr; |
| 118 | 118 | int argslen; | |
| 119 | /* Zip through all of the process entries */ | 119 | uintptr_t args_addr; |
| 120 | while((proc = readdir(procdir))) { | 120 | ; |
| 121 | int ps_fd; | 121 | uintptr_t *args_vecs; |
| 122 | int as_fd; | 122 | ; |
| 123 | off_t argoff; | 123 | int args_count; |
| 124 | int i; | 124 | |
| 125 | char *args; | 125 | if (proc->d_name[0] == '.') { |
| 126 | char *procname; | 126 | continue; |
| 127 | char *ptr; | 127 | } |
| 128 | int argslen; | 128 | |
| 129 | uintptr_t args_addr;; | 129 | sprintf(ps_name, "%s/%s/%s", PROC_DIR, proc->d_name, "psinfo"); |
| 130 | uintptr_t *args_vecs;; | 130 | sprintf(as_name, "%s/%s/%s", PROC_DIR, proc->d_name, "as"); |
| 131 | int args_count; | 131 | try_again: |
| 132 | 132 | if ((ps_fd = open(ps_name, O_RDONLY)) == -1) { | |
| 133 | if(proc->d_name[0] == '.') | 133 | continue; |
| 134 | continue; | 134 | } |
| 135 | 135 | ||
| 136 | sprintf(ps_name,"%s/%s/%s",PROC_DIR,proc->d_name,"psinfo"); | 136 | if ((as_fd = open(as_name, O_RDONLY)) == -1) { |
| 137 | sprintf(as_name,"%s/%s/%s",PROC_DIR,proc->d_name,"as"); | 137 | close(ps_fd); |
| 138 | try_again: | 138 | continue; |
| 139 | if((ps_fd = open(ps_name, O_RDONLY)) == -1) | 139 | } |
| 140 | continue; | 140 | |
| 141 | 141 | if (read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { | |
| 142 | if((as_fd = open(as_name, O_RDONLY)) == -1) { | 142 | int err = errno; |
| 143 | close(ps_fd); | 143 | close(ps_fd); |
| 144 | continue; | 144 | close(as_fd); |
| 145 | } | 145 | if (err == EAGAIN) { |
| 146 | 146 | goto try_again; | |
| 147 | if(read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { | 147 | } |
| 148 | int err = errno; | 148 | if (err != ENOENT) { |
| 149 | close(ps_fd); | 149 | fprintf(stderr, "%s: read() on %s: %s\n", szProg, ps_name, strerror(err)); |
| 150 | close(as_fd); | 150 | } |
| 151 | if(err == EAGAIN) goto try_again; | 151 | continue; |
| 152 | if(err != ENOENT) | 152 | } |
| 153 | fprintf(stderr, "%s: read() on %s: %s\n", szProg, | 153 | close(ps_fd); |
| 154 | ps_name, strerror(err)); | 154 | |
| 155 | continue; | 155 | /* system process, ignore since the previous version did */ |
| 156 | } | 156 | if (psinfo.pr_nlwp == 0 || strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0) { |
| 157 | close(ps_fd); | 157 | continue; |
| 158 | 158 | } | |
| 159 | /* system process, ignore since the previous version did */ | 159 | |
| 160 | if( | 160 | /* get the procname to match previous versions */ |
| 161 | psinfo.pr_nlwp == 0 || | 161 | procname = strdup(psinfo.pr_psargs); |
| 162 | strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0 | 162 | if ((ptr = strchr(procname, ' ')) != NULL) { |
| 163 | ) { | 163 | *ptr = '\0'; |
| 164 | continue; | 164 | } |
| 165 | } | 165 | if ((ptr = strrchr(procname, '/')) != NULL) { |
| 166 | 166 | ptr++; | |
| 167 | /* get the procname to match previous versions */ | 167 | } else { |
| 168 | procname = strdup(psinfo.pr_psargs); | 168 | ptr = procname; |
| 169 | if((ptr = strchr(procname, ' ')) != NULL) | 169 | } |
| 170 | *ptr = '\0'; | 170 | |
| 171 | if((ptr = strrchr(procname, '/')) != NULL) | 171 | /* |
| 172 | ptr++; | 172 | * print out what we currently know |
| 173 | else | 173 | */ |
| 174 | ptr = procname; | 174 | printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", psinfo.pr_lwp.pr_sname, psinfo.pr_euid, |
| 175 | 175 | psinfo.pr_pid, psinfo.pr_ppid, psinfo.pr_size, psinfo.pr_rssize, | |
| 176 | /* | 176 | ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), ptr); |
| 177 | * print out what we currently know | 177 | free(procname); |
| 178 | */ | 178 | |
| 179 | printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", | 179 | /* |
| 180 | psinfo.pr_lwp.pr_sname, | 180 | * and now for the command line stuff |
| 181 | psinfo.pr_euid, | 181 | */ |
| 182 | psinfo.pr_pid, | 182 | |
| 183 | psinfo.pr_ppid, | 183 | args_addr = psinfo.pr_argv; |
| 184 | psinfo.pr_size, | 184 | args_count = psinfo.pr_argc; |
| 185 | psinfo.pr_rssize, | 185 | args_vecs = malloc(args_count * sizeof(uintptr_t)); |
| 186 | ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), | 186 | |
| 187 | ptr | 187 | if (psinfo.pr_dmodel == PR_MODEL_NATIVE) { |
| 188 | ); | 188 | /* this process matches target process */ |
| 189 | free(procname); | 189 | pread(as_fd, args_vecs, args_count * sizeof(uintptr_t), args_addr); |
| 190 | 190 | } else { | |
| 191 | /* | 191 | /* this process is 64bit, target process is 32 bit*/ |
| 192 | * and now for the command line stuff | 192 | caddr32_t *args_vecs32 = (caddr32_t *)args_vecs; |
| 193 | */ | 193 | pread(as_fd, args_vecs32, args_count * sizeof(caddr32_t), args_addr); |
| 194 | 194 | for (i = args_count - 1; i >= 0; --i) { | |
| 195 | args_addr = psinfo.pr_argv; | 195 | args_vecs[i] = args_vecs32[i]; |
| 196 | args_count = psinfo.pr_argc; | 196 | } |
| 197 | args_vecs = malloc(args_count * sizeof(uintptr_t)); | 197 | } |
| 198 | 198 | ||
| 199 | if(psinfo.pr_dmodel == PR_MODEL_NATIVE) { | 199 | /* |
| 200 | /* this process matches target process */ | 200 | * now read in the args - if what we read in fills buffer |
| 201 | pread(as_fd,args_vecs, args_count * sizeof(uintptr_t), | 201 | * resize buffer and reread that bit again |
| 202 | args_addr); | 202 | */ |
| 203 | } else { | 203 | argslen = ARGS; |
| 204 | /* this process is 64bit, target process is 32 bit*/ | 204 | args = malloc(argslen + 1); |
| 205 | caddr32_t *args_vecs32 = (caddr32_t *)args_vecs; | 205 | for (i = 0; i < args_count; i++) { |
| 206 | pread(as_fd,args_vecs32,args_count * sizeof(caddr32_t), | 206 | memset(args, '\0', argslen + 1); |
| 207 | args_addr); | 207 | if (pread(as_fd, args, argslen, args_vecs[i]) <= 0) { |
| 208 | for (i=args_count-1;i>=0;--i) | 208 | break; |
| 209 | args_vecs[i]=args_vecs32[i]; | 209 | } |
| 210 | } | 210 | args[argslen] = '\0'; |
| 211 | 211 | if (strlen(args) == argslen) { | |
| 212 | /* | 212 | argslen += ARGS; |
| 213 | * now read in the args - if what we read in fills buffer | 213 | args = realloc(args, argslen + 1); |
| 214 | * resize buffer and reread that bit again | 214 | i--; |
| 215 | */ | 215 | continue; |
| 216 | argslen=ARGS; | 216 | } |
| 217 | args=malloc(argslen+1); | 217 | printf(" %s", args); |
| 218 | for(i=0;i<args_count;i++) { | 218 | } |
| 219 | memset(args,'\0',argslen+1); | 219 | free(args_vecs); |
| 220 | if(pread(as_fd, args, argslen, args_vecs[i]) <= 0) { | 220 | free(args); |
| 221 | break; | 221 | close(as_fd); |
| 222 | } | 222 | printf("\n"); |
| 223 | args[argslen]='\0'; | 223 | } |
| 224 | if(strlen(args) == argslen){ | 224 | |
| 225 | argslen += ARGS; | 225 | (void)closedir(procdir); |
| 226 | args = realloc(args, argslen + 1); | 226 | |
| 227 | i--; | 227 | return (0); |
| 228 | continue; | ||
| 229 | } | ||
| 230 | printf(" %s", args); | ||
| 231 | } | ||
| 232 | free(args_vecs); | ||
| 233 | free(args); | ||
| 234 | close(as_fd); | ||
| 235 | printf("\n"); | ||
| 236 | } | ||
| 237 | |||
| 238 | (void) closedir(procdir); | ||
| 239 | |||
| 240 | return (0); | ||
| 241 | } | 228 | } |
| 242 | 229 | ||
| 243 | /*----------------------------------------------------------------------------*/ | 230 | /*----------------------------------------------------------------------------*/ |
| 244 | 231 | ||
| 245 | void usage() { | 232 | void usage() { |
| 246 | printf("%s: Help output\n\n", szProg); | 233 | printf("%s: Help output\n\n", szProg); |
| 247 | printf("If this program is given any arguments, this help is displayed.\n"); | 234 | printf("If this program is given any arguments, this help is displayed.\n"); |
| 248 | printf("This command is used to print out the full command line for all\n"); | 235 | printf("This command is used to print out the full command line for all\n"); |
| 249 | printf("running processes because /usr/bin/ps is limited to 80 chars and\n"); | 236 | printf("running processes because /usr/bin/ps is limited to 80 chars and\n"); |
| 250 | printf("/usr/ucb/ps can merge columns together.\n\n"); | 237 | printf("/usr/ucb/ps can merge columns together.\n\n"); |
| 251 | printf("Columns are:\n"); | 238 | printf("Columns are:\n"); |
| 252 | printf("\tS - State of process - see 'ps' man page\n"); | 239 | printf("\tS - State of process - see 'ps' man page\n"); |
| 253 | printf("\tUID - UID of the process owner\n"); | 240 | printf("\tUID - UID of the process owner\n"); |
| 254 | printf("\tPID - PID of the process\n"); | 241 | printf("\tPID - PID of the process\n"); |
| 255 | printf("\tPPID - PID of the parent process\n"); | 242 | printf("\tPPID - PID of the parent process\n"); |
| 256 | printf("\tVSZ - Virtual memory usage (kilobytes)\n"); | 243 | printf("\tVSZ - Virtual memory usage (kilobytes)\n"); |
| 257 | printf("\tRSS - Real memory usage (kilobytes)\n"); | 244 | printf("\tRSS - Real memory usage (kilobytes)\n"); |
| 258 | printf("\t%%CPU - CPU usage\n"); | 245 | printf("\t%%CPU - CPU usage\n"); |
| 259 | printf("\tCOMMAND - Command being run\n"); | 246 | printf("\tCOMMAND - Command being run\n"); |
| 260 | printf("\tARGS - Full command line with arguments\n"); | 247 | printf("\tARGS - Full command line with arguments\n"); |
| 261 | return; | 248 | return; |
| 262 | } | 249 | } |
diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t index ce627736..70392154 100644 --- a/plugins-root/t/check_dhcp.t +++ b/plugins-root/t/check_dhcp.t | |||
| @@ -12,14 +12,14 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO", | |||
| 12 | "no" ); | 12 | "no" ); |
| 13 | 13 | ||
| 14 | if ($allow_sudo eq "yes" or $> == 0) { | 14 | if ($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 | } |
| 19 | my $sudo = $> == 0 ? '' : 'sudo'; | 19 | my $sudo = $> == 0 ? '' : 'sudo'; |
| 20 | 20 | ||
| 21 | my $successOutput = '/OK: Received \d+ DHCPOFFER\(s\), \d+ of 1 requested servers responded, max lease time = \d+ sec\./'; | 21 | my $successOutput = '/Received \d+ DHCPOFFER(s)*, max lease time = \d+ seconds/'; |
| 22 | my $failureOutput = '/CRITICAL: (No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; | 22 | my $failureOutput = '/(No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; |
| 23 | my $invalidOutput = '/Invalid hostname/'; | 23 | my $invalidOutput = '/Invalid hostname/'; |
| 24 | 24 | ||
| 25 | my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE", | 25 | my $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 | ||
| 37 | my $output_format = "--output-format mp-test-json"; | ||
| 38 | |||
| 37 | # try to determince interface | 39 | # try to determince interface |
| 38 | my $interface = ''; | 40 | my $interface = ''; |
| 39 | 41 | ||
| @@ -49,19 +51,21 @@ my $res; | |||
| 49 | SKIP: { | 51 | SKIP: { |
| 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 | ||
| 58 | SKIP: { | 61 | SKIP: { |
| 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 | ||
| 67 | SKIP: { | 71 | SKIP: { |
| @@ -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 | ||
| 14 | if ($allow_sudo eq "yes" or $> == 0) { | 14 | if ($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 | } |
| 19 | my $sudo = $> == 0 ? '' : 'sudo'; | 19 | my $sudo = $> == 0 ? '' : 'sudo'; |
| 20 | 20 | ||
| 21 | my $successOutput = '/OK - .*? rta (?:[\d\.]+ms)|(?:nan), lost \d+%/'; | ||
| 22 | my $failureOutput = '/(WARNING|CRITICAL) - .*? rta (?:[\d\.]+ms > [\d\.]+ms|nan)/'; | ||
| 23 | |||
| 24 | my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE", | 21 | my $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", | |||
| 36 | my $res; | 33 | my $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 | ); |
| 41 | is( $res->return_code, 0, "Syntax ok" ); | 38 | is( $res->return_code, 0, "Syntax ok" ); |
| 42 | like( $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 | ); |
| 47 | is( $res->return_code, 1, "Syntax ok, with forced warning" ); | 43 | is( $res->return_code, 1, "Syntax ok, with forced warning" ); |
| 48 | like( $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 | ); |
| 53 | is( $res->return_code, 2, "Syntax ok, with forced critical" ); | 48 | is( $res->return_code, 2, "Syntax ok, with forced critical" ); |
| 54 | like( $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 | ); |
| 59 | is( $res->return_code, 2, "Timeout - host nonresponsive" ); | 53 | is( $res->return_code, 2, "Timeout - host nonresponsive" ); |
| 60 | like( $res->output, '/pl=100%/', "Error contains 'pl=100%' string (for 100% packet loss)" ); | ||
| 61 | like( $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 | ); |
| 66 | is( $res->return_code, 3, "No hostname" ); | 58 | is( $res->return_code, 3, "No hostname" ); |
| 67 | like( $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 | ); |
| 72 | is( $res->return_code, 0, "One host nonresponsive - zero required" ); | 63 | is( $res->return_code, 0, "One host nonresponsive - zero required" ); |
| 73 | like( $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 | ); |
| 78 | is( $res->return_code, 0, "One of two host nonresponsive - one required" ); | 68 | is( $res->return_code, 0, "One of two host nonresponsive - one required" ); |
| 79 | like( $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 | ); |
| 84 | is( $res->return_code, 2, "One of two host nonresponsive - two required" ); | 73 | is( $res->return_code, 2, "One of two host nonresponsive - two required" ); |
| 85 | like( $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 | ); |
| 90 | is( $res->return_code, 0, "IPv4 source_ip accepted" ); | 78 | is( $res->return_code, 0, "IPv4 source_ip accepted" ); |
| 91 | like( $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 | ); |
| 96 | is( $res->return_code, 0, "Try max packet size" ); | 83 | is( $res->return_code, 0, "Try max packet size" ); |
| 97 | like( $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 | ); |
| 102 | is( $res->return_code, 0, "rta works" ); | 88 | is( $res->return_code, 0, "rta works" ); |
| 103 | like( $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 | ); |
| 107 | is( $res->return_code, 0, "pl works" ); | 92 | is( $res->return_code, 0, "pl works" ); |
| 108 | like( $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 | ); |
| 113 | is( $res->return_code, 0, "jitter works" ); | 97 | is( $res->return_code, 0, "jitter works" ); |
| 114 | like( $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 | ); |
| 119 | is( $res->return_code, 0, "mos works" ); | 102 | is( $res->return_code, 0, "mos works" ); |
| 120 | like( $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 | ); |
| 125 | is( $res->return_code, 0, "score works" ); | 107 | is( $res->return_code, 0, "score works" ); |
| 126 | like( $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 | ); |
| 131 | is( $res->return_code, 0, "order works" ); | 112 | is( $res->return_code, 0, "order works" ); |
| 132 | like( $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 | ); |
| 137 | is( $res->return_code, 0, "order works" ); | 117 | is( $res->return_code, 0, "order works" ); |
| 138 | like( $res->output, '/Packets in order/', "Output OK" ); | ||
| 139 | like( $res->output, '/Score \d/', "Output OK" ); | ||
| 140 | like( $res->output, '/MOS \d/', "Output OK" ); | ||
| 141 | like( $res->output, '/jitter \d/', "Output OK" ); | ||
| 142 | like( $res->output, '/lost 0%/', "Output OK" ); | ||
| 143 | like( $res->output, $successOutput, "Output OK" ); | ||
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d43c1971..877c0947 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am | |||
| @@ -13,8 +13,14 @@ AM_CFLAGS = -DNP_VERSION='"$(NP_VERSION)"' | |||
| 13 | 13 | ||
| 14 | VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t | 14 | VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t |
| 15 | 15 | ||
| 16 | AM_CPPFLAGS = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl \ | 16 | AM_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 | ||
| 19 | localedir = $(datadir)/locale | 25 | localedir = $(datadir)/locale |
| 20 | # gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this | 26 | # gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this |
| @@ -27,26 +33,69 @@ MATHLIBS = @MATHLIBS@ | |||
| 27 | #AM_CFLAGS = -Wall | 33 | #AM_CFLAGS = -Wall |
| 28 | 34 | ||
| 29 | libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ | 35 | libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ |
| 30 | check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_nwstat check_overcr check_ping \ | 36 | check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \ |
| 31 | check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ | 37 | check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ |
| 32 | check_ups check_users negate \ | 38 | check_ups check_users negate \ |
| 33 | urlize @EXTRAS@ | 39 | urlize @EXTRAS@ \ |
| 40 | check_snmp | ||
| 34 | 41 | ||
| 35 | check_tcp_programs = check_ftp check_imap check_nntp check_pop \ | 42 | check_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 | ||
| 38 | EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ | 45 | EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \ |
| 39 | check_swap check_fping check_ldap check_game check_dig \ | 46 | check_swap check_fping check_ldap check_game check_dig \ |
| 40 | check_nagios check_by_ssh check_dns check_nt check_ide_smart \ | 47 | check_nagios check_by_ssh check_dns check_ide_smart \ |
| 41 | check_procs check_mysql_query check_apt check_dbi check_curl \ | 48 | check_procs check_mysql_query check_apt check_dbi check_curl \ |
| 42 | \ | 49 | \ |
| 43 | tests/test_check_swap | 50 | tests/test_check_swap \ |
| 51 | tests/test_check_snmp \ | ||
| 52 | tests/test_check_disk | ||
| 44 | 53 | ||
| 45 | SUBDIRS = picohttpparser | 54 | SUBDIRS = picohttpparser |
| 46 | 55 | ||
| 47 | np_test_scripts = tests/test_check_swap.t | 56 | np_test_scripts = tests/test_check_swap.t \ |
| 48 | 57 | tests/test_check_snmp.t \ | |
| 49 | EXTRA_DIST = t tests $(np_test_scripts) check_swap.d | 58 | tests/test_check_disk.t |
| 59 | |||
| 60 | EXTRA_DIST = t \ | ||
| 61 | tests \ | ||
| 62 | $(np_test_scripts) \ | ||
| 63 | negate.d \ | ||
| 64 | check_swap.d \ | ||
| 65 | check_ldap.d \ | ||
| 66 | check_hpjd.d \ | ||
| 67 | check_game.d \ | ||
| 68 | check_radius.d \ | ||
| 69 | check_curl.d \ | ||
| 70 | check_disk.d \ | ||
| 71 | check_time.d \ | ||
| 72 | check_users.d \ | ||
| 73 | check_load.d \ | ||
| 74 | check_nagios.d \ | ||
| 75 | check_dbi.d \ | ||
| 76 | check_tcp.d \ | ||
| 77 | check_real.d \ | ||
| 78 | check_ssh.d \ | ||
| 79 | check_dns.d \ | ||
| 80 | check_mrtgtraf.d \ | ||
| 81 | check_mysql_query.d \ | ||
| 82 | check_mrtg.d \ | ||
| 83 | check_ntp_peer.d \ | ||
| 84 | check_apt.d \ | ||
| 85 | check_pgsql.d \ | ||
| 86 | check_procs.d \ | ||
| 87 | check_ping.d \ | ||
| 88 | check_by_ssh.d \ | ||
| 89 | check_smtp.d \ | ||
| 90 | check_snmp.d \ | ||
| 91 | check_mysql.d \ | ||
| 92 | check_ntp_time.d \ | ||
| 93 | check_dig.d \ | ||
| 94 | check_cluster.d \ | ||
| 95 | check_curl.d \ | ||
| 96 | check_cluster.d \ | ||
| 97 | check_ups.d \ | ||
| 98 | check_fping.d | ||
| 50 | 99 | ||
| 51 | PLUGINHDRS = common.h | 100 | PLUGINHDRS = common.h |
| 52 | 101 | ||
| @@ -85,9 +134,11 @@ check_cluster_LDADD = $(BASEOBJS) | |||
| 85 | check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser | 134 | check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser |
| 86 | check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser | 135 | check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser |
| 87 | check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a | 136 | check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a |
| 137 | check_curl_SOURCES = check_curl.c check_curl.d/check_curl_helpers.c | ||
| 88 | check_dbi_LDADD = $(NETLIBS) $(DBILIBS) | 138 | check_dbi_LDADD = $(NETLIBS) $(DBILIBS) |
| 89 | check_dig_LDADD = $(NETLIBS) | 139 | check_dig_LDADD = $(NETLIBS) |
| 90 | check_disk_LDADD = $(BASEOBJS) | 140 | check_disk_LDADD = $(BASEOBJS) |
| 141 | check_disk_SOURCES = check_disk.c check_disk.d/utils_disk.c | ||
| 91 | check_dns_LDADD = $(NETLIBS) | 142 | check_dns_LDADD = $(NETLIBS) |
| 92 | check_dummy_LDADD = $(BASEOBJS) | 143 | check_dummy_LDADD = $(BASEOBJS) |
| 93 | check_fping_LDADD = $(NETLIBS) | 144 | check_fping_LDADD = $(NETLIBS) |
| @@ -105,17 +156,17 @@ check_mysql_query_CFLAGS = $(AM_CFLAGS) $(MYSQLCFLAGS) | |||
| 105 | check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE) | 156 | check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE) |
| 106 | check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) | 157 | check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) |
| 107 | check_nagios_LDADD = $(BASEOBJS) | 158 | check_nagios_LDADD = $(BASEOBJS) |
| 108 | check_nt_LDADD = $(NETLIBS) | ||
| 109 | check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) | 159 | check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) |
| 110 | check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) | 160 | check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) |
| 111 | check_nwstat_LDADD = $(NETLIBS) | ||
| 112 | check_overcr_LDADD = $(NETLIBS) | ||
| 113 | check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) | 161 | check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) |
| 114 | check_ping_LDADD = $(NETLIBS) | 162 | check_ping_LDADD = $(NETLIBS) |
| 115 | check_procs_LDADD = $(BASEOBJS) | 163 | check_procs_LDADD = $(BASEOBJS) |
| 116 | check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) | 164 | check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) |
| 117 | check_real_LDADD = $(NETLIBS) | 165 | check_real_LDADD = $(NETLIBS) |
| 166 | check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c | ||
| 118 | check_snmp_LDADD = $(BASEOBJS) | 167 | check_snmp_LDADD = $(BASEOBJS) |
| 168 | check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs` | ||
| 169 | check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags` | ||
| 119 | check_smtp_LDADD = $(SSLOBJS) | 170 | check_smtp_LDADD = $(SSLOBJS) |
| 120 | check_ssh_LDADD = $(NETLIBS) | 171 | check_ssh_LDADD = $(NETLIBS) |
| 121 | check_swap_SOURCES = check_swap.c check_swap.d/swap.c | 172 | check_swap_SOURCES = check_swap.c check_swap.d/swap.c |
| @@ -124,6 +175,7 @@ check_tcp_LDADD = $(SSLOBJS) | |||
| 124 | check_time_LDADD = $(NETLIBS) | 175 | check_time_LDADD = $(NETLIBS) |
| 125 | check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) | 176 | check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) |
| 126 | check_ups_LDADD = $(NETLIBS) | 177 | check_ups_LDADD = $(NETLIBS) |
| 178 | check_users_SOURCES = check_users.c check_users.d/users.c | ||
| 127 | check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) | 179 | check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) |
| 128 | check_by_ssh_LDADD = $(NETLIBS) | 180 | check_by_ssh_LDADD = $(NETLIBS) |
| 129 | check_ide_smart_LDADD = $(BASEOBJS) | 181 | check_ide_smart_LDADD = $(BASEOBJS) |
| @@ -136,6 +188,10 @@ endif | |||
| 136 | 188 | ||
| 137 | tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap | 189 | tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap |
| 138 | tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c | 190 | tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c |
| 191 | tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap | ||
| 192 | tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c | ||
| 193 | tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap | ||
| 194 | tests_test_check_disk_SOURCES = tests/test_check_disk.c | ||
| 139 | 195 | ||
| 140 | ############################################################################## | 196 | ############################################################################## |
| 141 | # secondary dependencies | 197 | # secondary dependencies |
diff --git a/plugins/check_apt.c b/plugins/check_apt.c index 1eda45dd..9ed5b6cf 100644 --- a/plugins/check_apt.c +++ b/plugins/check_apt.c | |||
| @@ -29,92 +29,98 @@ | |||
| 29 | * | 29 | * |
| 30 | *****************************************************************************/ | 30 | *****************************************************************************/ |
| 31 | 31 | ||
| 32 | #include "perfdata.h" | ||
| 32 | const char *progname = "check_apt"; | 33 | const char *progname = "check_apt"; |
| 33 | const char *copyright = "2006-2024"; | 34 | const char *copyright = "2006-2024"; |
| 34 | const char *email = "devel@monitoring-plugins.org"; | 35 | const char *email = "devel@monitoring-plugins.org"; |
| 35 | 36 | ||
| 37 | #include "states.h" | ||
| 38 | #include "output.h" | ||
| 36 | #include "common.h" | 39 | #include "common.h" |
| 37 | #include "runcmd.h" | 40 | #include "runcmd.h" |
| 38 | #include "utils.h" | 41 | #include "utils.h" |
| 39 | #include "regex.h" | 42 | #include "regex.h" |
| 43 | #include "check_apt.d/config.h" | ||
| 40 | 44 | ||
| 41 | /* some constants */ | ||
| 42 | typedef enum { | ||
| 43 | UPGRADE, | ||
| 44 | DIST_UPGRADE, | ||
| 45 | NO_UPGRADE | ||
| 46 | } upgrade_type; | ||
| 47 | |||
| 48 | /* Character for hidden input file option (for testing). */ | ||
| 49 | #define INPUT_FILE_OPT CHAR_MAX + 1 | ||
| 50 | /* the default opts can be overridden via the cmdline */ | 45 | /* the default opts can be overridden via the cmdline */ |
| 51 | #define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" | 46 | const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq"; |
| 52 | #define UPDATE_DEFAULT_OPTS "-q" | 47 | const char *UPDATE_DEFAULT_OPTS = "-q"; |
| 48 | |||
| 53 | /* until i commit the configure.in patch which gets this, i'll define | 49 | /* until i commit the configure.in patch which gets this, i'll define |
| 54 | * it here as well */ | 50 | * it here as well */ |
| 55 | #ifndef PATH_TO_APTGET | 51 | #ifndef PATH_TO_APTGET |
| 56 | # define PATH_TO_APTGET "/usr/bin/apt-get" | 52 | # define PATH_TO_APTGET "/usr/bin/apt-get" |
| 57 | #endif /* PATH_TO_APTGET */ | 53 | #endif /* PATH_TO_APTGET */ |
| 54 | |||
| 58 | /* String found at the beginning of the apt output lines we're interested in */ | 55 | /* String found at the beginning of the apt output lines we're interested in */ |
| 59 | #define PKGINST_PREFIX "Inst " | 56 | const char *PKGINST_PREFIX = "Inst "; |
| 60 | /* the RE that catches security updates */ | 57 | /* the RE that catches security updates */ |
| 61 | #define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" | 58 | const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)"; |
| 62 | 59 | ||
| 63 | /* some standard functions */ | 60 | /* some standard functions */ |
| 64 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 61 | typedef struct { |
| 62 | int errorcode; | ||
| 63 | check_apt_config config; | ||
| 64 | } check_apt_config_wrapper; | ||
| 65 | static check_apt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 65 | static void print_help(void); | 66 | static void print_help(void); |
| 66 | void print_usage(void); | 67 | void print_usage(void); |
| 67 | 68 | ||
| 68 | /* construct the appropriate apt-get cmdline */ | 69 | /* construct the appropriate apt-get cmdline */ |
| 69 | static char *construct_cmdline(upgrade_type u, const char *opts); | 70 | static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/); |
| 71 | |||
| 70 | /* run an apt-get update */ | 72 | /* run an apt-get update */ |
| 71 | static int run_update(void); | 73 | typedef struct { |
| 74 | mp_subcheck sc; | ||
| 75 | bool stderr_warning; | ||
| 76 | bool exec_warning; | ||
| 77 | } run_update_result; | ||
| 78 | static run_update_result run_update(char *update_opts); | ||
| 72 | 79 | ||
| 73 | typedef struct { | 80 | typedef struct { |
| 74 | int errorcode; | 81 | int errorcode; |
| 75 | int package_count; | 82 | size_t package_count; |
| 76 | int security_package_count; | 83 | size_t security_package_count; |
| 77 | char **packages_list; | 84 | char **packages_list; |
| 78 | char **secpackages_list; | 85 | char **secpackages_list; |
| 86 | bool exec_warning; | ||
| 79 | } run_upgrade_result; | 87 | } run_upgrade_result; |
| 80 | 88 | ||
| 81 | /* run an apt-get upgrade */ | 89 | /* run an apt-get upgrade */ |
| 82 | static run_upgrade_result run_upgrade(void); | 90 | run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude, |
| 91 | const char *do_critical, const char *upgrade_opts, | ||
| 92 | const char *input_filename); | ||
| 83 | 93 | ||
| 84 | /* add another clause to a regexp */ | 94 | /* add another clause to a regexp */ |
| 85 | static char *add_to_regexp(char *expr, const char *next); | 95 | static char *add_to_regexp(char * /*expr*/, const char * /*next*/); |
| 86 | /* extract package name from Inst line */ | 96 | /* extract package name from Inst line */ |
| 87 | static char *pkg_name(char *line); | 97 | static char *pkg_name(char * /*line*/); |
| 88 | /* string comparison function for qsort */ | 98 | /* string comparison function for qsort */ |
| 89 | static int cmpstringp(const void *p1, const void *p2); | 99 | static int cmpstringp(const void * /*p1*/, const void * /*p2*/); |
| 90 | 100 | ||
| 91 | /* configuration variables */ | 101 | /* configuration variables */ |
| 92 | static int verbose = 0; /* -v */ | 102 | static int verbose = 0; /* -v */ |
| 93 | static bool list = false; /* list packages available for upgrade */ | ||
| 94 | static bool do_update = false; /* whether to call apt-get update */ | ||
| 95 | static bool only_critical = false; /* whether to warn about non-critical updates */ | ||
| 96 | static upgrade_type upgrade = UPGRADE; /* which type of upgrade to do */ | ||
| 97 | static char *upgrade_opts = NULL; /* options to override defaults for upgrade */ | ||
| 98 | static char *update_opts = NULL; /* options to override defaults for update */ | ||
| 99 | static char *do_include = NULL; /* regexp to only include certain packages */ | ||
| 100 | static char *do_exclude = NULL; /* regexp to only exclude certain packages */ | ||
| 101 | static char *do_critical = NULL; /* regexp specifying critical packages */ | ||
| 102 | static char *input_filename = NULL; /* input filename for testing */ | ||
| 103 | /* number of packages available for upgrade to return WARNING status */ | ||
| 104 | static int packages_warning = 1; | ||
| 105 | 103 | ||
| 106 | /* other global variables */ | 104 | /* other global variables */ |
| 107 | static int stderr_warning = 0; /* if a cmd issued output on stderr */ | 105 | static bool stderr_warning = false; /* if a cmd issued output on stderr */ |
| 108 | static int exec_warning = 0; /* if a cmd exited non-zero */ | 106 | static bool exec_warning = false; /* if a cmd exited non-zero */ |
| 109 | 107 | ||
| 110 | int main(int argc, char **argv) { | 108 | int main(int argc, char **argv) { |
| 111 | /* Parse extra opts if any */ | 109 | /* Parse extra opts if any */ |
| 112 | argv = np_extra_opts(&argc, argv, progname); | 110 | argv = np_extra_opts(&argc, argv, progname); |
| 113 | 111 | ||
| 114 | if (process_arguments(argc, argv) == ERROR) { | 112 | check_apt_config_wrapper tmp_config = process_arguments(argc, argv); |
| 113 | |||
| 114 | if (tmp_config.errorcode == ERROR) { | ||
| 115 | usage_va(_("Could not parse arguments")); | 115 | usage_va(_("Could not parse arguments")); |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | const check_apt_config config = tmp_config.config; | ||
| 119 | |||
| 120 | if (config.output_format_is_set) { | ||
| 121 | mp_set_format(config.output_format); | ||
| 122 | } | ||
| 123 | |||
| 118 | /* Set signal handling and alarm timeout */ | 124 | /* Set signal handling and alarm timeout */ |
| 119 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 125 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| 120 | usage_va(_("Cannot catch SIGALRM")); | 126 | usage_va(_("Cannot catch SIGALRM")); |
| @@ -123,54 +129,91 @@ int main(int argc, char **argv) { | |||
| 123 | /* handle timeouts gracefully... */ | 129 | /* handle timeouts gracefully... */ |
| 124 | alarm(timeout_interval); | 130 | alarm(timeout_interval); |
| 125 | 131 | ||
| 126 | int result = STATE_UNKNOWN; | 132 | mp_check overall = mp_check_init(); |
| 127 | /* if they want to run apt-get update first... */ | 133 | /* if they want to run apt-get update first... */ |
| 128 | if (do_update) { | 134 | if (config.do_update) { |
| 129 | result = run_update(); | 135 | run_update_result update_result = run_update(config.update_opts); |
| 136 | |||
| 137 | mp_add_subcheck_to_check(&overall, update_result.sc); | ||
| 130 | } | 138 | } |
| 131 | 139 | ||
| 132 | /* apt-get upgrade */ | 140 | /* apt-get upgrade */ |
| 133 | run_upgrade_result upgrad_res = run_upgrade(); | 141 | run_upgrade_result upgrad_res = |
| 142 | run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical, | ||
| 143 | config.upgrade_opts, config.input_filename); | ||
| 134 | 144 | ||
| 135 | result = max_state(result, upgrad_res.errorcode); | 145 | mp_subcheck sc_run_upgrade = mp_subcheck_init(); |
| 136 | int packages_available = upgrad_res.package_count; | 146 | if (upgrad_res.errorcode == OK) { |
| 137 | int sec_count = upgrad_res.security_package_count; | 147 | sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK); |
| 148 | } | ||
| 149 | xasprintf(&sc_run_upgrade.output, "Executed apt upgrade (dry run)"); | ||
| 150 | |||
| 151 | mp_add_subcheck_to_check(&overall, sc_run_upgrade); | ||
| 152 | |||
| 153 | size_t packages_available = upgrad_res.package_count; | ||
| 154 | size_t number_of_security_updates = upgrad_res.security_package_count; | ||
| 138 | char **packages_list = upgrad_res.packages_list; | 155 | char **packages_list = upgrad_res.packages_list; |
| 139 | char **secpackages_list = upgrad_res.secpackages_list; | 156 | char **secpackages_list = upgrad_res.secpackages_list; |
| 140 | 157 | ||
| 141 | if (sec_count > 0) { | 158 | mp_perfdata pd_security_updates = perfdata_init(); |
| 142 | result = max_state(result, STATE_CRITICAL); | 159 | pd_security_updates.value = mp_create_pd_value(number_of_security_updates); |
| 143 | } else if (packages_available >= packages_warning && only_critical == false) { | 160 | pd_security_updates.label = "critical_updates"; |
| 144 | result = max_state(result, STATE_WARNING); | 161 | |
| 145 | } else if (result > STATE_UNKNOWN) { | 162 | mp_subcheck sc_security_updates = mp_subcheck_init(); |
| 146 | result = STATE_UNKNOWN; | 163 | xasprintf(&sc_security_updates.output, "Security updates available: %zu", |
| 164 | number_of_security_updates); | ||
| 165 | mp_add_perfdata_to_subcheck(&sc_security_updates, pd_security_updates); | ||
| 166 | |||
| 167 | if (number_of_security_updates > 0) { | ||
| 168 | sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_CRITICAL); | ||
| 169 | } else { | ||
| 170 | sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_OK); | ||
| 147 | } | 171 | } |
| 148 | 172 | ||
| 149 | printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), | 173 | mp_perfdata pd_other_updates = perfdata_init(); |
| 150 | state_text(result), packages_available, (upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, | 174 | pd_other_updates.value = mp_create_pd_value(packages_available); |
| 151 | (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", | 175 | pd_other_updates.label = "available_upgrades"; |
| 152 | (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count); | ||
| 153 | 176 | ||
| 154 | if (list) { | 177 | mp_subcheck sc_other_updates = mp_subcheck_init(); |
| 155 | qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp); | ||
| 156 | qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp); | ||
| 157 | 178 | ||
| 158 | for (int i = 0; i < sec_count; i++) { | 179 | xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available); |
| 159 | printf("%s (security)\n", secpackages_list[i]); | 180 | sc_other_updates = mp_set_subcheck_default_state(sc_other_updates, STATE_OK); |
| 181 | mp_add_perfdata_to_subcheck(&sc_other_updates, pd_other_updates); | ||
| 182 | |||
| 183 | if (packages_available >= config.packages_warning && !config.only_critical) { | ||
| 184 | sc_other_updates = mp_set_subcheck_state(sc_other_updates, STATE_WARNING); | ||
| 185 | } | ||
| 186 | |||
| 187 | if (config.list) { | ||
| 188 | qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp); | ||
| 189 | qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *), | ||
| 190 | cmpstringp); | ||
| 191 | |||
| 192 | for (size_t i = 0; i < number_of_security_updates; i++) { | ||
| 193 | xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output, | ||
| 194 | secpackages_list[i]); | ||
| 160 | } | 195 | } |
| 161 | 196 | ||
| 162 | if (only_critical == false) { | 197 | if (!config.only_critical) { |
| 163 | for (int i = 0; i < packages_available - sec_count; i++) { | 198 | for (size_t i = 0; i < packages_available - number_of_security_updates; i++) { |
| 164 | printf("%s\n", packages_list[i]); | 199 | xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output, |
| 200 | packages_list[i]); | ||
| 165 | } | 201 | } |
| 166 | } | 202 | } |
| 167 | } | 203 | } |
| 204 | mp_add_subcheck_to_check(&overall, sc_security_updates); | ||
| 205 | mp_add_subcheck_to_check(&overall, sc_other_updates); | ||
| 168 | 206 | ||
| 169 | return result; | 207 | mp_exit(overall); |
| 170 | } | 208 | } |
| 171 | 209 | ||
| 172 | /* process command-line arguments */ | 210 | /* process command-line arguments */ |
| 173 | int process_arguments(int argc, char **argv) { | 211 | check_apt_config_wrapper process_arguments(int argc, char **argv) { |
| 212 | enum { | ||
| 213 | /* Character for hidden input file option (for testing). */ | ||
| 214 | INPUT_FILE_OPT = CHAR_MAX + 1, | ||
| 215 | output_format_index, | ||
| 216 | }; | ||
| 174 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, | 217 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, |
| 175 | {"help", no_argument, 0, 'h'}, | 218 | {"help", no_argument, 0, 'h'}, |
| 176 | {"verbose", no_argument, 0, 'v'}, | 219 | {"verbose", no_argument, 0, 'v'}, |
| @@ -179,15 +222,21 @@ int process_arguments(int argc, char **argv) { | |||
| 179 | {"upgrade", optional_argument, 0, 'U'}, | 222 | {"upgrade", optional_argument, 0, 'U'}, |
| 180 | {"no-upgrade", no_argument, 0, 'n'}, | 223 | {"no-upgrade", no_argument, 0, 'n'}, |
| 181 | {"dist-upgrade", optional_argument, 0, 'd'}, | 224 | {"dist-upgrade", optional_argument, 0, 'd'}, |
| 182 | {"list", no_argument, false, 'l'}, | 225 | {"list", no_argument, 0, 'l'}, |
| 183 | {"include", required_argument, 0, 'i'}, | 226 | {"include", required_argument, 0, 'i'}, |
| 184 | {"exclude", required_argument, 0, 'e'}, | 227 | {"exclude", required_argument, 0, 'e'}, |
| 185 | {"critical", required_argument, 0, 'c'}, | 228 | {"critical", required_argument, 0, 'c'}, |
| 186 | {"only-critical", no_argument, 0, 'o'}, | 229 | {"only-critical", no_argument, 0, 'o'}, |
| 187 | {"input-file", required_argument, 0, INPUT_FILE_OPT}, | 230 | {"input-file", required_argument, 0, INPUT_FILE_OPT}, |
| 188 | {"packages-warning", required_argument, 0, 'w'}, | 231 | {"packages-warning", required_argument, 0, 'w'}, |
| 232 | {"output-format", required_argument, 0, output_format_index}, | ||
| 189 | {0, 0, 0, 0}}; | 233 | {0, 0, 0, 0}}; |
| 190 | 234 | ||
| 235 | check_apt_config_wrapper result = { | ||
| 236 | .errorcode = OK, | ||
| 237 | .config = check_apt_config_init(), | ||
| 238 | }; | ||
| 239 | |||
| 191 | while (true) { | 240 | while (true) { |
| 192 | int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL); | 241 | int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL); |
| 193 | 242 | ||
| @@ -209,96 +258,110 @@ int process_arguments(int argc, char **argv) { | |||
| 209 | timeout_interval = atoi(optarg); | 258 | timeout_interval = atoi(optarg); |
| 210 | break; | 259 | break; |
| 211 | case 'd': | 260 | case 'd': |
| 212 | upgrade = DIST_UPGRADE; | 261 | result.config.upgrade = DIST_UPGRADE; |
| 213 | if (optarg != NULL) { | 262 | if (optarg != NULL) { |
| 214 | upgrade_opts = strdup(optarg); | 263 | result.config.upgrade_opts = strdup(optarg); |
| 215 | if (upgrade_opts == NULL) { | 264 | if (result.config.upgrade_opts == NULL) { |
| 216 | die(STATE_UNKNOWN, "strdup failed"); | 265 | die(STATE_UNKNOWN, "strdup failed"); |
| 217 | } | 266 | } |
| 218 | } | 267 | } |
| 219 | break; | 268 | break; |
| 220 | case 'U': | 269 | case 'U': |
| 221 | upgrade = UPGRADE; | 270 | result.config.upgrade = UPGRADE; |
| 222 | if (optarg != NULL) { | 271 | if (optarg != NULL) { |
| 223 | upgrade_opts = strdup(optarg); | 272 | result.config.upgrade_opts = strdup(optarg); |
| 224 | if (upgrade_opts == NULL) { | 273 | if (result.config.upgrade_opts == NULL) { |
| 225 | die(STATE_UNKNOWN, "strdup failed"); | 274 | die(STATE_UNKNOWN, "strdup failed"); |
| 226 | } | 275 | } |
| 227 | } | 276 | } |
| 228 | break; | 277 | break; |
| 229 | case 'n': | 278 | case 'n': |
| 230 | upgrade = NO_UPGRADE; | 279 | result.config.upgrade = NO_UPGRADE; |
| 231 | break; | 280 | break; |
| 232 | case 'u': | 281 | case 'u': |
| 233 | do_update = true; | 282 | result.config.do_update = true; |
| 234 | if (optarg != NULL) { | 283 | if (optarg != NULL) { |
| 235 | update_opts = strdup(optarg); | 284 | result.config.update_opts = strdup(optarg); |
| 236 | if (update_opts == NULL) { | 285 | if (result.config.update_opts == NULL) { |
| 237 | die(STATE_UNKNOWN, "strdup failed"); | 286 | die(STATE_UNKNOWN, "strdup failed"); |
| 238 | } | 287 | } |
| 239 | } | 288 | } |
| 240 | break; | 289 | break; |
| 241 | case 'l': | 290 | case 'l': |
| 242 | list = true; | 291 | result.config.list = true; |
| 243 | break; | 292 | break; |
| 244 | case 'i': | 293 | case 'i': |
| 245 | do_include = add_to_regexp(do_include, optarg); | 294 | result.config.do_include = add_to_regexp(result.config.do_include, optarg); |
| 246 | break; | 295 | break; |
| 247 | case 'e': | 296 | case 'e': |
| 248 | do_exclude = add_to_regexp(do_exclude, optarg); | 297 | result.config.do_exclude = add_to_regexp(result.config.do_exclude, optarg); |
| 249 | break; | 298 | break; |
| 250 | case 'c': | 299 | case 'c': |
| 251 | do_critical = add_to_regexp(do_critical, optarg); | 300 | result.config.do_critical = add_to_regexp(result.config.do_critical, optarg); |
| 252 | break; | 301 | break; |
| 253 | case 'o': | 302 | case 'o': |
| 254 | only_critical = true; | 303 | result.config.only_critical = true; |
| 255 | break; | 304 | break; |
| 256 | case INPUT_FILE_OPT: | 305 | case INPUT_FILE_OPT: |
| 257 | input_filename = optarg; | 306 | result.config.input_filename = optarg; |
| 258 | break; | 307 | break; |
| 259 | case 'w': | 308 | case 'w': |
| 260 | packages_warning = atoi(optarg); | 309 | result.config.packages_warning = atoi(optarg); |
| 310 | break; | ||
| 311 | case output_format_index: { | ||
| 312 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 313 | if (!parser.parsing_success) { | ||
| 314 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 315 | printf("Invalid output format: %s\n", optarg); | ||
| 316 | exit(STATE_UNKNOWN); | ||
| 317 | } | ||
| 318 | |||
| 319 | result.config.output_format_is_set = true; | ||
| 320 | result.config.output_format = parser.output_format; | ||
| 261 | break; | 321 | break; |
| 322 | } | ||
| 262 | default: | 323 | default: |
| 263 | /* print short usage statement if args not parsable */ | 324 | /* print short usage statement if args not parsable */ |
| 264 | usage5(); | 325 | usage5(); |
| 265 | } | 326 | } |
| 266 | } | 327 | } |
| 267 | 328 | ||
| 268 | return OK; | 329 | return result; |
| 269 | } | 330 | } |
| 270 | 331 | ||
| 271 | /* run an apt-get upgrade */ | 332 | /* run an apt-get upgrade */ |
| 272 | run_upgrade_result run_upgrade(void) { | 333 | run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include, |
| 273 | regex_t ereg; | 334 | const char *do_exclude, const char *do_critical, |
| 335 | const char *upgrade_opts, const char *input_filename) { | ||
| 336 | regex_t exclude_regex; | ||
| 274 | /* initialize ereg as it is possible it is printed while uninitialized */ | 337 | /* initialize ereg as it is possible it is printed while uninitialized */ |
| 275 | memset(&ereg, '\0', sizeof(ereg.buffer)); | 338 | memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer)); |
| 276 | 339 | ||
| 277 | run_upgrade_result result = { | 340 | run_upgrade_result result = { |
| 278 | .errorcode = STATE_UNKNOWN, | 341 | .errorcode = OK, |
| 279 | }; | 342 | }; |
| 280 | 343 | ||
| 281 | if (upgrade == NO_UPGRADE) { | 344 | if (upgrade == NO_UPGRADE) { |
| 282 | result.errorcode = STATE_OK; | 345 | result.errorcode = OK; |
| 283 | return result; | 346 | return result; |
| 284 | } | 347 | } |
| 285 | 348 | ||
| 286 | int regres = 0; | 349 | int regres = 0; |
| 287 | regex_t ireg; | 350 | regex_t include_regex; |
| 288 | char rerrbuf[64]; | 351 | char rerrbuf[64]; |
| 289 | /* compile the regexps */ | 352 | /* compile the regexps */ |
| 290 | if (do_include != NULL) { | 353 | if (do_include != NULL) { |
| 291 | regres = regcomp(&ireg, do_include, REG_EXTENDED); | 354 | regres = regcomp(&include_regex, do_include, REG_EXTENDED); |
| 292 | if (regres != 0) { | 355 | if (regres != 0) { |
| 293 | regerror(regres, &ireg, rerrbuf, 64); | 356 | regerror(regres, &include_regex, rerrbuf, 64); |
| 294 | die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); | 357 | die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); |
| 295 | } | 358 | } |
| 296 | } | 359 | } |
| 297 | 360 | ||
| 298 | if (do_exclude != NULL) { | 361 | if (do_exclude != NULL) { |
| 299 | regres = regcomp(&ereg, do_exclude, REG_EXTENDED); | 362 | regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED); |
| 300 | if (regres != 0) { | 363 | if (regres != 0) { |
| 301 | regerror(regres, &ereg, rerrbuf, 64); | 364 | regerror(regres, &exclude_regex, rerrbuf, 64); |
| 302 | die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); | 365 | die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); |
| 303 | } | 366 | } |
| 304 | } | 367 | } |
| @@ -307,12 +370,12 @@ run_upgrade_result run_upgrade(void) { | |||
| 307 | const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; | 370 | const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; |
| 308 | regres = regcomp(&sreg, crit_ptr, REG_EXTENDED); | 371 | regres = regcomp(&sreg, crit_ptr, REG_EXTENDED); |
| 309 | if (regres != 0) { | 372 | if (regres != 0) { |
| 310 | regerror(regres, &ereg, rerrbuf, 64); | 373 | regerror(regres, &exclude_regex, rerrbuf, 64); |
| 311 | die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); | 374 | die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); |
| 312 | } | 375 | } |
| 313 | 376 | ||
| 314 | struct output chld_out; | 377 | output chld_out; |
| 315 | struct output chld_err; | 378 | output chld_err; |
| 316 | char *cmdline = NULL; | 379 | char *cmdline = NULL; |
| 317 | cmdline = construct_cmdline(upgrade, upgrade_opts); | 380 | cmdline = construct_cmdline(upgrade, upgrade_opts); |
| 318 | if (input_filename != NULL) { | 381 | if (input_filename != NULL) { |
| @@ -323,16 +386,15 @@ run_upgrade_result run_upgrade(void) { | |||
| 323 | result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0); | 386 | result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0); |
| 324 | } | 387 | } |
| 325 | 388 | ||
| 326 | /* apt-get upgrade only changes exit status if there is an | 389 | // apt-get upgrade only changes exit status if there is an |
| 327 | * internal error when run in dry-run mode. therefore we will | 390 | // internal error when run in dry-run mode. |
| 328 | * treat such an error as UNKNOWN */ | 391 | if (result.errorcode != 0) { |
| 329 | if (result.errorcode != STATE_OK) { | 392 | result.exec_warning = true; |
| 330 | exec_warning = 1; | 393 | result.errorcode = ERROR; |
| 331 | result.errorcode = STATE_UNKNOWN; | 394 | // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); |
| 332 | fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); | ||
| 333 | } | 395 | } |
| 334 | 396 | ||
| 335 | char **pkglist = malloc(sizeof(char *) * chld_out.lines); | 397 | char **pkglist = malloc(sizeof(char *) * chld_out.lines); |
| 336 | if (!pkglist) { | 398 | if (!pkglist) { |
| 337 | die(STATE_UNKNOWN, "malloc failed!\n"); | 399 | die(STATE_UNKNOWN, "malloc failed!\n"); |
| 338 | } | 400 | } |
| @@ -350,27 +412,31 @@ run_upgrade_result run_upgrade(void) { | |||
| 350 | * we may need to switch to the --print-uris output format, | 412 | * we may need to switch to the --print-uris output format, |
| 351 | * in which case the logic here will slightly change. | 413 | * in which case the logic here will slightly change. |
| 352 | */ | 414 | */ |
| 353 | int package_counter = 0; | 415 | size_t package_counter = 0; |
| 354 | int security_package_counter = 0; | 416 | size_t security_package_counter = 0; |
| 355 | for (size_t i = 0; i < chld_out.lines; i++) { | 417 | for (size_t i = 0; i < chld_out.lines; i++) { |
| 356 | if (verbose) { | 418 | if (verbose) { |
| 357 | printf("%s\n", chld_out.line[i]); | 419 | printf("%s\n", chld_out.line[i]); |
| 358 | } | 420 | } |
| 421 | |||
| 359 | /* if it is a package we care about */ | 422 | /* if it is a package we care about */ |
| 360 | if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 && | 423 | if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 && |
| 361 | (do_include == NULL || regexec(&ireg, chld_out.line[i], 0, NULL, 0) == 0)) { | 424 | (do_include == NULL || regexec(&include_regex, chld_out.line[i], 0, NULL, 0) == 0)) { |
| 362 | /* if we're not excluding, or it's not in the | 425 | /* if we're not excluding, or it's not in the |
| 363 | * list of stuff to exclude */ | 426 | * list of stuff to exclude */ |
| 364 | if (do_exclude == NULL || regexec(&ereg, chld_out.line[i], 0, NULL, 0) != 0) { | 427 | if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) { |
| 365 | package_counter++; | 428 | package_counter++; |
| 366 | if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) { | 429 | if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) { |
| 367 | security_package_counter++; | 430 | security_package_counter++; |
| 431 | |||
| 368 | if (verbose) { | 432 | if (verbose) { |
| 369 | printf("*"); | 433 | printf("*"); |
| 370 | } | 434 | } |
| 435 | |||
| 371 | (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]); | 436 | (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]); |
| 372 | } else { | 437 | } else { |
| 373 | (pkglist)[package_counter - security_package_counter - 1] = pkg_name(chld_out.line[i]); | 438 | (pkglist)[package_counter - security_package_counter - 1] = |
| 439 | pkg_name(chld_out.line[i]); | ||
| 374 | } | 440 | } |
| 375 | if (verbose) { | 441 | if (verbose) { |
| 376 | printf("*%s\n", chld_out.line[i]); | 442 | printf("*%s\n", chld_out.line[i]); |
| @@ -378,6 +444,7 @@ run_upgrade_result run_upgrade(void) { | |||
| 378 | } | 444 | } |
| 379 | } | 445 | } |
| 380 | } | 446 | } |
| 447 | |||
| 381 | result.package_count = package_counter; | 448 | result.package_count = package_counter; |
| 382 | result.security_package_count = security_package_counter; | 449 | result.security_package_count = security_package_counter; |
| 383 | result.packages_list = pkglist; | 450 | result.packages_list = pkglist; |
| @@ -385,42 +452,56 @@ run_upgrade_result run_upgrade(void) { | |||
| 385 | 452 | ||
| 386 | /* If we get anything on stderr, at least set warning */ | 453 | /* If we get anything on stderr, at least set warning */ |
| 387 | if (input_filename == NULL && chld_err.buflen) { | 454 | if (input_filename == NULL && chld_err.buflen) { |
| 388 | stderr_warning = 1; | 455 | stderr_warning = true; |
| 389 | result.errorcode = max_state(result.errorcode, STATE_WARNING); | 456 | result.errorcode = ERROR; |
| 457 | |||
| 390 | if (verbose) { | 458 | if (verbose) { |
| 391 | for (size_t i = 0; i < chld_err.lines; i++) { | 459 | for (size_t i = 0; i < chld_err.lines; i++) { |
| 392 | fprintf(stderr, "%s\n", chld_err.line[i]); | 460 | fprintf(stderr, "%s\n", chld_err.line[i]); |
| 393 | } | 461 | } |
| 394 | } | 462 | } |
| 395 | } | 463 | } |
| 464 | |||
| 396 | if (do_include != NULL) { | 465 | if (do_include != NULL) { |
| 397 | regfree(&ireg); | 466 | regfree(&include_regex); |
| 398 | } | 467 | } |
| 468 | |||
| 399 | regfree(&sreg); | 469 | regfree(&sreg); |
| 470 | |||
| 400 | if (do_exclude != NULL) { | 471 | if (do_exclude != NULL) { |
| 401 | regfree(&ereg); | 472 | regfree(&exclude_regex); |
| 402 | } | 473 | } |
| 474 | |||
| 403 | free(cmdline); | 475 | free(cmdline); |
| 476 | |||
| 404 | return result; | 477 | return result; |
| 405 | } | 478 | } |
| 406 | 479 | ||
| 407 | /* run an apt-get update (needs root) */ | 480 | /* run an apt-get update (needs root) */ |
| 408 | int run_update(void) { | 481 | run_update_result run_update(char *update_opts) { |
| 409 | int result = STATE_UNKNOWN; | ||
| 410 | char *cmdline; | 482 | char *cmdline; |
| 411 | /* run the update */ | 483 | /* run the update */ |
| 412 | cmdline = construct_cmdline(NO_UPGRADE, update_opts); | 484 | cmdline = construct_cmdline(NO_UPGRADE, update_opts); |
| 413 | 485 | ||
| 414 | struct output chld_out; | 486 | run_update_result result = { |
| 415 | struct output chld_err; | 487 | .exec_warning = false, |
| 416 | result = np_runcmd(cmdline, &chld_out, &chld_err, 0); | 488 | .stderr_warning = false, |
| 489 | .sc = mp_subcheck_init(), | ||
| 490 | }; | ||
| 491 | |||
| 492 | result.sc = mp_set_subcheck_default_state(result.sc, STATE_OK); | ||
| 493 | xasprintf(&result.sc.output, "executing '%s' first", cmdline); | ||
| 494 | |||
| 495 | output chld_out; | ||
| 496 | output chld_err; | ||
| 497 | int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0); | ||
| 417 | /* apt-get update changes exit status if it can't fetch packages. | 498 | /* apt-get update changes exit status if it can't fetch packages. |
| 418 | * since we were explicitly asked to do so, this is treated as | 499 | * since we were explicitly asked to do so, this is treated as |
| 419 | * a critical error. */ | 500 | * a critical error. */ |
| 420 | if (result != 0) { | 501 | if (cmd_error != 0) { |
| 421 | exec_warning = 1; | 502 | exec_warning = true; |
| 422 | result = STATE_CRITICAL; | 503 | result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL); |
| 423 | fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); | 504 | xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline); |
| 424 | } | 505 | } |
| 425 | 506 | ||
| 426 | if (verbose) { | 507 | if (verbose) { |
| @@ -431,22 +512,25 @@ int run_update(void) { | |||
| 431 | 512 | ||
| 432 | /* If we get anything on stderr, at least set warning */ | 513 | /* If we get anything on stderr, at least set warning */ |
| 433 | if (chld_err.buflen) { | 514 | if (chld_err.buflen) { |
| 434 | stderr_warning = 1; | 515 | stderr_warning = true; |
| 435 | result = max_state(result, STATE_WARNING); | 516 | result.sc = mp_set_subcheck_state( |
| 517 | result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING)); | ||
| 436 | if (verbose) { | 518 | if (verbose) { |
| 437 | for (size_t i = 0; i < chld_err.lines; i++) { | 519 | for (size_t i = 0; i < chld_err.lines; i++) { |
| 438 | fprintf(stderr, "%s\n", chld_err.line[i]); | 520 | fprintf(stderr, "%s\n", chld_err.line[i]); |
| 439 | } | 521 | } |
| 440 | } | 522 | } |
| 441 | } | 523 | } |
| 524 | |||
| 442 | free(cmdline); | 525 | free(cmdline); |
| 526 | |||
| 443 | return result; | 527 | return result; |
| 444 | } | 528 | } |
| 445 | 529 | ||
| 446 | char *pkg_name(char *line) { | 530 | char *pkg_name(char *line) { |
| 447 | char *start = line + strlen(PKGINST_PREFIX); | 531 | char *start = line + strlen(PKGINST_PREFIX); |
| 448 | 532 | ||
| 449 | int len = strlen(start); | 533 | size_t len = strlen(start); |
| 450 | 534 | ||
| 451 | char *space = index(start, ' '); | 535 | char *space = index(start, ' '); |
| 452 | if (space != NULL) { | 536 | if (space != NULL) { |
| @@ -464,35 +548,37 @@ char *pkg_name(char *line) { | |||
| 464 | return pkg; | 548 | return pkg; |
| 465 | } | 549 | } |
| 466 | 550 | ||
| 467 | int cmpstringp(const void *p1, const void *p2) { return strcmp(*(char *const *)p1, *(char *const *)p2); } | 551 | int cmpstringp(const void *left_string, const void *right_string) { |
| 552 | return strcmp(*(char *const *)left_string, *(char *const *)right_string); | ||
| 553 | } | ||
| 468 | 554 | ||
| 469 | char *add_to_regexp(char *expr, const char *next) { | 555 | char *add_to_regexp(char *expr, const char *next) { |
| 470 | char *re = NULL; | 556 | char *regex_string = NULL; |
| 471 | 557 | ||
| 472 | if (expr == NULL) { | 558 | if (expr == NULL) { |
| 473 | re = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1)); | 559 | regex_string = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1)); |
| 474 | if (!re) { | 560 | if (!regex_string) { |
| 475 | die(STATE_UNKNOWN, "malloc failed!\n"); | 561 | die(STATE_UNKNOWN, "malloc failed!\n"); |
| 476 | } | 562 | } |
| 477 | sprintf(re, "(%s)", next); | 563 | sprintf(regex_string, "(%s)", next); |
| 478 | } else { | 564 | } else { |
| 479 | /* resize it, adding an extra char for the new '|' separator */ | 565 | /* resize it, adding an extra char for the new '|' separator */ |
| 480 | re = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1)); | 566 | regex_string = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1)); |
| 481 | if (!re) { | 567 | if (!regex_string) { |
| 482 | die(STATE_UNKNOWN, "realloc failed!\n"); | 568 | die(STATE_UNKNOWN, "realloc failed!\n"); |
| 483 | } | 569 | } |
| 484 | /* append it starting at ')' in the old re */ | 570 | /* append it starting at ')' in the old re */ |
| 485 | sprintf((char *)(re + strlen(re) - 1), "|%s)", next); | 571 | sprintf((char *)(regex_string + strlen(regex_string) - 1), "|%s)", next); |
| 486 | } | 572 | } |
| 487 | 573 | ||
| 488 | return re; | 574 | return regex_string; |
| 489 | } | 575 | } |
| 490 | 576 | ||
| 491 | char *construct_cmdline(upgrade_type u, const char *opts) { | 577 | char *construct_cmdline(upgrade_type upgrade, const char *opts) { |
| 492 | const char *opts_ptr = NULL; | 578 | const char *opts_ptr = NULL; |
| 493 | const char *aptcmd = NULL; | 579 | const char *aptcmd = NULL; |
| 494 | 580 | ||
| 495 | switch (u) { | 581 | switch (upgrade) { |
| 496 | case UPGRADE: | 582 | case UPGRADE: |
| 497 | if (opts == NULL) { | 583 | if (opts == NULL) { |
| 498 | opts_ptr = UPGRADE_DEFAULT_OPTS; | 584 | opts_ptr = UPGRADE_DEFAULT_OPTS; |
| @@ -519,7 +605,7 @@ char *construct_cmdline(upgrade_type u, const char *opts) { | |||
| 519 | break; | 605 | break; |
| 520 | } | 606 | } |
| 521 | 607 | ||
| 522 | int len = 0; | 608 | size_t len = 0; |
| 523 | len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */ | 609 | len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */ |
| 524 | len += strlen(opts_ptr) + 1; /* "opts " */ | 610 | len += strlen(opts_ptr) + 1; /* "opts " */ |
| 525 | len += strlen(aptcmd) + 1; /* "upgrade\0" */ | 611 | len += strlen(aptcmd) + 1; /* "upgrade\0" */ |
| @@ -557,7 +643,8 @@ void print_help(void) { | |||
| 557 | printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); | 643 | printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); |
| 558 | printf(" %s\n", _("name with security packages listed first.")); | 644 | printf(" %s\n", _("name with security packages listed first.")); |
| 559 | printf(" %s\n", "-i, --include=REGEXP"); | 645 | printf(" %s\n", "-i, --include=REGEXP"); |
| 560 | printf(" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); | 646 | printf(" %s\n", |
| 647 | _("Include only packages matching REGEXP. Can be specified multiple times")); | ||
| 561 | printf(" %s\n", _("the values will be combined together. Any packages matching this list")); | 648 | printf(" %s\n", _("the values will be combined together. Any packages matching this list")); |
| 562 | printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); | 649 | printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); |
| 563 | printf(" %s\n", _("Default is to include all packages.")); | 650 | printf(" %s\n", _("Default is to include all packages.")); |
| @@ -566,7 +653,8 @@ void print_help(void) { | |||
| 566 | printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values")); | 653 | printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values")); |
| 567 | printf(" %s\n", _("will be combined together. Default is to exclude no packages.")); | 654 | printf(" %s\n", _("will be combined together. Default is to exclude no packages.")); |
| 568 | printf(" %s\n", "-c, --critical=REGEXP"); | 655 | printf(" %s\n", "-c, --critical=REGEXP"); |
| 569 | printf(" %s\n", _("If the full package information of any of the upgradable packages match")); | 656 | printf(" %s\n", |
| 657 | _("If the full package information of any of the upgradable packages match")); | ||
| 570 | printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified")); | 658 | printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified")); |
| 571 | printf(" %s\n", _("multiple times like above. Default is a regexp matching security")); | 659 | printf(" %s\n", _("multiple times like above. Default is a regexp matching security")); |
| 572 | printf(" %s\n", _("upgrades for Debian and Ubuntu:")); | 660 | printf(" %s\n", _("upgrades for Debian and Ubuntu:")); |
| @@ -575,15 +663,21 @@ void print_help(void) { | |||
| 575 | printf(" %s\n", _("information is compared against the critical list.")); | 663 | printf(" %s\n", _("information is compared against the critical list.")); |
| 576 | printf(" %s\n", "-o, --only-critical"); | 664 | printf(" %s\n", "-o, --only-critical"); |
| 577 | printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number")); | 665 | printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number")); |
| 578 | printf(" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); | 666 | printf(" %s\n", |
| 667 | _("of upgrades will be printed, but any non-critical upgrades will not cause")); | ||
| 579 | printf(" %s\n", _("the plugin to return WARNING status.")); | 668 | printf(" %s\n", _("the plugin to return WARNING status.")); |
| 580 | printf(" %s\n", "-w, --packages-warning"); | 669 | printf(" %s\n", "-w, --packages-warning"); |
| 581 | printf(" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); | 670 | printf(" %s\n", |
| 671 | _("Minimum number of packages available for upgrade to return WARNING status.")); | ||
| 582 | printf(" %s\n\n", _("Default is 1 package.")); | 672 | printf(" %s\n\n", _("Default is 1 package.")); |
| 583 | 673 | ||
| 584 | printf("%s\n\n", _("The following options require root privileges and should be used with care:")); | 674 | printf(UT_OUTPUT_FORMAT); |
| 675 | |||
| 676 | printf("%s\n\n", | ||
| 677 | _("The following options require root privileges and should be used with care:")); | ||
| 585 | printf(" %s\n", "-u, --update=OPTS"); | 678 | printf(" %s\n", "-u, --update=OPTS"); |
| 586 | printf(" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); | 679 | printf(" %s\n", |
| 680 | _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); | ||
| 587 | printf(" %s\n", _("the default options. Note: you may also need to adjust the global")); | 681 | printf(" %s\n", _("the default options. Note: you may also need to adjust the global")); |
| 588 | printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); | 682 | printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); |
| 589 | printf(" %s\n", _("upgrade is expected to take longer than the default timeout.")); | 683 | printf(" %s\n", _("upgrade is expected to take longer than the default timeout.")); |
| @@ -592,8 +686,10 @@ void print_help(void) { | |||
| 592 | printf(" %s\n", _("apt-get will be run with these command line options instead of the")); | 686 | printf(" %s\n", _("apt-get will be run with these command line options instead of the")); |
| 593 | printf(" %s", _("default ")); | 687 | printf(" %s", _("default ")); |
| 594 | printf("(%s).\n", UPGRADE_DEFAULT_OPTS); | 688 | printf("(%s).\n", UPGRADE_DEFAULT_OPTS); |
| 595 | printf(" %s\n", _("Note that you may be required to have root privileges if you do not use")); | 689 | printf(" %s\n", |
| 596 | printf(" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); | 690 | _("Note that you may be required to have root privileges if you do not use")); |
| 691 | printf(" %s\n", | ||
| 692 | _("the default options, which will only run a simulation and NOT perform the upgrade")); | ||
| 597 | printf(" %s\n", "-d, --dist-upgrade=OPTS"); | 693 | printf(" %s\n", "-d, --dist-upgrade=OPTS"); |
| 598 | printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); | 694 | printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); |
| 599 | printf(" %s\n", _("can be provided to override the default options.")); | 695 | printf(" %s\n", _("can be provided to override the default options.")); |
diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h new file mode 100644 index 00000000..e4d622f1 --- /dev/null +++ b/plugins/check_apt.d/config.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | #include "../lib/output.h" | ||
| 6 | |||
| 7 | /* some constants */ | ||
| 8 | typedef enum { | ||
| 9 | UPGRADE, | ||
| 10 | DIST_UPGRADE, | ||
| 11 | NO_UPGRADE | ||
| 12 | } upgrade_type; | ||
| 13 | |||
| 14 | typedef struct { | ||
| 15 | bool do_update; /* whether to call apt-get update */ | ||
| 16 | upgrade_type upgrade; /* which type of upgrade to do */ | ||
| 17 | bool only_critical; /* whether to warn about non-critical updates */ | ||
| 18 | bool list; /* list packages available for upgrade */ | ||
| 19 | /* number of packages available for upgrade to return WARNING status */ | ||
| 20 | size_t packages_warning; | ||
| 21 | |||
| 22 | char *upgrade_opts; /* options to override defaults for upgrade */ | ||
| 23 | char *update_opts; /* options to override defaults for update */ | ||
| 24 | char *do_include; /* regexp to only include certain packages */ | ||
| 25 | char *do_exclude; /* regexp to only exclude certain packages */ | ||
| 26 | char *do_critical; /* regexp specifying critical packages */ | ||
| 27 | char *input_filename; /* input filename for testing */ | ||
| 28 | |||
| 29 | bool output_format_is_set; | ||
| 30 | mp_output_format output_format; | ||
| 31 | } check_apt_config; | ||
| 32 | |||
| 33 | check_apt_config check_apt_config_init() { | ||
| 34 | check_apt_config tmp = {.do_update = false, | ||
| 35 | .upgrade = UPGRADE, | ||
| 36 | .only_critical = false, | ||
| 37 | .list = false, | ||
| 38 | .packages_warning = 1, | ||
| 39 | .update_opts = NULL, | ||
| 40 | .do_include = NULL, | ||
| 41 | .do_exclude = NULL, | ||
| 42 | .do_critical = NULL, | ||
| 43 | .input_filename = NULL, | ||
| 44 | .output_format_is_set = false}; | ||
| 45 | return tmp; | ||
| 46 | } | ||
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index 905b2393..df8907d9 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c | |||
| @@ -26,54 +26,36 @@ | |||
| 26 | * | 26 | * |
| 27 | *****************************************************************************/ | 27 | *****************************************************************************/ |
| 28 | 28 | ||
| 29 | const char *progname = "check_by_ssh"; | ||
| 30 | const char *copyright = "2000-2024"; | ||
| 31 | const char *email = "devel@monitoring-plugins.org"; | ||
| 32 | |||
| 33 | #include "common.h" | 29 | #include "common.h" |
| 30 | #include "output.h" | ||
| 34 | #include "utils.h" | 31 | #include "utils.h" |
| 35 | #include "netutils.h" | ||
| 36 | #include "utils_cmd.h" | 32 | #include "utils_cmd.h" |
| 33 | #include "check_by_ssh.d/config.h" | ||
| 34 | #include "states.h" | ||
| 35 | |||
| 36 | const char *progname = "check_by_ssh"; | ||
| 37 | const char *copyright = "2000-2024"; | ||
| 38 | const char *email = "devel@monitoring-plugins.org"; | ||
| 37 | 39 | ||
| 38 | #ifndef NP_MAXARGS | 40 | #ifndef NP_MAXARGS |
| 39 | # define NP_MAXARGS 1024 | 41 | # define NP_MAXARGS 1024 |
| 40 | #endif | 42 | #endif |
| 41 | 43 | ||
| 42 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 44 | typedef struct { |
| 43 | static int validate_arguments(void); | 45 | int errorcode; |
| 44 | static void comm_append(const char * /*str*/); | 46 | check_by_ssh_config config; |
| 47 | } check_by_ssh_config_wrapper; | ||
| 48 | static check_by_ssh_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 49 | static check_by_ssh_config_wrapper | ||
| 50 | validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/); | ||
| 51 | |||
| 52 | static command_construct comm_append(command_construct /*cmd*/, const char * /*str*/); | ||
| 45 | static void print_help(void); | 53 | static void print_help(void); |
| 46 | void print_usage(void); | 54 | void print_usage(void); |
| 47 | 55 | ||
| 48 | static unsigned int commands = 0; | ||
| 49 | static unsigned int services = 0; | ||
| 50 | static int skip_stdout = 0; | ||
| 51 | static int skip_stderr = 0; | ||
| 52 | static int warn_on_stderr = 0; | ||
| 53 | static bool unknown_timeout = false; | ||
| 54 | static char *remotecmd = NULL; | ||
| 55 | static char **commargv = NULL; | ||
| 56 | static int commargc = 0; | ||
| 57 | static char *hostname = NULL; | ||
| 58 | static char *outputfile = NULL; | ||
| 59 | static char *host_shortname = NULL; | ||
| 60 | static char **service; | ||
| 61 | static bool passive = false; | ||
| 62 | static bool verbose = false; | 56 | static bool verbose = false; |
| 63 | 57 | ||
| 64 | int main(int argc, char **argv) { | 58 | int main(int argc, char **argv) { |
| 65 | |||
| 66 | char *status_text; | ||
| 67 | int cresult; | ||
| 68 | int result = STATE_UNKNOWN; | ||
| 69 | time_t local_time; | ||
| 70 | FILE *file_pointer = NULL; | ||
| 71 | output chld_out; | ||
| 72 | output chld_err; | ||
| 73 | |||
| 74 | remotecmd = ""; | ||
| 75 | comm_append(SSH_COMMAND); | ||
| 76 | |||
| 77 | setlocale(LC_ALL, ""); | 59 | setlocale(LC_ALL, ""); |
| 78 | bindtextdomain(PACKAGE, LOCALEDIR); | 60 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 79 | textdomain(PACKAGE); | 61 | textdomain(PACKAGE); |
| @@ -81,9 +63,18 @@ int main(int argc, char **argv) { | |||
| 81 | /* Parse extra opts if any */ | 63 | /* Parse extra opts if any */ |
| 82 | argv = np_extra_opts(&argc, argv, progname); | 64 | argv = np_extra_opts(&argc, argv, progname); |
| 83 | 65 | ||
| 66 | check_by_ssh_config_wrapper tmp_config = process_arguments(argc, argv); | ||
| 67 | |||
| 84 | /* process arguments */ | 68 | /* process arguments */ |
| 85 | if (process_arguments(argc, argv) == ERROR) | 69 | if (tmp_config.errorcode == ERROR) { |
| 86 | usage_va(_("Could not parse arguments")); | 70 | usage_va(_("Could not parse arguments")); |
| 71 | } | ||
| 72 | |||
| 73 | const check_by_ssh_config config = tmp_config.config; | ||
| 74 | |||
| 75 | if (config.output_format_is_set) { | ||
| 76 | mp_set_format(config.output_format); | ||
| 77 | } | ||
| 87 | 78 | ||
| 88 | /* Set signal handling and alarm timeout */ | 79 | /* Set signal handling and alarm timeout */ |
| 89 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 80 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| @@ -93,48 +84,105 @@ int main(int argc, char **argv) { | |||
| 93 | 84 | ||
| 94 | /* run the command */ | 85 | /* run the command */ |
| 95 | if (verbose) { | 86 | if (verbose) { |
| 96 | printf("Command: %s\n", commargv[0]); | 87 | printf("Command: %s\n", config.cmd.commargv[0]); |
| 97 | for (int i = 1; i < commargc; i++) | 88 | for (int i = 1; i < config.cmd.commargc; i++) { |
| 98 | printf("Argument %i: %s\n", i, commargv[i]); | 89 | printf("Argument %i: %s\n", i, config.cmd.commargv[i]); |
| 90 | } | ||
| 99 | } | 91 | } |
| 100 | 92 | ||
| 101 | result = cmd_run_array(commargv, &chld_out, &chld_err, 0); | 93 | cmd_run_result child_result = cmd_run_array2(config.cmd.commargv, 0); |
| 94 | mp_check overall = mp_check_init(); | ||
| 102 | 95 | ||
| 103 | /* SSH returns 255 if connection attempt fails; include the first line of error output */ | 96 | /* SSH returns 255 if connection attempt fails; include the first line of error output */ |
| 104 | if (result == 255 && unknown_timeout) { | 97 | // we can sadly not detect other SSH errors |
| 105 | printf(_("SSH connection failed: %s\n"), chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); | 98 | if (child_result.cmd_error_code == 255 && config.unknown_timeout) { |
| 106 | return STATE_UNKNOWN; | 99 | mp_subcheck sc_ssh_execution = mp_subcheck_init(); |
| 100 | xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", | ||
| 101 | child_result.stderr.lines > 0 ? child_result.stderr.line[0] | ||
| 102 | : "(no error output)"); | ||
| 103 | |||
| 104 | sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); | ||
| 105 | mp_add_subcheck_to_check(&overall, sc_ssh_execution); | ||
| 106 | mp_exit(overall); | ||
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | if (verbose) { | 109 | if (verbose) { |
| 110 | for (size_t i = 0; i < chld_out.lines; i++) | 110 | for (size_t i = 0; i < child_result.stdout.lines; i++) { |
| 111 | printf("stdout: %s\n", chld_out.line[i]); | 111 | printf("stdout: %s\n", child_result.stdout.line[i]); |
| 112 | for (size_t i = 0; i < chld_err.lines; i++) | 112 | } |
| 113 | printf("stderr: %s\n", chld_err.line[i]); | 113 | for (size_t i = 0; i < child_result.stderr.lines; i++) { |
| 114 | printf("stderr: %s\n", child_result.stderr.line[i]); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | size_t skip_stdout = 0; | ||
| 119 | if (config.skip_stdout) { /* --skip-stdout specified without argument */ | ||
| 120 | skip_stdout = child_result.stdout.lines; | ||
| 121 | } else { | ||
| 122 | skip_stdout = config.stdout_lines_to_ignore; | ||
| 123 | } | ||
| 124 | |||
| 125 | size_t skip_stderr = 0; | ||
| 126 | if (config.skip_stderr) { /* --skip-stderr specified without argument */ | ||
| 127 | skip_stderr = child_result.stderr.lines; | ||
| 128 | } else { | ||
| 129 | skip_stderr = config.sterr_lines_to_ignore; | ||
| 114 | } | 130 | } |
| 115 | 131 | ||
| 116 | if (skip_stdout == -1) /* --skip-stdout specified without argument */ | 132 | /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ |
| 117 | skip_stdout = chld_out.lines; | 133 | if (child_result.stderr.lines > skip_stderr && |
| 118 | if (skip_stderr == -1) /* --skip-stderr specified without argument */ | 134 | (config.unknown_on_stderr || config.warn_on_stderr)) { |
| 119 | skip_stderr = chld_err.lines; | 135 | mp_subcheck sc_stderr = mp_subcheck_init(); |
| 120 | 136 | xasprintf(&sc_stderr.output, "remote command execution failed: %s", | |
| 121 | /* UNKNOWN or worse if (non-skipped) output found on stderr */ | 137 | child_result.stderr.line[skip_stderr]); |
| 122 | if (chld_err.lines > (size_t)skip_stderr) { | 138 | |
| 123 | printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); | 139 | if (config.unknown_on_stderr) { |
| 124 | if (warn_on_stderr) | 140 | sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN); |
| 125 | return max_state_alt(result, STATE_WARNING); | 141 | } |
| 126 | return max_state_alt(result, STATE_UNKNOWN); | 142 | |
| 143 | if (config.warn_on_stderr) { | ||
| 144 | sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_WARNING); | ||
| 145 | } | ||
| 146 | |||
| 147 | mp_add_subcheck_to_check(&overall, sc_stderr); | ||
| 148 | // TODO still exit here? | ||
| 127 | } | 149 | } |
| 128 | 150 | ||
| 129 | /* this is simple if we're not supposed to be passive. | 151 | /* this is simple if we're not supposed to be passive. |
| 130 | * Wrap up quickly and keep the tricks below */ | 152 | * Wrap up quickly and keep the tricks below */ |
| 131 | if (!passive) { | 153 | if (!config.passive) { |
| 132 | if (chld_out.lines > (size_t)skip_stdout) | 154 | mp_subcheck sc_active_check = mp_subcheck_init(); |
| 133 | for (size_t i = skip_stdout; i < chld_out.lines; i++) | 155 | xasprintf(&sc_active_check.output, "command stdout:"); |
| 134 | puts(chld_out.line[i]); | 156 | |
| 135 | else | 157 | if (child_result.stdout.lines > skip_stdout) { |
| 136 | printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), state_text(result), remotecmd, result); | 158 | for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { |
| 137 | return result; /* return error status from remote command */ | 159 | xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output, |
| 160 | child_result.stdout.line[i]); | ||
| 161 | } | ||
| 162 | } else { | ||
| 163 | xasprintf(&sc_active_check.output, "remote command '%s' returned status %d", | ||
| 164 | config.remotecmd, child_result.cmd_error_code); | ||
| 165 | } | ||
| 166 | |||
| 167 | /* return error status from remote command */ | ||
| 168 | |||
| 169 | switch (child_result.cmd_error_code) { | ||
| 170 | case 0: | ||
| 171 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_OK); | ||
| 172 | break; | ||
| 173 | case 1: | ||
| 174 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_WARNING); | ||
| 175 | break; | ||
| 176 | case 2: | ||
| 177 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_CRITICAL); | ||
| 178 | break; | ||
| 179 | default: | ||
| 180 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_UNKNOWN); | ||
| 181 | break; | ||
| 182 | } | ||
| 183 | |||
| 184 | mp_add_subcheck_to_check(&overall, sc_active_check); | ||
| 185 | mp_exit(overall); | ||
| 138 | } | 186 | } |
| 139 | 187 | ||
| 140 | /* | 188 | /* |
| @@ -142,78 +190,117 @@ int main(int argc, char **argv) { | |||
| 142 | */ | 190 | */ |
| 143 | 191 | ||
| 144 | /* process output */ | 192 | /* process output */ |
| 145 | if (!(file_pointer = fopen(outputfile, "a"))) { | 193 | mp_subcheck sc_passive_file = mp_subcheck_init(); |
| 146 | printf(_("SSH WARNING: could not open %s\n"), outputfile); | 194 | FILE *output_file = NULL; |
| 147 | exit(STATE_UNKNOWN); | 195 | if (!(output_file = fopen(config.outputfile, "a"))) { |
| 196 | xasprintf(&sc_passive_file.output, "could not open %s", config.outputfile); | ||
| 197 | sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_UNKNOWN); | ||
| 198 | |||
| 199 | mp_add_subcheck_to_check(&overall, sc_passive_file); | ||
| 200 | mp_exit(overall); | ||
| 148 | } | 201 | } |
| 149 | 202 | ||
| 150 | local_time = time(NULL); | 203 | xasprintf(&sc_passive_file.output, "opened output file %s", config.outputfile); |
| 151 | commands = 0; | 204 | sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_OK); |
| 152 | for (size_t i = skip_stdout; i < chld_out.lines; i++) { | 205 | mp_add_subcheck_to_check(&overall, sc_passive_file); |
| 153 | status_text = chld_out.line[i++]; | ||
| 154 | if (i == chld_out.lines || strstr(chld_out.line[i], "STATUS CODE: ") == NULL) | ||
| 155 | die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); | ||
| 156 | 206 | ||
| 157 | if (service[commands] && status_text && sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { | 207 | time_t local_time = time(NULL); |
| 158 | fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, host_shortname, service[commands++], | 208 | unsigned int commands = 0; |
| 159 | cresult, status_text); | 209 | char *status_text; |
| 210 | int cresult; | ||
| 211 | mp_subcheck sc_parse_passive = mp_subcheck_init(); | ||
| 212 | for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { | ||
| 213 | status_text = child_result.stdout.line[i++]; | ||
| 214 | if (i == child_result.stdout.lines || | ||
| 215 | strstr(child_result.stdout.line[i], "STATUS CODE: ") == NULL) { | ||
| 216 | |||
| 217 | sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN); | ||
| 218 | xasprintf(&sc_parse_passive.output, "failed to parse output"); | ||
| 219 | mp_add_subcheck_to_check(&overall, sc_parse_passive); | ||
| 220 | mp_exit(overall); | ||
| 221 | } | ||
| 222 | |||
| 223 | if (config.service[commands] && status_text && | ||
| 224 | sscanf(child_result.stdout.line[i], "STATUS CODE: %d", &cresult) == 1) { | ||
| 225 | fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, | ||
| 226 | config.host_shortname, config.service[commands++], cresult, status_text); | ||
| 160 | } | 227 | } |
| 161 | } | 228 | } |
| 162 | 229 | ||
| 230 | sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_OK); | ||
| 231 | xasprintf(&sc_parse_passive.output, "parsed and wrote output"); | ||
| 232 | mp_add_subcheck_to_check(&overall, sc_parse_passive); | ||
| 233 | |||
| 163 | /* Multiple commands and passive checking should always return OK */ | 234 | /* Multiple commands and passive checking should always return OK */ |
| 164 | return result; | 235 | mp_exit(overall); |
| 165 | } | 236 | } |
| 166 | 237 | ||
| 167 | /* process command-line arguments */ | 238 | /* process command-line arguments */ |
| 168 | int process_arguments(int argc, char **argv) { | 239 | check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { |
| 169 | int c; | 240 | enum { |
| 170 | char *p1; | 241 | output_format_index = CHAR_MAX + 1, |
| 171 | char *p2; | 242 | }; |
| 243 | |||
| 244 | static struct option longopts[] = { | ||
| 245 | {"version", no_argument, 0, 'V'}, | ||
| 246 | {"help", no_argument, 0, 'h'}, | ||
| 247 | {"verbose", no_argument, 0, 'v'}, | ||
| 248 | {"fork", no_argument, 0, 'f'}, | ||
| 249 | {"timeout", required_argument, 0, 't'}, | ||
| 250 | {"unknown-timeout", no_argument, 0, 'U'}, | ||
| 251 | {"host", required_argument, 0, 'H'}, /* backward compatibility */ | ||
| 252 | {"hostname", required_argument, 0, 'H'}, | ||
| 253 | {"port", required_argument, 0, 'p'}, | ||
| 254 | {"output", required_argument, 0, 'O'}, | ||
| 255 | {"name", required_argument, 0, 'n'}, | ||
| 256 | {"services", required_argument, 0, 's'}, | ||
| 257 | {"identity", required_argument, 0, 'i'}, | ||
| 258 | {"user", required_argument, 0, 'u'}, /* backwards compatibility */ | ||
| 259 | {"logname", required_argument, 0, 'l'}, | ||
| 260 | {"command", required_argument, 0, 'C'}, | ||
| 261 | {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ | ||
| 262 | {"skip-stdout", optional_argument, 0, 'S'}, | ||
| 263 | {"skip-stderr", optional_argument, 0, 'E'}, | ||
| 264 | {"unknown-on-stderr", no_argument, 0, 'e'}, | ||
| 265 | {"warn-on-stderr", no_argument, 0, 'W'}, | ||
| 266 | {"proto1", no_argument, 0, '1'}, | ||
| 267 | {"proto2", no_argument, 0, '2'}, | ||
| 268 | {"use-ipv4", no_argument, 0, '4'}, | ||
| 269 | {"use-ipv6", no_argument, 0, '6'}, | ||
| 270 | {"ssh-option", required_argument, 0, 'o'}, | ||
| 271 | {"quiet", no_argument, 0, 'q'}, | ||
| 272 | {"configfile", optional_argument, 0, 'F'}, | ||
| 273 | {"output-format", required_argument, 0, output_format_index}, | ||
| 274 | {0, 0, 0, 0}}; | ||
| 275 | |||
| 276 | check_by_ssh_config_wrapper result = { | ||
| 277 | .errorcode = OK, | ||
| 278 | .config = check_by_ssh_config_init(), | ||
| 279 | }; | ||
| 280 | |||
| 281 | if (argc < 2) { | ||
| 282 | result.errorcode = ERROR; | ||
| 283 | return result; | ||
| 284 | } | ||
| 285 | |||
| 286 | for (int index = 1; index < argc; index++) { | ||
| 287 | if (strcmp("-to", argv[index]) == 0) { | ||
| 288 | strcpy(argv[index], "-t"); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | result.config.cmd = comm_append(result.config.cmd, SSH_COMMAND); | ||
| 172 | 293 | ||
| 173 | int option = 0; | 294 | int option = 0; |
| 174 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, | 295 | while (true) { |
| 175 | {"help", no_argument, 0, 'h'}, | 296 | int opt_index = |
| 176 | {"verbose", no_argument, 0, 'v'}, | 297 | getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option); |
| 177 | {"fork", no_argument, 0, 'f'}, | 298 | |
| 178 | {"timeout", required_argument, 0, 't'}, | 299 | if (opt_index == -1 || opt_index == EOF) { |
| 179 | {"unknown-timeout", no_argument, 0, 'U'}, | ||
| 180 | {"host", required_argument, 0, 'H'}, /* backward compatibility */ | ||
| 181 | {"hostname", required_argument, 0, 'H'}, | ||
| 182 | {"port", required_argument, 0, 'p'}, | ||
| 183 | {"output", required_argument, 0, 'O'}, | ||
| 184 | {"name", required_argument, 0, 'n'}, | ||
| 185 | {"services", required_argument, 0, 's'}, | ||
| 186 | {"identity", required_argument, 0, 'i'}, | ||
| 187 | {"user", required_argument, 0, 'u'}, | ||
| 188 | {"logname", required_argument, 0, 'l'}, | ||
| 189 | {"command", required_argument, 0, 'C'}, | ||
| 190 | {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ | ||
| 191 | {"skip-stdout", optional_argument, 0, 'S'}, | ||
| 192 | {"skip-stderr", optional_argument, 0, 'E'}, | ||
| 193 | {"warn-on-stderr", no_argument, 0, 'W'}, | ||
| 194 | {"proto1", no_argument, 0, '1'}, | ||
| 195 | {"proto2", no_argument, 0, '2'}, | ||
| 196 | {"use-ipv4", no_argument, 0, '4'}, | ||
| 197 | {"use-ipv6", no_argument, 0, '6'}, | ||
| 198 | {"ssh-option", required_argument, 0, 'o'}, | ||
| 199 | {"quiet", no_argument, 0, 'q'}, | ||
| 200 | {"configfile", optional_argument, 0, 'F'}, | ||
| 201 | {0, 0, 0, 0}}; | ||
| 202 | |||
| 203 | if (argc < 2) | ||
| 204 | return ERROR; | ||
| 205 | |||
| 206 | for (c = 1; c < argc; c++) | ||
| 207 | if (strcmp("-to", argv[c]) == 0) | ||
| 208 | strcpy(argv[c], "-t"); | ||
| 209 | |||
| 210 | while (1) { | ||
| 211 | c = getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option); | ||
| 212 | |||
| 213 | if (c == -1 || c == EOF) | ||
| 214 | break; | 300 | break; |
| 301 | } | ||
| 215 | 302 | ||
| 216 | switch (c) { | 303 | switch (opt_index) { |
| 217 | case 'V': /* version */ | 304 | case 'V': /* version */ |
| 218 | print_revision(progname, NP_VERSION); | 305 | print_revision(progname, NP_VERSION); |
| 219 | exit(STATE_UNKNOWN); | 306 | exit(STATE_UNKNOWN); |
| @@ -224,162 +311,221 @@ int process_arguments(int argc, char **argv) { | |||
| 224 | verbose = true; | 311 | verbose = true; |
| 225 | break; | 312 | break; |
| 226 | case 't': /* timeout period */ | 313 | case 't': /* timeout period */ |
| 227 | if (!is_integer(optarg)) | 314 | if (!is_integer(optarg)) { |
| 228 | usage_va(_("Timeout interval must be a positive integer")); | 315 | usage_va(_("Timeout interval must be a positive integer")); |
| 229 | else | 316 | } else { |
| 230 | timeout_interval = atoi(optarg); | 317 | timeout_interval = atoi(optarg); |
| 318 | } | ||
| 231 | break; | 319 | break; |
| 232 | case 'U': | 320 | case 'U': |
| 233 | unknown_timeout = true; | 321 | result.config.unknown_timeout = true; |
| 234 | break; | 322 | break; |
| 235 | case 'H': /* host */ | 323 | case 'H': /* host */ |
| 236 | hostname = optarg; | 324 | result.config.hostname = optarg; |
| 237 | break; | 325 | break; |
| 238 | case 'p': /* port number */ | 326 | case 'p': /* port number */ |
| 239 | if (!is_integer(optarg)) | 327 | if (!is_integer(optarg)) { |
| 240 | usage_va(_("Port must be a positive integer")); | 328 | usage_va(_("Port must be a positive integer")); |
| 241 | comm_append("-p"); | 329 | } |
| 242 | comm_append(optarg); | 330 | result.config.cmd = comm_append(result.config.cmd, "-p"); |
| 331 | result.config.cmd = comm_append(result.config.cmd, optarg); | ||
| 243 | break; | 332 | break; |
| 244 | case 'O': /* output file */ | 333 | case 'O': /* output file */ |
| 245 | outputfile = optarg; | 334 | result.config.outputfile = optarg; |
| 246 | passive = true; | 335 | result.config.passive = true; |
| 247 | break; | 336 | break; |
| 248 | case 's': /* description of service to check */ | 337 | case 's': /* description of service to check */ { |
| 338 | char *p1; | ||
| 339 | char *p2; | ||
| 340 | |||
| 249 | p1 = optarg; | 341 | p1 = optarg; |
| 250 | service = realloc(service, (++services) * sizeof(char *)); | 342 | result.config.service = realloc(result.config.service, |
| 343 | (++result.config.number_of_services) * sizeof(char *)); | ||
| 251 | while ((p2 = index(p1, ':'))) { | 344 | while ((p2 = index(p1, ':'))) { |
| 252 | *p2 = '\0'; | 345 | *p2 = '\0'; |
| 253 | service[services - 1] = p1; | 346 | result.config.service[result.config.number_of_services - 1] = p1; |
| 254 | service = realloc(service, (++services) * sizeof(char *)); | 347 | result.config.service = realloc( |
| 348 | result.config.service, (++result.config.number_of_services) * sizeof(char *)); | ||
| 255 | p1 = p2 + 1; | 349 | p1 = p2 + 1; |
| 256 | } | 350 | } |
| 257 | service[services - 1] = p1; | 351 | result.config.service[result.config.number_of_services - 1] = p1; |
| 258 | break; | 352 | break; |
| 259 | case 'n': /* short name of host in the monitoring configuration */ | 353 | case 'n': /* short name of host in the monitoring configuration */ |
| 260 | host_shortname = optarg; | 354 | result.config.host_shortname = optarg; |
| 261 | break; | 355 | } break; |
| 262 | |||
| 263 | case 'u': | 356 | case 'u': |
| 264 | comm_append("-l"); | 357 | result.config.cmd = comm_append(result.config.cmd, "-l"); |
| 265 | comm_append(optarg); | 358 | result.config.cmd = comm_append(result.config.cmd, optarg); |
| 266 | break; | 359 | break; |
| 267 | case 'l': /* login name */ | 360 | case 'l': /* login name */ |
| 268 | comm_append("-l"); | 361 | result.config.cmd = comm_append(result.config.cmd, "-l"); |
| 269 | comm_append(optarg); | 362 | result.config.cmd = comm_append(result.config.cmd, optarg); |
| 270 | break; | 363 | break; |
| 271 | case 'i': /* identity */ | 364 | case 'i': /* identity */ |
| 272 | comm_append("-i"); | 365 | result.config.cmd = comm_append(result.config.cmd, "-i"); |
| 273 | comm_append(optarg); | 366 | result.config.cmd = comm_append(result.config.cmd, optarg); |
| 274 | break; | 367 | break; |
| 275 | 368 | ||
| 276 | case '1': /* Pass these switches directly to ssh */ | 369 | case '1': /* Pass these switches directly to ssh */ |
| 277 | comm_append("-1"); | 370 | result.config.cmd = comm_append(result.config.cmd, "-1"); |
| 278 | break; | 371 | break; |
| 279 | case '2': /* 1 to force version 1, 2 to force version 2 */ | 372 | case '2': /* 1 to force version 1, 2 to force version 2 */ |
| 280 | comm_append("-2"); | 373 | result.config.cmd = comm_append(result.config.cmd, "-2"); |
| 281 | break; | 374 | break; |
| 282 | case '4': /* -4 for IPv4 */ | 375 | case '4': /* -4 for IPv4 */ |
| 283 | comm_append("-4"); | 376 | result.config.cmd = comm_append(result.config.cmd, "-4"); |
| 284 | break; | 377 | break; |
| 285 | case '6': /* -6 for IPv6 */ | 378 | case '6': /* -6 for IPv6 */ |
| 286 | comm_append("-6"); | 379 | result.config.cmd = comm_append(result.config.cmd, "-6"); |
| 287 | break; | 380 | break; |
| 288 | case 'f': /* fork to background */ | 381 | case 'f': /* fork to background */ |
| 289 | comm_append("-f"); | 382 | result.config.cmd = comm_append(result.config.cmd, "-f"); |
| 290 | break; | 383 | break; |
| 291 | case 'C': /* Command for remote machine */ | 384 | case 'C': /* Command for remote machine */ |
| 292 | commands++; | 385 | result.config.commands++; |
| 293 | if (commands > 1) | 386 | if (result.config.commands > 1) { |
| 294 | xasprintf(&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); | 387 | xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", |
| 295 | xasprintf(&remotecmd, "%s%s", remotecmd, optarg); | 388 | result.config.remotecmd); |
| 389 | } | ||
| 390 | xasprintf(&result.config.remotecmd, "%s%s", result.config.remotecmd, optarg); | ||
| 296 | break; | 391 | break; |
| 297 | case 'S': /* skip n (or all) lines on stdout */ | 392 | case 'S': /* skip n (or all) lines on stdout */ |
| 298 | if (optarg == NULL) | 393 | if (optarg == NULL) { |
| 299 | skip_stdout = -1; /* skip all output on stdout */ | 394 | result.config.skip_stdout = true; /* skip all output on stdout */ |
| 300 | else if (!is_integer(optarg)) | 395 | |
| 396 | if (verbose) { | ||
| 397 | printf("Setting the skip_stdout flag\n"); | ||
| 398 | } | ||
| 399 | } else if (!is_integer(optarg)) { | ||
| 301 | usage_va(_("skip-stdout argument must be an integer")); | 400 | usage_va(_("skip-stdout argument must be an integer")); |
| 302 | else | 401 | } else { |
| 303 | skip_stdout = atoi(optarg); | 402 | result.config.stdout_lines_to_ignore = atoi(optarg); |
| 403 | } | ||
| 304 | break; | 404 | break; |
| 305 | case 'E': /* skip n (or all) lines on stderr */ | 405 | case 'E': /* skip n (or all) lines on stderr */ |
| 306 | if (optarg == NULL) | 406 | if (optarg == NULL) { |
| 307 | skip_stderr = -1; /* skip all output on stderr */ | 407 | result.config.skip_stderr = true; /* skip all output on stderr */ |
| 308 | else if (!is_integer(optarg)) | 408 | if (verbose) { |
| 409 | printf("Setting the skip_stderr flag\n"); | ||
| 410 | } | ||
| 411 | } else if (!is_integer(optarg)) { | ||
| 309 | usage_va(_("skip-stderr argument must be an integer")); | 412 | usage_va(_("skip-stderr argument must be an integer")); |
| 310 | else | 413 | } else { |
| 311 | skip_stderr = atoi(optarg); | 414 | result.config.sterr_lines_to_ignore = atoi(optarg); |
| 415 | } | ||
| 416 | break; | ||
| 417 | case 'e': /* exit with unknown if there is an output on stderr */ | ||
| 418 | result.config.unknown_on_stderr = true; | ||
| 312 | break; | 419 | break; |
| 313 | case 'W': /* exit with warning if there is an output on stderr */ | 420 | case 'W': /* exit with warning if there is an output on stderr */ |
| 314 | warn_on_stderr = 1; | 421 | result.config.warn_on_stderr = true; |
| 315 | break; | 422 | break; |
| 316 | case 'o': /* Extra options for the ssh command */ | 423 | case 'o': /* Extra options for the ssh command */ |
| 317 | comm_append("-o"); | 424 | result.config.cmd = comm_append(result.config.cmd, "-o"); |
| 318 | comm_append(optarg); | 425 | result.config.cmd = comm_append(result.config.cmd, optarg); |
| 319 | break; | 426 | break; |
| 320 | case 'q': /* Tell the ssh command to be quiet */ | 427 | case 'q': /* Tell the ssh command to be quiet */ |
| 321 | comm_append("-q"); | 428 | result.config.cmd = comm_append(result.config.cmd, "-q"); |
| 322 | break; | 429 | break; |
| 323 | case 'F': /* ssh configfile */ | 430 | case 'F': /* ssh configfile */ |
| 324 | comm_append("-F"); | 431 | result.config.cmd = comm_append(result.config.cmd, "-F"); |
| 325 | comm_append(optarg); | 432 | result.config.cmd = comm_append(result.config.cmd, optarg); |
| 326 | break; | 433 | break; |
| 434 | case output_format_index: { | ||
| 435 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 436 | if (!parser.parsing_success) { | ||
| 437 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 438 | printf("Invalid output format: %s\n", optarg); | ||
| 439 | exit(STATE_UNKNOWN); | ||
| 440 | } | ||
| 441 | |||
| 442 | result.config.output_format_is_set = true; | ||
| 443 | result.config.output_format = parser.output_format; | ||
| 444 | break; | ||
| 445 | } | ||
| 327 | default: /* help */ | 446 | default: /* help */ |
| 328 | usage5(); | 447 | usage5(); |
| 329 | } | 448 | } |
| 330 | } | 449 | } |
| 331 | 450 | ||
| 332 | c = optind; | 451 | int c = optind; |
| 333 | if (hostname == NULL) { | 452 | if (result.config.hostname == NULL) { |
| 334 | if (c <= argc) { | 453 | if (c <= argc) { |
| 335 | die(STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname); | 454 | die(STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname); |
| 336 | } | 455 | } |
| 337 | hostname = argv[c++]; | 456 | result.config.hostname = argv[c++]; |
| 338 | } | 457 | } |
| 339 | 458 | ||
| 340 | if (strlen(remotecmd) == 0) { | 459 | if (strlen(result.config.remotecmd) == 0) { |
| 341 | for (; c < argc; c++) | 460 | for (; c < argc; c++) { |
| 342 | if (strlen(remotecmd) > 0) | 461 | if (strlen(result.config.remotecmd) > 0) { |
| 343 | xasprintf(&remotecmd, "%s %s", remotecmd, argv[c]); | 462 | xasprintf(&result.config.remotecmd, "%s %s", result.config.remotecmd, argv[c]); |
| 344 | else | 463 | } else { |
| 345 | xasprintf(&remotecmd, "%s", argv[c]); | 464 | xasprintf(&result.config.remotecmd, "%s", argv[c]); |
| 465 | } | ||
| 466 | } | ||
| 346 | } | 467 | } |
| 347 | 468 | ||
| 348 | if (commands > 1 || passive) | 469 | if (result.config.commands > 1 || result.config.passive) { |
| 349 | xasprintf(&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); | 470 | xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", result.config.remotecmd); |
| 471 | } | ||
| 350 | 472 | ||
| 351 | if (remotecmd == NULL || strlen(remotecmd) <= 1) | 473 | if (result.config.remotecmd == NULL || strlen(result.config.remotecmd) <= 1) { |
| 352 | usage_va(_("No remotecmd")); | 474 | usage_va(_("No remotecmd")); |
| 475 | } | ||
| 353 | 476 | ||
| 354 | comm_append(hostname); | 477 | result.config.cmd = comm_append(result.config.cmd, result.config.hostname); |
| 355 | comm_append(remotecmd); | 478 | result.config.cmd = comm_append(result.config.cmd, result.config.remotecmd); |
| 356 | 479 | ||
| 357 | return validate_arguments(); | 480 | return validate_arguments(result); |
| 358 | } | 481 | } |
| 359 | 482 | ||
| 360 | void comm_append(const char *str) { | 483 | command_construct comm_append(command_construct cmd, const char *str) { |
| 484 | |||
| 485 | if (verbose) { | ||
| 486 | for (int i = 0; i < cmd.commargc; i++) { | ||
| 487 | printf("Current command: [%i] %s\n", i, cmd.commargv[i]); | ||
| 488 | } | ||
| 361 | 489 | ||
| 362 | if (++commargc > NP_MAXARGS) | 490 | printf("Appending: %s\n", str); |
| 491 | } | ||
| 492 | |||
| 493 | if (++cmd.commargc > NP_MAXARGS) { | ||
| 363 | die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS); | 494 | die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS); |
| 495 | } | ||
| 364 | 496 | ||
| 365 | if ((commargv = (char **)realloc(commargv, (commargc + 1) * sizeof(char *))) == NULL) | 497 | if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) == |
| 498 | NULL) { | ||
| 366 | die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); | 499 | die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); |
| 500 | } | ||
| 501 | |||
| 502 | cmd.commargv[cmd.commargc - 1] = strdup(str); | ||
| 503 | cmd.commargv[cmd.commargc] = NULL; | ||
| 367 | 504 | ||
| 368 | commargv[commargc - 1] = strdup(str); | 505 | return cmd; |
| 369 | commargv[commargc] = NULL; | ||
| 370 | } | 506 | } |
| 371 | 507 | ||
| 372 | int validate_arguments(void) { | 508 | check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper config_wrapper) { |
| 373 | if (remotecmd == NULL || hostname == NULL) | 509 | if (config_wrapper.config.remotecmd == NULL || config_wrapper.config.hostname == NULL) { |
| 374 | return ERROR; | 510 | config_wrapper.errorcode = ERROR; |
| 511 | return config_wrapper; | ||
| 512 | } | ||
| 375 | 513 | ||
| 376 | if (passive && commands != services) | 514 | if (config_wrapper.config.passive && |
| 377 | die(STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname); | 515 | config_wrapper.config.commands != config_wrapper.config.number_of_services) { |
| 516 | die(STATE_UNKNOWN, | ||
| 517 | _("%s: In passive mode, you must provide a service name for each command.\n"), | ||
| 518 | progname); | ||
| 519 | } | ||
| 378 | 520 | ||
| 379 | if (passive && host_shortname == NULL) | 521 | if (config_wrapper.config.passive && config_wrapper.config.host_shortname == NULL) { |
| 380 | die(STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the monitoring configs.\n"), progname); | 522 | die(STATE_UNKNOWN, |
| 523 | _("%s: In passive mode, you must provide the host short name from the monitoring " | ||
| 524 | "configs.\n"), | ||
| 525 | progname); | ||
| 526 | } | ||
| 381 | 527 | ||
| 382 | return OK; | 528 | return config_wrapper; |
| 383 | } | 529 | } |
| 384 | 530 | ||
| 385 | void print_help(void) { | 531 | void print_help(void) { |
| @@ -410,10 +556,13 @@ void print_help(void) { | |||
| 410 | printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]")); | 556 | printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]")); |
| 411 | printf(" %s\n", "-E, --skip-stderr[=n]"); | 557 | printf(" %s\n", "-E, --skip-stderr[=n]"); |
| 412 | printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]")); | 558 | printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]")); |
| 413 | printf(" %s\n", "-W, --warn-on-stderr]"); | 559 | printf(" %s\n", "-e, --unknown-on-stderr"); |
| 414 | printf(" %s\n", _("Exit with an warning, if there is an output on STDERR")); | 560 | printf(" %s\n", _("Exit with UNKNOWN, if there is output on STDERR")); |
| 561 | printf(" %s\n", "-W, --warn-on-stderr"); | ||
| 562 | printf(" %s\n", _("Exit with WARNING, if there is output on STDERR")); | ||
| 415 | printf(" %s\n", "-f"); | 563 | printf(" %s\n", "-f"); |
| 416 | printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed")); | 564 | printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always " |
| 565 | "return OK if ssh is executed")); | ||
| 417 | printf(" %s\n", "-C, --command='COMMAND STRING'"); | 566 | printf(" %s\n", "-C, --command='COMMAND STRING'"); |
| 418 | printf(" %s\n", _("command to execute on the remote machine")); | 567 | printf(" %s\n", _("command to execute on the remote machine")); |
| 419 | printf(" %s\n", "-l, --logname=USERNAME"); | 568 | printf(" %s\n", "-l, --logname=USERNAME"); |
| @@ -432,11 +581,11 @@ void print_help(void) { | |||
| 432 | printf(" %s\n", _("Tell ssh to use this configfile [optional]")); | 581 | printf(" %s\n", _("Tell ssh to use this configfile [optional]")); |
| 433 | printf(" %s\n", "-q, --quiet"); | 582 | printf(" %s\n", "-q, --quiet"); |
| 434 | printf(" %s\n", _("Tell ssh to suppress warning and diagnostic messages [optional]")); | 583 | printf(" %s\n", _("Tell ssh to suppress warning and diagnostic messages [optional]")); |
| 435 | printf(UT_WARN_CRIT); | ||
| 436 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 584 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 437 | printf(" %s\n", "-U, --unknown-timeout"); | 585 | printf(" %s\n", "-U, --unknown-timeout"); |
| 438 | printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); | 586 | printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); |
| 439 | printf(UT_VERBOSE); | 587 | printf(UT_VERBOSE); |
| 588 | printf(UT_OUTPUT_FORMAT); | ||
| 440 | printf("\n"); | 589 | printf("\n"); |
| 441 | printf(" %s\n", _("The most common mode of use is to refer to a local identity file with")); | 590 | printf(" %s\n", _("The most common mode of use is to refer to a local identity file with")); |
| 442 | printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); | 591 | printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); |
| @@ -450,7 +599,8 @@ void print_help(void) { | |||
| 450 | printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); | 599 | printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); |
| 451 | printf("\n"); | 600 | printf("\n"); |
| 452 | printf("%s\n", _("Examples:")); | 601 | printf("%s\n", _("Examples:")); |
| 453 | printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); | 602 | printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C " |
| 603 | "uptime -O /tmp/foo"); | ||
| 454 | printf(" %s\n", "$ cat /tmp/foo"); | 604 | printf(" %s\n", "$ cat /tmp/foo"); |
| 455 | printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); | 605 | printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); |
| 456 | printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); | 606 | printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); |
| @@ -462,7 +612,7 @@ void print_help(void) { | |||
| 462 | void print_usage(void) { | 612 | void print_usage(void) { |
| 463 | printf("%s\n", _("Usage:")); | 613 | printf("%s\n", _("Usage:")); |
| 464 | printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n" | 614 | printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n" |
| 465 | " [-S [lines]] [-E [lines]] [-W] [-t timeout] [-i identity]\n" | 615 | " [-S [lines]] [-E [lines]] [-e|-W] [-t timeout] [-i identity]\n" |
| 466 | " [-l user] [-n name] [-s servicelist] [-O outputfile]\n" | 616 | " [-l user] [-n name] [-s servicelist] [-O outputfile]\n" |
| 467 | " [-p port] [-o ssh-option] [-F configfile]\n", | 617 | " [-p port] [-o ssh-option] [-F configfile]\n", |
| 468 | progname); | 618 | progname); |
diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h new file mode 100644 index 00000000..b6a57964 --- /dev/null +++ b/plugins/check_by_ssh.d/config.h | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include <stddef.h> | ||
| 6 | |||
| 7 | typedef struct { | ||
| 8 | int commargc; | ||
| 9 | char **commargv; | ||
| 10 | } command_construct; | ||
| 11 | |||
| 12 | typedef struct { | ||
| 13 | char *hostname; | ||
| 14 | char *host_shortname; | ||
| 15 | |||
| 16 | char **service; | ||
| 17 | unsigned int number_of_services; | ||
| 18 | |||
| 19 | unsigned int commands; // Not needed during actual test run | ||
| 20 | char *remotecmd; | ||
| 21 | |||
| 22 | command_construct cmd; | ||
| 23 | |||
| 24 | bool unknown_timeout; | ||
| 25 | bool unknown_on_stderr; | ||
| 26 | bool warn_on_stderr; | ||
| 27 | bool skip_stdout; | ||
| 28 | size_t stdout_lines_to_ignore; | ||
| 29 | bool skip_stderr; | ||
| 30 | size_t sterr_lines_to_ignore; | ||
| 31 | |||
| 32 | bool passive; | ||
| 33 | char *outputfile; | ||
| 34 | |||
| 35 | bool output_format_is_set; | ||
| 36 | mp_output_format output_format; | ||
| 37 | } check_by_ssh_config; | ||
| 38 | |||
| 39 | check_by_ssh_config check_by_ssh_config_init() { | ||
| 40 | check_by_ssh_config tmp = { | ||
| 41 | .hostname = NULL, | ||
| 42 | .host_shortname = NULL, | ||
| 43 | |||
| 44 | .service = NULL, | ||
| 45 | .number_of_services = 0, | ||
| 46 | |||
| 47 | .commands = 0, | ||
| 48 | .remotecmd = "", | ||
| 49 | |||
| 50 | .cmd = | ||
| 51 | { | ||
| 52 | .commargc = 0, | ||
| 53 | .commargv = NULL, | ||
| 54 | }, | ||
| 55 | |||
| 56 | .unknown_timeout = false, | ||
| 57 | .unknown_on_stderr = false, | ||
| 58 | .warn_on_stderr = false, | ||
| 59 | |||
| 60 | .skip_stderr = false, | ||
| 61 | .stdout_lines_to_ignore = 0, | ||
| 62 | .skip_stdout = false, | ||
| 63 | .sterr_lines_to_ignore = 0, | ||
| 64 | |||
| 65 | .passive = false, | ||
| 66 | .outputfile = NULL, | ||
| 67 | |||
| 68 | .output_format_is_set = false, | ||
| 69 | }; | ||
| 70 | return tmp; | ||
| 71 | } | ||
diff --git a/plugins/check_cluster.c b/plugins/check_cluster.c index b40c38c7..1cbdcd60 100644 --- a/plugins/check_cluster.c +++ b/plugins/check_cluster.c | |||
| @@ -26,45 +26,25 @@ const char *progname = "check_cluster"; | |||
| 26 | const char *copyright = "2000-2024"; | 26 | const char *copyright = "2000-2024"; |
| 27 | const char *email = "devel@monitoring-plugins.org"; | 27 | const char *email = "devel@monitoring-plugins.org"; |
| 28 | 28 | ||
| 29 | #include "output.h" | ||
| 30 | #include "states.h" | ||
| 29 | #include "common.h" | 31 | #include "common.h" |
| 30 | #include "utils.h" | 32 | #include "utils.h" |
| 31 | #include "utils_base.h" | 33 | #include "utils_base.h" |
| 32 | 34 | #include "check_cluster.d/config.h" | |
| 33 | enum { | ||
| 34 | CHECK_SERVICES = 1, | ||
| 35 | CHECK_HOSTS = 2 | ||
| 36 | }; | ||
| 37 | 35 | ||
| 38 | static void print_help(void); | 36 | static void print_help(void); |
| 39 | void print_usage(void); | 37 | void print_usage(void); |
| 40 | 38 | ||
| 41 | static int total_services_ok = 0; | ||
| 42 | static int total_services_warning = 0; | ||
| 43 | static int total_services_unknown = 0; | ||
| 44 | static int total_services_critical = 0; | ||
| 45 | |||
| 46 | static int total_hosts_up = 0; | ||
| 47 | static int total_hosts_down = 0; | ||
| 48 | static int total_hosts_unreachable = 0; | ||
| 49 | |||
| 50 | static char *warn_threshold; | ||
| 51 | static char *crit_threshold; | ||
| 52 | |||
| 53 | static int check_type = CHECK_SERVICES; | ||
| 54 | |||
| 55 | static char *data_vals = NULL; | ||
| 56 | static char *label = NULL; | ||
| 57 | |||
| 58 | static int verbose = 0; | 39 | static int verbose = 0; |
| 59 | 40 | ||
| 60 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 41 | typedef struct { |
| 42 | int errorcode; | ||
| 43 | check_cluster_config config; | ||
| 44 | } check_cluster_config_wrapper; | ||
| 45 | static check_cluster_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 61 | 46 | ||
| 62 | int main(int argc, char **argv) { | 47 | int main(int argc, char **argv) { |
| 63 | char *ptr; | ||
| 64 | int data_val; | ||
| 65 | int return_code = STATE_OK; | ||
| 66 | thresholds *thresholds = NULL; | ||
| 67 | |||
| 68 | setlocale(LC_ALL, ""); | 48 | setlocale(LC_ALL, ""); |
| 69 | bindtextdomain(PACKAGE, LOCALEDIR); | 49 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 70 | textdomain(PACKAGE); | 50 | textdomain(PACKAGE); |
| @@ -72,20 +52,35 @@ int main(int argc, char **argv) { | |||
| 72 | /* Parse extra opts if any */ | 52 | /* Parse extra opts if any */ |
| 73 | argv = np_extra_opts(&argc, argv, progname); | 53 | argv = np_extra_opts(&argc, argv, progname); |
| 74 | 54 | ||
| 75 | if (process_arguments(argc, argv) == ERROR) | 55 | check_cluster_config_wrapper tmp_config = process_arguments(argc, argv); |
| 56 | if (tmp_config.errorcode == ERROR) { | ||
| 76 | usage(_("Could not parse arguments")); | 57 | usage(_("Could not parse arguments")); |
| 58 | } | ||
| 59 | |||
| 60 | const check_cluster_config config = tmp_config.config; | ||
| 61 | |||
| 62 | if (config.output_format_is_set) { | ||
| 63 | mp_set_format(config.output_format); | ||
| 64 | } | ||
| 77 | 65 | ||
| 78 | /* Initialize the thresholds */ | 66 | /* Initialize the thresholds */ |
| 79 | set_thresholds(&thresholds, warn_threshold, crit_threshold); | 67 | if (verbose) { |
| 80 | if (verbose) | 68 | print_thresholds("check_cluster", config.thresholds); |
| 81 | print_thresholds("check_cluster", thresholds); | 69 | } |
| 82 | 70 | ||
| 71 | int data_val; | ||
| 72 | int total_services_ok = 0; | ||
| 73 | int total_services_warning = 0; | ||
| 74 | int total_services_unknown = 0; | ||
| 75 | int total_services_critical = 0; | ||
| 76 | int total_hosts_up = 0; | ||
| 77 | int total_hosts_down = 0; | ||
| 78 | int total_hosts_unreachable = 0; | ||
| 83 | /* check the data values */ | 79 | /* check the data values */ |
| 84 | for (ptr = strtok(data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) { | 80 | for (char *ptr = strtok(config.data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) { |
| 85 | |||
| 86 | data_val = atoi(ptr); | 81 | data_val = atoi(ptr); |
| 87 | 82 | ||
| 88 | if (check_type == CHECK_SERVICES) { | 83 | if (config.check_type == CHECK_SERVICES) { |
| 89 | switch (data_val) { | 84 | switch (data_val) { |
| 90 | case 0: | 85 | case 0: |
| 91 | total_services_ok++; | 86 | total_services_ok++; |
| @@ -119,101 +114,141 @@ int main(int argc, char **argv) { | |||
| 119 | } | 114 | } |
| 120 | } | 115 | } |
| 121 | 116 | ||
| 117 | mp_check overall = mp_check_init(); | ||
| 118 | mp_subcheck sc_real_test = mp_subcheck_init(); | ||
| 119 | sc_real_test = mp_set_subcheck_default_state(sc_real_test, STATE_OK); | ||
| 120 | |||
| 122 | /* return the status of the cluster */ | 121 | /* return the status of the cluster */ |
| 123 | if (check_type == CHECK_SERVICES) { | 122 | if (config.check_type == CHECK_SERVICES) { |
| 124 | return_code = get_status(total_services_warning + total_services_unknown + total_services_critical, thresholds); | 123 | sc_real_test = mp_set_subcheck_state( |
| 125 | printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", state_text(return_code), | 124 | sc_real_test, |
| 126 | (label == NULL) ? "Service cluster" : label, total_services_ok, total_services_warning, total_services_unknown, | 125 | get_status(total_services_warning + total_services_unknown + total_services_critical, |
| 127 | total_services_critical); | 126 | config.thresholds)); |
| 127 | xasprintf(&sc_real_test.output, "%s: %d ok, %d warning, %d unknown, %d critical", | ||
| 128 | (config.label == NULL) ? "Service cluster" : config.label, total_services_ok, | ||
| 129 | total_services_warning, total_services_unknown, total_services_critical); | ||
| 128 | } else { | 130 | } else { |
| 129 | return_code = get_status(total_hosts_down + total_hosts_unreachable, thresholds); | 131 | sc_real_test = mp_set_subcheck_state( |
| 130 | printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", state_text(return_code), (label == NULL) ? "Host cluster" : label, | 132 | sc_real_test, |
| 131 | total_hosts_up, total_hosts_down, total_hosts_unreachable); | 133 | get_status(total_hosts_down + total_hosts_unreachable, config.thresholds)); |
| 134 | xasprintf(&sc_real_test.output, "%s: %d up, %d down, %d unreachable\n", | ||
| 135 | (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up, | ||
| 136 | total_hosts_down, total_hosts_unreachable); | ||
| 132 | } | 137 | } |
| 133 | 138 | ||
| 134 | return return_code; | 139 | mp_add_subcheck_to_check(&overall, sc_real_test); |
| 140 | |||
| 141 | mp_exit(overall); | ||
| 135 | } | 142 | } |
| 136 | 143 | ||
| 137 | int process_arguments(int argc, char **argv) { | 144 | check_cluster_config_wrapper process_arguments(int argc, char **argv) { |
| 138 | int c; | 145 | enum { |
| 139 | char *ptr; | 146 | output_format_index = CHAR_MAX + 1, |
| 140 | int option = 0; | 147 | }; |
| 141 | static struct option longopts[] = {{"data", required_argument, 0, 'd'}, {"warning", required_argument, 0, 'w'}, | 148 | |
| 142 | {"critical", required_argument, 0, 'c'}, {"label", required_argument, 0, 'l'}, | 149 | static struct option longopts[] = {{"data", required_argument, 0, 'd'}, |
| 143 | {"host", no_argument, 0, 'h'}, {"service", no_argument, 0, 's'}, | 150 | {"warning", required_argument, 0, 'w'}, |
| 144 | {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, | 151 | {"critical", required_argument, 0, 'c'}, |
| 145 | {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}}; | 152 | {"label", required_argument, 0, 'l'}, |
| 153 | {"host", no_argument, 0, 'h'}, | ||
| 154 | {"service", no_argument, 0, 's'}, | ||
| 155 | {"verbose", no_argument, 0, 'v'}, | ||
| 156 | {"version", no_argument, 0, 'V'}, | ||
| 157 | {"help", no_argument, 0, 'H'}, | ||
| 158 | {"output-format", required_argument, 0, output_format_index}, | ||
| 159 | {0, 0, 0, 0}}; | ||
| 160 | |||
| 161 | check_cluster_config_wrapper result = { | ||
| 162 | .errorcode = OK, | ||
| 163 | .config = check_cluster_config_init(), | ||
| 164 | }; | ||
| 146 | 165 | ||
| 147 | /* no options were supplied */ | 166 | /* no options were supplied */ |
| 148 | if (argc < 2) | 167 | if (argc < 2) { |
| 149 | return ERROR; | 168 | result.errorcode = ERROR; |
| 150 | 169 | return result; | |
| 151 | while (1) { | 170 | } |
| 152 | 171 | ||
| 153 | c = getopt_long(argc, argv, "hHsvVw:c:d:l:", longopts, &option); | 172 | int option = 0; |
| 173 | char *warn_threshold = NULL; | ||
| 174 | char *crit_threshold = NULL; | ||
| 175 | while (true) { | ||
| 176 | int option_index = getopt_long(argc, argv, "hHsvVw:c:d:l:", longopts, &option); | ||
| 154 | 177 | ||
| 155 | if (c == -1 || c == EOF || c == 1) | 178 | if (option_index == -1 || option_index == EOF || option_index == 1) { |
| 156 | break; | 179 | break; |
| 180 | } | ||
| 157 | 181 | ||
| 158 | switch (c) { | 182 | switch (option_index) { |
| 159 | |||
| 160 | case 'h': /* host cluster */ | 183 | case 'h': /* host cluster */ |
| 161 | check_type = CHECK_HOSTS; | 184 | result.config.check_type = CHECK_HOSTS; |
| 162 | break; | 185 | break; |
| 163 | |||
| 164 | case 's': /* service cluster */ | 186 | case 's': /* service cluster */ |
| 165 | check_type = CHECK_SERVICES; | 187 | result.config.check_type = CHECK_SERVICES; |
| 166 | break; | 188 | break; |
| 167 | |||
| 168 | case 'w': /* warning threshold */ | 189 | case 'w': /* warning threshold */ |
| 169 | warn_threshold = strdup(optarg); | 190 | warn_threshold = strdup(optarg); |
| 170 | break; | 191 | break; |
| 171 | |||
| 172 | case 'c': /* warning threshold */ | 192 | case 'c': /* warning threshold */ |
| 173 | crit_threshold = strdup(optarg); | 193 | crit_threshold = strdup(optarg); |
| 174 | break; | 194 | break; |
| 175 | |||
| 176 | case 'd': /* data values */ | 195 | case 'd': /* data values */ |
| 177 | data_vals = (char *)strdup(optarg); | 196 | result.config.data_vals = strdup(optarg); |
| 178 | /* validate data */ | 197 | /* validate data */ |
| 179 | for (ptr = data_vals; ptr != NULL; ptr += 2) { | 198 | for (char *ptr = result.config.data_vals; ptr != NULL; ptr += 2) { |
| 180 | if (ptr[0] < '0' || ptr[0] > '3') | 199 | if (ptr[0] < '0' || ptr[0] > '3') { |
| 181 | return ERROR; | 200 | result.errorcode = ERROR; |
| 182 | if (ptr[1] == '\0') | 201 | return result; |
| 202 | } | ||
| 203 | if (ptr[1] == '\0') { | ||
| 183 | break; | 204 | break; |
| 184 | if (ptr[1] != ',') | 205 | } |
| 185 | return ERROR; | 206 | if (ptr[1] != ',') { |
| 207 | result.errorcode = ERROR; | ||
| 208 | return result; | ||
| 209 | } | ||
| 186 | } | 210 | } |
| 187 | break; | 211 | break; |
| 188 | |||
| 189 | case 'l': /* text label */ | 212 | case 'l': /* text label */ |
| 190 | label = (char *)strdup(optarg); | 213 | result.config.label = strdup(optarg); |
| 191 | break; | 214 | break; |
| 192 | |||
| 193 | case 'v': /* verbose */ | 215 | case 'v': /* verbose */ |
| 194 | verbose++; | 216 | verbose++; |
| 195 | break; | 217 | break; |
| 196 | |||
| 197 | case 'V': /* version */ | 218 | case 'V': /* version */ |
| 198 | print_revision(progname, NP_VERSION); | 219 | print_revision(progname, NP_VERSION); |
| 199 | exit(STATE_UNKNOWN); | 220 | exit(STATE_UNKNOWN); |
| 200 | break; | 221 | break; |
| 201 | |||
| 202 | case 'H': /* help */ | 222 | case 'H': /* help */ |
| 203 | print_help(); | 223 | print_help(); |
| 204 | exit(STATE_UNKNOWN); | 224 | exit(STATE_UNKNOWN); |
| 205 | break; | 225 | break; |
| 226 | case output_format_index: { | ||
| 227 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 228 | if (!parser.parsing_success) { | ||
| 229 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 230 | printf("Invalid output format: %s\n", optarg); | ||
| 231 | exit(STATE_UNKNOWN); | ||
| 232 | } | ||
| 206 | 233 | ||
| 234 | result.config.output_format_is_set = true; | ||
| 235 | result.config.output_format = parser.output_format; | ||
| 236 | break; | ||
| 237 | } | ||
| 207 | default: | 238 | default: |
| 208 | return ERROR; | 239 | result.errorcode = ERROR; |
| 240 | return result; | ||
| 209 | break; | 241 | break; |
| 210 | } | 242 | } |
| 211 | } | 243 | } |
| 212 | 244 | ||
| 213 | if (data_vals == NULL) | 245 | if (result.config.data_vals == NULL) { |
| 214 | return ERROR; | 246 | result.errorcode = ERROR; |
| 247 | return result; | ||
| 248 | } | ||
| 215 | 249 | ||
| 216 | return OK; | 250 | set_thresholds(&result.config.thresholds, warn_threshold, crit_threshold); |
| 251 | return result; | ||
| 217 | } | 252 | } |
| 218 | 253 | ||
| 219 | void print_help(void) { | 254 | void print_help(void) { |
| @@ -247,6 +282,8 @@ void print_help(void) { | |||
| 247 | 282 | ||
| 248 | printf(UT_VERBOSE); | 283 | printf(UT_VERBOSE); |
| 249 | 284 | ||
| 285 | printf(UT_OUTPUT_FORMAT); | ||
| 286 | |||
| 250 | printf("\n"); | 287 | printf("\n"); |
| 251 | printf("%s\n", _("Notes:")); | 288 | printf("%s\n", _("Notes:")); |
| 252 | printf(UT_THRESHOLDS_NOTES); | 289 | printf(UT_THRESHOLDS_NOTES); |
| @@ -254,7 +291,8 @@ void print_help(void) { | |||
| 254 | printf("\n"); | 291 | printf("\n"); |
| 255 | printf("%s\n", _("Examples:")); | 292 | printf("%s\n", _("Examples:")); |
| 256 | printf(" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:"); | 293 | printf(" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:"); |
| 257 | printf(" %s\n", _("Will alert critical if there are 3 or more service data points in a non-OK")); | 294 | printf(" %s\n", |
| 295 | _("Will alert critical if there are 3 or more service data points in a non-OK")); | ||
| 258 | printf(" %s\n", _("state.")); | 296 | printf(" %s\n", _("state.")); |
| 259 | 297 | ||
| 260 | printf(UT_SUPPORT); | 298 | printf(UT_SUPPORT); |
diff --git a/plugins/check_cluster.d/config.h b/plugins/check_cluster.d/config.h new file mode 100644 index 00000000..054657b0 --- /dev/null +++ b/plugins/check_cluster.d/config.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "../../lib/thresholds.h" | ||
| 5 | #include "output.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | |||
| 8 | enum { | ||
| 9 | CHECK_SERVICES = 1, | ||
| 10 | CHECK_HOSTS = 2 | ||
| 11 | }; | ||
| 12 | |||
| 13 | typedef struct { | ||
| 14 | char *data_vals; | ||
| 15 | thresholds *thresholds; | ||
| 16 | int check_type; | ||
| 17 | char *label; | ||
| 18 | |||
| 19 | mp_output_format output_format; | ||
| 20 | bool output_format_is_set; | ||
| 21 | } check_cluster_config; | ||
| 22 | |||
| 23 | check_cluster_config check_cluster_config_init() { | ||
| 24 | check_cluster_config tmp = { | ||
| 25 | .data_vals = NULL, | ||
| 26 | .thresholds = NULL, | ||
| 27 | .check_type = CHECK_SERVICES, | ||
| 28 | .label = NULL, | ||
| 29 | |||
| 30 | .output_format_is_set = false, | ||
| 31 | }; | ||
| 32 | return tmp; | ||
| 33 | } | ||
diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 748201e8..e3e514ff 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c | |||
| @@ -32,16 +32,23 @@ | |||
| 32 | * | 32 | * |
| 33 | * | 33 | * |
| 34 | *****************************************************************************/ | 34 | *****************************************************************************/ |
| 35 | const char *progname = "check_curl"; | ||
| 36 | 35 | ||
| 36 | const char *progname = "check_curl"; | ||
| 37 | const char *copyright = "2006-2024"; | 37 | const char *copyright = "2006-2024"; |
| 38 | const char *email = "devel@monitoring-plugins.org"; | 38 | const char *email = "devel@monitoring-plugins.org"; |
| 39 | 39 | ||
| 40 | #include "check_curl.d/config.h" | ||
| 41 | #include "states.h" | ||
| 42 | #include "thresholds.h" | ||
| 40 | #include <stdbool.h> | 43 | #include <stdbool.h> |
| 41 | #include <ctype.h> | 44 | #include <ctype.h> |
| 45 | #include "output.h" | ||
| 46 | #include "perfdata.h" | ||
| 42 | 47 | ||
| 48 | #include <assert.h> | ||
| 43 | #include "common.h" | 49 | #include "common.h" |
| 44 | #include "utils.h" | 50 | #include "utils.h" |
| 51 | #include "./check_curl.d/check_curl_helpers.h" | ||
| 45 | 52 | ||
| 46 | #ifndef LIBCURL_PROTOCOL_HTTP | 53 | #ifndef LIBCURL_PROTOCOL_HTTP |
| 47 | # error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense | 54 | # error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense |
| @@ -50,8 +57,6 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 50 | #include "curl/curl.h" | 57 | #include "curl/curl.h" |
| 51 | #include "curl/easy.h" | 58 | #include "curl/easy.h" |
| 52 | 59 | ||
| 53 | #include "picohttpparser.h" | ||
| 54 | |||
| 55 | #include "uriparser/Uri.h" | 60 | #include "uriparser/Uri.h" |
| 56 | 61 | ||
| 57 | #include <arpa/inet.h> | 62 | #include <arpa/inet.h> |
| @@ -63,207 +68,58 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 63 | 68 | ||
| 64 | #include <netdb.h> | 69 | #include <netdb.h> |
| 65 | 70 | ||
| 66 | #define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch)) | ||
| 67 | |||
| 68 | #define DEFAULT_BUFFER_SIZE 2048 | ||
| 69 | #define DEFAULT_SERVER_URL "/" | ||
| 70 | #define HTTP_EXPECT "HTTP/" | ||
| 71 | #define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN | ||
| 72 | enum { | 71 | enum { |
| 73 | MAX_IPV4_HOSTLENGTH = 255, | 72 | REGS = 2, |
| 74 | HTTP_PORT = 80, | ||
| 75 | HTTPS_PORT = 443, | ||
| 76 | MAX_PORT = 65535, | ||
| 77 | DEFAULT_MAX_REDIRS = 15 | ||
| 78 | }; | 73 | }; |
| 79 | 74 | ||
| 80 | enum { | 75 | #include "regex.h" |
| 81 | STICKY_NONE = 0, | ||
| 82 | STICKY_HOST = 1, | ||
| 83 | STICKY_PORT = 2 | ||
| 84 | }; | ||
| 85 | 76 | ||
| 86 | enum { | 77 | // Globals |
| 87 | FOLLOW_HTTP_CURL = 0, | 78 | int verbose = 0; |
| 88 | FOLLOW_LIBCURL = 1 | ||
| 89 | }; | ||
| 90 | 79 | ||
| 91 | /* for buffers for header and body */ | 80 | extern char errbuf[MAX_INPUT_BUFFER]; |
| 92 | typedef struct { | 81 | extern bool is_openssl_callback; |
| 93 | char *buf; | 82 | extern bool add_sslctx_verify_fun; |
| 94 | size_t buflen; | 83 | |
| 95 | size_t bufsize; | 84 | #if defined(HAVE_SSL) && defined(USE_OPENSSL) |
| 96 | } curlhelp_write_curlbuf; | 85 | static X509 *cert = NULL; |
| 86 | #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ | ||
| 97 | 87 | ||
| 98 | /* for buffering the data sent in PUT */ | ||
| 99 | typedef struct { | 88 | typedef struct { |
| 100 | char *buf; | 89 | int errorcode; |
| 101 | size_t buflen; | 90 | check_curl_config config; |
| 102 | off_t pos; | 91 | } check_curl_config_wrapper; |
| 103 | } curlhelp_read_curlbuf; | 92 | static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 93 | |||
| 94 | static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState, | ||
| 95 | int redir_depth); | ||
| 104 | 96 | ||
| 105 | /* for parsing the HTTP status line */ | ||
| 106 | typedef struct { | 97 | typedef struct { |
| 107 | int http_major; /* major version of the protocol, always 1 (HTTP/0.9 | 98 | int redir_depth; |
| 108 | * never reached the big internet most likely) */ | 99 | check_curl_working_state working_state; |
| 109 | int http_minor; /* minor version of the protocol, usually 0 or 1 */ | 100 | int error_code; |
| 110 | int http_code; /* HTTP return code as in RFC 2145 */ | 101 | check_curl_global_state curl_state; |
| 111 | int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see | 102 | } redir_wrapper; |
| 112 | * http://support.microsoft.com/kb/318380/en-us */ | 103 | static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, |
| 113 | const char *msg; /* the human readable message */ | 104 | int redir_depth, check_curl_working_state working_state); |
| 114 | char *first_line; /* a copy of the first line */ | ||
| 115 | } curlhelp_statusline; | ||
| 116 | |||
| 117 | /* to know the underlying SSL library used by libcurl */ | ||
| 118 | typedef enum curlhelp_ssl_library { | ||
| 119 | CURLHELP_SSL_LIBRARY_UNKNOWN, | ||
| 120 | CURLHELP_SSL_LIBRARY_OPENSSL, | ||
| 121 | CURLHELP_SSL_LIBRARY_LIBRESSL, | ||
| 122 | CURLHELP_SSL_LIBRARY_GNUTLS, | ||
| 123 | CURLHELP_SSL_LIBRARY_NSS | ||
| 124 | } curlhelp_ssl_library; | ||
| 125 | 105 | ||
| 126 | enum { | ||
| 127 | REGS = 2, | ||
| 128 | MAX_RE_SIZE = 1024 | ||
| 129 | }; | ||
| 130 | #include "regex.h" | ||
| 131 | static regex_t preg; | ||
| 132 | static regmatch_t pmatch[REGS]; | ||
| 133 | static char regexp[MAX_RE_SIZE]; | ||
| 134 | static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; | ||
| 135 | static int errcode; | ||
| 136 | static bool invert_regex = false; | ||
| 137 | static int state_regex = STATE_CRITICAL; | ||
| 138 | |||
| 139 | static char *server_address = NULL; | ||
| 140 | static char *host_name = NULL; | ||
| 141 | static char *server_url = 0; | ||
| 142 | static struct curl_slist *server_ips = NULL; | ||
| 143 | static bool specify_port = false; | ||
| 144 | static unsigned short server_port = HTTP_PORT; | ||
| 145 | static unsigned short virtual_port = 0; | ||
| 146 | static int host_name_length; | ||
| 147 | static char output_header_search[30] = ""; | ||
| 148 | static char output_string_search[30] = ""; | ||
| 149 | static char *warning_thresholds = NULL; | ||
| 150 | static char *critical_thresholds = NULL; | ||
| 151 | static int days_till_exp_warn, days_till_exp_crit; | ||
| 152 | static thresholds *thlds; | ||
| 153 | static char user_agent[DEFAULT_BUFFER_SIZE]; | ||
| 154 | static int verbose = 0; | ||
| 155 | static bool show_extended_perfdata = false; | ||
| 156 | static bool show_body = false; | ||
| 157 | static int min_page_len = 0; | ||
| 158 | static int max_page_len = 0; | ||
| 159 | static int redir_depth = 0; | ||
| 160 | static int max_depth = DEFAULT_MAX_REDIRS; | ||
| 161 | static char *http_method = NULL; | ||
| 162 | static char *http_post_data = NULL; | ||
| 163 | static char *http_content_type = NULL; | ||
| 164 | static CURL *curl; | ||
| 165 | static bool curl_global_initialized = false; | ||
| 166 | static bool curl_easy_initialized = false; | ||
| 167 | static struct curl_slist *header_list = NULL; | ||
| 168 | static bool body_buf_initialized = false; | ||
| 169 | static curlhelp_write_curlbuf body_buf; | ||
| 170 | static bool header_buf_initialized = false; | ||
| 171 | static curlhelp_write_curlbuf header_buf; | ||
| 172 | static bool status_line_initialized = false; | ||
| 173 | static curlhelp_statusline status_line; | ||
| 174 | static bool put_buf_initialized = false; | ||
| 175 | static curlhelp_read_curlbuf put_buf; | ||
| 176 | static char http_header[DEFAULT_BUFFER_SIZE]; | ||
| 177 | static long code; | ||
| 178 | static long socket_timeout = DEFAULT_SOCKET_TIMEOUT; | ||
| 179 | static double total_time; | ||
| 180 | static double time_connect; | ||
| 181 | static double time_appconnect; | ||
| 182 | static double time_headers; | ||
| 183 | static double time_firstbyte; | ||
| 184 | static char errbuf[MAX_INPUT_BUFFER]; | ||
| 185 | static CURLcode res; | ||
| 186 | static char url[DEFAULT_BUFFER_SIZE]; | ||
| 187 | static char msg[DEFAULT_BUFFER_SIZE]; | ||
| 188 | static char perfstring[DEFAULT_BUFFER_SIZE]; | ||
| 189 | static char header_expect[MAX_INPUT_BUFFER] = ""; | ||
| 190 | static char string_expect[MAX_INPUT_BUFFER] = ""; | ||
| 191 | static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; | ||
| 192 | static int server_expect_yn = 0; | ||
| 193 | static char user_auth[MAX_INPUT_BUFFER] = ""; | ||
| 194 | static char proxy_auth[MAX_INPUT_BUFFER] = ""; | ||
| 195 | static char **http_opt_headers; | ||
| 196 | static int http_opt_headers_count = 0; | ||
| 197 | static bool display_html = false; | ||
| 198 | static int onredirect = STATE_OK; | ||
| 199 | static int followmethod = FOLLOW_HTTP_CURL; | ||
| 200 | static int followsticky = STICKY_NONE; | ||
| 201 | static bool use_ssl = false; | ||
| 202 | static bool check_cert = false; | ||
| 203 | static bool continue_after_check_cert = false; | ||
| 204 | typedef union { | ||
| 205 | struct curl_slist *to_info; | ||
| 206 | struct curl_certinfo *to_certinfo; | ||
| 207 | } cert_ptr_union; | ||
| 208 | static cert_ptr_union cert_ptr; | ||
| 209 | static int ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 210 | static char *client_cert = NULL; | ||
| 211 | static char *client_privkey = NULL; | ||
| 212 | static char *ca_cert = NULL; | ||
| 213 | static bool verify_peer_and_host = false; | ||
| 214 | static bool is_openssl_callback = false; | ||
| 215 | static bool add_sslctx_verify_fun = false; | ||
| 216 | #if defined(HAVE_SSL) && defined(USE_OPENSSL) | ||
| 217 | static X509 *cert = NULL; | ||
| 218 | #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ | ||
| 219 | static bool no_body = false; | ||
| 220 | static int maximum_age = -1; | ||
| 221 | static int address_family = AF_UNSPEC; | ||
| 222 | static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 223 | static int curl_http_version = CURL_HTTP_VERSION_NONE; | ||
| 224 | static bool automatic_decompression = false; | ||
| 225 | static char *cookie_jar_file = NULL; | ||
| 226 | static bool haproxy_protocol = false; | ||
| 227 | |||
| 228 | static bool process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 229 | static void handle_curl_option_return_code(CURLcode res, const char *option); | ||
| 230 | static int check_http(void); | ||
| 231 | static void redir(curlhelp_write_curlbuf * /*header_buf*/); | ||
| 232 | static char *perfd_time(double elapsed_time); | ||
| 233 | static char *perfd_time_connect(double elapsed_time_connect); | ||
| 234 | static char *perfd_time_ssl(double elapsed_time_ssl); | ||
| 235 | static char *perfd_time_firstbyte(double elapsed_time_firstbyte); | ||
| 236 | static char *perfd_time_headers(double elapsed_time_headers); | ||
| 237 | static char *perfd_time_transfer(double elapsed_time_transfer); | ||
| 238 | static char *perfd_size(int page_len); | ||
| 239 | static void print_help(void); | 106 | static void print_help(void); |
| 240 | void print_usage(void); | 107 | void print_usage(void); |
| 108 | |||
| 241 | static void print_curl_version(void); | 109 | static void print_curl_version(void); |
| 242 | static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); | 110 | |
| 243 | static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); | 111 | // typedef struct { |
| 244 | static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); | 112 | // int errorcode; |
| 245 | static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); | 113 | // } check_curl_evaluation_wrapper; |
| 246 | static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); | 114 | // check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config, |
| 247 | static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); | 115 | // mp_check overall[static 1]) {} |
| 248 | static curlhelp_ssl_library curlhelp_get_ssl_library(void); | ||
| 249 | static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); | ||
| 250 | int net_noopenssl_check_certificate(cert_ptr_union *, int, int); | ||
| 251 | |||
| 252 | static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); | ||
| 253 | static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); | ||
| 254 | static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); | ||
| 255 | static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE]); | ||
| 256 | static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); | ||
| 257 | 116 | ||
| 258 | #if defined(HAVE_SSL) && defined(USE_OPENSSL) | 117 | #if defined(HAVE_SSL) && defined(USE_OPENSSL) |
| 259 | int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); | 118 | mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, |
| 119 | int days_till_exp_crit); | ||
| 260 | #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ | 120 | #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ |
| 261 | 121 | ||
| 262 | static void test_file(char * /*path*/); | ||
| 263 | |||
| 264 | int main(int argc, char **argv) { | 122 | int main(int argc, char **argv) { |
| 265 | int result = STATE_UNKNOWN; | ||
| 266 | |||
| 267 | setlocale(LC_ALL, ""); | 123 | setlocale(LC_ALL, ""); |
| 268 | bindtextdomain(PACKAGE, LOCALEDIR); | 124 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 269 | textdomain(PACKAGE); | 125 | textdomain(PACKAGE); |
| @@ -271,24 +127,30 @@ int main(int argc, char **argv) { | |||
| 271 | /* Parse extra opts if any */ | 127 | /* Parse extra opts if any */ |
| 272 | argv = np_extra_opts(&argc, argv, progname); | 128 | argv = np_extra_opts(&argc, argv, progname); |
| 273 | 129 | ||
| 274 | /* set defaults */ | ||
| 275 | snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version()); | ||
| 276 | |||
| 277 | /* parse arguments */ | 130 | /* parse arguments */ |
| 278 | if (process_arguments(argc, argv) == false) | 131 | check_curl_config_wrapper tmp_config = process_arguments(argc, argv); |
| 132 | if (tmp_config.errorcode == ERROR) { | ||
| 279 | usage4(_("Could not parse arguments")); | 133 | usage4(_("Could not parse arguments")); |
| 134 | } | ||
| 280 | 135 | ||
| 281 | if (display_html) | 136 | const check_curl_config config = tmp_config.config; |
| 282 | printf("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", use_ssl ? "https" : "http", host_name ? host_name : server_address, | ||
| 283 | virtual_port ? virtual_port : server_port, server_url); | ||
| 284 | 137 | ||
| 285 | result = check_http(); | 138 | if (config.output_format_is_set) { |
| 286 | return result; | 139 | mp_set_format(config.output_format); |
| 140 | } | ||
| 141 | |||
| 142 | check_curl_working_state working_state = config.initial_config; | ||
| 143 | |||
| 144 | mp_check overall = mp_check_init(); | ||
| 145 | mp_subcheck sc_test = check_http(config, working_state, 0); | ||
| 146 | |||
| 147 | mp_add_subcheck_to_check(&overall, sc_test); | ||
| 148 | |||
| 149 | mp_exit(overall); | ||
| 287 | } | 150 | } |
| 288 | 151 | ||
| 289 | #ifdef HAVE_SSL | 152 | #ifdef HAVE_SSL |
| 290 | # ifdef USE_OPENSSL | 153 | # ifdef USE_OPENSSL |
| 291 | |||
| 292 | int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { | 154 | int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { |
| 293 | (void)preverify_ok; | 155 | (void)preverify_ok; |
| 294 | /* TODO: we get all certificates of the chain, so which ones | 156 | /* TODO: we get all certificates of the chain, so which ones |
| @@ -301,19 +163,21 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { | |||
| 301 | # endif | 163 | # endif |
| 302 | if (verbose >= 2) { | 164 | if (verbose >= 2) { |
| 303 | puts("* SSL verify callback with certificate:"); | 165 | puts("* SSL verify callback with certificate:"); |
| 304 | X509_NAME *subject; | ||
| 305 | X509_NAME *issuer; | ||
| 306 | printf("* issuer:\n"); | 166 | printf("* issuer:\n"); |
| 307 | issuer = X509_get_issuer_name(cert); | 167 | X509_NAME *issuer = X509_get_issuer_name(cert); |
| 308 | X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); | 168 | X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); |
| 309 | printf("* curl verify_callback:\n* subject:\n"); | 169 | printf("* curl verify_callback:\n* subject:\n"); |
| 310 | subject = X509_get_subject_name(cert); | 170 | X509_NAME *subject = X509_get_subject_name(cert); |
| 311 | X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); | 171 | X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); |
| 312 | puts(""); | 172 | puts(""); |
| 313 | } | 173 | } |
| 314 | return 1; | 174 | return 1; |
| 315 | } | 175 | } |
| 176 | # endif /* USE_OPENSSL */ | ||
| 177 | #endif /* HAVE_SSL */ | ||
| 316 | 178 | ||
| 179 | #ifdef HAVE_SSL | ||
| 180 | # ifdef USE_OPENSSL | ||
| 317 | CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { | 181 | CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { |
| 318 | (void)curl; // ignore unused parameter | 182 | (void)curl; // ignore unused parameter |
| 319 | (void)parm; // ignore unused parameter | 183 | (void)parm; // ignore unused parameter |
| @@ -330,877 +194,508 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { | |||
| 330 | 194 | ||
| 331 | return CURLE_OK; | 195 | return CURLE_OK; |
| 332 | } | 196 | } |
| 333 | |||
| 334 | # endif /* USE_OPENSSL */ | 197 | # endif /* USE_OPENSSL */ |
| 335 | #endif /* HAVE_SSL */ | 198 | #endif /* HAVE_SSL */ |
| 336 | 199 | ||
| 337 | /* returns a string "HTTP/1.x" or "HTTP/2" */ | 200 | mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState, |
| 338 | static char *string_statuscode(int major, int minor) { | 201 | int redir_depth) { |
| 339 | static char buf[10]; | ||
| 340 | |||
| 341 | switch (major) { | ||
| 342 | case 1: | ||
| 343 | snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); | ||
| 344 | break; | ||
| 345 | case 2: | ||
| 346 | case 3: | ||
| 347 | snprintf(buf, sizeof(buf), "HTTP/%d", major); | ||
| 348 | break; | ||
| 349 | default: | ||
| 350 | /* assuming here HTTP/N with N>=4 */ | ||
| 351 | snprintf(buf, sizeof(buf), "HTTP/%d", major); | ||
| 352 | break; | ||
| 353 | } | ||
| 354 | |||
| 355 | return buf; | ||
| 356 | } | ||
| 357 | |||
| 358 | /* Checks if the server 'reply' is one of the expected 'statuscodes' */ | ||
| 359 | static int expected_statuscode(const char *reply, const char *statuscodes) { | ||
| 360 | char *expected; | ||
| 361 | char *code; | ||
| 362 | int result = 0; | ||
| 363 | |||
| 364 | if ((expected = strdup(statuscodes)) == NULL) | ||
| 365 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); | ||
| 366 | |||
| 367 | for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) | ||
| 368 | if (strstr(reply, code) != NULL) { | ||
| 369 | result = 1; | ||
| 370 | break; | ||
| 371 | } | ||
| 372 | |||
| 373 | free(expected); | ||
| 374 | return result; | ||
| 375 | } | ||
| 376 | |||
| 377 | void handle_curl_option_return_code(CURLcode res, const char *option) { | ||
| 378 | if (res != CURLE_OK) { | ||
| 379 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, | ||
| 380 | curl_easy_strerror(res)); | ||
| 381 | die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | int lookup_host(const char *host, char *buf, size_t buflen) { | ||
| 386 | struct addrinfo hints, *res, *result; | ||
| 387 | char addrstr[100]; | ||
| 388 | size_t addrstr_len; | ||
| 389 | int errcode; | ||
| 390 | void *ptr = {0}; | ||
| 391 | size_t buflen_remaining = buflen - 1; | ||
| 392 | |||
| 393 | memset(&hints, 0, sizeof(hints)); | ||
| 394 | hints.ai_family = address_family; | ||
| 395 | hints.ai_socktype = SOCK_STREAM; | ||
| 396 | hints.ai_flags |= AI_CANONNAME; | ||
| 397 | |||
| 398 | errcode = getaddrinfo(host, NULL, &hints, &result); | ||
| 399 | if (errcode != 0) | ||
| 400 | return errcode; | ||
| 401 | |||
| 402 | strcpy(buf, ""); | ||
| 403 | res = result; | ||
| 404 | |||
| 405 | while (res) { | ||
| 406 | switch (res->ai_family) { | ||
| 407 | case AF_INET: | ||
| 408 | ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; | ||
| 409 | break; | ||
| 410 | case AF_INET6: | ||
| 411 | ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; | ||
| 412 | break; | ||
| 413 | } | ||
| 414 | |||
| 415 | inet_ntop(res->ai_family, ptr, addrstr, 100); | ||
| 416 | if (verbose >= 1) { | ||
| 417 | printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr); | ||
| 418 | } | ||
| 419 | |||
| 420 | // Append all IPs to buf as a comma-separated string | ||
| 421 | addrstr_len = strlen(addrstr); | ||
| 422 | if (buflen_remaining > addrstr_len + 1) { | ||
| 423 | if (buf[0] != '\0') { | ||
| 424 | strncat(buf, ",", buflen_remaining); | ||
| 425 | buflen_remaining -= 1; | ||
| 426 | } | ||
| 427 | strncat(buf, addrstr, buflen_remaining); | ||
| 428 | buflen_remaining -= addrstr_len; | ||
| 429 | } | ||
| 430 | |||
| 431 | res = res->ai_next; | ||
| 432 | } | ||
| 433 | 202 | ||
| 434 | freeaddrinfo(result); | 203 | // ======================= |
| 204 | // Initialisation for curl | ||
| 205 | // ======================= | ||
| 206 | check_curl_configure_curl_wrapper conf_curl_struct = check_curl_configure_curl( | ||
| 207 | config.curl_config, workingState, config.check_cert, config.on_redirect_dependent, | ||
| 208 | config.followmethod, config.max_depth); | ||
| 435 | 209 | ||
| 436 | return 0; | 210 | check_curl_global_state curl_state = conf_curl_struct.curl_state; |
| 437 | } | 211 | workingState = conf_curl_struct.working_state; |
| 438 | 212 | ||
| 439 | static void cleanup(void) { | 213 | mp_subcheck sc_result = mp_subcheck_init(); |
| 440 | if (status_line_initialized) | ||
| 441 | curlhelp_free_statusline(&status_line); | ||
| 442 | status_line_initialized = false; | ||
| 443 | if (curl_easy_initialized) | ||
| 444 | curl_easy_cleanup(curl); | ||
| 445 | curl_easy_initialized = false; | ||
| 446 | if (curl_global_initialized) | ||
| 447 | curl_global_cleanup(); | ||
| 448 | curl_global_initialized = false; | ||
| 449 | if (body_buf_initialized) | ||
| 450 | curlhelp_freewritebuffer(&body_buf); | ||
| 451 | body_buf_initialized = false; | ||
| 452 | if (header_buf_initialized) | ||
| 453 | curlhelp_freewritebuffer(&header_buf); | ||
| 454 | header_buf_initialized = false; | ||
| 455 | if (put_buf_initialized) | ||
| 456 | curlhelp_freereadbuffer(&put_buf); | ||
| 457 | put_buf_initialized = false; | ||
| 458 | } | ||
| 459 | |||
| 460 | int check_http(void) { | ||
| 461 | int result = STATE_OK; | ||
| 462 | int result_ssl = STATE_OK; | ||
| 463 | int page_len = 0; | ||
| 464 | int i; | ||
| 465 | char *force_host_header = NULL; | ||
| 466 | struct curl_slist *host = NULL; | ||
| 467 | char addrstr[DEFAULT_BUFFER_SIZE / 2]; | ||
| 468 | char dnscache[DEFAULT_BUFFER_SIZE]; | ||
| 469 | |||
| 470 | /* initialize curl */ | ||
| 471 | if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) | ||
| 472 | die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); | ||
| 473 | curl_global_initialized = true; | ||
| 474 | |||
| 475 | if ((curl = curl_easy_init()) == NULL) { | ||
| 476 | die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); | ||
| 477 | } | ||
| 478 | curl_easy_initialized = true; | ||
| 479 | 214 | ||
| 480 | /* register cleanup function to shut down libcurl properly */ | 215 | char *url = fmt_url(workingState); |
| 481 | atexit(cleanup); | 216 | xasprintf(&sc_result.output, "Testing %s", url); |
| 217 | // TODO add some output here URL or something | ||
| 218 | free(url); | ||
| 482 | 219 | ||
| 483 | if (verbose >= 1) | 220 | // ============== |
| 484 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); | 221 | // do the request |
| 222 | // ============== | ||
| 223 | CURLcode res = curl_easy_perform(curl_state.curl); | ||
| 485 | 224 | ||
| 486 | /* print everything on stdout like check_http would do */ | 225 | if (verbose >= 2 && workingState.http_post_data) { |
| 487 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); | 226 | printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); |
| 488 | |||
| 489 | if (automatic_decompression) | ||
| 490 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) | ||
| 491 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); | ||
| 492 | #else | ||
| 493 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); | ||
| 494 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ | ||
| 495 | |||
| 496 | /* initialize buffer for body of the answer */ | ||
| 497 | if (curlhelp_initwritebuffer(&body_buf) < 0) | ||
| 498 | die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); | ||
| 499 | body_buf_initialized = true; | ||
| 500 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), | ||
| 501 | "CURLOPT_WRITEFUNCTION"); | ||
| 502 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); | ||
| 503 | |||
| 504 | /* initialize buffer for header of the answer */ | ||
| 505 | if (curlhelp_initwritebuffer(&header_buf) < 0) | ||
| 506 | die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); | ||
| 507 | header_buf_initialized = true; | ||
| 508 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), | ||
| 509 | "CURLOPT_HEADERFUNCTION"); | ||
| 510 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER"); | ||
| 511 | |||
| 512 | /* set the error buffer */ | ||
| 513 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER"); | ||
| 514 | |||
| 515 | /* set timeouts */ | ||
| 516 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT"); | ||
| 517 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT"); | ||
| 518 | |||
| 519 | /* enable haproxy protocol */ | ||
| 520 | if (haproxy_protocol) { | ||
| 521 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL"); | ||
| 522 | } | 227 | } |
| 523 | 228 | ||
| 524 | // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we | 229 | mp_subcheck sc_curl = mp_subcheck_init(); |
| 525 | // use the host_name later on to make SNI happy | ||
| 526 | if (use_ssl && host_name != NULL) { | ||
| 527 | if ((res = lookup_host(server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { | ||
| 528 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), server_address, res, | ||
| 529 | gai_strerror(res)); | ||
| 530 | die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); | ||
| 531 | } | ||
| 532 | snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr); | ||
| 533 | host = curl_slist_append(NULL, dnscache); | ||
| 534 | curl_easy_setopt(curl, CURLOPT_RESOLVE, host); | ||
| 535 | if (verbose >= 1) | ||
| 536 | printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); | ||
| 537 | } | ||
| 538 | 230 | ||
| 539 | // If server_address is an IPv6 address it must be surround by square brackets | 231 | /* Curl errors, result in critical Nagios state */ |
| 540 | struct in6_addr tmp_in_addr; | 232 | if (res != CURLE_OK) { |
| 541 | if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) { | 233 | xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"), |
| 542 | char *new_server_address = malloc(strlen(server_address) + 3); | 234 | res, errbuf[0] ? errbuf : curl_easy_strerror(res)); |
| 543 | if (new_server_address == NULL) { | 235 | sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL); |
| 544 | die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); | 236 | mp_add_subcheck_to_subcheck(&sc_result, sc_curl); |
| 545 | } | 237 | return sc_result; |
| 546 | snprintf(new_server_address, strlen(server_address) + 3, "[%s]", server_address); | ||
| 547 | free(server_address); | ||
| 548 | server_address = new_server_address; | ||
| 549 | } | ||
| 550 | |||
| 551 | /* compose URL: use the address we want to connect to, set Host: header later */ | ||
| 552 | snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http", | ||
| 553 | (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url); | ||
| 554 | |||
| 555 | if (verbose >= 1) | ||
| 556 | printf("* curl CURLOPT_URL: %s\n", url); | ||
| 557 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); | ||
| 558 | |||
| 559 | /* extract proxy information for legacy proxy https requests */ | ||
| 560 | if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) { | ||
| 561 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY"); | ||
| 562 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT"); | ||
| 563 | if (verbose >= 2) | ||
| 564 | printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port); | ||
| 565 | http_method = "GET"; | ||
| 566 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL"); | ||
| 567 | } | ||
| 568 | |||
| 569 | /* disable body for HEAD request */ | ||
| 570 | if (http_method && !strcmp(http_method, "HEAD")) { | ||
| 571 | no_body = true; | ||
| 572 | } | 238 | } |
| 573 | 239 | ||
| 574 | /* set HTTP protocol version */ | 240 | /* get status line of answer, check sanity of HTTP code */ |
| 575 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION"); | 241 | if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { |
| 576 | 242 | sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); | |
| 577 | /* set HTTP method */ | 243 | /* we cannot know the major/minor version here for sure as we cannot parse the first |
| 578 | if (http_method) { | 244 | * line */ |
| 579 | if (!strcmp(http_method, "POST")) | 245 | xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line"); |
| 580 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); | 246 | return sc_result; |
| 581 | else if (!strcmp(http_method, "PUT")) | ||
| 582 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); | ||
| 583 | else | ||
| 584 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST"); | ||
| 585 | } | 247 | } |
| 586 | 248 | ||
| 587 | /* check if Host header is explicitly set in options */ | 249 | curl_state.status_line_initialized = true; |
| 588 | if (http_opt_headers_count) { | ||
| 589 | for (i = 0; i < http_opt_headers_count; i++) { | ||
| 590 | if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { | ||
| 591 | force_host_header = http_opt_headers[i]; | ||
| 592 | } | ||
| 593 | } | ||
| 594 | } | ||
| 595 | 250 | ||
| 596 | /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */ | 251 | size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); |
| 597 | if (host_name != NULL && force_host_header == NULL) { | ||
| 598 | if ((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) { | ||
| 599 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port); | ||
| 600 | } else { | ||
| 601 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name); | ||
| 602 | } | ||
| 603 | header_list = curl_slist_append(header_list, http_header); | ||
| 604 | } | ||
| 605 | 252 | ||
| 606 | /* always close connection, be nice to servers */ | 253 | double total_time; |
| 607 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); | 254 | handle_curl_option_return_code( |
| 608 | header_list = curl_slist_append(header_list, http_header); | 255 | curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), |
| 256 | "CURLINFO_TOTAL_TIME"); | ||
| 609 | 257 | ||
| 610 | /* attach additional headers supplied by the user */ | 258 | xasprintf( |
| 611 | /* optionally send any other header tag */ | 259 | &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time", |
| 612 | if (http_opt_headers_count) { | 260 | string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), |
| 613 | for (i = 0; i < http_opt_headers_count; i++) { | 261 | curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time); |
| 614 | header_list = curl_slist_append(header_list, http_opt_headers[i]); | 262 | sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK); |
| 615 | } | 263 | mp_add_subcheck_to_subcheck(&sc_result, sc_curl); |
| 616 | /* This cannot be free'd here because a redirection will then try to access this and segfault */ | ||
| 617 | /* Covered in a testcase in tests/check_http.t */ | ||
| 618 | /* free(http_opt_headers); */ | ||
| 619 | } | ||
| 620 | 264 | ||
| 621 | /* set HTTP headers */ | 265 | // ========== |
| 622 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); | 266 | // Evaluation |
| 267 | // ========== | ||
| 623 | 268 | ||
| 624 | #ifdef LIBCURL_FEATURE_SSL | 269 | #ifdef LIBCURL_FEATURE_SSL |
| 270 | if (workingState.use_ssl && config.check_cert) { | ||
| 271 | mp_subcheck sc_certificate = check_curl_certificate_checks( | ||
| 272 | curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit); | ||
| 625 | 273 | ||
| 626 | /* set SSL version, warn about insecure or unsupported versions */ | 274 | mp_add_subcheck_to_subcheck(&sc_result, sc_certificate); |
| 627 | if (use_ssl) { | 275 | if (!config.continue_after_check_cert) { |
| 628 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION"); | 276 | return sc_result; |
| 629 | } | ||
| 630 | |||
| 631 | /* client certificate and key to present to server (SSL) */ | ||
| 632 | if (client_cert) | ||
| 633 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT"); | ||
| 634 | if (client_privkey) | ||
| 635 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY"); | ||
| 636 | if (ca_cert) { | ||
| 637 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO"); | ||
| 638 | } | ||
| 639 | if (ca_cert || verify_peer_and_host) { | ||
| 640 | /* per default if we have a CA verify both the peer and the | ||
| 641 | * hostname in the certificate, can be switched off later */ | ||
| 642 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); | ||
| 643 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST"); | ||
| 644 | } else { | ||
| 645 | /* backward-compatible behaviour, be tolerant in checks | ||
| 646 | * TODO: depending on more options have aspects we want | ||
| 647 | * to be less tolerant about ssl verfications | ||
| 648 | */ | ||
| 649 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER"); | ||
| 650 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST"); | ||
| 651 | } | ||
| 652 | |||
| 653 | /* detect SSL library used by libcurl */ | ||
| 654 | ssl_library = curlhelp_get_ssl_library(); | ||
| 655 | |||
| 656 | /* try hard to get a stack of certificates to verify against */ | ||
| 657 | if (check_cert) { | ||
| 658 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) | ||
| 659 | /* inform curl to report back certificates */ | ||
| 660 | switch (ssl_library) { | ||
| 661 | case CURLHELP_SSL_LIBRARY_OPENSSL: | ||
| 662 | case CURLHELP_SSL_LIBRARY_LIBRESSL: | ||
| 663 | /* set callback to extract certificate with OpenSSL context function (works with | ||
| 664 | * OpenSSL-style libraries only!) */ | ||
| 665 | # ifdef USE_OPENSSL | ||
| 666 | /* libcurl and monitoring plugins built with OpenSSL, good */ | ||
| 667 | add_sslctx_verify_fun = true; | ||
| 668 | is_openssl_callback = true; | ||
| 669 | # endif /* USE_OPENSSL */ | ||
| 670 | /* libcurl is built with OpenSSL, monitoring plugins, so falling | ||
| 671 | * back to manually extracting certificate information */ | ||
| 672 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); | ||
| 673 | break; | ||
| 674 | |||
| 675 | case CURLHELP_SSL_LIBRARY_NSS: | ||
| 676 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 677 | /* NSS: support for CERTINFO is implemented since 7.34.0 */ | ||
| 678 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); | ||
| 679 | # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 680 | die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", | ||
| 681 | curlhelp_get_ssl_library_string(ssl_library)); | ||
| 682 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 683 | break; | ||
| 684 | |||
| 685 | case CURLHELP_SSL_LIBRARY_GNUTLS: | ||
| 686 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) | ||
| 687 | /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ | ||
| 688 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); | ||
| 689 | # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ | ||
| 690 | die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", | ||
| 691 | curlhelp_get_ssl_library_string(ssl_library)); | ||
| 692 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ | ||
| 693 | break; | ||
| 694 | |||
| 695 | case CURLHELP_SSL_LIBRARY_UNKNOWN: | ||
| 696 | default: | ||
| 697 | die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", | ||
| 698 | curlhelp_get_ssl_library_string(ssl_library)); | ||
| 699 | break; | ||
| 700 | } | 277 | } |
| 701 | # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ | ||
| 702 | /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ | ||
| 703 | if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) | ||
| 704 | add_sslctx_verify_fun = true; | ||
| 705 | else | ||
| 706 | die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " | ||
| 707 | "too old and has no CURLOPT_CERTINFO)\n"); | ||
| 708 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ | ||
| 709 | } | 278 | } |
| 710 | |||
| 711 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ | ||
| 712 | // ssl ctx function is not available with all ssl backends | ||
| 713 | if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) | ||
| 714 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); | ||
| 715 | # endif | ||
| 716 | |||
| 717 | #endif /* LIBCURL_FEATURE_SSL */ | ||
| 718 | |||
| 719 | /* set default or user-given user agent identification */ | ||
| 720 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); | ||
| 721 | |||
| 722 | /* proxy-authentication */ | ||
| 723 | if (strcmp(proxy_auth, "")) | ||
| 724 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD"); | ||
| 725 | |||
| 726 | /* authentication */ | ||
| 727 | if (strcmp(user_auth, "")) | ||
| 728 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD"); | ||
| 729 | |||
| 730 | /* TODO: parameter auth method, bitfield of following methods: | ||
| 731 | * CURLAUTH_BASIC (default) | ||
| 732 | * CURLAUTH_DIGEST | ||
| 733 | * CURLAUTH_DIGEST_IE | ||
| 734 | * CURLAUTH_NEGOTIATE | ||
| 735 | * CURLAUTH_NTLM | ||
| 736 | * CURLAUTH_NTLM_WB | ||
| 737 | * | ||
| 738 | * convenience tokens for typical sets of methods: | ||
| 739 | * CURLAUTH_ANYSAFE: most secure, without BASIC | ||
| 740 | * or CURLAUTH_ANY: most secure, even BASIC if necessary | ||
| 741 | * | ||
| 742 | * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); | ||
| 743 | */ | ||
| 744 | |||
| 745 | /* handle redirections */ | ||
| 746 | if (onredirect == STATE_DEPENDENT) { | ||
| 747 | if (followmethod == FOLLOW_LIBCURL) { | ||
| 748 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); | ||
| 749 | |||
| 750 | /* default -1 is infinite, not good, could lead to zombie plugins! | ||
| 751 | Setting it to one bigger than maximal limit to handle errors nicely below | ||
| 752 | */ | ||
| 753 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS"); | ||
| 754 | |||
| 755 | /* for now allow only http and https (we are a http(s) check plugin in the end) */ | ||
| 756 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) | ||
| 757 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), | ||
| 758 | "CURLOPT_REDIR_PROTOCOLS_STR"); | ||
| 759 | #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) | ||
| 760 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), | ||
| 761 | "CURLOPT_REDIRECT_PROTOCOLS"); | ||
| 762 | #endif | 279 | #endif |
| 763 | 280 | ||
| 764 | /* TODO: handle the following aspects of redirection, make them | 281 | /* we got the data and we executed the request in a given time, so we can append |
| 765 | * command line options too later: | 282 | * performance data to the answer always |
| 766 | CURLOPT_POSTREDIR: method switch | 283 | */ |
| 767 | CURLINFO_REDIRECT_URL: custom redirect option | ||
| 768 | CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols | ||
| 769 | CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size? | ||
| 770 | */ | ||
| 771 | } else { | ||
| 772 | /* old style redirection is handled below */ | ||
| 773 | } | ||
| 774 | } | ||
| 775 | |||
| 776 | /* no-body */ | ||
| 777 | if (no_body) | ||
| 778 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); | ||
| 779 | |||
| 780 | /* IPv4 or IPv6 forced DNS resolution */ | ||
| 781 | if (address_family == AF_UNSPEC) | ||
| 782 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), | ||
| 783 | "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); | ||
| 784 | else if (address_family == AF_INET) | ||
| 785 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), | ||
| 786 | "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); | ||
| 787 | #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) | ||
| 788 | else if (address_family == AF_INET6) | ||
| 789 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), | ||
| 790 | "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); | ||
| 791 | #endif | ||
| 792 | 284 | ||
| 793 | /* either send http POST data (any data, not only POST)*/ | 285 | // total time the query took |
| 794 | if (!strcmp(http_method, "POST") || !strcmp(http_method, "PUT")) { | 286 | mp_perfdata pd_total_time = perfdata_init(); |
| 795 | /* set content of payload for POST and PUT */ | 287 | mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time); |
| 796 | if (http_content_type) { | 288 | pd_total_time.value = pd_val_total_time; |
| 797 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type); | 289 | pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds); |
| 798 | header_list = curl_slist_append(header_list, http_header); | 290 | pd_total_time.label = "time"; |
| 799 | } | 291 | pd_total_time.uom = "s"; |
| 800 | /* NULL indicates "HTTP Continue" in libcurl, provide an empty string | 292 | |
| 801 | * in case of no POST/PUT data */ | 293 | mp_subcheck sc_total_time = mp_subcheck_init(); |
| 802 | if (!http_post_data) | 294 | sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time)); |
| 803 | http_post_data = ""; | 295 | xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time); |
| 804 | if (!strcmp(http_method, "POST")) { | 296 | mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time); |
| 805 | /* POST method, set payload with CURLOPT_POSTFIELDS */ | 297 | |
| 806 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); | 298 | mp_add_subcheck_to_subcheck(&sc_result, sc_total_time); |
| 807 | } else if (!strcmp(http_method, "PUT")) { | 299 | |
| 808 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), | 300 | if (config.show_extended_perfdata) { |
| 809 | "CURLOPT_READFUNCTION"); | 301 | // overall connection time |
| 810 | if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) | 302 | mp_perfdata pd_time_connect = perfdata_init(); |
| 811 | die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); | 303 | double time_connect; |
| 812 | put_buf_initialized = true; | 304 | handle_curl_option_return_code( |
| 813 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); | 305 | curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect), |
| 814 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), | 306 | "CURLINFO_CONNECT_TIME"); |
| 815 | "CURLOPT_INFILESIZE"); | 307 | |
| 308 | mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect); | ||
| 309 | pd_time_connect.value = pd_val_time_connect; | ||
| 310 | pd_time_connect.label = "time_connect"; | ||
| 311 | pd_time_connect.uom = "s"; | ||
| 312 | pd_time_connect = mp_set_pd_max_value( | ||
| 313 | pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout)); | ||
| 314 | |||
| 315 | pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds); | ||
| 316 | mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect); | ||
| 317 | |||
| 318 | // application connection time, used to compute other timings | ||
| 319 | double time_appconnect; | ||
| 320 | handle_curl_option_return_code( | ||
| 321 | curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), | ||
| 322 | "CURLINFO_APPCONNECT_TIME"); | ||
| 323 | |||
| 324 | if (workingState.use_ssl) { | ||
| 325 | mp_perfdata pd_time_tls = perfdata_init(); | ||
| 326 | { | ||
| 327 | mp_perfdata_value pd_val_time_tls = | ||
| 328 | mp_create_pd_value(time_appconnect - time_connect); | ||
| 329 | |||
| 330 | pd_time_tls.value = pd_val_time_tls; | ||
| 331 | } | ||
| 332 | pd_time_tls.label = "time_tls"; | ||
| 333 | pd_time_tls.uom = "s"; | ||
| 334 | mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls); | ||
| 816 | } | 335 | } |
| 817 | } | ||
| 818 | |||
| 819 | /* cookie handling */ | ||
| 820 | if (cookie_jar_file != NULL) { | ||
| 821 | /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */ | ||
| 822 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE"); | ||
| 823 | /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */ | ||
| 824 | if (*cookie_jar_file) | ||
| 825 | handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR"); | ||
| 826 | } | ||
| 827 | |||
| 828 | /* do the request */ | ||
| 829 | res = curl_easy_perform(curl); | ||
| 830 | 336 | ||
| 831 | if (verbose >= 2 && http_post_data) | 337 | mp_perfdata pd_time_headers = perfdata_init(); |
| 832 | printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); | 338 | { |
| 339 | double time_headers; | ||
| 340 | handle_curl_option_return_code( | ||
| 341 | curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), | ||
| 342 | "CURLINFO_PRETRANSFER_TIME"); | ||
| 833 | 343 | ||
| 834 | /* free header and server IP resolve lists, we don't need it anymore */ | 344 | mp_perfdata_value pd_val_time_headers = |
| 835 | curl_slist_free_all(header_list); | 345 | mp_create_pd_value(time_headers - time_appconnect); |
| 836 | header_list = NULL; | ||
| 837 | curl_slist_free_all(server_ips); | ||
| 838 | server_ips = NULL; | ||
| 839 | if (host) { | ||
| 840 | curl_slist_free_all(host); | ||
| 841 | host = NULL; | ||
| 842 | } | ||
| 843 | 346 | ||
| 844 | /* Curl errors, result in critical Nagios state */ | 347 | pd_time_headers.value = pd_val_time_headers; |
| 845 | if (res != CURLE_OK) { | 348 | } |
| 846 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), server_port, | 349 | pd_time_headers.label = "time_headers"; |
| 847 | res, errbuf[0] ? errbuf : curl_easy_strerror(res)); | 350 | pd_time_headers.uom = "s"; |
| 848 | die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); | 351 | mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers); |
| 849 | } | ||
| 850 | 352 | ||
| 851 | /* certificate checks */ | 353 | mp_perfdata pd_time_firstbyte = perfdata_init(); |
| 852 | #ifdef LIBCURL_FEATURE_SSL | 354 | double time_firstbyte; |
| 853 | if (use_ssl) { | 355 | handle_curl_option_return_code( |
| 854 | if (check_cert) { | 356 | curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), |
| 855 | if (is_openssl_callback) { | 357 | "CURLINFO_STARTTRANSFER_TIME"); |
| 856 | # ifdef USE_OPENSSL | ||
| 857 | /* check certificate with OpenSSL functions, curl has been built against OpenSSL | ||
| 858 | * and we actually have OpenSSL in the monitoring tools | ||
| 859 | */ | ||
| 860 | result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); | ||
| 861 | if (!continue_after_check_cert) { | ||
| 862 | return result_ssl; | ||
| 863 | } | ||
| 864 | # else /* USE_OPENSSL */ | ||
| 865 | die(STATE_CRITICAL, | ||
| 866 | "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n"); | ||
| 867 | # endif /* USE_OPENSSL */ | ||
| 868 | } else { | ||
| 869 | int i; | ||
| 870 | struct curl_slist *slist; | ||
| 871 | 358 | ||
| 872 | cert_ptr.to_info = NULL; | 359 | mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte); |
| 873 | res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); | 360 | pd_time_firstbyte.value = pd_val_time_firstbyte; |
| 874 | if (!res && cert_ptr.to_info) { | 361 | pd_time_firstbyte.label = "time_firstbyte"; |
| 875 | # ifdef USE_OPENSSL | 362 | pd_time_firstbyte.uom = "s"; |
| 876 | /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing | 363 | mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte); |
| 877 | * We only check the first certificate and assume it's the one of the server | ||
| 878 | */ | ||
| 879 | const char *raw_cert = NULL; | ||
| 880 | for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { | ||
| 881 | for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { | ||
| 882 | if (verbose >= 2) | ||
| 883 | printf("%d ** %s\n", i, slist->data); | ||
| 884 | if (strncmp(slist->data, "Cert:", 5) == 0) { | ||
| 885 | raw_cert = &slist->data[5]; | ||
| 886 | goto GOT_FIRST_CERT; | ||
| 887 | } | ||
| 888 | } | ||
| 889 | } | ||
| 890 | GOT_FIRST_CERT: | ||
| 891 | if (!raw_cert) { | ||
| 892 | snprintf(msg, DEFAULT_BUFFER_SIZE, | ||
| 893 | _("Cannot retrieve certificates from CERTINFO information - certificate data was empty")); | ||
| 894 | die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); | ||
| 895 | } | ||
| 896 | BIO *cert_BIO = BIO_new(BIO_s_mem()); | ||
| 897 | BIO_write(cert_BIO, raw_cert, strlen(raw_cert)); | ||
| 898 | cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); | ||
| 899 | if (!cert) { | ||
| 900 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error")); | ||
| 901 | die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); | ||
| 902 | } | ||
| 903 | BIO_free(cert_BIO); | ||
| 904 | result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); | ||
| 905 | if (!continue_after_check_cert) { | ||
| 906 | return result_ssl; | ||
| 907 | } | ||
| 908 | # else /* USE_OPENSSL */ | ||
| 909 | /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal, | ||
| 910 | * so we use the libcurl CURLINFO data | ||
| 911 | */ | ||
| 912 | result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit); | ||
| 913 | if (!continue_after_check_cert) { | ||
| 914 | return result_ssl; | ||
| 915 | } | ||
| 916 | # endif /* USE_OPENSSL */ | ||
| 917 | } else { | ||
| 918 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), res, | ||
| 919 | curl_easy_strerror(res)); | ||
| 920 | die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); | ||
| 921 | } | ||
| 922 | } | ||
| 923 | } | ||
| 924 | } | ||
| 925 | #endif /* LIBCURL_FEATURE_SSL */ | ||
| 926 | 364 | ||
| 927 | /* we got the data and we executed the request in a given time, so we can append | 365 | mp_perfdata pd_time_transfer = perfdata_init(); |
| 928 | * performance data to the answer always | 366 | pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte); |
| 929 | */ | 367 | pd_time_transfer.label = "time_transfer"; |
| 930 | handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); | 368 | pd_time_transfer.uom = "s"; |
| 931 | page_len = get_content_length(&header_buf, &body_buf); | 369 | mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer); |
| 932 | if (show_extended_perfdata) { | ||
| 933 | handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); | ||
| 934 | handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); | ||
| 935 | handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); | ||
| 936 | handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), | ||
| 937 | "CURLINFO_STARTTRANSFER_TIME"); | ||
| 938 | snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time), perfd_size(page_len), | ||
| 939 | perfd_time_connect(time_connect), use_ssl ? perfd_time_ssl(time_appconnect - time_connect) : "", | ||
| 940 | perfd_time_headers(time_headers - time_appconnect), perfd_time_firstbyte(time_firstbyte - time_headers), | ||
| 941 | perfd_time_transfer(total_time - time_firstbyte)); | ||
| 942 | } else { | ||
| 943 | snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time), perfd_size(page_len)); | ||
| 944 | } | 370 | } |
| 945 | 371 | ||
| 946 | /* return a CRITICAL status if we couldn't read any data */ | 372 | /* return a CRITICAL status if we couldn't read any data */ |
| 947 | if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) | 373 | if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) { |
| 948 | die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); | 374 | sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); |
| 949 | 375 | xasprintf(&sc_result.output, "No header received from host"); | |
| 950 | /* get status line of answer, check sanity of HTTP code */ | 376 | return sc_result; |
| 951 | if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) { | ||
| 952 | snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); | ||
| 953 | /* we cannot know the major/minor version here for sure as we cannot parse the first line */ | ||
| 954 | die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg); | ||
| 955 | } | 377 | } |
| 956 | status_line_initialized = true; | ||
| 957 | 378 | ||
| 958 | /* get result code from cURL */ | 379 | /* get result code from cURL */ |
| 959 | handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); | 380 | long httpReturnCode; |
| 960 | if (verbose >= 2) | 381 | handle_curl_option_return_code( |
| 961 | printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); | 382 | curl_easy_getinfo(curl_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), |
| 383 | "CURLINFO_RESPONSE_CODE"); | ||
| 384 | if (verbose >= 2) { | ||
| 385 | printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode); | ||
| 386 | } | ||
| 962 | 387 | ||
| 963 | /* print status line, header, body if verbose */ | 388 | /* print status line, header, body if verbose */ |
| 964 | if (verbose >= 2) { | 389 | if (verbose >= 2) { |
| 965 | printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (no_body ? " [[ skipped ]]" : body_buf.buf)); | 390 | printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf, |
| 391 | (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf)); | ||
| 966 | } | 392 | } |
| 967 | 393 | ||
| 968 | /* make sure the status line matches the response we are looking for */ | 394 | /* make sure the status line matches the response we are looking for */ |
| 969 | if (!expected_statuscode(status_line.first_line, server_expect)) { | 395 | mp_subcheck sc_expect = mp_subcheck_init(); |
| 970 | if (server_port == HTTP_PORT) | 396 | sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK); |
| 971 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); | 397 | if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) { |
| 972 | else | 398 | if (workingState.serverPort == HTTP_PORT) { |
| 973 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, | 399 | xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"), |
| 974 | status_line.first_line); | 400 | curl_state.status_line->first_line); |
| 975 | die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); | 401 | } else { |
| 402 | xasprintf(&sc_expect.output, | ||
| 403 | _("Invalid HTTP response received from host on port %d: %s\n"), | ||
| 404 | workingState.serverPort, curl_state.status_line->first_line); | ||
| 405 | } | ||
| 406 | sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL); | ||
| 407 | } else { | ||
| 408 | xasprintf(&sc_expect.output, _("Status line output matched \"%s\""), | ||
| 409 | config.server_expect.string); | ||
| 976 | } | 410 | } |
| 411 | mp_add_subcheck_to_subcheck(&sc_result, sc_expect); | ||
| 977 | 412 | ||
| 978 | if (server_expect_yn) { | 413 | if (!config.server_expect.is_present) { |
| 979 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect); | ||
| 980 | if (verbose) | ||
| 981 | printf("%s\n", msg); | ||
| 982 | result = STATE_OK; | ||
| 983 | } else { | ||
| 984 | /* illegal return codes result in a critical state */ | 414 | /* illegal return codes result in a critical state */ |
| 985 | if (code >= 600 || code < 100) { | 415 | mp_subcheck sc_return_code = mp_subcheck_init(); |
| 986 | die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); | 416 | sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK); |
| 987 | /* server errors result in a critical state */ | 417 | xasprintf(&sc_return_code.output, "HTTP return code: %d", |
| 988 | } else if (code >= 500) { | 418 | curl_state.status_line->http_code); |
| 989 | result = STATE_CRITICAL; | 419 | |
| 420 | if (httpReturnCode >= 600 || httpReturnCode < 100) { | ||
| 421 | sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL); | ||
| 422 | xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"), | ||
| 423 | curl_state.status_line->http_code, curl_state.status_line->msg); | ||
| 424 | mp_add_subcheck_to_subcheck(&sc_result, sc_return_code); | ||
| 425 | return sc_result; | ||
| 426 | } | ||
| 427 | |||
| 428 | // server errors result in a critical state | ||
| 429 | if (httpReturnCode >= 500) { | ||
| 430 | sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL); | ||
| 990 | /* client errors result in a warning state */ | 431 | /* client errors result in a warning state */ |
| 991 | } else if (code >= 400) { | 432 | } else if (httpReturnCode >= 400) { |
| 992 | result = STATE_WARNING; | 433 | sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING); |
| 993 | /* check redirected page if specified */ | 434 | /* check redirected page if specified */ |
| 994 | } else if (code >= 300) { | 435 | } else if (httpReturnCode >= 300) { |
| 995 | if (onredirect == STATE_DEPENDENT) { | 436 | if (config.on_redirect_dependent) { |
| 996 | if (followmethod == FOLLOW_LIBCURL) { | 437 | if (config.followmethod == FOLLOW_LIBCURL) { |
| 997 | code = status_line.http_code; | 438 | httpReturnCode = curl_state.status_line->http_code; |
| 439 | handle_curl_option_return_code( | ||
| 440 | curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), | ||
| 441 | "CURLINFO_REDIRECT_COUNT"); | ||
| 442 | |||
| 443 | if (verbose >= 2) { | ||
| 444 | printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); | ||
| 445 | } | ||
| 446 | |||
| 447 | mp_subcheck sc_redir_depth = mp_subcheck_init(); | ||
| 448 | if (redir_depth > config.max_depth) { | ||
| 449 | xasprintf(&sc_redir_depth.output, | ||
| 450 | "maximum redirection depth %d exceeded in libcurl", | ||
| 451 | config.max_depth); | ||
| 452 | sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL); | ||
| 453 | mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); | ||
| 454 | return sc_result; | ||
| 455 | } | ||
| 456 | xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)", | ||
| 457 | redir_depth, config.max_depth); | ||
| 458 | mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); | ||
| 459 | |||
| 998 | } else { | 460 | } else { |
| 999 | /* old check_http style redirection, if we come | 461 | /* old check_http style redirection, if we come |
| 1000 | * back here, we are in the same status as with | 462 | * back here, we are in the same status as with |
| 1001 | * the libcurl method | 463 | * the libcurl method |
| 1002 | */ | 464 | */ |
| 1003 | redir(&header_buf); | 465 | redir_wrapper redir_result = |
| 466 | redir(curl_state.header_buf, config, redir_depth, workingState); | ||
| 467 | cleanup(curl_state); | ||
| 468 | mp_subcheck sc_redir = | ||
| 469 | check_http(config, redir_result.working_state, redir_result.redir_depth); | ||
| 470 | mp_add_subcheck_to_subcheck(&sc_result, sc_redir); | ||
| 471 | |||
| 472 | return sc_result; | ||
| 1004 | } | 473 | } |
| 1005 | } else { | 474 | } else { |
| 1006 | /* this is a specific code in the command line to | 475 | /* this is a specific code in the command line to |
| 1007 | * be returned when a redirection is encountered | 476 | * be returned when a redirection is encountered |
| 1008 | */ | 477 | */ |
| 478 | sc_return_code = | ||
| 479 | mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state); | ||
| 1009 | } | 480 | } |
| 1010 | result = max_state_alt(onredirect, result); | ||
| 1011 | /* all other codes are considered ok */ | ||
| 1012 | } else { | 481 | } else { |
| 1013 | result = STATE_OK; | 482 | sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK); |
| 1014 | } | 483 | } |
| 1015 | } | ||
| 1016 | 484 | ||
| 1017 | /* libcurl redirection internally, handle error states here */ | 485 | mp_add_subcheck_to_subcheck(&sc_result, sc_return_code); |
| 1018 | if (followmethod == FOLLOW_LIBCURL) { | ||
| 1019 | handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); | ||
| 1020 | if (verbose >= 2) | ||
| 1021 | printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); | ||
| 1022 | if (redir_depth > max_depth) { | ||
| 1023 | snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth); | ||
| 1024 | die(STATE_WARNING, "HTTP WARNING - %s", msg); | ||
| 1025 | } | ||
| 1026 | } | 486 | } |
| 1027 | 487 | ||
| 1028 | /* check status codes, set exit status accordingly */ | 488 | /* check status codes, set exit status accordingly */ |
| 1029 | if (status_line.http_code != code) { | 489 | if (curl_state.status_line->http_code != httpReturnCode) { |
| 1030 | die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), | 490 | mp_subcheck sc_http_return_code_sanity = mp_subcheck_init(); |
| 1031 | string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); | 491 | sc_http_return_code_sanity = |
| 492 | mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL); | ||
| 493 | xasprintf(&sc_http_return_code_sanity.output, | ||
| 494 | _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), | ||
| 495 | string_statuscode(curl_state.status_line->http_major, | ||
| 496 | curl_state.status_line->http_minor), | ||
| 497 | curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); | ||
| 498 | |||
| 499 | mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity); | ||
| 500 | return sc_result; | ||
| 1032 | } | 501 | } |
| 1033 | 502 | ||
| 1034 | if (maximum_age >= 0) { | 503 | if (config.maximum_age >= 0) { |
| 1035 | result = max_state_alt(check_document_dates(&header_buf, &msg), result); | 504 | mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age); |
| 505 | mp_add_subcheck_to_subcheck(&sc_result, sc_max_age); | ||
| 1036 | } | 506 | } |
| 1037 | 507 | ||
| 1038 | /* Page and Header content checks go here */ | 508 | /* Page and Header content checks go here */ |
| 509 | if (strlen(config.header_expect)) { | ||
| 510 | mp_subcheck sc_header_expect = mp_subcheck_init(); | ||
| 511 | sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK); | ||
| 512 | xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect); | ||
| 1039 | 513 | ||
| 1040 | if (strlen(header_expect)) { | 514 | if (!strstr(curl_state.header_buf->buf, config.header_expect)) { |
| 1041 | if (!strstr(header_buf.buf, header_expect)) { | 515 | char output_header_search[30] = ""; |
| 1042 | 516 | strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); | |
| 1043 | strncpy(&output_header_search[0], header_expect, sizeof(output_header_search)); | ||
| 1044 | 517 | ||
| 1045 | if (output_header_search[sizeof(output_header_search) - 1] != '\0') { | 518 | if (output_header_search[sizeof(output_header_search) - 1] != '\0') { |
| 1046 | bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); | 519 | bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); |
| 1047 | } | 520 | } |
| 1048 | 521 | ||
| 1049 | char tmp[DEFAULT_BUFFER_SIZE]; | 522 | xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "), |
| 1050 | 523 | output_header_search, workingState.use_ssl ? "https" : "http", | |
| 1051 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, | 524 | workingState.host_name ? workingState.host_name : workingState.server_address, |
| 1052 | use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); | 525 | workingState.serverPort, workingState.server_url); |
| 1053 | |||
| 1054 | strcpy(msg, tmp); | ||
| 1055 | 526 | ||
| 1056 | result = STATE_CRITICAL; | 527 | sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL); |
| 1057 | } | 528 | } |
| 529 | |||
| 530 | mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect); | ||
| 1058 | } | 531 | } |
| 1059 | 532 | ||
| 1060 | if (strlen(string_expect)) { | 533 | if (strlen(config.string_expect)) { |
| 1061 | if (!strstr(body_buf.buf, string_expect)) { | 534 | mp_subcheck sc_string_expect = mp_subcheck_init(); |
| 535 | sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK); | ||
| 536 | xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect); | ||
| 1062 | 537 | ||
| 1063 | strncpy(&output_string_search[0], string_expect, sizeof(output_string_search)); | 538 | if (!strstr(curl_state.body_buf->buf, config.string_expect)) { |
| 539 | char output_string_search[30] = ""; | ||
| 540 | strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); | ||
| 1064 | 541 | ||
| 1065 | if (output_string_search[sizeof(output_string_search) - 1] != '\0') { | 542 | if (output_string_search[sizeof(output_string_search) - 1] != '\0') { |
| 1066 | bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); | 543 | bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); |
| 1067 | } | 544 | } |
| 1068 | 545 | ||
| 1069 | char tmp[DEFAULT_BUFFER_SIZE]; | 546 | xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "), |
| 1070 | 547 | output_string_search, workingState.use_ssl ? "https" : "http", | |
| 1071 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, | 548 | workingState.host_name ? workingState.host_name : workingState.server_address, |
| 1072 | use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); | 549 | workingState.serverPort, workingState.server_url); |
| 1073 | 550 | ||
| 1074 | strcpy(msg, tmp); | 551 | sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL); |
| 1075 | |||
| 1076 | result = STATE_CRITICAL; | ||
| 1077 | } | 552 | } |
| 553 | |||
| 554 | mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect); | ||
| 1078 | } | 555 | } |
| 1079 | 556 | ||
| 1080 | if (strlen(regexp)) { | 557 | if (strlen(config.regexp)) { |
| 1081 | errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); | 558 | mp_subcheck sc_body_regex = mp_subcheck_init(); |
| 1082 | if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) { | 559 | xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp); |
| 1083 | /* OK - No-op to avoid changing the logic around it */ | 560 | regmatch_t pmatch[REGS]; |
| 1084 | result = max_state_alt(STATE_OK, result); | ||
| 1085 | } else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) { | ||
| 1086 | if (!invert_regex) { | ||
| 1087 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 1088 | 561 | ||
| 1089 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); | 562 | int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0); |
| 1090 | strcpy(msg, tmp); | ||
| 1091 | 563 | ||
| 564 | if (errcode == 0) { | ||
| 565 | // got a match | ||
| 566 | if (config.invert_regex) { | ||
| 567 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); | ||
| 1092 | } else { | 568 | } else { |
| 1093 | char tmp[DEFAULT_BUFFER_SIZE]; | 569 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); |
| 570 | } | ||
| 571 | } else if (errcode == REG_NOMATCH) { | ||
| 572 | // got no match | ||
| 573 | xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); | ||
| 1094 | 574 | ||
| 1095 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); | 575 | if (config.invert_regex) { |
| 1096 | strcpy(msg, tmp); | 576 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); |
| 577 | } else { | ||
| 578 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); | ||
| 1097 | } | 579 | } |
| 1098 | result = state_regex; | ||
| 1099 | } else { | 580 | } else { |
| 1100 | regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); | 581 | // error in regexec |
| 1101 | 582 | char error_buffer[DEFAULT_BUFFER_SIZE]; | |
| 1102 | char tmp[DEFAULT_BUFFER_SIZE]; | 583 | regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE); |
| 1103 | 584 | xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer); | |
| 1104 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); | 585 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN); |
| 1105 | strcpy(msg, tmp); | ||
| 1106 | result = STATE_UNKNOWN; | ||
| 1107 | } | 586 | } |
| 587 | |||
| 588 | mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex); | ||
| 1108 | } | 589 | } |
| 1109 | 590 | ||
| 1110 | /* make sure the page is of an appropriate size */ | 591 | // size a.k.a. page length |
| 1111 | if ((max_page_len > 0) && (page_len > max_page_len)) { | 592 | mp_perfdata pd_page_length = perfdata_init(); |
| 1112 | char tmp[DEFAULT_BUFFER_SIZE]; | 593 | mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len); |
| 594 | pd_page_length.value = pd_val_page_length; | ||
| 595 | pd_page_length.label = "size"; | ||
| 596 | pd_page_length.uom = "B"; | ||
| 597 | pd_page_length.min = mp_create_pd_value(0); | ||
| 598 | pd_page_length.warn = config.page_length_limits; | ||
| 599 | pd_page_length.warn_present = true; | ||
| 1113 | 600 | ||
| 1114 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); | 601 | /* make sure the page is of an appropriate size */ |
| 602 | if (config.page_length_limits_is_set) { | ||
| 603 | mp_thresholds page_length_threshold = mp_thresholds_init(); | ||
| 604 | page_length_threshold.warning = config.page_length_limits; | ||
| 605 | page_length_threshold.warning_is_set = true; | ||
| 1115 | 606 | ||
| 1116 | strcpy(msg, tmp); | 607 | pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold); |
| 1117 | 608 | ||
| 1118 | result = max_state_alt(STATE_WARNING, result); | 609 | mp_subcheck sc_page_length = mp_subcheck_init(); |
| 1119 | 610 | ||
| 1120 | } else if ((min_page_len > 0) && (page_len < min_page_len)) { | 611 | mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length); |
| 1121 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 1122 | 612 | ||
| 1123 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); | 613 | mp_state_enum tmp_state = mp_get_pd_status(pd_page_length); |
| 1124 | strcpy(msg, tmp); | 614 | sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state); |
| 1125 | result = max_state_alt(STATE_WARNING, result); | ||
| 1126 | } | ||
| 1127 | 615 | ||
| 1128 | /* -w, -c: check warning and critical level */ | 616 | switch (tmp_state) { |
| 1129 | result = max_state_alt(get_status(total_time, thlds), result); | 617 | case STATE_CRITICAL: |
| 618 | case STATE_WARNING: | ||
| 619 | xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len); | ||
| 620 | break; | ||
| 621 | case STATE_OK: | ||
| 622 | xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len); | ||
| 623 | break; | ||
| 624 | default: | ||
| 625 | assert(false); | ||
| 626 | } | ||
| 1130 | 627 | ||
| 1131 | /* Cut-off trailing characters */ | 628 | mp_add_subcheck_to_subcheck(&sc_result, sc_page_length); |
| 1132 | if (strlen(msg) >= 2) { | ||
| 1133 | if (msg[strlen(msg) - 2] == ',') | ||
| 1134 | msg[strlen(msg) - 2] = '\0'; | ||
| 1135 | else | ||
| 1136 | msg[strlen(msg) - 3] = '\0'; | ||
| 1137 | } | 629 | } |
| 1138 | 630 | ||
| 1139 | /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ | 631 | return sc_result; |
| 1140 | die(max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", state_text(result), | ||
| 1141 | string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, | ||
| 1142 | strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (display_html ? "</A>" : ""), perfstring, (show_body ? body_buf.buf : ""), | ||
| 1143 | (show_body ? "\n" : "")); | ||
| 1144 | |||
| 1145 | return max_state_alt(result, result_ssl); | ||
| 1146 | } | 632 | } |
| 1147 | 633 | ||
| 1148 | int uri_strcmp(const UriTextRangeA range, const char *s) { | 634 | int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) { |
| 1149 | if (!range.first) | 635 | if (!range.first) { |
| 1150 | return -1; | 636 | return -1; |
| 1151 | if ((size_t)(range.afterLast - range.first) < strlen(s)) | 637 | } |
| 638 | if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) { | ||
| 1152 | return -1; | 639 | return -1; |
| 1153 | return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); | 640 | } |
| 641 | return strncmp(stringToCompare, range.first, | ||
| 642 | min((size_t)(range.afterLast - range.first), strlen(stringToCompare))); | ||
| 1154 | } | 643 | } |
| 1155 | 644 | ||
| 1156 | char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { | 645 | char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { |
| 1157 | if (!range.first) | 646 | if (!range.first) { |
| 1158 | return "(null)"; | 647 | return "(null)"; |
| 648 | } | ||
| 1159 | strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first))); | 649 | strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first))); |
| 1160 | buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0'; | 650 | buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0'; |
| 1161 | buf[range.afterLast - range.first] = '\0'; | 651 | buf[range.afterLast - range.first] = '\0'; |
| 1162 | return buf; | 652 | return buf; |
| 1163 | } | 653 | } |
| 1164 | 654 | ||
| 1165 | void redir(curlhelp_write_curlbuf *header_buf) { | 655 | redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, |
| 1166 | char *location = NULL; | 656 | int redir_depth, check_curl_working_state working_state) { |
| 1167 | curlhelp_statusline status_line; | 657 | curlhelp_statusline status_line; |
| 1168 | struct phr_header headers[255]; | 658 | struct phr_header headers[255]; |
| 1169 | size_t nof_headers = 255; | ||
| 1170 | size_t msglen; | 659 | size_t msglen; |
| 1171 | char buf[DEFAULT_BUFFER_SIZE]; | 660 | size_t nof_headers = 255; |
| 1172 | char ipstr[INET_ADDR_MAX_SIZE]; | 661 | int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, |
| 1173 | int new_port; | 662 | &status_line.http_minor, &status_line.http_code, &status_line.msg, |
| 1174 | char *new_host; | 663 | &msglen, headers, &nof_headers, 0); |
| 1175 | char *new_url; | ||
| 1176 | |||
| 1177 | int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, | ||
| 1178 | &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); | ||
| 1179 | 664 | ||
| 1180 | if (res == -1) { | 665 | if (res == -1) { |
| 1181 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); | 666 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); |
| 1182 | } | 667 | } |
| 1183 | 668 | ||
| 1184 | location = get_header_value(headers, nof_headers, "location"); | 669 | char *location = get_header_value(headers, nof_headers, "location"); |
| 670 | |||
| 671 | if (location == NULL) { | ||
| 672 | // location header not found | ||
| 673 | die(STATE_UNKNOWN, "HTTP UNKNOWN - could not find \"location\" header\n"); | ||
| 674 | } | ||
| 1185 | 675 | ||
| 1186 | if (verbose >= 2) | 676 | if (verbose >= 2) { |
| 1187 | printf(_("* Seen redirect location %s\n"), location); | 677 | printf(_("* Seen redirect location %s\n"), location); |
| 678 | } | ||
| 1188 | 679 | ||
| 1189 | if (++redir_depth > max_depth) | 680 | if (++redir_depth > config.max_depth) { |
| 1190 | die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, | 681 | die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"), |
| 1191 | (display_html ? "</A>" : "")); | 682 | config.max_depth, location); |
| 683 | } | ||
| 1192 | 684 | ||
| 1193 | UriParserStateA state; | 685 | UriParserStateA state; |
| 1194 | UriUriA uri; | 686 | UriUriA uri; |
| 1195 | state.uri = &uri; | 687 | state.uri = &uri; |
| 1196 | if (uriParseUriA(&state, location) != URI_SUCCESS) { | 688 | if (uriParseUriA(&state, location) != URI_SUCCESS) { |
| 1197 | if (state.errorCode == URI_ERROR_SYNTAX) { | 689 | if (state.errorCode == URI_ERROR_SYNTAX) { |
| 1198 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, (display_html ? "</A>" : "")); | 690 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"), |
| 691 | location); | ||
| 1199 | } else if (state.errorCode == URI_ERROR_MALLOC) { | 692 | } else if (state.errorCode == URI_ERROR_MALLOC) { |
| 1200 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); | 693 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); |
| 1201 | } | 694 | } |
| 1202 | } | 695 | } |
| 1203 | 696 | ||
| 697 | char ipstr[INET_ADDR_MAX_SIZE]; | ||
| 698 | char buf[DEFAULT_BUFFER_SIZE]; | ||
| 1204 | if (verbose >= 2) { | 699 | if (verbose >= 2) { |
| 1205 | printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE)); | 700 | printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE)); |
| 1206 | printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); | 701 | printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); |
| @@ -1215,9 +710,9 @@ void redir(curlhelp_write_curlbuf *header_buf) { | |||
| 1215 | } | 710 | } |
| 1216 | if (uri.pathHead) { | 711 | if (uri.pathHead) { |
| 1217 | printf(_("** path: ")); | 712 | printf(_("** path: ")); |
| 1218 | const UriPathSegmentA *p = uri.pathHead; | 713 | for (UriPathSegmentA *path_segment = uri.pathHead; path_segment; |
| 1219 | for (; p; p = p->next) { | 714 | path_segment = path_segment->next) { |
| 1220 | printf("/%s", uri_string(p->text, buf, DEFAULT_BUFFER_SIZE)); | 715 | printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE)); |
| 1221 | } | 716 | } |
| 1222 | puts(""); | 717 | puts(""); |
| 1223 | } | 718 | } |
| @@ -1230,99 +725,104 @@ void redir(curlhelp_write_curlbuf *header_buf) { | |||
| 1230 | } | 725 | } |
| 1231 | 726 | ||
| 1232 | if (uri.scheme.first) { | 727 | if (uri.scheme.first) { |
| 1233 | if (!uri_strcmp(uri.scheme, "https")) | 728 | working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https")); |
| 1234 | use_ssl = true; | ||
| 1235 | else | ||
| 1236 | use_ssl = false; | ||
| 1237 | } | 729 | } |
| 1238 | 730 | ||
| 1239 | /* we do a sloppy test here only, because uriparser would have failed | 731 | /* we do a sloppy test here only, because uriparser would have failed |
| 1240 | * above, if the port would be invalid, we just check for MAX_PORT | 732 | * above, if the port would be invalid, we just check for MAX_PORT |
| 1241 | */ | 733 | */ |
| 734 | int new_port; | ||
| 1242 | if (uri.portText.first) { | 735 | if (uri.portText.first) { |
| 1243 | new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); | 736 | new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); |
| 1244 | } else { | 737 | } else { |
| 1245 | new_port = HTTP_PORT; | 738 | new_port = HTTP_PORT; |
| 1246 | if (use_ssl) | 739 | if (working_state.use_ssl) { |
| 1247 | new_port = HTTPS_PORT; | 740 | new_port = HTTPS_PORT; |
| 741 | } | ||
| 742 | } | ||
| 743 | if (new_port > MAX_PORT) { | ||
| 744 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s\n"), MAX_PORT, | ||
| 745 | location); | ||
| 1248 | } | 746 | } |
| 1249 | if (new_port > MAX_PORT) | ||
| 1250 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "</A>" : ""); | ||
| 1251 | 747 | ||
| 1252 | /* by RFC 7231 relative URLs in Location should be taken relative to | 748 | /* by RFC 7231 relative URLs in Location should be taken relative to |
| 1253 | * the original URL, so we try to form a new absolute URL here | 749 | * the original URL, so we try to form a new absolute URL here |
| 1254 | */ | 750 | */ |
| 751 | char *new_host; | ||
| 1255 | if (!uri.scheme.first && !uri.hostText.first) { | 752 | if (!uri.scheme.first && !uri.hostText.first) { |
| 1256 | new_host = strdup(host_name ? host_name : server_address); | 753 | new_host = strdup(working_state.host_name ? working_state.host_name |
| 1257 | new_port = server_port; | 754 | : working_state.server_address); |
| 1258 | if (use_ssl) | 755 | new_port = working_state.serverPort; |
| 756 | if (working_state.use_ssl) { | ||
| 1259 | uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); | 757 | uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); |
| 758 | } | ||
| 1260 | } else { | 759 | } else { |
| 1261 | new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); | 760 | new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); |
| 1262 | } | 761 | } |
| 1263 | 762 | ||
| 1264 | /* compose new path */ | 763 | /* compose new path */ |
| 1265 | /* TODO: handle fragments and query part of URL */ | 764 | /* TODO: handle fragments and query part of URL */ |
| 1266 | new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); | 765 | char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); |
| 1267 | if (uri.pathHead) { | 766 | if (uri.pathHead) { |
| 1268 | const UriPathSegmentA *p = uri.pathHead; | 767 | for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment; |
| 1269 | for (; p; p = p->next) { | 768 | pathSegment = pathSegment->next) { |
| 1270 | strncat(new_url, "/", DEFAULT_BUFFER_SIZE); | 769 | strncat(new_url, "/", DEFAULT_BUFFER_SIZE); |
| 1271 | strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); | 770 | strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE), |
| 771 | DEFAULT_BUFFER_SIZE - 1); | ||
| 1272 | } | 772 | } |
| 1273 | } | 773 | } |
| 1274 | 774 | ||
| 1275 | if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && | 775 | if (working_state.serverPort == new_port && |
| 1276 | (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) | 776 | !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) && |
| 1277 | die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", | 777 | (working_state.host_name && |
| 1278 | new_host, new_port, new_url, (display_html ? "</A>" : "")); | 778 | !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && |
| 779 | !strcmp(working_state.server_url, new_url)) { | ||
| 780 | die(STATE_CRITICAL, | ||
| 781 | _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"), | ||
| 782 | working_state.use_ssl ? "https" : "http", new_host, new_port, new_url); | ||
| 783 | } | ||
| 1279 | 784 | ||
| 1280 | /* set new values for redirected request */ | 785 | /* set new values for redirected request */ |
| 1281 | 786 | ||
| 1282 | if (!(followsticky & STICKY_HOST)) { | 787 | if (!(config.followsticky & STICKY_HOST)) { |
| 1283 | free(server_address); | 788 | // free(working_state.server_address); |
| 1284 | server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); | 789 | working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); |
| 1285 | } | 790 | } |
| 1286 | if (!(followsticky & STICKY_PORT)) { | 791 | if (!(config.followsticky & STICKY_PORT)) { |
| 1287 | server_port = (unsigned short)new_port; | 792 | working_state.serverPort = (unsigned short)new_port; |
| 1288 | } | 793 | } |
| 1289 | 794 | ||
| 1290 | free(host_name); | 795 | // free(working_state.host_name); |
| 1291 | host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); | 796 | working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); |
| 1292 | 797 | ||
| 1293 | /* reset virtual port */ | 798 | /* reset virtual port */ |
| 1294 | virtual_port = server_port; | 799 | working_state.virtualPort = working_state.serverPort; |
| 1295 | 800 | ||
| 1296 | free(new_host); | 801 | free(new_host); |
| 1297 | free(server_url); | 802 | // free(working_state.server_url); |
| 1298 | server_url = new_url; | 803 | working_state.server_url = new_url; |
| 1299 | 804 | ||
| 1300 | uriFreeUriMembersA(&uri); | 805 | uriFreeUriMembersA(&uri); |
| 1301 | 806 | ||
| 1302 | if (verbose) | 807 | if (verbose) { |
| 1303 | printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, | 808 | printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http", |
| 1304 | server_url); | 809 | working_state.host_name ? working_state.host_name : working_state.server_address, |
| 810 | working_state.serverPort, working_state.server_url); | ||
| 811 | } | ||
| 1305 | 812 | ||
| 1306 | /* TODO: the hash component MUST be taken from the original URL and | 813 | /* TODO: the hash component MUST be taken from the original URL and |
| 1307 | * attached to the URL in Location | 814 | * attached to the URL in Location |
| 1308 | */ | 815 | */ |
| 1309 | 816 | ||
| 1310 | cleanup(); | 817 | redir_wrapper result = { |
| 1311 | check_http(); | 818 | .redir_depth = redir_depth, |
| 1312 | } | 819 | .working_state = working_state, |
| 1313 | 820 | .error_code = OK, | |
| 1314 | /* check whether a file exists */ | 821 | }; |
| 1315 | void test_file(char *path) { | 822 | return result; |
| 1316 | if (access(path, R_OK) == 0) | ||
| 1317 | return; | ||
| 1318 | usage2(_("file does not exist or is not readable"), path); | ||
| 1319 | } | 823 | } |
| 1320 | 824 | ||
| 1321 | bool process_arguments(int argc, char **argv) { | 825 | check_curl_config_wrapper process_arguments(int argc, char **argv) { |
| 1322 | char *p; | ||
| 1323 | int c = 1; | ||
| 1324 | char *temp; | ||
| 1325 | |||
| 1326 | enum { | 826 | enum { |
| 1327 | INVERT_REGEX = CHAR_MAX + 1, | 827 | INVERT_REGEX = CHAR_MAX + 1, |
| 1328 | SNI_OPTION, | 828 | SNI_OPTION, |
| @@ -1333,81 +833,101 @@ bool process_arguments(int argc, char **argv) { | |||
| 1333 | AUTOMATIC_DECOMPRESSION, | 833 | AUTOMATIC_DECOMPRESSION, |
| 1334 | COOKIE_JAR, | 834 | COOKIE_JAR, |
| 1335 | HAPROXY_PROTOCOL, | 835 | HAPROXY_PROTOCOL, |
| 1336 | STATE_REGEX | 836 | STATE_REGEX, |
| 837 | OUTPUT_FORMAT | ||
| 1337 | }; | 838 | }; |
| 1338 | 839 | ||
| 1339 | int option = 0; | 840 | static struct option longopts[] = { |
| 1340 | int got_plus = 0; | 841 | STD_LONG_OPTS, |
| 1341 | static struct option longopts[] = {STD_LONG_OPTS, | 842 | {"link", no_argument, 0, 'L'}, |
| 1342 | {"link", no_argument, 0, 'L'}, | 843 | {"nohtml", no_argument, 0, 'n'}, |
| 1343 | {"nohtml", no_argument, 0, 'n'}, | 844 | {"ssl", optional_argument, 0, 'S'}, |
| 1344 | {"ssl", optional_argument, 0, 'S'}, | 845 | {"sni", no_argument, 0, SNI_OPTION}, |
| 1345 | {"sni", no_argument, 0, SNI_OPTION}, | 846 | {"post", required_argument, 0, 'P'}, |
| 1346 | {"post", required_argument, 0, 'P'}, | 847 | {"method", required_argument, 0, 'j'}, |
| 1347 | {"method", required_argument, 0, 'j'}, | 848 | {"IP-address", required_argument, 0, 'I'}, |
| 1348 | {"IP-address", required_argument, 0, 'I'}, | 849 | {"url", required_argument, 0, 'u'}, |
| 1349 | {"url", required_argument, 0, 'u'}, | 850 | {"port", required_argument, 0, 'p'}, |
| 1350 | {"port", required_argument, 0, 'p'}, | 851 | {"authorization", required_argument, 0, 'a'}, |
| 1351 | {"authorization", required_argument, 0, 'a'}, | 852 | {"proxy-authorization", required_argument, 0, 'b'}, |
| 1352 | {"proxy-authorization", required_argument, 0, 'b'}, | 853 | {"header-string", required_argument, 0, 'd'}, |
| 1353 | {"header-string", required_argument, 0, 'd'}, | 854 | {"string", required_argument, 0, 's'}, |
| 1354 | {"string", required_argument, 0, 's'}, | 855 | {"expect", required_argument, 0, 'e'}, |
| 1355 | {"expect", required_argument, 0, 'e'}, | 856 | {"regex", required_argument, 0, 'r'}, |
| 1356 | {"regex", required_argument, 0, 'r'}, | 857 | {"ereg", required_argument, 0, 'r'}, |
| 1357 | {"ereg", required_argument, 0, 'r'}, | 858 | {"eregi", required_argument, 0, 'R'}, |
| 1358 | {"eregi", required_argument, 0, 'R'}, | 859 | {"linespan", no_argument, 0, 'l'}, |
| 1359 | {"linespan", no_argument, 0, 'l'}, | 860 | {"onredirect", required_argument, 0, 'f'}, |
| 1360 | {"onredirect", required_argument, 0, 'f'}, | 861 | {"certificate", required_argument, 0, 'C'}, |
| 1361 | {"certificate", required_argument, 0, 'C'}, | 862 | {"client-cert", required_argument, 0, 'J'}, |
| 1362 | {"client-cert", required_argument, 0, 'J'}, | 863 | {"private-key", required_argument, 0, 'K'}, |
| 1363 | {"private-key", required_argument, 0, 'K'}, | 864 | {"ca-cert", required_argument, 0, CA_CERT_OPTION}, |
| 1364 | {"ca-cert", required_argument, 0, CA_CERT_OPTION}, | 865 | {"verify-cert", no_argument, 0, 'D'}, |
| 1365 | {"verify-cert", no_argument, 0, 'D'}, | 866 | {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, |
| 1366 | {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, | 867 | {"useragent", required_argument, 0, 'A'}, |
| 1367 | {"useragent", required_argument, 0, 'A'}, | 868 | {"header", required_argument, 0, 'k'}, |
| 1368 | {"header", required_argument, 0, 'k'}, | 869 | {"no-body", no_argument, 0, 'N'}, |
| 1369 | {"no-body", no_argument, 0, 'N'}, | 870 | {"max-age", required_argument, 0, 'M'}, |
| 1370 | {"max-age", required_argument, 0, 'M'}, | 871 | {"content-type", required_argument, 0, 'T'}, |
| 1371 | {"content-type", required_argument, 0, 'T'}, | 872 | {"pagesize", required_argument, 0, 'm'}, |
| 1372 | {"pagesize", required_argument, 0, 'm'}, | 873 | {"invert-regex", no_argument, NULL, INVERT_REGEX}, |
| 1373 | {"invert-regex", no_argument, NULL, INVERT_REGEX}, | 874 | {"state-regex", required_argument, 0, STATE_REGEX}, |
| 1374 | {"state-regex", required_argument, 0, STATE_REGEX}, | 875 | {"use-ipv4", no_argument, 0, '4'}, |
| 1375 | {"use-ipv4", no_argument, 0, '4'}, | 876 | {"use-ipv6", no_argument, 0, '6'}, |
| 1376 | {"use-ipv6", no_argument, 0, '6'}, | 877 | {"extended-perfdata", no_argument, 0, 'E'}, |
| 1377 | {"extended-perfdata", no_argument, 0, 'E'}, | 878 | {"show-body", no_argument, 0, 'B'}, |
| 1378 | {"show-body", no_argument, 0, 'B'}, | 879 | {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, |
| 1379 | {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, | 880 | {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, |
| 1380 | {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, | 881 | {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, |
| 1381 | {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, | 882 | {"cookie-jar", required_argument, 0, COOKIE_JAR}, |
| 1382 | {"cookie-jar", required_argument, 0, COOKIE_JAR}, | 883 | {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, |
| 1383 | {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, | 884 | {"output-format", required_argument, 0, OUTPUT_FORMAT}, |
| 1384 | {0, 0, 0, 0}}; | 885 | {0, 0, 0, 0}}; |
| 1385 | 886 | ||
| 1386 | if (argc < 2) | 887 | check_curl_config_wrapper result = { |
| 1387 | return false; | 888 | .errorcode = OK, |
| 889 | .config = check_curl_config_init(), | ||
| 890 | }; | ||
| 1388 | 891 | ||
| 1389 | /* support check_http compatible arguments */ | 892 | if (argc < 2) { |
| 1390 | for (c = 1; c < argc; c++) { | 893 | result.errorcode = ERROR; |
| 1391 | if (strcmp("-to", argv[c]) == 0) | 894 | return result; |
| 1392 | strcpy(argv[c], "-t"); | ||
| 1393 | if (strcmp("-hn", argv[c]) == 0) | ||
| 1394 | strcpy(argv[c], "-H"); | ||
| 1395 | if (strcmp("-wt", argv[c]) == 0) | ||
| 1396 | strcpy(argv[c], "-w"); | ||
| 1397 | if (strcmp("-ct", argv[c]) == 0) | ||
| 1398 | strcpy(argv[c], "-c"); | ||
| 1399 | if (strcmp("-nohtml", argv[c]) == 0) | ||
| 1400 | strcpy(argv[c], "-n"); | ||
| 1401 | } | 895 | } |
| 1402 | 896 | ||
| 1403 | server_url = strdup(DEFAULT_SERVER_URL); | 897 | /* support check_http compatible arguments */ |
| 898 | for (int index = 1; index < argc; index++) { | ||
| 899 | if (strcmp("-to", argv[index]) == 0) { | ||
| 900 | strcpy(argv[index], "-t"); | ||
| 901 | } | ||
| 902 | if (strcmp("-hn", argv[index]) == 0) { | ||
| 903 | strcpy(argv[index], "-H"); | ||
| 904 | } | ||
| 905 | if (strcmp("-wt", argv[index]) == 0) { | ||
| 906 | strcpy(argv[index], "-w"); | ||
| 907 | } | ||
| 908 | if (strcmp("-ct", argv[index]) == 0) { | ||
| 909 | strcpy(argv[index], "-c"); | ||
| 910 | } | ||
| 911 | if (strcmp("-nohtml", argv[index]) == 0) { | ||
| 912 | strcpy(argv[index], "-n"); | ||
| 913 | } | ||
| 914 | } | ||
| 1404 | 915 | ||
| 1405 | while (1) { | 916 | int option = 0; |
| 1406 | c = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); | 917 | int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; |
| 1407 | if (c == -1 || c == EOF || c == 1) | 918 | bool specify_port = false; |
| 919 | bool enable_tls = false; | ||
| 920 | char *tls_option_optarg = NULL; | ||
| 921 | |||
| 922 | while (true) { | ||
| 923 | int option_index = getopt_long( | ||
| 924 | argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", | ||
| 925 | longopts, &option); | ||
| 926 | if (option_index == -1 || option_index == EOF || option_index == 1) { | ||
| 1408 | break; | 927 | break; |
| 928 | } | ||
| 1409 | 929 | ||
| 1410 | switch (c) { | 930 | switch (option_index) { |
| 1411 | case 'h': | 931 | case 'h': |
| 1412 | print_help(); | 932 | print_help(); |
| 1413 | exit(STATE_UNKNOWN); | 933 | exit(STATE_UNKNOWN); |
| @@ -1421,270 +941,253 @@ bool process_arguments(int argc, char **argv) { | |||
| 1421 | verbose++; | 941 | verbose++; |
| 1422 | break; | 942 | break; |
| 1423 | case 't': /* timeout period */ | 943 | case 't': /* timeout period */ |
| 1424 | if (!is_intnonneg(optarg)) | 944 | if (!is_intnonneg(optarg)) { |
| 1425 | usage2(_("Timeout interval must be a positive integer"), optarg); | 945 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 1426 | else | 946 | } else { |
| 1427 | socket_timeout = (int)strtol(optarg, NULL, 10); | 947 | result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10); |
| 948 | } | ||
| 1428 | break; | 949 | break; |
| 1429 | case 'c': /* critical time threshold */ | 950 | case 'c': /* critical time threshold */ |
| 1430 | critical_thresholds = optarg; | 951 | { |
| 1431 | break; | 952 | mp_range_parsed critical_range = mp_parse_range_string(optarg); |
| 953 | if (critical_range.error != MP_PARSING_SUCCES) { | ||
| 954 | die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg); | ||
| 955 | } | ||
| 956 | result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range); | ||
| 957 | } break; | ||
| 1432 | case 'w': /* warning time threshold */ | 958 | case 'w': /* warning time threshold */ |
| 1433 | warning_thresholds = optarg; | 959 | { |
| 1434 | break; | 960 | mp_range_parsed warning_range = mp_parse_range_string(optarg); |
| 961 | |||
| 962 | if (warning_range.error != MP_PARSING_SUCCES) { | ||
| 963 | die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg); | ||
| 964 | } | ||
| 965 | result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range); | ||
| 966 | } break; | ||
| 1435 | case 'H': /* virtual host */ | 967 | case 'H': /* virtual host */ |
| 1436 | host_name = strdup(optarg); | 968 | result.config.initial_config.host_name = strdup(optarg); |
| 1437 | if (host_name[0] == '[') { | 969 | char *tmp_string; |
| 1438 | if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */ | 970 | size_t host_name_length; |
| 1439 | virtual_port = atoi(p + 2); | 971 | if (result.config.initial_config.host_name[0] == '[') { |
| 972 | if ((tmp_string = strstr(result.config.initial_config.host_name, "]:")) != | ||
| 973 | NULL) { /* [IPv6]:port */ | ||
| 974 | result.config.initial_config.virtualPort = atoi(tmp_string + 2); | ||
| 1440 | /* cut off the port */ | 975 | /* cut off the port */ |
| 1441 | host_name_length = strlen(host_name) - strlen(p) - 1; | 976 | host_name_length = |
| 1442 | free(host_name); | 977 | strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1; |
| 1443 | host_name = strndup(optarg, host_name_length); | 978 | free(result.config.initial_config.host_name); |
| 979 | result.config.initial_config.host_name = strndup(optarg, host_name_length); | ||
| 1444 | } | 980 | } |
| 1445 | } else if ((p = strchr(host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ | 981 | } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL && |
| 1446 | virtual_port = atoi(p); | 982 | strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */ |
| 983 | result.config.initial_config.virtualPort = atoi(tmp_string); | ||
| 1447 | /* cut off the port */ | 984 | /* cut off the port */ |
| 1448 | host_name_length = strlen(host_name) - strlen(p) - 1; | 985 | host_name_length = |
| 1449 | free(host_name); | 986 | strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1; |
| 1450 | host_name = strndup(optarg, host_name_length); | 987 | free(result.config.initial_config.host_name); |
| 988 | result.config.initial_config.host_name = strndup(optarg, host_name_length); | ||
| 1451 | } | 989 | } |
| 1452 | break; | 990 | break; |
| 1453 | case 'I': /* internet address */ | 991 | case 'I': /* internet address */ |
| 1454 | server_address = strdup(optarg); | 992 | result.config.initial_config.server_address = strdup(optarg); |
| 1455 | break; | 993 | break; |
| 1456 | case 'u': /* URL path */ | 994 | case 'u': /* URL path */ |
| 1457 | server_url = strdup(optarg); | 995 | result.config.initial_config.server_url = strdup(optarg); |
| 1458 | break; | 996 | break; |
| 1459 | case 'p': /* Server port */ | 997 | case 'p': /* Server port */ |
| 1460 | if (!is_intnonneg(optarg)) | 998 | if (!is_intnonneg(optarg)) { |
| 1461 | usage2(_("Invalid port number, expecting a non-negative number"), optarg); | 999 | usage2(_("Invalid port number, expecting a non-negative number"), optarg); |
| 1462 | else { | 1000 | } else { |
| 1463 | if (strtol(optarg, NULL, 10) > MAX_PORT) | 1001 | if (strtol(optarg, NULL, 10) > MAX_PORT) { |
| 1464 | usage2(_("Invalid port number, supplied port number is too big"), optarg); | 1002 | usage2(_("Invalid port number, supplied port number is too big"), optarg); |
| 1465 | server_port = (unsigned short)strtol(optarg, NULL, 10); | 1003 | } |
| 1004 | result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10); | ||
| 1466 | specify_port = true; | 1005 | specify_port = true; |
| 1467 | } | 1006 | } |
| 1468 | break; | 1007 | break; |
| 1469 | case 'a': /* authorization info */ | 1008 | case 'a': /* authorization info */ |
| 1470 | strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1); | 1009 | strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1); |
| 1471 | user_auth[MAX_INPUT_BUFFER - 1] = 0; | 1010 | result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0; |
| 1472 | break; | 1011 | break; |
| 1473 | case 'b': /* proxy-authorization info */ | 1012 | case 'b': /* proxy-authorization info */ |
| 1474 | strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1); | 1013 | strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); |
| 1475 | proxy_auth[MAX_INPUT_BUFFER - 1] = 0; | 1014 | result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; |
| 1476 | break; | 1015 | break; |
| 1477 | case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ | 1016 | case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ |
| 1478 | if (!http_post_data) | 1017 | if (!result.config.initial_config.http_post_data) { |
| 1479 | http_post_data = strdup(optarg); | 1018 | result.config.initial_config.http_post_data = strdup(optarg); |
| 1480 | if (!http_method) | 1019 | } |
| 1481 | http_method = strdup("POST"); | 1020 | if (!result.config.initial_config.http_method) { |
| 1021 | result.config.initial_config.http_method = strdup("POST"); | ||
| 1022 | } | ||
| 1482 | break; | 1023 | break; |
| 1483 | case 'j': /* Set HTTP method */ | 1024 | case 'j': /* Set HTTP method */ |
| 1484 | if (http_method) | 1025 | if (result.config.initial_config.http_method) { |
| 1485 | free(http_method); | 1026 | free(result.config.initial_config.http_method); |
| 1486 | http_method = strdup(optarg); | 1027 | } |
| 1028 | result.config.initial_config.http_method = strdup(optarg); | ||
| 1487 | break; | 1029 | break; |
| 1488 | case 'A': /* useragent */ | 1030 | case 'A': /* useragent */ |
| 1489 | strncpy(user_agent, optarg, DEFAULT_BUFFER_SIZE); | 1031 | strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE); |
| 1490 | user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; | 1032 | result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; |
| 1491 | break; | 1033 | break; |
| 1492 | case 'k': /* Additional headers */ | 1034 | case 'k': /* Additional headers */ |
| 1493 | if (http_opt_headers_count == 0) | 1035 | if (result.config.curl_config.http_opt_headers_count == 0) { |
| 1494 | http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); | 1036 | result.config.curl_config.http_opt_headers = |
| 1495 | else | 1037 | malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count)); |
| 1496 | http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); | 1038 | } else { |
| 1497 | http_opt_headers[http_opt_headers_count - 1] = optarg; | 1039 | result.config.curl_config.http_opt_headers = |
| 1040 | realloc(result.config.curl_config.http_opt_headers, | ||
| 1041 | sizeof(char *) * (++result.config.curl_config.http_opt_headers_count)); | ||
| 1042 | } | ||
| 1043 | result.config.curl_config | ||
| 1044 | .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg; | ||
| 1498 | break; | 1045 | break; |
| 1499 | case 'L': /* show html link */ | 1046 | case 'L': /* show html link */ |
| 1500 | display_html = true; | ||
| 1501 | break; | ||
| 1502 | case 'n': /* do not show html link */ | 1047 | case 'n': /* do not show html link */ |
| 1503 | display_html = false; | 1048 | // HTML link related options are deprecated |
| 1504 | break; | 1049 | break; |
| 1505 | case 'C': /* Check SSL cert validity */ | 1050 | case 'C': /* Check SSL cert validity */ |
| 1506 | #ifdef LIBCURL_FEATURE_SSL | 1051 | #ifndef LIBCURL_FEATURE_SSL |
| 1507 | if ((temp = strchr(optarg, ',')) != NULL) { | 1052 | usage4(_("Invalid option - SSL is not available")); |
| 1508 | *temp = '\0'; | ||
| 1509 | if (!is_intnonneg(optarg)) | ||
| 1510 | usage2(_("Invalid certificate expiration period"), optarg); | ||
| 1511 | days_till_exp_warn = atoi(optarg); | ||
| 1512 | *temp = ','; | ||
| 1513 | temp++; | ||
| 1514 | if (!is_intnonneg(temp)) | ||
| 1515 | usage2(_("Invalid certificate expiration period"), temp); | ||
| 1516 | days_till_exp_crit = atoi(temp); | ||
| 1517 | } else { | ||
| 1518 | days_till_exp_crit = 0; | ||
| 1519 | if (!is_intnonneg(optarg)) | ||
| 1520 | usage2(_("Invalid certificate expiration period"), optarg); | ||
| 1521 | days_till_exp_warn = atoi(optarg); | ||
| 1522 | } | ||
| 1523 | check_cert = true; | ||
| 1524 | goto enable_ssl; | ||
| 1525 | #endif | 1053 | #endif |
| 1054 | { | ||
| 1055 | char *temp; | ||
| 1056 | if ((temp = strchr(optarg, ',')) != NULL) { | ||
| 1057 | *temp = '\0'; | ||
| 1058 | if (!is_intnonneg(optarg)) { | ||
| 1059 | usage2(_("Invalid certificate expiration period"), optarg); | ||
| 1060 | } | ||
| 1061 | result.config.days_till_exp_warn = atoi(optarg); | ||
| 1062 | *temp = ','; | ||
| 1063 | temp++; | ||
| 1064 | if (!is_intnonneg(temp)) { | ||
| 1065 | usage2(_("Invalid certificate expiration period"), temp); | ||
| 1066 | } | ||
| 1067 | result.config.days_till_exp_crit = atoi(temp); | ||
| 1068 | } else { | ||
| 1069 | result.config.days_till_exp_crit = 0; | ||
| 1070 | if (!is_intnonneg(optarg)) { | ||
| 1071 | usage2(_("Invalid certificate expiration period"), optarg); | ||
| 1072 | } | ||
| 1073 | result.config.days_till_exp_warn = atoi(optarg); | ||
| 1074 | } | ||
| 1075 | result.config.check_cert = true; | ||
| 1076 | enable_tls = true; | ||
| 1077 | } | ||
| 1078 | break; | ||
| 1526 | case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ | 1079 | case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ |
| 1527 | #ifdef HAVE_SSL | 1080 | #ifdef HAVE_SSL |
| 1528 | continue_after_check_cert = true; | 1081 | result.config.continue_after_check_cert = true; |
| 1529 | break; | 1082 | break; |
| 1530 | #endif | 1083 | #endif |
| 1531 | case 'J': /* use client certificate */ | 1084 | case 'J': /* use client certificate */ |
| 1532 | #ifdef LIBCURL_FEATURE_SSL | 1085 | #ifndef LIBCURL_FEATURE_SSL |
| 1533 | test_file(optarg); | 1086 | usage4(_("Invalid option - SSL is not available")); |
| 1534 | client_cert = optarg; | ||
| 1535 | goto enable_ssl; | ||
| 1536 | #endif | 1087 | #endif |
| 1537 | case 'K': /* use client private key */ | ||
| 1538 | #ifdef LIBCURL_FEATURE_SSL | ||
| 1539 | test_file(optarg); | 1088 | test_file(optarg); |
| 1540 | client_privkey = optarg; | 1089 | result.config.curl_config.client_cert = optarg; |
| 1541 | goto enable_ssl; | 1090 | enable_tls = true; |
| 1091 | break; | ||
| 1092 | case 'K': /* use client private key */ | ||
| 1093 | #ifndef LIBCURL_FEATURE_SSL | ||
| 1094 | usage4(_("Invalid option - SSL is not available")); | ||
| 1542 | #endif | 1095 | #endif |
| 1543 | #ifdef LIBCURL_FEATURE_SSL | ||
| 1544 | case CA_CERT_OPTION: /* use CA chain file */ | ||
| 1545 | test_file(optarg); | 1096 | test_file(optarg); |
| 1546 | ca_cert = optarg; | 1097 | result.config.curl_config.client_privkey = optarg; |
| 1547 | goto enable_ssl; | 1098 | enable_tls = true; |
| 1099 | break; | ||
| 1100 | case CA_CERT_OPTION: /* use CA chain file */ | ||
| 1101 | #ifndef LIBCURL_FEATURE_SSL | ||
| 1102 | usage4(_("Invalid option - SSL is not available")); | ||
| 1548 | #endif | 1103 | #endif |
| 1549 | #ifdef LIBCURL_FEATURE_SSL | 1104 | test_file(optarg); |
| 1550 | case 'D': /* verify peer certificate & host */ | 1105 | result.config.curl_config.ca_cert = optarg; |
| 1551 | verify_peer_and_host = true; | 1106 | enable_tls = true; |
| 1552 | break; | 1107 | break; |
| 1108 | case 'D': /* verify peer certificate & host */ | ||
| 1109 | #ifndef LIBCURL_FEATURE_SSL | ||
| 1110 | usage4(_("Invalid option - SSL is not available")); | ||
| 1553 | #endif | 1111 | #endif |
| 1554 | case 'S': /* use SSL */ | 1112 | result.config.curl_config.verify_peer_and_host = true; |
| 1555 | #ifdef LIBCURL_FEATURE_SSL | 1113 | enable_tls = true; |
| 1556 | enable_ssl: | ||
| 1557 | use_ssl = true; | ||
| 1558 | /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. | ||
| 1559 | * Only set if it's non-zero. This helps when we include multiple | ||
| 1560 | * parameters, like -S and -C combinations */ | ||
| 1561 | ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1562 | if (c == 'S' && optarg != NULL) { | ||
| 1563 | char *plus_ptr = strchr(optarg, '+'); | ||
| 1564 | if (plus_ptr) { | ||
| 1565 | got_plus = 1; | ||
| 1566 | *plus_ptr = '\0'; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | if (optarg[0] == '2') | ||
| 1570 | ssl_version = CURL_SSLVERSION_SSLv2; | ||
| 1571 | else if (optarg[0] == '3') | ||
| 1572 | ssl_version = CURL_SSLVERSION_SSLv3; | ||
| 1573 | else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) | ||
| 1574 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 1575 | ssl_version = CURL_SSLVERSION_TLSv1_0; | ||
| 1576 | # else | ||
| 1577 | ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1578 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 1579 | else if (!strcmp(optarg, "1.1")) | ||
| 1580 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 1581 | ssl_version = CURL_SSLVERSION_TLSv1_1; | ||
| 1582 | # else | ||
| 1583 | ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1584 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 1585 | else if (!strcmp(optarg, "1.2")) | ||
| 1586 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 1587 | ssl_version = CURL_SSLVERSION_TLSv1_2; | ||
| 1588 | # else | ||
| 1589 | ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1590 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 1591 | else if (!strcmp(optarg, "1.3")) | ||
| 1592 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) | ||
| 1593 | ssl_version = CURL_SSLVERSION_TLSv1_3; | ||
| 1594 | # else | ||
| 1595 | ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1596 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ | ||
| 1597 | else | ||
| 1598 | usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)")); | ||
| 1599 | } | ||
| 1600 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) | ||
| 1601 | if (got_plus) { | ||
| 1602 | switch (ssl_version) { | ||
| 1603 | case CURL_SSLVERSION_TLSv1_3: | ||
| 1604 | ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; | ||
| 1605 | break; | ||
| 1606 | case CURL_SSLVERSION_TLSv1_2: | ||
| 1607 | case CURL_SSLVERSION_TLSv1_1: | ||
| 1608 | case CURL_SSLVERSION_TLSv1_0: | ||
| 1609 | ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; | ||
| 1610 | break; | ||
| 1611 | } | ||
| 1612 | } else { | ||
| 1613 | switch (ssl_version) { | ||
| 1614 | case CURL_SSLVERSION_TLSv1_3: | ||
| 1615 | ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; | ||
| 1616 | break; | ||
| 1617 | case CURL_SSLVERSION_TLSv1_2: | ||
| 1618 | ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; | ||
| 1619 | break; | ||
| 1620 | case CURL_SSLVERSION_TLSv1_1: | ||
| 1621 | ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; | ||
| 1622 | break; | ||
| 1623 | case CURL_SSLVERSION_TLSv1_0: | ||
| 1624 | ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; | ||
| 1625 | break; | ||
| 1626 | } | ||
| 1627 | } | ||
| 1628 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ | ||
| 1629 | if (verbose >= 2) | ||
| 1630 | printf(_("* Set SSL/TLS version to %d\n"), ssl_version); | ||
| 1631 | if (!specify_port) | ||
| 1632 | server_port = HTTPS_PORT; | ||
| 1633 | break; | 1114 | break; |
| 1634 | #else /* LIBCURL_FEATURE_SSL */ | 1115 | case 'S': /* use SSL */ |
| 1635 | /* -C -J and -K fall through to here without SSL */ | 1116 | tls_option_optarg = optarg; |
| 1117 | enable_tls = true; | ||
| 1118 | #ifndef LIBCURL_FEATURE_SSL | ||
| 1636 | usage4(_("Invalid option - SSL is not available")); | 1119 | usage4(_("Invalid option - SSL is not available")); |
| 1120 | #endif | ||
| 1637 | break; | 1121 | break; |
| 1638 | case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */ | 1122 | case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */ |
| 1639 | use_sni = true; | 1123 | #ifndef LIBCURL_FEATURE_SSL |
| 1640 | break; | 1124 | usage4(_("Invalid option - SSL is not available")); |
| 1641 | #endif /* LIBCURL_FEATURE_SSL */ | 1125 | #endif /* LIBCURL_FEATURE_SSL */ |
| 1126 | break; | ||
| 1642 | case MAX_REDIRS_OPTION: | 1127 | case MAX_REDIRS_OPTION: |
| 1643 | if (!is_intnonneg(optarg)) | 1128 | if (!is_intnonneg(optarg)) { |
| 1644 | usage2(_("Invalid max_redirs count"), optarg); | 1129 | usage2(_("Invalid max_redirs count"), optarg); |
| 1645 | else { | 1130 | } else { |
| 1646 | max_depth = atoi(optarg); | 1131 | result.config.max_depth = atoi(optarg); |
| 1647 | } | 1132 | } |
| 1648 | break; | 1133 | break; |
| 1649 | case 'f': /* onredirect */ | 1134 | case 'f': /* onredirect */ |
| 1650 | if (!strcmp(optarg, "ok")) | 1135 | if (!strcmp(optarg, "ok")) { |
| 1651 | onredirect = STATE_OK; | 1136 | result.config.on_redirect_result_state = STATE_OK; |
| 1652 | else if (!strcmp(optarg, "warning")) | 1137 | result.config.on_redirect_dependent = false; |
| 1653 | onredirect = STATE_WARNING; | 1138 | } else if (!strcmp(optarg, "warning")) { |
| 1654 | else if (!strcmp(optarg, "critical")) | 1139 | result.config.on_redirect_result_state = STATE_WARNING; |
| 1655 | onredirect = STATE_CRITICAL; | 1140 | result.config.on_redirect_dependent = false; |
| 1656 | else if (!strcmp(optarg, "unknown")) | 1141 | } else if (!strcmp(optarg, "critical")) { |
| 1657 | onredirect = STATE_UNKNOWN; | 1142 | result.config.on_redirect_result_state = STATE_CRITICAL; |
| 1658 | else if (!strcmp(optarg, "follow")) | 1143 | result.config.on_redirect_dependent = false; |
| 1659 | onredirect = STATE_DEPENDENT; | 1144 | } else if (!strcmp(optarg, "unknown")) { |
| 1660 | else if (!strcmp(optarg, "stickyport")) | 1145 | result.config.on_redirect_result_state = STATE_UNKNOWN; |
| 1661 | onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; | 1146 | result.config.on_redirect_dependent = false; |
| 1662 | else if (!strcmp(optarg, "sticky")) | 1147 | } else if (!strcmp(optarg, "follow")) { |
| 1663 | onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; | 1148 | result.config.on_redirect_dependent = true; |
| 1664 | else if (!strcmp(optarg, "follow")) | 1149 | } else if (!strcmp(optarg, "stickyport")) { |
| 1665 | onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; | 1150 | result.config.on_redirect_dependent = true; |
| 1666 | else if (!strcmp(optarg, "curl")) | 1151 | result.config.followmethod = FOLLOW_HTTP_CURL, |
| 1667 | onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; | 1152 | result.config.followsticky = STICKY_HOST | STICKY_PORT; |
| 1668 | else | 1153 | } else if (!strcmp(optarg, "sticky")) { |
| 1154 | result.config.on_redirect_dependent = true; | ||
| 1155 | result.config.followmethod = FOLLOW_HTTP_CURL, | ||
| 1156 | result.config.followsticky = STICKY_HOST; | ||
| 1157 | } else if (!strcmp(optarg, "follow")) { | ||
| 1158 | result.config.on_redirect_dependent = true; | ||
| 1159 | result.config.followmethod = FOLLOW_HTTP_CURL, | ||
| 1160 | result.config.followsticky = STICKY_NONE; | ||
| 1161 | } else if (!strcmp(optarg, "curl")) { | ||
| 1162 | result.config.on_redirect_dependent = true; | ||
| 1163 | result.config.followmethod = FOLLOW_LIBCURL; | ||
| 1164 | } else { | ||
| 1669 | usage2(_("Invalid onredirect option"), optarg); | 1165 | usage2(_("Invalid onredirect option"), optarg); |
| 1670 | if (verbose >= 2) | 1166 | } |
| 1671 | printf(_("* Following redirects set to %s\n"), state_text(onredirect)); | 1167 | if (verbose >= 2) { |
| 1168 | if (result.config.on_redirect_dependent) { | ||
| 1169 | printf(_("* Following redirects\n")); | ||
| 1170 | } else { | ||
| 1171 | printf(_("* Following redirects set to state %s\n"), | ||
| 1172 | state_text(result.config.on_redirect_result_state)); | ||
| 1173 | } | ||
| 1174 | } | ||
| 1672 | break; | 1175 | break; |
| 1673 | case 'd': /* string or substring */ | 1176 | case 'd': /* string or substring */ |
| 1674 | strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); | 1177 | strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1); |
| 1675 | header_expect[MAX_INPUT_BUFFER - 1] = 0; | 1178 | result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0; |
| 1676 | break; | 1179 | break; |
| 1677 | case 's': /* string or substring */ | 1180 | case 's': /* string or substring */ |
| 1678 | strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1); | 1181 | strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1); |
| 1679 | string_expect[MAX_INPUT_BUFFER - 1] = 0; | 1182 | result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0; |
| 1680 | break; | 1183 | break; |
| 1681 | case 'e': /* string or substring */ | 1184 | case 'e': /* string or substring */ |
| 1682 | strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1); | 1185 | strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1); |
| 1683 | server_expect[MAX_INPUT_BUFFER - 1] = 0; | 1186 | result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0; |
| 1684 | server_expect_yn = 1; | 1187 | result.config.server_expect.is_present = true; |
| 1685 | break; | 1188 | break; |
| 1686 | case 'T': /* Content-type */ | 1189 | case 'T': /* Content-type */ |
| 1687 | http_content_type = strdup(optarg); | 1190 | result.config.curl_config.http_content_type = strdup(optarg); |
| 1688 | break; | 1191 | break; |
| 1689 | case 'l': /* linespan */ | 1192 | case 'l': /* linespan */ |
| 1690 | cflags &= ~REG_NEWLINE; | 1193 | cflags &= ~REG_NEWLINE; |
| @@ -1693,185 +1196,258 @@ bool process_arguments(int argc, char **argv) { | |||
| 1693 | cflags |= REG_ICASE; | 1196 | cflags |= REG_ICASE; |
| 1694 | // fall through | 1197 | // fall through |
| 1695 | case 'r': /* regex */ | 1198 | case 'r': /* regex */ |
| 1696 | strncpy(regexp, optarg, MAX_RE_SIZE - 1); | 1199 | strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1); |
| 1697 | regexp[MAX_RE_SIZE - 1] = 0; | 1200 | result.config.regexp[MAX_RE_SIZE - 1] = 0; |
| 1698 | errcode = regcomp(&preg, regexp, cflags); | 1201 | regex_t preg; |
| 1202 | int errcode = regcomp(&preg, result.config.regexp, cflags); | ||
| 1699 | if (errcode != 0) { | 1203 | if (errcode != 0) { |
| 1700 | (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); | 1204 | (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); |
| 1701 | printf(_("Could Not Compile Regular Expression: %s"), errbuf); | 1205 | printf(_("Could Not Compile Regular Expression: %s"), errbuf); |
| 1702 | return false; | 1206 | result.errorcode = ERROR; |
| 1207 | return result; | ||
| 1703 | } | 1208 | } |
| 1209 | |||
| 1210 | result.config.compiled_regex = preg; | ||
| 1704 | break; | 1211 | break; |
| 1705 | case INVERT_REGEX: | 1212 | case INVERT_REGEX: |
| 1706 | invert_regex = true; | 1213 | result.config.invert_regex = true; |
| 1707 | break; | 1214 | break; |
| 1708 | case STATE_REGEX: | 1215 | case STATE_REGEX: |
| 1709 | if (!strcasecmp(optarg, "critical")) | 1216 | if (!strcasecmp(optarg, "critical")) { |
| 1710 | state_regex = STATE_CRITICAL; | 1217 | result.config.state_regex = STATE_CRITICAL; |
| 1711 | else if (!strcasecmp(optarg, "warning")) | 1218 | } else if (!strcasecmp(optarg, "warning")) { |
| 1712 | state_regex = STATE_WARNING; | 1219 | result.config.state_regex = STATE_WARNING; |
| 1713 | else | 1220 | } else { |
| 1714 | usage2(_("Invalid state-regex option"), optarg); | 1221 | usage2(_("Invalid state-regex option"), optarg); |
| 1222 | } | ||
| 1715 | break; | 1223 | break; |
| 1716 | case '4': | 1224 | case '4': |
| 1717 | address_family = AF_INET; | 1225 | result.config.curl_config.sin_family = AF_INET; |
| 1718 | break; | 1226 | break; |
| 1719 | case '6': | 1227 | case '6': |
| 1720 | #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) | 1228 | #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) |
| 1721 | address_family = AF_INET6; | 1229 | result.config.curl_config.sin_family = AF_INET6; |
| 1722 | #else | 1230 | #else |
| 1723 | usage4(_("IPv6 support not available")); | 1231 | usage4(_("IPv6 support not available")); |
| 1724 | #endif | 1232 | #endif |
| 1725 | break; | 1233 | break; |
| 1726 | case 'm': /* min_page_length */ | 1234 | case 'm': /* min_page_length */ |
| 1727 | { | 1235 | { |
| 1728 | char *tmp; | 1236 | mp_range_parsed foo = mp_parse_range_string(optarg); |
| 1729 | if (strchr(optarg, ':') != (char *)NULL) { | 1237 | |
| 1730 | /* range, so get two values, min:max */ | 1238 | if (foo.error != MP_PARSING_SUCCES) { |
| 1731 | tmp = strtok(optarg, ":"); | 1239 | die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg); |
| 1732 | if (tmp == NULL) { | 1240 | } |
| 1733 | printf("Bad format: try \"-m min:max\"\n"); | 1241 | |
| 1734 | exit(STATE_WARNING); | 1242 | result.config.page_length_limits = foo.range; |
| 1735 | } else | 1243 | result.config.page_length_limits_is_set = true; |
| 1736 | min_page_len = atoi(tmp); | ||
| 1737 | |||
| 1738 | tmp = strtok(NULL, ":"); | ||
| 1739 | if (tmp == NULL) { | ||
| 1740 | printf("Bad format: try \"-m min:max\"\n"); | ||
| 1741 | exit(STATE_WARNING); | ||
| 1742 | } else | ||
| 1743 | max_page_len = atoi(tmp); | ||
| 1744 | } else | ||
| 1745 | min_page_len = atoi(optarg); | ||
| 1746 | break; | 1244 | break; |
| 1747 | } | 1245 | } |
| 1748 | case 'N': /* no-body */ | 1246 | case 'N': /* no-body */ |
| 1749 | no_body = true; | 1247 | result.config.initial_config.no_body = true; |
| 1750 | break; | 1248 | break; |
| 1751 | case 'M': /* max-age */ | 1249 | case 'M': /* max-age */ |
| 1752 | { | 1250 | { |
| 1753 | int L = strlen(optarg); | 1251 | size_t option_length = strlen(optarg); |
| 1754 | if (L && optarg[L - 1] == 'm') | 1252 | if (option_length && optarg[option_length - 1] == 'm') { |
| 1755 | maximum_age = atoi(optarg) * 60; | 1253 | result.config.maximum_age = atoi(optarg) * 60; |
| 1756 | else if (L && optarg[L - 1] == 'h') | 1254 | } else if (option_length && optarg[option_length - 1] == 'h') { |
| 1757 | maximum_age = atoi(optarg) * 60 * 60; | 1255 | result.config.maximum_age = atoi(optarg) * 60 * 60; |
| 1758 | else if (L && optarg[L - 1] == 'd') | 1256 | } else if (option_length && optarg[option_length - 1] == 'd') { |
| 1759 | maximum_age = atoi(optarg) * 60 * 60 * 24; | 1257 | result.config.maximum_age = atoi(optarg) * 60 * 60 * 24; |
| 1760 | else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) | 1258 | } else if (option_length && |
| 1761 | maximum_age = atoi(optarg); | 1259 | (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) { |
| 1762 | else { | 1260 | result.config.maximum_age = atoi(optarg); |
| 1261 | } else { | ||
| 1763 | fprintf(stderr, "unparsable max-age: %s\n", optarg); | 1262 | fprintf(stderr, "unparsable max-age: %s\n", optarg); |
| 1764 | exit(STATE_WARNING); | 1263 | exit(STATE_WARNING); |
| 1765 | } | 1264 | } |
| 1766 | if (verbose >= 2) | 1265 | if (verbose >= 2) { |
| 1767 | printf("* Maximal age of document set to %d seconds\n", maximum_age); | 1266 | printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age); |
| 1267 | } | ||
| 1768 | } break; | 1268 | } break; |
| 1769 | case 'E': /* show extended perfdata */ | 1269 | case 'E': /* show extended perfdata */ |
| 1770 | show_extended_perfdata = true; | 1270 | result.config.show_extended_perfdata = true; |
| 1771 | break; | 1271 | break; |
| 1772 | case 'B': /* print body content after status line */ | 1272 | case 'B': /* print body content after status line */ |
| 1773 | show_body = true; | 1273 | result.config.show_body = true; |
| 1774 | break; | 1274 | break; |
| 1775 | case HTTP_VERSION_OPTION: | 1275 | case HTTP_VERSION_OPTION: |
| 1776 | curl_http_version = CURL_HTTP_VERSION_NONE; | 1276 | result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE; |
| 1777 | if (strcmp(optarg, "1.0") == 0) { | 1277 | if (strcmp(optarg, "1.0") == 0) { |
| 1778 | curl_http_version = CURL_HTTP_VERSION_1_0; | 1278 | result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0; |
| 1779 | } else if (strcmp(optarg, "1.1") == 0) { | 1279 | } else if (strcmp(optarg, "1.1") == 0) { |
| 1780 | curl_http_version = CURL_HTTP_VERSION_1_1; | 1280 | result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1; |
| 1781 | } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { | 1281 | } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { |
| 1782 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) | 1282 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) |
| 1783 | curl_http_version = CURL_HTTP_VERSION_2_0; | 1283 | result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0; |
| 1784 | #else | 1284 | #else |
| 1785 | curl_http_version = CURL_HTTP_VERSION_NONE; | 1285 | result.config.curl_http_version = CURL_HTTP_VERSION_NONE; |
| 1786 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ | 1286 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ |
| 1287 | } else if ((strcmp(optarg, "3") == 0)) { | ||
| 1288 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) | ||
| 1289 | result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3; | ||
| 1290 | #else | ||
| 1291 | result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE; | ||
| 1292 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */ | ||
| 1787 | } else { | 1293 | } else { |
| 1788 | fprintf(stderr, "unknown http-version parameter: %s\n", optarg); | 1294 | fprintf(stderr, "unknown http-version parameter: %s\n", optarg); |
| 1789 | exit(STATE_WARNING); | 1295 | exit(STATE_WARNING); |
| 1790 | } | 1296 | } |
| 1791 | break; | 1297 | break; |
| 1792 | case AUTOMATIC_DECOMPRESSION: | 1298 | case AUTOMATIC_DECOMPRESSION: |
| 1793 | automatic_decompression = true; | 1299 | result.config.curl_config.automatic_decompression = true; |
| 1794 | break; | 1300 | break; |
| 1795 | case COOKIE_JAR: | 1301 | case COOKIE_JAR: |
| 1796 | cookie_jar_file = optarg; | 1302 | result.config.curl_config.cookie_jar_file = optarg; |
| 1797 | break; | 1303 | break; |
| 1798 | case HAPROXY_PROTOCOL: | 1304 | case HAPROXY_PROTOCOL: |
| 1799 | haproxy_protocol = true; | 1305 | result.config.curl_config.haproxy_protocol = true; |
| 1800 | break; | 1306 | break; |
| 1801 | case '?': | 1307 | case '?': |
| 1802 | /* print short usage statement if args not parsable */ | 1308 | /* print short usage statement if args not parsable */ |
| 1803 | usage5(); | 1309 | usage5(); |
| 1804 | break; | 1310 | break; |
| 1311 | case OUTPUT_FORMAT: { | ||
| 1312 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 1313 | if (!parser.parsing_success) { | ||
| 1314 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 1315 | printf("Invalid output format: %s\n", optarg); | ||
| 1316 | exit(STATE_UNKNOWN); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | result.config.output_format_is_set = true; | ||
| 1320 | result.config.output_format = parser.output_format; | ||
| 1321 | break; | ||
| 1322 | } | ||
| 1805 | } | 1323 | } |
| 1806 | } | 1324 | } |
| 1807 | 1325 | ||
| 1808 | c = optind; | 1326 | if (enable_tls) { |
| 1327 | bool got_plus = false; | ||
| 1328 | result.config.initial_config.use_ssl = true; | ||
| 1329 | /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. | ||
| 1330 | * Only set if it's non-zero. This helps when we include multiple | ||
| 1331 | * parameters, like -S and -C combinations */ | ||
| 1332 | result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1333 | if (tls_option_optarg != NULL) { | ||
| 1334 | char *plus_ptr = strchr(optarg, '+'); | ||
| 1335 | if (plus_ptr) { | ||
| 1336 | got_plus = true; | ||
| 1337 | *plus_ptr = '\0'; | ||
| 1338 | } | ||
| 1809 | 1339 | ||
| 1810 | if (server_address == NULL && c < argc) | 1340 | if (optarg[0] == '2') { |
| 1811 | server_address = strdup(argv[c++]); | 1341 | result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2; |
| 1342 | } else if (optarg[0] == '3') { | ||
| 1343 | result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3; | ||
| 1344 | } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { | ||
| 1345 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 1346 | result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0; | ||
| 1347 | #else | ||
| 1348 | result.config.ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1349 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 1350 | } else if (!strcmp(optarg, "1.1")) { | ||
| 1351 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 1352 | result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1; | ||
| 1353 | #else | ||
| 1354 | result.config.ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1355 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 1356 | } else if (!strcmp(optarg, "1.2")) { | ||
| 1357 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 1358 | result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2; | ||
| 1359 | #else | ||
| 1360 | result.config.ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1361 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 1362 | } else if (!strcmp(optarg, "1.3")) { | ||
| 1363 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) | ||
| 1364 | result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3; | ||
| 1365 | #else | ||
| 1366 | result.config.ssl_version = CURL_SSLVERSION_DEFAULT; | ||
| 1367 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ | ||
| 1368 | } else { | ||
| 1369 | usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " | ||
| 1370 | "(with optional '+' suffix)")); | ||
| 1371 | } | ||
| 1372 | } | ||
| 1373 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) | ||
| 1374 | if (got_plus) { | ||
| 1375 | switch (result.config.curl_config.ssl_version) { | ||
| 1376 | case CURL_SSLVERSION_TLSv1_3: | ||
| 1377 | result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; | ||
| 1378 | break; | ||
| 1379 | case CURL_SSLVERSION_TLSv1_2: | ||
| 1380 | case CURL_SSLVERSION_TLSv1_1: | ||
| 1381 | case CURL_SSLVERSION_TLSv1_0: | ||
| 1382 | result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; | ||
| 1383 | break; | ||
| 1384 | } | ||
| 1385 | } else { | ||
| 1386 | switch (result.config.curl_config.ssl_version) { | ||
| 1387 | case CURL_SSLVERSION_TLSv1_3: | ||
| 1388 | result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; | ||
| 1389 | break; | ||
| 1390 | case CURL_SSLVERSION_TLSv1_2: | ||
| 1391 | result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; | ||
| 1392 | break; | ||
| 1393 | case CURL_SSLVERSION_TLSv1_1: | ||
| 1394 | result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; | ||
| 1395 | break; | ||
| 1396 | case CURL_SSLVERSION_TLSv1_0: | ||
| 1397 | result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; | ||
| 1398 | break; | ||
| 1399 | } | ||
| 1400 | } | ||
| 1401 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ | ||
| 1402 | if (verbose >= 2) { | ||
| 1403 | printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version); | ||
| 1404 | } | ||
| 1405 | if (!specify_port) { | ||
| 1406 | result.config.initial_config.serverPort = HTTPS_PORT; | ||
| 1407 | } | ||
| 1408 | } | ||
| 1812 | 1409 | ||
| 1813 | if (host_name == NULL && c < argc) | 1410 | int option_counter = optind; |
| 1814 | host_name = strdup(argv[c++]); | ||
| 1815 | 1411 | ||
| 1816 | if (server_address == NULL) { | 1412 | if (result.config.initial_config.server_address == NULL && option_counter < argc) { |
| 1817 | if (host_name == NULL) | 1413 | result.config.initial_config.server_address = strdup(argv[option_counter++]); |
| 1818 | usage4(_("You must specify a server address or host name")); | ||
| 1819 | else | ||
| 1820 | server_address = strdup(host_name); | ||
| 1821 | } | 1414 | } |
| 1822 | 1415 | ||
| 1823 | set_thresholds(&thlds, warning_thresholds, critical_thresholds); | 1416 | if (result.config.initial_config.host_name == NULL && option_counter < argc) { |
| 1417 | result.config.initial_config.host_name = strdup(argv[option_counter++]); | ||
| 1418 | } | ||
| 1824 | 1419 | ||
| 1825 | if (critical_thresholds && thlds->critical->end > (double)socket_timeout) | 1420 | if (result.config.initial_config.server_address == NULL) { |
| 1826 | socket_timeout = (int)thlds->critical->end + 1; | 1421 | if (result.config.initial_config.host_name == NULL) { |
| 1827 | if (verbose >= 2) | 1422 | usage4(_("You must specify a server address or host name")); |
| 1828 | printf("* Socket timeout set to %ld seconds\n", socket_timeout); | 1423 | } else { |
| 1424 | result.config.initial_config.server_address = | ||
| 1425 | strdup(result.config.initial_config.host_name); | ||
| 1426 | } | ||
| 1427 | } | ||
| 1829 | 1428 | ||
| 1830 | if (http_method == NULL) | 1429 | if (result.config.initial_config.http_method == NULL) { |
| 1831 | http_method = strdup("GET"); | 1430 | result.config.initial_config.http_method = strdup("GET"); |
| 1431 | } | ||
| 1832 | 1432 | ||
| 1833 | if (client_cert && !client_privkey) | 1433 | if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) { |
| 1834 | usage4(_("If you use a client certificate you must also specify a private key file")); | 1434 | usage4(_("If you use a client certificate you must also specify a private key file")); |
| 1835 | |||
| 1836 | if (virtual_port == 0) | ||
| 1837 | virtual_port = server_port; | ||
| 1838 | else { | ||
| 1839 | if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT)) | ||
| 1840 | if (!specify_port) | ||
| 1841 | server_port = virtual_port; | ||
| 1842 | } | 1435 | } |
| 1843 | 1436 | ||
| 1844 | return true; | 1437 | if (result.config.initial_config.virtualPort == 0) { |
| 1845 | } | 1438 | result.config.initial_config.virtualPort = result.config.initial_config.serverPort; |
| 1846 | 1439 | } else { | |
| 1847 | char *perfd_time(double elapsed_time) { | 1440 | if ((result.config.initial_config.use_ssl && |
| 1848 | return fperfdata("time", elapsed_time, "s", thlds->warning ? true : false, thlds->warning ? thlds->warning->end : 0, | 1441 | result.config.initial_config.serverPort == HTTPS_PORT) || |
| 1849 | thlds->critical ? true : false, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); | 1442 | (!result.config.initial_config.use_ssl && |
| 1850 | } | 1443 | result.config.initial_config.serverPort == HTTP_PORT)) { |
| 1851 | 1444 | if (!specify_port) { | |
| 1852 | char *perfd_time_connect(double elapsed_time_connect) { | 1445 | result.config.initial_config.serverPort = result.config.initial_config.virtualPort; |
| 1853 | return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); | 1446 | } |
| 1854 | } | 1447 | } |
| 1855 | 1448 | } | |
| 1856 | char *perfd_time_ssl(double elapsed_time_ssl) { | ||
| 1857 | return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | char *perfd_time_headers(double elapsed_time_headers) { | ||
| 1861 | return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); | ||
| 1862 | } | ||
| 1863 | |||
| 1864 | char *perfd_time_firstbyte(double elapsed_time_firstbyte) { | ||
| 1865 | return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | char *perfd_time_transfer(double elapsed_time_transfer) { | ||
| 1869 | return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); | ||
| 1870 | } | ||
| 1871 | 1449 | ||
| 1872 | char *perfd_size(int page_len) { | 1450 | return result; |
| 1873 | return perfdata("size", page_len, "B", (min_page_len > 0 ? true : false), min_page_len, (min_page_len > 0 ? true : false), 0, true, 0, | ||
| 1874 | false, 0); | ||
| 1875 | } | 1451 | } |
| 1876 | 1452 | ||
| 1877 | void print_help(void) { | 1453 | void print_help(void) { |
| @@ -1885,7 +1461,8 @@ void print_help(void) { | |||
| 1885 | printf("%s\n", _("strings and regular expressions, check connection times, and report on")); | 1461 | printf("%s\n", _("strings and regular expressions, check connection times, and report on")); |
| 1886 | printf("%s\n", _("certificate expiration times.")); | 1462 | printf("%s\n", _("certificate expiration times.")); |
| 1887 | printf("\n"); | 1463 | printf("\n"); |
| 1888 | printf("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); | 1464 | printf("%s\n", |
| 1465 | _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); | ||
| 1889 | printf("%s\n", _("as possible.")); | 1466 | printf("%s\n", _("as possible.")); |
| 1890 | 1467 | ||
| 1891 | printf("\n\n"); | 1468 | printf("\n\n"); |
| @@ -1903,7 +1480,10 @@ void print_help(void) { | |||
| 1903 | printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); | 1480 | printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); |
| 1904 | printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); | 1481 | printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); |
| 1905 | printf(" %s\n", "-I, --IP-address=ADDRESS"); | 1482 | printf(" %s\n", "-I, --IP-address=ADDRESS"); |
| 1906 | printf(" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); | 1483 | printf(" %s\n", |
| 1484 | "IP address or name (use numeric address if possible to bypass DNS lookup)."); | ||
| 1485 | printf(" %s\n", | ||
| 1486 | "This overwrites the network address of the target while leaving everything else (HTTP headers) as they are"); | ||
| 1907 | printf(" %s\n", "-p, --port=INTEGER"); | 1487 | printf(" %s\n", "-p, --port=INTEGER"); |
| 1908 | printf(" %s", _("Port number (default: ")); | 1488 | printf(" %s", _("Port number (default: ")); |
| 1909 | printf("%d)\n", HTTP_PORT); | 1489 | printf("%d)\n", HTTP_PORT); |
| @@ -1912,27 +1492,36 @@ void print_help(void) { | |||
| 1912 | 1492 | ||
| 1913 | #ifdef LIBCURL_FEATURE_SSL | 1493 | #ifdef LIBCURL_FEATURE_SSL |
| 1914 | printf(" %s\n", "-S, --ssl=VERSION[+]"); | 1494 | printf(" %s\n", "-S, --ssl=VERSION[+]"); |
| 1915 | printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); | 1495 | printf(" %s\n", |
| 1496 | _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); | ||
| 1916 | printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); | 1497 | printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); |
| 1917 | printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); | 1498 | printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are " |
| 1918 | printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually disabled in libcurl")); | 1499 | "also accepted.")); |
| 1500 | printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually " | ||
| 1501 | "disabled in libcurl")); | ||
| 1919 | printf(" %s\n", "--sni"); | 1502 | printf(" %s\n", "--sni"); |
| 1920 | printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); | 1503 | printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); |
| 1921 | # if LIBCURL_VERSION_NUM >= 0x071801 | 1504 | # if LIBCURL_VERSION_NUM >= 0x071801 |
| 1922 | printf(" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); | 1505 | printf(" %s\n", |
| 1506 | _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); | ||
| 1923 | printf(" %s\n", _(" SNI only really works since TLSv1.0")); | 1507 | printf(" %s\n", _(" SNI only really works since TLSv1.0")); |
| 1924 | # else | 1508 | # else |
| 1925 | printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); | 1509 | printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); |
| 1926 | # endif | 1510 | # endif |
| 1927 | printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); | 1511 | printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); |
| 1928 | printf(" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); | 1512 | printf(" %s\n", |
| 1929 | printf(" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the")); | 1513 | _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); |
| 1930 | printf(" %s\n", _("first agument's value. If there is a second argument and the certificate's")); | 1514 | printf(" %s\n", |
| 1515 | _("A STATE_WARNING is returned if the certificate has a validity less than the")); | ||
| 1516 | printf(" %s\n", | ||
| 1517 | _("first agument's value. If there is a second argument and the certificate's")); | ||
| 1931 | printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned.")); | 1518 | printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned.")); |
| 1932 | printf(" %s\n", _("(When this option is used the URL is not checked by default. You can use")); | 1519 | printf(" %s\n", |
| 1520 | _("(When this option is used the URL is not checked by default. You can use")); | ||
| 1933 | printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); | 1521 | printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); |
| 1934 | printf(" %s\n", "--continue-after-certificate"); | 1522 | printf(" %s\n", "--continue-after-certificate"); |
| 1935 | printf(" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); | 1523 | printf(" %s\n", |
| 1524 | _("Allows the HTTP check to continue after performing the certificate check.")); | ||
| 1936 | printf(" %s\n", _("Does nothing unless -C is used.")); | 1525 | printf(" %s\n", _("Does nothing unless -C is used.")); |
| 1937 | printf(" %s\n", "-J, --client-cert=FILE"); | 1526 | printf(" %s\n", "-J, --client-cert=FILE"); |
| 1938 | printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); | 1527 | printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); |
| @@ -1950,16 +1539,19 @@ void print_help(void) { | |||
| 1950 | printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); | 1539 | printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); |
| 1951 | printf(" %s", _("the first (status) line of the server response (default: ")); | 1540 | printf(" %s", _("the first (status) line of the server response (default: ")); |
| 1952 | printf("%s)\n", HTTP_EXPECT); | 1541 | printf("%s)\n", HTTP_EXPECT); |
| 1953 | printf(" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); | 1542 | printf(" %s\n", |
| 1543 | _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); | ||
| 1954 | printf(" %s\n", "-d, --header-string=STRING"); | 1544 | printf(" %s\n", "-d, --header-string=STRING"); |
| 1955 | printf(" %s\n", _("String to expect in the response headers")); | 1545 | printf(" %s\n", _("String to expect in the response headers")); |
| 1956 | printf(" %s\n", "-s, --string=STRING"); | 1546 | printf(" %s\n", "-s, --string=STRING"); |
| 1957 | printf(" %s\n", _("String to expect in the content")); | 1547 | printf(" %s\n", _("String to expect in the content")); |
| 1958 | printf(" %s\n", "-u, --url=PATH"); | 1548 | printf(" %s\n", "-u, --url=PATH"); |
| 1959 | printf(" %s\n", _("URL to GET or POST (default: /)")); | 1549 | printf(" %s\n", _("URL to GET or POST (default: /)")); |
| 1550 | printf(" %s\n", _("This is the part after the address in a URL, so for \"https://example.com/index.html\" it would be '-u /index.html'")); | ||
| 1960 | printf(" %s\n", "-P, --post=STRING"); | 1551 | printf(" %s\n", "-P, --post=STRING"); |
| 1961 | printf(" %s\n", _("URL decoded http POST data")); | 1552 | printf(" %s\n", _("URL decoded http POST data")); |
| 1962 | printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); | 1553 | printf(" %s\n", |
| 1554 | "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); | ||
| 1963 | printf(" %s\n", _("Set HTTP method.")); | 1555 | printf(" %s\n", _("Set HTTP method.")); |
| 1964 | printf(" %s\n", "-N, --no-body"); | 1556 | printf(" %s\n", "-N, --no-body"); |
| 1965 | printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); | 1557 | printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); |
| @@ -1968,7 +1560,7 @@ void print_help(void) { | |||
| 1968 | printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); | 1560 | printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); |
| 1969 | printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); | 1561 | printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); |
| 1970 | printf(" %s\n", "-T, --content-type=STRING"); | 1562 | printf(" %s\n", "-T, --content-type=STRING"); |
| 1971 | printf(" %s\n", _("specify Content-Type header media type when POSTing\n")); | 1563 | printf(" %s\n", _("specify Content-Type header media type when POSTing")); |
| 1972 | printf(" %s\n", "-l, --linespan"); | 1564 | printf(" %s\n", "-l, --linespan"); |
| 1973 | printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)")); | 1565 | printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)")); |
| 1974 | printf(" %s\n", "-r, --regex, --ereg=STRING"); | 1566 | printf(" %s\n", "-r, --regex, --ereg=STRING"); |
| @@ -1979,7 +1571,8 @@ void print_help(void) { | |||
| 1979 | printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); | 1571 | printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); |
| 1980 | printf(" %s\n", _("can be changed with --state--regex)")); | 1572 | printf(" %s\n", _("can be changed with --state--regex)")); |
| 1981 | printf(" %s\n", "--state-regex=STATE"); | 1573 | printf(" %s\n", "--state-regex=STATE"); |
| 1982 | printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of \"critical\",\"warning\"")); | 1574 | printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of " |
| 1575 | "\"critical\",\"warning\"")); | ||
| 1983 | printf(" %s\n", "-a, --authorization=AUTH_PAIR"); | 1576 | printf(" %s\n", "-a, --authorization=AUTH_PAIR"); |
| 1984 | printf(" %s\n", _("Username:password on sites with basic authentication")); | 1577 | printf(" %s\n", _("Username:password on sites with basic authentication")); |
| 1985 | printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); | 1578 | printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); |
| @@ -1987,13 +1580,14 @@ void print_help(void) { | |||
| 1987 | printf(" %s\n", "-A, --useragent=STRING"); | 1580 | printf(" %s\n", "-A, --useragent=STRING"); |
| 1988 | printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); | 1581 | printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); |
| 1989 | printf(" %s\n", "-k, --header=STRING"); | 1582 | printf(" %s\n", "-k, --header=STRING"); |
| 1990 | printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); | 1583 | printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for " |
| 1584 | "additional headers")); | ||
| 1991 | printf(" %s\n", "-E, --extended-perfdata"); | 1585 | printf(" %s\n", "-E, --extended-perfdata"); |
| 1992 | printf(" %s\n", _("Print additional performance data")); | 1586 | printf(" %s\n", _("Print additional performance data")); |
| 1993 | printf(" %s\n", "-B, --show-body"); | 1587 | printf(" %s\n", "-B, --show-body"); |
| 1994 | printf(" %s\n", _("Print body content below status line")); | 1588 | printf(" %s\n", _("Print body content below status line")); |
| 1995 | printf(" %s\n", "-L, --link"); | 1589 | // printf(" %s\n", "-L, --link"); |
| 1996 | printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); | 1590 | // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); |
| 1997 | printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>"); | 1591 | printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>"); |
| 1998 | printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); | 1592 | printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); |
| 1999 | printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); | 1593 | printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); |
| @@ -2003,20 +1597,25 @@ void print_help(void) { | |||
| 2003 | printf(" %s", _("Maximal number of redirects (default: ")); | 1597 | printf(" %s", _("Maximal number of redirects (default: ")); |
| 2004 | printf("%d)\n", DEFAULT_MAX_REDIRS); | 1598 | printf("%d)\n", DEFAULT_MAX_REDIRS); |
| 2005 | printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); | 1599 | printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); |
| 2006 | printf(" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); | 1600 | printf(" %s\n", |
| 1601 | _("Minimum page size required (bytes) : Maximum page size required (bytes)")); | ||
| 2007 | printf("\n"); | 1602 | printf("\n"); |
| 2008 | printf(" %s\n", "--http-version=VERSION"); | 1603 | printf(" %s\n", "--http-version=VERSION"); |
| 2009 | printf(" %s\n", _("Connect via specific HTTP protocol.")); | 1604 | printf(" %s\n", _("Connect via specific HTTP protocol.")); |
| 2010 | printf(" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); | 1605 | printf(" %s\n", |
| 1606 | _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); | ||
| 2011 | printf(" %s\n", "--enable-automatic-decompression"); | 1607 | printf(" %s\n", "--enable-automatic-decompression"); |
| 2012 | printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING).")); | 1608 | printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING).")); |
| 2013 | printf(" %s\n", "--haproxy-protocol"); | 1609 | printf(" %s\n", "--haproxy-protocol"); |
| 2014 | printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL).")); | 1610 | printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL).")); |
| 2015 | printf(" %s\n", "--cookie-jar=FILE"); | 1611 | printf(" %s\n", "--cookie-jar=FILE"); |
| 2016 | printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested.")); | 1612 | printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested.")); |
| 2017 | printf(" %s\n", _("Specify an empty string as FILE to enable curl's cookie engine without saving")); | 1613 | printf(" %s\n", |
| 2018 | printf(" %s\n", _("the cookies to disk. Only enabling the engine without saving to disk requires")); | 1614 | _("Specify an empty string as FILE to enable curl's cookie engine without saving")); |
| 2019 | printf(" %s\n", _("handling multiple requests internally to curl, so use it with --onredirect=curl")); | 1615 | printf(" %s\n", |
| 1616 | _("the cookies to disk. Only enabling the engine without saving to disk requires")); | ||
| 1617 | printf(" %s\n", | ||
| 1618 | _("handling multiple requests internally to curl, so use it with --onredirect=curl")); | ||
| 2020 | printf("\n"); | 1619 | printf("\n"); |
| 2021 | 1620 | ||
| 2022 | printf(UT_WARN_CRIT); | 1621 | printf(UT_WARN_CRIT); |
| @@ -2025,13 +1624,18 @@ void print_help(void) { | |||
| 2025 | 1624 | ||
| 2026 | printf(UT_VERBOSE); | 1625 | printf(UT_VERBOSE); |
| 2027 | 1626 | ||
| 1627 | printf(UT_OUTPUT_FORMAT); | ||
| 1628 | |||
| 2028 | printf("\n"); | 1629 | printf("\n"); |
| 2029 | printf("%s\n", _("Notes:")); | 1630 | printf("%s\n", _("Notes:")); |
| 2030 | printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); | 1631 | printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); |
| 2031 | printf(" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); | 1632 | printf(" %s\n", |
| 2032 | printf(" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); | 1633 | _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); |
| 1634 | printf(" %s\n", | ||
| 1635 | _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); | ||
| 2033 | printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); | 1636 | printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); |
| 2034 | printf(" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); | 1637 | printf(" %s\n", |
| 1638 | _("checking a virtual server that uses 'host headers' you must supply the FQDN")); | ||
| 2035 | printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); | 1639 | printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); |
| 2036 | 1640 | ||
| 2037 | #ifdef LIBCURL_FEATURE_SSL | 1641 | #ifdef LIBCURL_FEATURE_SSL |
| @@ -2047,38 +1651,53 @@ void print_help(void) { | |||
| 2047 | printf("%s\n", _("Examples:")); | 1651 | printf("%s\n", _("Examples:")); |
| 2048 | printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); | 1652 | printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); |
| 2049 | printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); | 1653 | printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); |
| 2050 | printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); | 1654 | printf(" %s\n", |
| 2051 | printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); | 1655 | _("a STATE_OK will be returned. When the server returns its content but exceeds")); |
| 1656 | printf(" %s\n", | ||
| 1657 | _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); | ||
| 2052 | printf(" %s\n", _("a STATE_CRITICAL will be returned.")); | 1658 | printf(" %s\n", _("a STATE_CRITICAL will be returned.")); |
| 2053 | printf("\n"); | 1659 | printf("\n"); |
| 2054 | printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); | 1660 | printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); |
| 2055 | printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); | 1661 | printf(" %s\n", |
| 2056 | printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); | 1662 | _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); |
| 2057 | printf(" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); | 1663 | printf(" %s\n", |
| 1664 | _("a STATE_OK is returned. When the certificate is still valid, but for less than")); | ||
| 1665 | printf(" %s\n", | ||
| 1666 | _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); | ||
| 2058 | printf(" %s\n\n", _("the certificate is expired.")); | 1667 | printf(" %s\n\n", _("the certificate is expired.")); |
| 2059 | printf("\n"); | 1668 | printf("\n"); |
| 2060 | printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); | 1669 | printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); |
| 2061 | printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); | 1670 | printf(" %s\n", |
| 2062 | printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); | 1671 | _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); |
| 1672 | printf(" %s\n", | ||
| 1673 | _("a STATE_OK is returned. When the certificate is still valid, but for less than")); | ||
| 2063 | printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); | 1674 | printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); |
| 2064 | printf(" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); | 1675 | printf(" %s\n", |
| 1676 | _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); | ||
| 2065 | #endif | 1677 | #endif |
| 2066 | 1678 | ||
| 2067 | printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); | 1679 | printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); |
| 2068 | printf(" %s\n", _("It is recommended to use an environment proxy like:")); | 1680 | printf(" %s\n", _("It is recommended to use an environment proxy like:")); |
| 2069 | printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); | 1681 | printf(" %s\n", |
| 1682 | _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); | ||
| 2070 | printf(" %s\n", _("legacy proxy requests in check_http style still work:")); | 1683 | printf(" %s\n", _("legacy proxy requests in check_http style still work:")); |
| 2071 | printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org")); | 1684 | printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ " |
| 1685 | "-H www.monitoring-plugins.org")); | ||
| 2072 | 1686 | ||
| 2073 | #ifdef LIBCURL_FEATURE_SSL | 1687 | #ifdef LIBCURL_FEATURE_SSL |
| 2074 | printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); | 1688 | printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); |
| 2075 | printf(" %s\n", _("It is recommended to use an environment proxy like:")); | 1689 | printf(" %s\n", _("It is recommended to use an environment proxy like:")); |
| 2076 | printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); | 1690 | printf(" %s\n", |
| 2077 | printf(" %s\n", _("legacy proxy requests in check_http style still work:")); | 1691 | _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); |
| 2078 | printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); | 1692 | printf(" %s\n", _("legacy proxy requests in check_http style might still work, but are frowned upon, so DONT:")); |
| 2079 | printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>")); | 1693 | printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j " |
| 2080 | printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); | 1694 | "CONNECT -H www.verisign.com ")); |
| 2081 | printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); | 1695 | printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> " |
| 1696 | "-S(sl) -j CONNECT -H <webserver>")); | ||
| 1697 | printf(" %s\n", | ||
| 1698 | _("a STATE_OK will be returned. When the server returns its content but exceeds")); | ||
| 1699 | printf(" %s\n", | ||
| 1700 | _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); | ||
| 2082 | printf(" %s\n", _("a STATE_CRITICAL will be returned.")); | 1701 | printf(" %s\n", _("a STATE_CRITICAL will be returned.")); |
| 2083 | 1702 | ||
| 2084 | #endif | 1703 | #endif |
| @@ -2089,10 +1708,12 @@ void print_help(void) { | |||
| 2089 | void print_usage(void) { | 1708 | void print_usage(void) { |
| 2090 | printf("%s\n", _("Usage:")); | 1709 | printf("%s\n", _("Usage:")); |
| 2091 | printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); | 1710 | printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); |
| 2092 | printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>] [-D]\n"); | 1711 | printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate " |
| 1712 | "file>] [-D]\n"); | ||
| 2093 | printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); | 1713 | printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); |
| 2094 | printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n"); | 1714 | printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n"); |
| 2095 | printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); | 1715 | printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive " |
| 1716 | "regex>]\n"); | ||
| 2096 | printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); | 1717 | printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); |
| 2097 | printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n"); | 1718 | printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n"); |
| 2098 | printf(" [-T <content-type>] [-j method]\n"); | 1719 | printf(" [-T <content-type>] [-j method]\n"); |
| @@ -2109,435 +1730,49 @@ void print_usage(void) { | |||
| 2109 | 1730 | ||
| 2110 | void print_curl_version(void) { printf("%s\n", curl_version()); } | 1731 | void print_curl_version(void) { printf("%s\n", curl_version()); } |
| 2111 | 1732 | ||
| 2112 | int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) { | ||
| 2113 | buf->bufsize = DEFAULT_BUFFER_SIZE; | ||
| 2114 | buf->buflen = 0; | ||
| 2115 | buf->buf = (char *)malloc((size_t)buf->bufsize); | ||
| 2116 | if (buf->buf == NULL) | ||
| 2117 | return -1; | ||
| 2118 | return 0; | ||
| 2119 | } | ||
| 2120 | |||
| 2121 | size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) { | ||
| 2122 | curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; | ||
| 2123 | |||
| 2124 | while (buf->bufsize < buf->buflen + size * nmemb + 1) { | ||
| 2125 | buf->bufsize = buf->bufsize * 2; | ||
| 2126 | buf->buf = (char *)realloc(buf->buf, buf->bufsize); | ||
| 2127 | if (buf->buf == NULL) { | ||
| 2128 | fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); | ||
| 2129 | return -1; | ||
| 2130 | } | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | memcpy(buf->buf + buf->buflen, buffer, size * nmemb); | ||
| 2134 | buf->buflen += size * nmemb; | ||
| 2135 | buf->buf[buf->buflen] = '\0'; | ||
| 2136 | |||
| 2137 | return (int)(size * nmemb); | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { | ||
| 2141 | curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; | ||
| 2142 | |||
| 2143 | size_t n = min(nmemb * size, buf->buflen - buf->pos); | ||
| 2144 | |||
| 2145 | memcpy(buffer, buf->buf + buf->pos, n); | ||
| 2146 | buf->pos += n; | ||
| 2147 | |||
| 2148 | return (int)n; | ||
| 2149 | } | ||
| 2150 | |||
| 2151 | void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { | ||
| 2152 | free(buf->buf); | ||
| 2153 | buf->buf = NULL; | ||
| 2154 | } | ||
| 2155 | |||
| 2156 | int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) { | ||
| 2157 | buf->buflen = datalen; | ||
| 2158 | buf->buf = (char *)malloc((size_t)buf->buflen); | ||
| 2159 | if (buf->buf == NULL) | ||
| 2160 | return -1; | ||
| 2161 | memcpy(buf->buf, data, datalen); | ||
| 2162 | buf->pos = 0; | ||
| 2163 | return 0; | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { | ||
| 2167 | free(buf->buf); | ||
| 2168 | buf->buf = NULL; | ||
| 2169 | } | ||
| 2170 | |||
| 2171 | /* TODO: where to put this, it's actually part of sstrings2 (logically)? | ||
| 2172 | */ | ||
| 2173 | const char *strrstr2(const char *haystack, const char *needle) { | ||
| 2174 | int counter; | ||
| 2175 | size_t len; | ||
| 2176 | const char *prev_pos; | ||
| 2177 | const char *pos; | ||
| 2178 | |||
| 2179 | if (haystack == NULL || needle == NULL) | ||
| 2180 | return NULL; | ||
| 2181 | |||
| 2182 | if (haystack[0] == '\0' || needle[0] == '\0') | ||
| 2183 | return NULL; | ||
| 2184 | |||
| 2185 | counter = 0; | ||
| 2186 | prev_pos = NULL; | ||
| 2187 | pos = haystack; | ||
| 2188 | len = strlen(needle); | ||
| 2189 | for (;;) { | ||
| 2190 | pos = strstr(pos, needle); | ||
| 2191 | if (pos == NULL) { | ||
| 2192 | if (counter == 0) | ||
| 2193 | return NULL; | ||
| 2194 | return prev_pos; | ||
| 2195 | } | ||
| 2196 | counter++; | ||
| 2197 | prev_pos = pos; | ||
| 2198 | pos += len; | ||
| 2199 | if (*pos == '\0') | ||
| 2200 | return prev_pos; | ||
| 2201 | } | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { | ||
| 2205 | char *first_line_end; | ||
| 2206 | char *p; | ||
| 2207 | size_t first_line_len; | ||
| 2208 | char *pp; | ||
| 2209 | const char *start; | ||
| 2210 | char *first_line_buf; | ||
| 2211 | |||
| 2212 | /* find last start of a new header */ | ||
| 2213 | start = strrstr2(buf, "\r\nHTTP/"); | ||
| 2214 | if (start != NULL) { | ||
| 2215 | start += 2; | ||
| 2216 | buf = start; | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | first_line_end = strstr(buf, "\r\n"); | ||
| 2220 | if (first_line_end == NULL) | ||
| 2221 | return -1; | ||
| 2222 | |||
| 2223 | first_line_len = (size_t)(first_line_end - buf); | ||
| 2224 | status_line->first_line = (char *)malloc(first_line_len + 1); | ||
| 2225 | if (status_line->first_line == NULL) | ||
| 2226 | return -1; | ||
| 2227 | memcpy(status_line->first_line, buf, first_line_len); | ||
| 2228 | status_line->first_line[first_line_len] = '\0'; | ||
| 2229 | first_line_buf = strdup(status_line->first_line); | ||
| 2230 | |||
| 2231 | /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ | ||
| 2232 | |||
| 2233 | p = strtok(first_line_buf, "/"); | ||
| 2234 | if (p == NULL) { | ||
| 2235 | free(first_line_buf); | ||
| 2236 | return -1; | ||
| 2237 | } | ||
| 2238 | if (strcmp(p, "HTTP") != 0) { | ||
| 2239 | free(first_line_buf); | ||
| 2240 | return -1; | ||
| 2241 | } | ||
| 2242 | |||
| 2243 | p = strtok(NULL, " "); | ||
| 2244 | if (p == NULL) { | ||
| 2245 | free(first_line_buf); | ||
| 2246 | return -1; | ||
| 2247 | } | ||
| 2248 | if (strchr(p, '.') != NULL) { | ||
| 2249 | |||
| 2250 | /* HTTP 1.x case */ | ||
| 2251 | strtok(p, "."); | ||
| 2252 | status_line->http_major = (int)strtol(p, &pp, 10); | ||
| 2253 | if (*pp != '\0') { | ||
| 2254 | free(first_line_buf); | ||
| 2255 | return -1; | ||
| 2256 | } | ||
| 2257 | strtok(NULL, " "); | ||
| 2258 | status_line->http_minor = (int)strtol(p, &pp, 10); | ||
| 2259 | if (*pp != '\0') { | ||
| 2260 | free(first_line_buf); | ||
| 2261 | return -1; | ||
| 2262 | } | ||
| 2263 | p += 4; /* 1.x SP */ | ||
| 2264 | } else { | ||
| 2265 | /* HTTP 2 case */ | ||
| 2266 | status_line->http_major = (int)strtol(p, &pp, 10); | ||
| 2267 | status_line->http_minor = 0; | ||
| 2268 | p += 2; /* 2 SP */ | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | /* status code: "404" or "404.1", then SP */ | ||
| 2272 | |||
| 2273 | p = strtok(p, " "); | ||
| 2274 | if (p == NULL) { | ||
| 2275 | free(first_line_buf); | ||
| 2276 | return -1; | ||
| 2277 | } | ||
| 2278 | if (strchr(p, '.') != NULL) { | ||
| 2279 | char *ppp; | ||
| 2280 | ppp = strtok(p, "."); | ||
| 2281 | status_line->http_code = (int)strtol(ppp, &pp, 10); | ||
| 2282 | if (*pp != '\0') { | ||
| 2283 | free(first_line_buf); | ||
| 2284 | return -1; | ||
| 2285 | } | ||
| 2286 | ppp = strtok(NULL, ""); | ||
| 2287 | status_line->http_subcode = (int)strtol(ppp, &pp, 10); | ||
| 2288 | if (*pp != '\0') { | ||
| 2289 | free(first_line_buf); | ||
| 2290 | return -1; | ||
| 2291 | } | ||
| 2292 | p += 6; /* 400.1 SP */ | ||
| 2293 | } else { | ||
| 2294 | status_line->http_code = (int)strtol(p, &pp, 10); | ||
| 2295 | status_line->http_subcode = -1; | ||
| 2296 | if (*pp != '\0') { | ||
| 2297 | free(first_line_buf); | ||
| 2298 | return -1; | ||
| 2299 | } | ||
| 2300 | p += 4; /* 400 SP */ | ||
| 2301 | } | ||
| 2302 | |||
| 2303 | /* Human readable message: "Not Found" CRLF */ | ||
| 2304 | |||
| 2305 | p = strtok(p, ""); | ||
| 2306 | if (p == NULL) { | ||
| 2307 | status_line->msg = ""; | ||
| 2308 | return 0; | ||
| 2309 | } | ||
| 2310 | status_line->msg = status_line->first_line + (p - first_line_buf); | ||
| 2311 | free(first_line_buf); | ||
| 2312 | |||
| 2313 | return 0; | ||
| 2314 | } | ||
| 2315 | |||
| 2316 | void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } | ||
| 2317 | |||
| 2318 | char *get_header_value(const struct phr_header *headers, const size_t nof_headers, const char *header) { | ||
| 2319 | for (size_t i = 0; i < nof_headers; i++) { | ||
| 2320 | if (headers[i].name != NULL && strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { | ||
| 2321 | return strndup(headers[i].value, headers[i].value_len); | ||
| 2322 | } | ||
| 2323 | } | ||
| 2324 | return NULL; | ||
| 2325 | } | ||
| 2326 | |||
| 2327 | int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) { | ||
| 2328 | char *server_date = NULL; | ||
| 2329 | char *document_date = NULL; | ||
| 2330 | int date_result = STATE_OK; | ||
| 2331 | curlhelp_statusline status_line; | ||
| 2332 | struct phr_header headers[255]; | ||
| 2333 | size_t nof_headers = 255; | ||
| 2334 | size_t msglen; | ||
| 2335 | |||
| 2336 | int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, | ||
| 2337 | &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); | ||
| 2338 | |||
| 2339 | if (res == -1) { | ||
| 2340 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); | ||
| 2341 | } | ||
| 2342 | |||
| 2343 | server_date = get_header_value(headers, nof_headers, "date"); | ||
| 2344 | document_date = get_header_value(headers, nof_headers, "last-modified"); | ||
| 2345 | |||
| 2346 | if (!server_date || !*server_date) { | ||
| 2347 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 2348 | |||
| 2349 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg); | ||
| 2350 | strcpy(*msg, tmp); | ||
| 2351 | |||
| 2352 | date_result = max_state_alt(STATE_UNKNOWN, date_result); | ||
| 2353 | |||
| 2354 | } else if (!document_date || !*document_date) { | ||
| 2355 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 2356 | |||
| 2357 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg); | ||
| 2358 | strcpy(*msg, tmp); | ||
| 2359 | |||
| 2360 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 2361 | |||
| 2362 | } else { | ||
| 2363 | time_t srv_data = curl_getdate(server_date, NULL); | ||
| 2364 | time_t doc_data = curl_getdate(document_date, NULL); | ||
| 2365 | if (verbose >= 2) | ||
| 2366 | printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); | ||
| 2367 | if (srv_data <= 0) { | ||
| 2368 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 2369 | |||
| 2370 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); | ||
| 2371 | strcpy(*msg, tmp); | ||
| 2372 | |||
| 2373 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 2374 | } else if (doc_data <= 0) { | ||
| 2375 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 2376 | |||
| 2377 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); | ||
| 2378 | strcpy(*msg, tmp); | ||
| 2379 | |||
| 2380 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 2381 | } else if (doc_data > srv_data + 30) { | ||
| 2382 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 2383 | |||
| 2384 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); | ||
| 2385 | strcpy(*msg, tmp); | ||
| 2386 | |||
| 2387 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 2388 | } else if (doc_data < srv_data - maximum_age) { | ||
| 2389 | int n = (srv_data - doc_data); | ||
| 2390 | if (n > (60 * 60 * 24 * 2)) { | ||
| 2391 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 2392 | |||
| 2393 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float)n) / (60 * 60 * 24)); | ||
| 2394 | strcpy(*msg, tmp); | ||
| 2395 | |||
| 2396 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 2397 | } else { | ||
| 2398 | char tmp[DEFAULT_BUFFER_SIZE]; | ||
| 2399 | |||
| 2400 | snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); | ||
| 2401 | strcpy(*msg, tmp); | ||
| 2402 | |||
| 2403 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 2404 | } | ||
| 2405 | } | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | if (server_date) | ||
| 2409 | free(server_date); | ||
| 2410 | if (document_date) | ||
| 2411 | free(document_date); | ||
| 2412 | |||
| 2413 | return date_result; | ||
| 2414 | } | ||
| 2415 | |||
| 2416 | int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) { | ||
| 2417 | size_t content_length = 0; | ||
| 2418 | struct phr_header headers[255]; | ||
| 2419 | size_t nof_headers = 255; | ||
| 2420 | size_t msglen; | ||
| 2421 | char *content_length_s = NULL; | ||
| 2422 | curlhelp_statusline status_line; | ||
| 2423 | |||
| 2424 | int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, | ||
| 2425 | &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); | ||
| 2426 | |||
| 2427 | if (res == -1) { | ||
| 2428 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); | ||
| 2429 | } | ||
| 2430 | |||
| 2431 | content_length_s = get_header_value(headers, nof_headers, "content-length"); | ||
| 2432 | if (!content_length_s) { | ||
| 2433 | return header_buf->buflen + body_buf->buflen; | ||
| 2434 | } | ||
| 2435 | content_length_s += strspn(content_length_s, " \t"); | ||
| 2436 | content_length = atoi(content_length_s); | ||
| 2437 | if (content_length != body_buf->buflen) { | ||
| 2438 | /* TODO: should we warn if the actual and the reported body length don't match? */ | ||
| 2439 | } | ||
| 2440 | |||
| 2441 | if (content_length_s) | ||
| 2442 | free(content_length_s); | ||
| 2443 | |||
| 2444 | return header_buf->buflen + body_buf->buflen; | ||
| 2445 | } | ||
| 2446 | |||
| 2447 | /* TODO: is there a better way in libcurl to check for the SSL library? */ | ||
| 2448 | curlhelp_ssl_library curlhelp_get_ssl_library(void) { | ||
| 2449 | curl_version_info_data *version_data; | ||
| 2450 | char *ssl_version; | ||
| 2451 | char *library; | ||
| 2452 | curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 2453 | |||
| 2454 | version_data = curl_version_info(CURLVERSION_NOW); | ||
| 2455 | if (version_data == NULL) | ||
| 2456 | return CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 2457 | |||
| 2458 | ssl_version = strdup(version_data->ssl_version); | ||
| 2459 | if (ssl_version == NULL) | ||
| 2460 | return CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 2461 | |||
| 2462 | library = strtok(ssl_version, "/"); | ||
| 2463 | if (library == NULL) | ||
| 2464 | return CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 2465 | |||
| 2466 | if (strcmp(library, "OpenSSL") == 0) | ||
| 2467 | ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; | ||
| 2468 | else if (strcmp(library, "LibreSSL") == 0) | ||
| 2469 | ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; | ||
| 2470 | else if (strcmp(library, "GnuTLS") == 0) | ||
| 2471 | ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; | ||
| 2472 | else if (strcmp(library, "NSS") == 0) | ||
| 2473 | ssl_library = CURLHELP_SSL_LIBRARY_NSS; | ||
| 2474 | |||
| 2475 | if (verbose >= 2) | ||
| 2476 | printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); | ||
| 2477 | |||
| 2478 | free(ssl_version); | ||
| 2479 | |||
| 2480 | return ssl_library; | ||
| 2481 | } | ||
| 2482 | |||
| 2483 | const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) { | ||
| 2484 | switch (ssl_library) { | ||
| 2485 | case CURLHELP_SSL_LIBRARY_OPENSSL: | ||
| 2486 | return "OpenSSL"; | ||
| 2487 | case CURLHELP_SSL_LIBRARY_LIBRESSL: | ||
| 2488 | return "LibreSSL"; | ||
| 2489 | case CURLHELP_SSL_LIBRARY_GNUTLS: | ||
| 2490 | return "GnuTLS"; | ||
| 2491 | case CURLHELP_SSL_LIBRARY_NSS: | ||
| 2492 | return "NSS"; | ||
| 2493 | case CURLHELP_SSL_LIBRARY_UNKNOWN: | ||
| 2494 | default: | ||
| 2495 | return "unknown"; | ||
| 2496 | } | ||
| 2497 | } | ||
| 2498 | |||
| 2499 | #ifdef LIBCURL_FEATURE_SSL | 1733 | #ifdef LIBCURL_FEATURE_SSL |
| 2500 | # ifndef USE_OPENSSL | 1734 | # ifndef USE_OPENSSL |
| 2501 | time_t parse_cert_date(const char *s) { | 1735 | time_t parse_cert_date(const char *s) { |
| 2502 | struct tm tm; | 1736 | if (!s) { |
| 2503 | time_t date; | ||
| 2504 | char *res; | ||
| 2505 | |||
| 2506 | if (!s) | ||
| 2507 | return -1; | 1737 | return -1; |
| 1738 | } | ||
| 2508 | 1739 | ||
| 2509 | /* Jan 17 14:25:12 2020 GMT */ | 1740 | /* Jan 17 14:25:12 2020 GMT */ |
| 2510 | res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); | 1741 | struct tm tm; |
| 1742 | char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); | ||
| 2511 | /* Sep 11 12:00:00 2020 GMT */ | 1743 | /* Sep 11 12:00:00 2020 GMT */ |
| 2512 | if (res == NULL) | 1744 | if (res == NULL) { |
| 2513 | strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); | 1745 | strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); |
| 2514 | date = mktime(&tm); | 1746 | } |
| 1747 | time_t date = mktime(&tm); | ||
| 2515 | 1748 | ||
| 2516 | return date; | 1749 | return date; |
| 2517 | } | 1750 | } |
| 1751 | # endif /* USE_OPENSSL */ | ||
| 1752 | #endif /* LIBCURL_FEATURE_SSL */ | ||
| 2518 | 1753 | ||
| 1754 | #ifdef LIBCURL_FEATURE_SSL | ||
| 1755 | # ifndef USE_OPENSSL | ||
| 2519 | /* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to | 1756 | /* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to |
| 2520 | * OpenSSL could be this function | 1757 | * OpenSSL could be this function |
| 2521 | */ | 1758 | */ |
| 2522 | int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { | 1759 | int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, |
| 2523 | int i; | 1760 | int days_till_exp_crit) { |
| 2524 | struct curl_slist *slist; | ||
| 2525 | int cname_found = 0; | ||
| 2526 | char *start_date_str = NULL; | ||
| 2527 | char *end_date_str = NULL; | ||
| 2528 | time_t start_date; | ||
| 2529 | time_t end_date; | ||
| 2530 | char *tz; | ||
| 2531 | float time_left; | ||
| 2532 | int days_left; | ||
| 2533 | int time_remaining; | ||
| 2534 | char timestamp[50] = ""; | ||
| 2535 | int status = STATE_UNKNOWN; | ||
| 2536 | 1761 | ||
| 2537 | if (verbose >= 2) | 1762 | if (verbose >= 2) { |
| 2538 | printf("**** REQUEST CERTIFICATES ****\n"); | 1763 | printf("**** REQUEST CERTIFICATES ****\n"); |
| 1764 | } | ||
| 1765 | |||
| 1766 | char *start_date_str = NULL; | ||
| 1767 | char *end_date_str = NULL; | ||
| 1768 | bool have_first_cert = false; | ||
| 1769 | bool cname_found = false; | ||
| 1770 | for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { | ||
| 1771 | if (have_first_cert) { | ||
| 1772 | break; | ||
| 1773 | } | ||
| 2539 | 1774 | ||
| 2540 | for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { | 1775 | struct curl_slist *slist; |
| 2541 | for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { | 1776 | for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { |
| 2542 | /* find first common name in subject, | 1777 | /* find first common name in subject, |
| 2543 | * TODO: check alternative subjects for | 1778 | * TODO: check alternative subjects for |
| @@ -2553,7 +1788,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ | |||
| 2553 | } | 1788 | } |
| 2554 | if (p != NULL) { | 1789 | if (p != NULL) { |
| 2555 | if (strncmp(host_name, p + d, strlen(host_name)) == 0) { | 1790 | if (strncmp(host_name, p + d, strlen(host_name)) == 0) { |
| 2556 | cname_found = 1; | 1791 | cname_found = true; |
| 2557 | } | 1792 | } |
| 2558 | } | 1793 | } |
| 2559 | } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) { | 1794 | } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) { |
| @@ -2561,78 +1796,93 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ | |||
| 2561 | } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) { | 1796 | } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) { |
| 2562 | end_date_str = &slist->data[12]; | 1797 | end_date_str = &slist->data[12]; |
| 2563 | } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { | 1798 | } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { |
| 2564 | goto HAVE_FIRST_CERT; | 1799 | have_first_cert = true; |
| 1800 | break; | ||
| 2565 | } | 1801 | } |
| 2566 | if (verbose >= 2) | 1802 | if (verbose >= 2) { |
| 2567 | printf("%d ** %s\n", i, slist->data); | 1803 | printf("%d ** %s\n", i, slist->data); |
| 1804 | } | ||
| 2568 | } | 1805 | } |
| 2569 | } | 1806 | } |
| 2570 | HAVE_FIRST_CERT: | ||
| 2571 | 1807 | ||
| 2572 | if (verbose >= 2) | 1808 | if (verbose >= 2) { |
| 2573 | printf("**** REQUEST CERTIFICATES ****\n"); | 1809 | printf("**** REQUEST CERTIFICATES ****\n"); |
| 1810 | } | ||
| 2574 | 1811 | ||
| 2575 | if (!cname_found) { | 1812 | if (!cname_found) { |
| 2576 | printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); | 1813 | printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); |
| 2577 | return STATE_CRITICAL; | 1814 | return STATE_CRITICAL; |
| 2578 | } | 1815 | } |
| 2579 | 1816 | ||
| 2580 | start_date = parse_cert_date(start_date_str); | 1817 | time_t start_date = parse_cert_date(start_date_str); |
| 2581 | if (start_date <= 0) { | 1818 | if (start_date <= 0) { |
| 2582 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); | 1819 | snprintf(msg, DEFAULT_BUFFER_SIZE, |
| 1820 | _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); | ||
| 2583 | puts(msg); | 1821 | puts(msg); |
| 2584 | return STATE_WARNING; | 1822 | return STATE_WARNING; |
| 2585 | } | 1823 | } |
| 2586 | 1824 | ||
| 2587 | end_date = parse_cert_date(end_date_str); | 1825 | time_t end_date = parse_cert_date(end_date_str); |
| 2588 | if (end_date <= 0) { | 1826 | if (end_date <= 0) { |
| 2589 | snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); | 1827 | snprintf(msg, DEFAULT_BUFFER_SIZE, |
| 1828 | _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); | ||
| 2590 | puts(msg); | 1829 | puts(msg); |
| 2591 | return STATE_WARNING; | 1830 | return STATE_WARNING; |
| 2592 | } | 1831 | } |
| 2593 | 1832 | ||
| 2594 | time_left = difftime(end_date, time(NULL)); | 1833 | float time_left = difftime(end_date, time(NULL)); |
| 2595 | days_left = time_left / 86400; | 1834 | int days_left = time_left / 86400; |
| 2596 | tz = getenv("TZ"); | 1835 | char *tz = getenv("TZ"); |
| 2597 | setenv("TZ", "GMT", 1); | 1836 | setenv("TZ", "GMT", 1); |
| 2598 | tzset(); | 1837 | tzset(); |
| 1838 | |||
| 1839 | char timestamp[50] = ""; | ||
| 2599 | strftime(timestamp, 50, "%c %z", localtime(&end_date)); | 1840 | strftime(timestamp, 50, "%c %z", localtime(&end_date)); |
| 2600 | if (tz) | 1841 | if (tz) { |
| 2601 | setenv("TZ", tz, 1); | 1842 | setenv("TZ", tz, 1); |
| 2602 | else | 1843 | } else { |
| 2603 | unsetenv("TZ"); | 1844 | unsetenv("TZ"); |
| 1845 | } | ||
| 2604 | tzset(); | 1846 | tzset(); |
| 2605 | 1847 | ||
| 1848 | mp_state_enum status = STATE_UNKNOWN; | ||
| 1849 | int time_remaining; | ||
| 2606 | if (days_left > 0 && days_left <= days_till_exp_warn) { | 1850 | if (days_left > 0 && days_left <= days_till_exp_warn) { |
| 2607 | printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", | 1851 | printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), |
| 2608 | host_name, days_left, timestamp); | 1852 | (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left, |
| 2609 | if (days_left > days_till_exp_crit) | 1853 | timestamp); |
| 1854 | if (days_left > days_till_exp_crit) { | ||
| 2610 | status = STATE_WARNING; | 1855 | status = STATE_WARNING; |
| 2611 | else | 1856 | } else { |
| 2612 | status = STATE_CRITICAL; | 1857 | status = STATE_CRITICAL; |
| 1858 | } | ||
| 2613 | } else if (days_left == 0 && time_left > 0) { | 1859 | } else if (days_left == 0 && time_left > 0) { |
| 2614 | if (time_left >= 3600) | 1860 | if (time_left >= 3600) { |
| 2615 | time_remaining = (int)time_left / 3600; | 1861 | time_remaining = (int)time_left / 3600; |
| 2616 | else | 1862 | } else { |
| 2617 | time_remaining = (int)time_left / 60; | 1863 | time_remaining = (int)time_left / 60; |
| 1864 | } | ||
| 2618 | 1865 | ||
| 2619 | printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, | 1866 | printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), |
| 2620 | time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); | 1867 | (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining, |
| 1868 | time_left >= 3600 ? "hours" : "minutes", timestamp); | ||
| 2621 | 1869 | ||
| 2622 | if (days_left > days_till_exp_crit) | 1870 | if (days_left > days_till_exp_crit) { |
| 2623 | status = STATE_WARNING; | 1871 | status = STATE_WARNING; |
| 2624 | else | 1872 | } else { |
| 2625 | status = STATE_CRITICAL; | 1873 | status = STATE_CRITICAL; |
| 1874 | } | ||
| 2626 | } else if (time_left < 0) { | 1875 | } else if (time_left < 0) { |
| 2627 | printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); | 1876 | printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); |
| 2628 | status = STATE_CRITICAL; | 1877 | status = STATE_CRITICAL; |
| 2629 | } else if (days_left == 0) { | 1878 | } else if (days_left == 0) { |
| 2630 | printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, | 1879 | printf(_("%s - Certificate '%s' just expired (%s).\n"), |
| 2631 | timestamp); | 1880 | (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp); |
| 2632 | if (days_left > days_till_exp_crit) | 1881 | if (days_left > days_till_exp_crit) { |
| 2633 | status = STATE_WARNING; | 1882 | status = STATE_WARNING; |
| 2634 | else | 1883 | } else { |
| 2635 | status = STATE_CRITICAL; | 1884 | status = STATE_CRITICAL; |
| 1885 | } | ||
| 2636 | } else { | 1886 | } else { |
| 2637 | printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); | 1887 | printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); |
| 2638 | status = STATE_OK; | 1888 | status = STATE_OK; |
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c new file mode 100644 index 00000000..e4e7bef6 --- /dev/null +++ b/plugins/check_curl.d/check_curl_helpers.c | |||
| @@ -0,0 +1,1295 @@ | |||
| 1 | #include "./check_curl_helpers.h" | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <arpa/inet.h> | ||
| 4 | #include <netinet/in.h> | ||
| 5 | #include <netdb.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include "../utils.h" | ||
| 9 | #include "check_curl.d/config.h" | ||
| 10 | #include "output.h" | ||
| 11 | #include "perfdata.h" | ||
| 12 | #include "states.h" | ||
| 13 | |||
| 14 | extern int verbose; | ||
| 15 | char errbuf[MAX_INPUT_BUFFER]; | ||
| 16 | bool is_openssl_callback = false; | ||
| 17 | bool add_sslctx_verify_fun = false; | ||
| 18 | |||
| 19 | check_curl_configure_curl_wrapper | ||
| 20 | check_curl_configure_curl(const check_curl_static_curl_config config, | ||
| 21 | check_curl_working_state working_state, bool check_cert, | ||
| 22 | bool on_redirect_dependent, int follow_method, int max_depth) { | ||
| 23 | check_curl_configure_curl_wrapper result = { | ||
| 24 | .errorcode = OK, | ||
| 25 | .curl_state = | ||
| 26 | { | ||
| 27 | .curl_global_initialized = false, | ||
| 28 | .curl_easy_initialized = false, | ||
| 29 | .curl = NULL, | ||
| 30 | |||
| 31 | .body_buf_initialized = false, | ||
| 32 | .body_buf = NULL, | ||
| 33 | .header_buf_initialized = false, | ||
| 34 | .header_buf = NULL, | ||
| 35 | .status_line_initialized = false, | ||
| 36 | .status_line = NULL, | ||
| 37 | .put_buf_initialized = false, | ||
| 38 | .put_buf = NULL, | ||
| 39 | |||
| 40 | .header_list = NULL, | ||
| 41 | .host = NULL, | ||
| 42 | }, | ||
| 43 | }; | ||
| 44 | |||
| 45 | if ((result.curl_state.status_line = calloc(1, sizeof(curlhelp_statusline))) == NULL) { | ||
| 46 | die(STATE_UNKNOWN, "HTTP UNKNOWN - allocation of statusline failed\n"); | ||
| 47 | } | ||
| 48 | |||
| 49 | if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { | ||
| 50 | die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); | ||
| 51 | } | ||
| 52 | result.curl_state.curl_global_initialized = true; | ||
| 53 | |||
| 54 | if ((result.curl_state.curl = curl_easy_init()) == NULL) { | ||
| 55 | die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); | ||
| 56 | } | ||
| 57 | result.curl_state.curl_easy_initialized = true; | ||
| 58 | |||
| 59 | if (verbose >= 1) { | ||
| 60 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), | ||
| 61 | "CURLOPT_VERBOSE"); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* print everything on stdout like check_http would do */ | ||
| 65 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout), | ||
| 66 | "CURLOPT_STDERR"); | ||
| 67 | |||
| 68 | if (config.automatic_decompression) { | ||
| 69 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) | ||
| 70 | handle_curl_option_return_code( | ||
| 71 | curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""), | ||
| 72 | "CURLOPT_ACCEPT_ENCODING"); | ||
| 73 | #else | ||
| 74 | handle_curl_option_return_code( | ||
| 75 | curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); | ||
| 76 | #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ | ||
| 77 | } | ||
| 78 | |||
| 79 | /* initialize buffer for body of the answer */ | ||
| 80 | if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) { | ||
| 81 | die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); | ||
| 82 | } | ||
| 83 | result.curl_state.body_buf_initialized = true; | ||
| 84 | |||
| 85 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION, | ||
| 86 | curlhelp_buffer_write_callback), | ||
| 87 | "CURLOPT_WRITEFUNCTION"); | ||
| 88 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA, | ||
| 89 | (void *)result.curl_state.body_buf), | ||
| 90 | "CURLOPT_WRITEDATA"); | ||
| 91 | |||
| 92 | /* initialize buffer for header of the answer */ | ||
| 93 | if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) { | ||
| 94 | die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); | ||
| 95 | } | ||
| 96 | result.curl_state.header_buf_initialized = true; | ||
| 97 | |||
| 98 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION, | ||
| 99 | curlhelp_buffer_write_callback), | ||
| 100 | "CURLOPT_HEADERFUNCTION"); | ||
| 101 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER, | ||
| 102 | (void *)result.curl_state.header_buf), | ||
| 103 | "CURLOPT_WRITEHEADER"); | ||
| 104 | |||
| 105 | /* set the error buffer */ | ||
| 106 | handle_curl_option_return_code( | ||
| 107 | curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf), | ||
| 108 | "CURLOPT_ERRORBUFFER"); | ||
| 109 | |||
| 110 | /* set timeouts */ | ||
| 111 | handle_curl_option_return_code( | ||
| 112 | curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), | ||
| 113 | "CURLOPT_CONNECTTIMEOUT"); | ||
| 114 | |||
| 115 | handle_curl_option_return_code( | ||
| 116 | curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), | ||
| 117 | "CURLOPT_TIMEOUT"); | ||
| 118 | |||
| 119 | /* enable haproxy protocol */ | ||
| 120 | if (config.haproxy_protocol) { | ||
| 121 | handle_curl_option_return_code( | ||
| 122 | curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), | ||
| 123 | "CURLOPT_HAPROXYPROTOCOL"); | ||
| 124 | } | ||
| 125 | |||
| 126 | // fill dns resolve cache to make curl connect to the given server_address instead of the | ||
| 127 | // host_name, only required for ssl, because we use the host_name later on to make SNI happy | ||
| 128 | char dnscache[DEFAULT_BUFFER_SIZE]; | ||
| 129 | char addrstr[DEFAULT_BUFFER_SIZE / 2]; | ||
| 130 | if (working_state.use_ssl && working_state.host_name != NULL) { | ||
| 131 | char *tmp_mod_address; | ||
| 132 | |||
| 133 | /* lookup_host() requires an IPv6 address without the brackets. */ | ||
| 134 | if ((strnlen(working_state.server_address, MAX_IPV4_HOSTLENGTH) > 2) && | ||
| 135 | (working_state.server_address[0] == '[')) { | ||
| 136 | // Duplicate and strip the leading '[' | ||
| 137 | tmp_mod_address = | ||
| 138 | strndup(working_state.server_address + 1, strlen(working_state.server_address) - 2); | ||
| 139 | } else { | ||
| 140 | tmp_mod_address = working_state.server_address; | ||
| 141 | } | ||
| 142 | |||
| 143 | int res; | ||
| 144 | if ((res = lookup_host(tmp_mod_address, addrstr, DEFAULT_BUFFER_SIZE / 2, | ||
| 145 | config.sin_family)) != 0) { | ||
| 146 | die(STATE_CRITICAL, | ||
| 147 | _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), | ||
| 148 | working_state.server_address, res, gai_strerror(res)); | ||
| 149 | } | ||
| 150 | |||
| 151 | snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, | ||
| 152 | working_state.serverPort, addrstr); | ||
| 153 | result.curl_state.host = curl_slist_append(NULL, dnscache); | ||
| 154 | curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); | ||
| 155 | |||
| 156 | if (verbose >= 1) { | ||
| 157 | printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | // If server_address is an IPv6 address it must be surround by square brackets | ||
| 162 | struct in6_addr tmp_in_addr; | ||
| 163 | if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) { | ||
| 164 | char *new_server_address = calloc(strlen(working_state.server_address) + 3, sizeof(char)); | ||
| 165 | if (new_server_address == NULL) { | ||
| 166 | die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); | ||
| 167 | } | ||
| 168 | snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]", | ||
| 169 | working_state.server_address); | ||
| 170 | working_state.server_address = new_server_address; | ||
| 171 | } | ||
| 172 | |||
| 173 | /* compose URL: use the address we want to connect to, set Host: header later */ | ||
| 174 | char *url = fmt_url(working_state); | ||
| 175 | |||
| 176 | if (verbose >= 1) { | ||
| 177 | printf("* curl CURLOPT_URL: %s\n", url); | ||
| 178 | } | ||
| 179 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), | ||
| 180 | "CURLOPT_URL"); | ||
| 181 | |||
| 182 | free(url); | ||
| 183 | |||
| 184 | /* extract proxy information for legacy proxy https requests */ | ||
| 185 | if (!strcmp(working_state.http_method, "CONNECT") || | ||
| 186 | strstr(working_state.server_url, "http") == working_state.server_url) { | ||
| 187 | handle_curl_option_return_code( | ||
| 188 | curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address), | ||
| 189 | "CURLOPT_PROXY"); | ||
| 190 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT, | ||
| 191 | (long)working_state.serverPort), | ||
| 192 | "CURLOPT_PROXYPORT"); | ||
| 193 | if (verbose >= 2) { | ||
| 194 | printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address, | ||
| 195 | working_state.serverPort); | ||
| 196 | } | ||
| 197 | working_state.http_method = "GET"; | ||
| 198 | handle_curl_option_return_code( | ||
| 199 | curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url), | ||
| 200 | "CURLOPT_URL"); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* disable body for HEAD request */ | ||
| 204 | if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) { | ||
| 205 | working_state.no_body = true; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* set HTTP protocol version */ | ||
| 209 | handle_curl_option_return_code( | ||
| 210 | curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), | ||
| 211 | "CURLOPT_HTTP_VERSION"); | ||
| 212 | |||
| 213 | /* set HTTP method */ | ||
| 214 | if (working_state.http_method) { | ||
| 215 | if (!strcmp(working_state.http_method, "POST")) { | ||
| 216 | handle_curl_option_return_code( | ||
| 217 | curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); | ||
| 218 | } else if (!strcmp(working_state.http_method, "PUT")) { | ||
| 219 | handle_curl_option_return_code( | ||
| 220 | curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); | ||
| 221 | } else { | ||
| 222 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, | ||
| 223 | CURLOPT_CUSTOMREQUEST, | ||
| 224 | working_state.http_method), | ||
| 225 | "CURLOPT_CUSTOMREQUEST"); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | char *force_host_header = NULL; | ||
| 230 | /* check if Host header is explicitly set in options */ | ||
| 231 | if (config.http_opt_headers_count) { | ||
| 232 | for (size_t i = 0; i < config.http_opt_headers_count; i++) { | ||
| 233 | if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { | ||
| 234 | force_host_header = config.http_opt_headers[i]; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in | ||
| 240 | * anyway */ | ||
| 241 | char http_header[DEFAULT_BUFFER_SIZE]; | ||
| 242 | if (working_state.host_name != NULL && force_host_header == NULL) { | ||
| 243 | if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) || | ||
| 244 | (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) { | ||
| 245 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name, | ||
| 246 | working_state.virtualPort); | ||
| 247 | } else { | ||
| 248 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name); | ||
| 249 | } | ||
| 250 | result.curl_state.header_list = | ||
| 251 | curl_slist_append(result.curl_state.header_list, http_header); | ||
| 252 | } | ||
| 253 | |||
| 254 | /* always close connection, be nice to servers */ | ||
| 255 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); | ||
| 256 | result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header); | ||
| 257 | |||
| 258 | /* attach additional headers supplied by the user */ | ||
| 259 | /* optionally send any other header tag */ | ||
| 260 | if (config.http_opt_headers_count) { | ||
| 261 | for (size_t i = 0; i < config.http_opt_headers_count; i++) { | ||
| 262 | result.curl_state.header_list = | ||
| 263 | curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | /* set HTTP headers */ | ||
| 268 | handle_curl_option_return_code( | ||
| 269 | curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list), | ||
| 270 | "CURLOPT_HTTPHEADER"); | ||
| 271 | |||
| 272 | #ifdef LIBCURL_FEATURE_SSL | ||
| 273 | /* set SSL version, warn about insecure or unsupported versions */ | ||
| 274 | if (working_state.use_ssl) { | ||
| 275 | handle_curl_option_return_code( | ||
| 276 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version), | ||
| 277 | "CURLOPT_SSLVERSION"); | ||
| 278 | } | ||
| 279 | |||
| 280 | /* client certificate and key to present to server (SSL) */ | ||
| 281 | if (config.client_cert) { | ||
| 282 | handle_curl_option_return_code( | ||
| 283 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert), | ||
| 284 | "CURLOPT_SSLCERT"); | ||
| 285 | } | ||
| 286 | |||
| 287 | if (config.client_privkey) { | ||
| 288 | handle_curl_option_return_code( | ||
| 289 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey), | ||
| 290 | "CURLOPT_SSLKEY"); | ||
| 291 | } | ||
| 292 | |||
| 293 | if (config.ca_cert) { | ||
| 294 | handle_curl_option_return_code( | ||
| 295 | curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert), | ||
| 296 | "CURLOPT_CAINFO"); | ||
| 297 | } | ||
| 298 | |||
| 299 | if (config.ca_cert || config.verify_peer_and_host) { | ||
| 300 | /* per default if we have a CA verify both the peer and the | ||
| 301 | * hostname in the certificate, can be switched off later */ | ||
| 302 | handle_curl_option_return_code( | ||
| 303 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), | ||
| 304 | "CURLOPT_SSL_VERIFYPEER"); | ||
| 305 | handle_curl_option_return_code( | ||
| 306 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), | ||
| 307 | "CURLOPT_SSL_VERIFYHOST"); | ||
| 308 | } else { | ||
| 309 | /* backward-compatible behaviour, be tolerant in checks | ||
| 310 | * TODO: depending on more options have aspects we want | ||
| 311 | * to be less tolerant about ssl verfications | ||
| 312 | */ | ||
| 313 | handle_curl_option_return_code( | ||
| 314 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), | ||
| 315 | "CURLOPT_SSL_VERIFYPEER"); | ||
| 316 | handle_curl_option_return_code( | ||
| 317 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), | ||
| 318 | "CURLOPT_SSL_VERIFYHOST"); | ||
| 319 | } | ||
| 320 | |||
| 321 | /* detect SSL library used by libcurl */ | ||
| 322 | curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); | ||
| 323 | |||
| 324 | /* try hard to get a stack of certificates to verify against */ | ||
| 325 | if (check_cert) { | ||
| 326 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) | ||
| 327 | /* inform curl to report back certificates */ | ||
| 328 | switch (ssl_library) { | ||
| 329 | case CURLHELP_SSL_LIBRARY_OPENSSL: | ||
| 330 | case CURLHELP_SSL_LIBRARY_LIBRESSL: | ||
| 331 | /* set callback to extract certificate with OpenSSL context function (works with | ||
| 332 | * OpenSSL-style libraries only!) */ | ||
| 333 | # ifdef USE_OPENSSL | ||
| 334 | /* libcurl and monitoring plugins built with OpenSSL, good */ | ||
| 335 | add_sslctx_verify_fun = true; | ||
| 336 | is_openssl_callback = true; | ||
| 337 | # endif /* USE_OPENSSL */ | ||
| 338 | /* libcurl is built with OpenSSL, monitoring plugins, so falling | ||
| 339 | * back to manually extracting certificate information */ | ||
| 340 | handle_curl_option_return_code( | ||
| 341 | curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); | ||
| 342 | break; | ||
| 343 | |||
| 344 | case CURLHELP_SSL_LIBRARY_NSS: | ||
| 345 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) | ||
| 346 | /* NSS: support for CERTINFO is implemented since 7.34.0 */ | ||
| 347 | handle_curl_option_return_code( | ||
| 348 | curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); | ||
| 349 | # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 350 | die(STATE_CRITICAL, | ||
| 351 | "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " | ||
| 352 | "'%s' is too old)\n", | ||
| 353 | curlhelp_get_ssl_library_string(ssl_library)); | ||
| 354 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ | ||
| 355 | break; | ||
| 356 | |||
| 357 | case CURLHELP_SSL_LIBRARY_GNUTLS: | ||
| 358 | # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) | ||
| 359 | /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ | ||
| 360 | handle_curl_option_return_code( | ||
| 361 | curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); | ||
| 362 | # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ | ||
| 363 | die(STATE_CRITICAL, | ||
| 364 | "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " | ||
| 365 | "'%s' is too old)\n", | ||
| 366 | curlhelp_get_ssl_library_string(ssl_library)); | ||
| 367 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ | ||
| 368 | break; | ||
| 369 | |||
| 370 | case CURLHELP_SSL_LIBRARY_UNKNOWN: | ||
| 371 | default: | ||
| 372 | die(STATE_CRITICAL, | ||
| 373 | "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must " | ||
| 374 | "implement first)\n", | ||
| 375 | curlhelp_get_ssl_library_string(ssl_library)); | ||
| 376 | break; | ||
| 377 | } | ||
| 378 | # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ | ||
| 379 | /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ | ||
| 380 | if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || | ||
| 381 | ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { | ||
| 382 | add_sslctx_verify_fun = true; | ||
| 383 | } else { | ||
| 384 | die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no " | ||
| 385 | "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " | ||
| 386 | "too old and has no CURLOPT_CERTINFO)\n"); | ||
| 387 | } | ||
| 388 | # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ | ||
| 389 | } | ||
| 390 | |||
| 391 | # if LIBCURL_VERSION_NUM >= \ | ||
| 392 | MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ | ||
| 393 | // ssl ctx function is not available with all ssl backends | ||
| 394 | if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != | ||
| 395 | CURLE_UNKNOWN_OPTION) { | ||
| 396 | handle_curl_option_return_code( | ||
| 397 | curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), | ||
| 398 | "CURLOPT_SSL_CTX_FUNCTION"); | ||
| 399 | } | ||
| 400 | # endif | ||
| 401 | #endif /* LIBCURL_FEATURE_SSL */ | ||
| 402 | |||
| 403 | /* set default or user-given user agent identification */ | ||
| 404 | handle_curl_option_return_code( | ||
| 405 | curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent), | ||
| 406 | "CURLOPT_USERAGENT"); | ||
| 407 | |||
| 408 | /* proxy-authentication */ | ||
| 409 | if (strcmp(config.proxy_auth, "")) { | ||
| 410 | handle_curl_option_return_code( | ||
| 411 | curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), | ||
| 412 | "CURLOPT_PROXYUSERPWD"); | ||
| 413 | } | ||
| 414 | |||
| 415 | /* authentication */ | ||
| 416 | if (strcmp(config.user_auth, "")) { | ||
| 417 | handle_curl_option_return_code( | ||
| 418 | curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth), | ||
| 419 | "CURLOPT_USERPWD"); | ||
| 420 | } | ||
| 421 | /* TODO: parameter auth method, bitfield of following methods: | ||
| 422 | * CURLAUTH_BASIC (default) | ||
| 423 | * CURLAUTH_DIGEST | ||
| 424 | * CURLAUTH_DIGEST_IE | ||
| 425 | * CURLAUTH_NEGOTIATE | ||
| 426 | * CURLAUTH_NTLM | ||
| 427 | * CURLAUTH_NTLM_WB | ||
| 428 | * | ||
| 429 | * convenience tokens for typical sets of methods: | ||
| 430 | * CURLAUTH_ANYSAFE: most secure, without BASIC | ||
| 431 | * or CURLAUTH_ANY: most secure, even BASIC if necessary | ||
| 432 | * | ||
| 433 | * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, | ||
| 434 | * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); | ||
| 435 | */ | ||
| 436 | |||
| 437 | /* handle redirections */ | ||
| 438 | if (on_redirect_dependent) { | ||
| 439 | if (follow_method == FOLLOW_LIBCURL) { | ||
| 440 | handle_curl_option_return_code( | ||
| 441 | curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), | ||
| 442 | "CURLOPT_FOLLOWLOCATION"); | ||
| 443 | |||
| 444 | /* default -1 is infinite, not good, could lead to zombie plugins! | ||
| 445 | Setting it to one bigger than maximal limit to handle errors nicely below | ||
| 446 | */ | ||
| 447 | handle_curl_option_return_code( | ||
| 448 | curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1), | ||
| 449 | "CURLOPT_MAXREDIRS"); | ||
| 450 | |||
| 451 | /* for now allow only http and https (we are a http(s) check plugin in the end) */ | ||
| 452 | #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) | ||
| 453 | handle_curl_option_return_code( | ||
| 454 | curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), | ||
| 455 | "CURLOPT_REDIR_PROTOCOLS_STR"); | ||
| 456 | #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) | ||
| 457 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, | ||
| 458 | CURLOPT_REDIR_PROTOCOLS, | ||
| 459 | CURLPROTO_HTTP | CURLPROTO_HTTPS), | ||
| 460 | "CURLOPT_REDIRECT_PROTOCOLS"); | ||
| 461 | #endif | ||
| 462 | |||
| 463 | /* TODO: handle the following aspects of redirection, make them | ||
| 464 | * command line options too later: | ||
| 465 | CURLOPT_POSTREDIR: method switch | ||
| 466 | CURLINFO_REDIRECT_URL: custom redirect option | ||
| 467 | CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols | ||
| 468 | CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range | ||
| 469 | option here is nice like for expected page size? | ||
| 470 | */ | ||
| 471 | } else { | ||
| 472 | /* old style redirection*/ | ||
| 473 | } | ||
| 474 | } | ||
| 475 | /* no-body */ | ||
| 476 | if (working_state.no_body) { | ||
| 477 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), | ||
| 478 | "CURLOPT_NOBODY"); | ||
| 479 | } | ||
| 480 | |||
| 481 | /* IPv4 or IPv6 forced DNS resolution */ | ||
| 482 | if (config.sin_family == AF_UNSPEC) { | ||
| 483 | handle_curl_option_return_code( | ||
| 484 | curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), | ||
| 485 | "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); | ||
| 486 | } else if (config.sin_family == AF_INET) { | ||
| 487 | handle_curl_option_return_code( | ||
| 488 | curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), | ||
| 489 | "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); | ||
| 490 | } | ||
| 491 | #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) | ||
| 492 | else if (config.sin_family == AF_INET6) { | ||
| 493 | handle_curl_option_return_code( | ||
| 494 | curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), | ||
| 495 | "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); | ||
| 496 | } | ||
| 497 | #endif | ||
| 498 | |||
| 499 | /* either send http POST data (any data, not only POST)*/ | ||
| 500 | if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) { | ||
| 501 | /* set content of payload for POST and PUT */ | ||
| 502 | if (config.http_content_type) { | ||
| 503 | snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", | ||
| 504 | config.http_content_type); | ||
| 505 | result.curl_state.header_list = | ||
| 506 | curl_slist_append(result.curl_state.header_list, http_header); | ||
| 507 | } | ||
| 508 | /* NULL indicates "HTTP Continue" in libcurl, provide an empty string | ||
| 509 | * in case of no POST/PUT data */ | ||
| 510 | if (!working_state.http_post_data) { | ||
| 511 | working_state.http_post_data = ""; | ||
| 512 | } | ||
| 513 | |||
| 514 | if (!strcmp(working_state.http_method, "POST")) { | ||
| 515 | /* POST method, set payload with CURLOPT_POSTFIELDS */ | ||
| 516 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, | ||
| 517 | CURLOPT_POSTFIELDS, | ||
| 518 | working_state.http_post_data), | ||
| 519 | "CURLOPT_POSTFIELDS"); | ||
| 520 | } else if (!strcmp(working_state.http_method, "PUT")) { | ||
| 521 | handle_curl_option_return_code( | ||
| 522 | curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION, | ||
| 523 | (curl_read_callback)curlhelp_buffer_read_callback), | ||
| 524 | "CURLOPT_READFUNCTION"); | ||
| 525 | if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data, | ||
| 526 | strlen(working_state.http_post_data)) < 0) { | ||
| 527 | die(STATE_UNKNOWN, | ||
| 528 | "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); | ||
| 529 | } | ||
| 530 | result.curl_state.put_buf_initialized = true; | ||
| 531 | handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, | ||
| 532 | CURLOPT_READDATA, | ||
| 533 | (void *)result.curl_state.put_buf), | ||
| 534 | "CURLOPT_READDATA"); | ||
| 535 | handle_curl_option_return_code( | ||
| 536 | curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, | ||
| 537 | (curl_off_t)strlen(working_state.http_post_data)), | ||
| 538 | "CURLOPT_INFILESIZE"); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | /* cookie handling */ | ||
| 543 | if (config.cookie_jar_file != NULL) { | ||
| 544 | /* enable reading cookies from a file, and if the filename is an empty string, only | ||
| 545 | * enable the curl cookie engine */ | ||
| 546 | handle_curl_option_return_code( | ||
| 547 | curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), | ||
| 548 | "CURLOPT_COOKIEFILE"); | ||
| 549 | /* now enable saving cookies to a file, but only if the filename is not an empty string, | ||
| 550 | * since writing it would fail */ | ||
| 551 | if (*config.cookie_jar_file) { | ||
| 552 | handle_curl_option_return_code( | ||
| 553 | curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), | ||
| 554 | "CURLOPT_COOKIEJAR"); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | result.working_state = working_state; | ||
| 559 | |||
| 560 | return result; | ||
| 561 | } | ||
| 562 | |||
| 563 | void handle_curl_option_return_code(CURLcode res, const char *option) { | ||
| 564 | if (res != CURLE_OK) { | ||
| 565 | die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"), | ||
| 566 | option, res, curl_easy_strerror(res)); | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | char *get_header_value(const struct phr_header *headers, const size_t nof_headers, | ||
| 571 | const char *header) { | ||
| 572 | for (size_t i = 0; i < nof_headers; i++) { | ||
| 573 | if (headers[i].name != NULL && | ||
| 574 | strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { | ||
| 575 | return strndup(headers[i].value, headers[i].value_len); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | return NULL; | ||
| 579 | } | ||
| 580 | |||
| 581 | check_curl_working_state check_curl_working_state_init() { | ||
| 582 | check_curl_working_state result = { | ||
| 583 | .server_address = NULL, | ||
| 584 | .server_url = DEFAULT_SERVER_URL, | ||
| 585 | .host_name = NULL, | ||
| 586 | .http_method = NULL, | ||
| 587 | .http_post_data = NULL, | ||
| 588 | .virtualPort = 0, | ||
| 589 | .serverPort = HTTP_PORT, | ||
| 590 | .use_ssl = false, | ||
| 591 | .no_body = false, | ||
| 592 | }; | ||
| 593 | return result; | ||
| 594 | } | ||
| 595 | |||
| 596 | check_curl_config check_curl_config_init() { | ||
| 597 | check_curl_config tmp = { | ||
| 598 | .initial_config = check_curl_working_state_init(), | ||
| 599 | |||
| 600 | .curl_config = | ||
| 601 | { | ||
| 602 | .automatic_decompression = false, | ||
| 603 | .socket_timeout = DEFAULT_SOCKET_TIMEOUT, | ||
| 604 | .haproxy_protocol = false, | ||
| 605 | .sin_family = AF_UNSPEC, | ||
| 606 | .curl_http_version = CURL_HTTP_VERSION_NONE, | ||
| 607 | .http_opt_headers = NULL, | ||
| 608 | .http_opt_headers_count = 0, | ||
| 609 | .ssl_version = CURL_SSLVERSION_DEFAULT, | ||
| 610 | .client_cert = NULL, | ||
| 611 | .client_privkey = NULL, | ||
| 612 | .ca_cert = NULL, | ||
| 613 | .verify_peer_and_host = false, | ||
| 614 | .user_agent = {'\0'}, | ||
| 615 | .proxy_auth = "", | ||
| 616 | .user_auth = "", | ||
| 617 | .http_content_type = NULL, | ||
| 618 | .cookie_jar_file = NULL, | ||
| 619 | }, | ||
| 620 | .max_depth = DEFAULT_MAX_REDIRS, | ||
| 621 | .followmethod = FOLLOW_HTTP_CURL, | ||
| 622 | .followsticky = STICKY_NONE, | ||
| 623 | |||
| 624 | .maximum_age = -1, | ||
| 625 | .regexp = {}, | ||
| 626 | .compiled_regex = {}, | ||
| 627 | .state_regex = STATE_CRITICAL, | ||
| 628 | .invert_regex = false, | ||
| 629 | .check_cert = false, | ||
| 630 | .continue_after_check_cert = false, | ||
| 631 | .days_till_exp_warn = 0, | ||
| 632 | .days_till_exp_crit = 0, | ||
| 633 | .thlds = mp_thresholds_init(), | ||
| 634 | .page_length_limits = mp_range_init(), | ||
| 635 | .page_length_limits_is_set = false, | ||
| 636 | .server_expect = | ||
| 637 | { | ||
| 638 | .string = HTTP_EXPECT, | ||
| 639 | .is_present = false, | ||
| 640 | }, | ||
| 641 | .string_expect = "", | ||
| 642 | .header_expect = "", | ||
| 643 | .on_redirect_result_state = STATE_OK, | ||
| 644 | .on_redirect_dependent = false, | ||
| 645 | |||
| 646 | .show_extended_perfdata = false, | ||
| 647 | .show_body = false, | ||
| 648 | |||
| 649 | .output_format_is_set = false, | ||
| 650 | }; | ||
| 651 | |||
| 652 | snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", | ||
| 653 | "check_curl", NP_VERSION, VERSION, curl_version()); | ||
| 654 | |||
| 655 | return tmp; | ||
| 656 | } | ||
| 657 | |||
| 658 | /* TODO: is there a better way in libcurl to check for the SSL library? */ | ||
| 659 | curlhelp_ssl_library curlhelp_get_ssl_library(void) { | ||
| 660 | curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 661 | |||
| 662 | curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW); | ||
| 663 | if (version_data == NULL) { | ||
| 664 | return CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 665 | } | ||
| 666 | |||
| 667 | char *ssl_version = strdup(version_data->ssl_version); | ||
| 668 | if (ssl_version == NULL) { | ||
| 669 | return CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 670 | } | ||
| 671 | |||
| 672 | char *library = strtok(ssl_version, "/"); | ||
| 673 | if (library == NULL) { | ||
| 674 | return CURLHELP_SSL_LIBRARY_UNKNOWN; | ||
| 675 | } | ||
| 676 | |||
| 677 | if (strcmp(library, "OpenSSL") == 0) { | ||
| 678 | ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; | ||
| 679 | } else if (strcmp(library, "LibreSSL") == 0) { | ||
| 680 | ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; | ||
| 681 | } else if (strcmp(library, "GnuTLS") == 0) { | ||
| 682 | ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; | ||
| 683 | } else if (strcmp(library, "NSS") == 0) { | ||
| 684 | ssl_library = CURLHELP_SSL_LIBRARY_NSS; | ||
| 685 | } | ||
| 686 | |||
| 687 | if (verbose >= 2) { | ||
| 688 | printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, | ||
| 689 | ssl_library); | ||
| 690 | } | ||
| 691 | |||
| 692 | free(ssl_version); | ||
| 693 | |||
| 694 | return ssl_library; | ||
| 695 | } | ||
| 696 | |||
| 697 | const char *curlhelp_get_ssl_library_string(const curlhelp_ssl_library ssl_library) { | ||
| 698 | switch (ssl_library) { | ||
| 699 | case CURLHELP_SSL_LIBRARY_OPENSSL: | ||
| 700 | return "OpenSSL"; | ||
| 701 | case CURLHELP_SSL_LIBRARY_LIBRESSL: | ||
| 702 | return "LibreSSL"; | ||
| 703 | case CURLHELP_SSL_LIBRARY_GNUTLS: | ||
| 704 | return "GnuTLS"; | ||
| 705 | case CURLHELP_SSL_LIBRARY_NSS: | ||
| 706 | return "NSS"; | ||
| 707 | case CURLHELP_SSL_LIBRARY_UNKNOWN: | ||
| 708 | default: | ||
| 709 | return "unknown"; | ||
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | size_t get_content_length(const curlhelp_write_curlbuf *header_buf, | ||
| 714 | const curlhelp_write_curlbuf *body_buf) { | ||
| 715 | struct phr_header headers[255]; | ||
| 716 | size_t nof_headers = 255; | ||
| 717 | size_t msglen; | ||
| 718 | curlhelp_statusline status_line; | ||
| 719 | int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, | ||
| 720 | &status_line.http_minor, &status_line.http_code, &status_line.msg, | ||
| 721 | &msglen, headers, &nof_headers, 0); | ||
| 722 | |||
| 723 | if (res == -1) { | ||
| 724 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); | ||
| 725 | } | ||
| 726 | |||
| 727 | char *content_length_s = get_header_value(headers, nof_headers, "content-length"); | ||
| 728 | if (!content_length_s) { | ||
| 729 | return header_buf->buflen + body_buf->buflen; | ||
| 730 | } | ||
| 731 | |||
| 732 | content_length_s += strspn(content_length_s, " \t"); | ||
| 733 | size_t content_length = atoi(content_length_s); | ||
| 734 | if (content_length != body_buf->buflen) { | ||
| 735 | /* TODO: should we warn if the actual and the reported body length don't match? */ | ||
| 736 | } | ||
| 737 | |||
| 738 | if (content_length_s) { | ||
| 739 | free(content_length_s); | ||
| 740 | } | ||
| 741 | |||
| 742 | return header_buf->buflen + body_buf->buflen; | ||
| 743 | } | ||
| 744 | |||
| 745 | mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) { | ||
| 746 | struct phr_header headers[255]; | ||
| 747 | size_t nof_headers = 255; | ||
| 748 | curlhelp_statusline status_line; | ||
| 749 | size_t msglen; | ||
| 750 | int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, | ||
| 751 | &status_line.http_minor, &status_line.http_code, &status_line.msg, | ||
| 752 | &msglen, headers, &nof_headers, 0); | ||
| 753 | |||
| 754 | if (res == -1) { | ||
| 755 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); | ||
| 756 | } | ||
| 757 | |||
| 758 | char *server_date = get_header_value(headers, nof_headers, "date"); | ||
| 759 | char *document_date = get_header_value(headers, nof_headers, "last-modified"); | ||
| 760 | |||
| 761 | mp_subcheck sc_document_dates = mp_subcheck_init(); | ||
| 762 | if (!server_date || !*server_date) { | ||
| 763 | xasprintf(&sc_document_dates.output, _("Server date unknown")); | ||
| 764 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN); | ||
| 765 | } else if (!document_date || !*document_date) { | ||
| 766 | xasprintf(&sc_document_dates.output, _("Document modification date unknown, ")); | ||
| 767 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); | ||
| 768 | } else { | ||
| 769 | time_t srv_data = curl_getdate(server_date, NULL); | ||
| 770 | time_t doc_data = curl_getdate(document_date, NULL); | ||
| 771 | |||
| 772 | if (verbose >= 2) { | ||
| 773 | printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, | ||
| 774 | document_date, (int)doc_data); | ||
| 775 | } | ||
| 776 | |||
| 777 | if (srv_data <= 0) { | ||
| 778 | xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"), | ||
| 779 | server_date); | ||
| 780 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); | ||
| 781 | } else if (doc_data <= 0) { | ||
| 782 | |||
| 783 | xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"), | ||
| 784 | document_date); | ||
| 785 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); | ||
| 786 | } else if (doc_data > srv_data + 30) { | ||
| 787 | |||
| 788 | xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"), | ||
| 789 | (int)doc_data - (int)srv_data); | ||
| 790 | |||
| 791 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); | ||
| 792 | } else if (doc_data < srv_data - maximum_age) { | ||
| 793 | time_t last_modified = (srv_data - doc_data); | ||
| 794 | if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded? | ||
| 795 | xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"), | ||
| 796 | ((float)last_modified) / (60 * 60 * 24)); | ||
| 797 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); | ||
| 798 | } else { | ||
| 799 | xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), | ||
| 800 | last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); | ||
| 801 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); | ||
| 802 | } | ||
| 803 | } else { | ||
| 804 | // TODO is this the OK case? | ||
| 805 | time_t last_modified = (srv_data - doc_data); | ||
| 806 | xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), | ||
| 807 | last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); | ||
| 808 | sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK); | ||
| 809 | } | ||
| 810 | } | ||
| 811 | |||
| 812 | if (server_date) { | ||
| 813 | free(server_date); | ||
| 814 | } | ||
| 815 | if (document_date) { | ||
| 816 | free(document_date); | ||
| 817 | } | ||
| 818 | |||
| 819 | return sc_document_dates; | ||
| 820 | } | ||
| 821 | |||
| 822 | void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } | ||
| 823 | |||
| 824 | int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { | ||
| 825 | /* find last start of a new header */ | ||
| 826 | const char *start = strrstr2(buf, "\r\nHTTP/"); | ||
| 827 | if (start != NULL) { | ||
| 828 | start += 2; | ||
| 829 | buf = start; | ||
| 830 | } | ||
| 831 | |||
| 832 | // Accept either LF or CRLF as end of line for the status line | ||
| 833 | // CRLF is the standard (RFC9112), but it is recommended to accept both | ||
| 834 | size_t length_of_first_line = strcspn(buf, "\r\n"); | ||
| 835 | const char *first_line_end = &buf[length_of_first_line]; | ||
| 836 | if (first_line_end == NULL) { | ||
| 837 | return -1; | ||
| 838 | } | ||
| 839 | |||
| 840 | size_t first_line_len = (size_t)(first_line_end - buf); | ||
| 841 | status_line->first_line = (char *)calloc(first_line_len + 1, sizeof(char)); | ||
| 842 | if (status_line->first_line == NULL) { | ||
| 843 | return -1; | ||
| 844 | } | ||
| 845 | |||
| 846 | memcpy(status_line->first_line, buf, first_line_len); | ||
| 847 | status_line->first_line[first_line_len] = '\0'; | ||
| 848 | char *first_line_buf = strdup(status_line->first_line); | ||
| 849 | |||
| 850 | /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ | ||
| 851 | char *temp_string = strtok(first_line_buf, "/"); | ||
| 852 | if (temp_string == NULL) { | ||
| 853 | if (verbose > 1) { | ||
| 854 | printf("%s: no / found\n", __func__); | ||
| 855 | } | ||
| 856 | free(first_line_buf); | ||
| 857 | return -1; | ||
| 858 | } | ||
| 859 | |||
| 860 | if (strcmp(temp_string, "HTTP") != 0) { | ||
| 861 | if (verbose > 1) { | ||
| 862 | printf("%s: string 'HTTP' not found\n", __func__); | ||
| 863 | } | ||
| 864 | free(first_line_buf); | ||
| 865 | return -1; | ||
| 866 | } | ||
| 867 | |||
| 868 | // try to find a space in the remaining string? | ||
| 869 | // the space after HTTP/1.1 probably | ||
| 870 | temp_string = strtok(NULL, " "); | ||
| 871 | if (temp_string == NULL) { | ||
| 872 | if (verbose > 1) { | ||
| 873 | printf("%s: no space after protocol definition\n", __func__); | ||
| 874 | } | ||
| 875 | free(first_line_buf); | ||
| 876 | return -1; | ||
| 877 | } | ||
| 878 | |||
| 879 | char *temp_string_2; | ||
| 880 | if (strchr(temp_string, '.') != NULL) { | ||
| 881 | /* HTTP 1.x case */ | ||
| 882 | strtok(temp_string, "."); | ||
| 883 | status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); | ||
| 884 | if (*temp_string_2 != '\0') { | ||
| 885 | free(first_line_buf); | ||
| 886 | return -1; | ||
| 887 | } | ||
| 888 | strtok(NULL, " "); | ||
| 889 | status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10); | ||
| 890 | if (*temp_string_2 != '\0') { | ||
| 891 | free(first_line_buf); | ||
| 892 | return -1; | ||
| 893 | } | ||
| 894 | temp_string += 4; /* 1.x SP */ | ||
| 895 | } else { | ||
| 896 | /* HTTP 2 case */ | ||
| 897 | status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); | ||
| 898 | status_line->http_minor = 0; | ||
| 899 | temp_string += 2; /* 2 SP */ | ||
| 900 | } | ||
| 901 | |||
| 902 | /* status code: "404" or "404.1", then SP */ | ||
| 903 | temp_string = strtok(temp_string, " "); | ||
| 904 | if (temp_string == NULL) { | ||
| 905 | free(first_line_buf); | ||
| 906 | return -1; | ||
| 907 | } | ||
| 908 | if (strchr(temp_string, '.') != NULL) { | ||
| 909 | char *ppp; | ||
| 910 | ppp = strtok(temp_string, "."); | ||
| 911 | status_line->http_code = (int)strtol(ppp, &temp_string_2, 10); | ||
| 912 | if (*temp_string_2 != '\0') { | ||
| 913 | free(first_line_buf); | ||
| 914 | return -1; | ||
| 915 | } | ||
| 916 | ppp = strtok(NULL, ""); | ||
| 917 | status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10); | ||
| 918 | if (*temp_string_2 != '\0') { | ||
| 919 | free(first_line_buf); | ||
| 920 | return -1; | ||
| 921 | } | ||
| 922 | temp_string += 6; /* 400.1 SP */ | ||
| 923 | } else { | ||
| 924 | status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10); | ||
| 925 | status_line->http_subcode = -1; | ||
| 926 | if (*temp_string_2 != '\0') { | ||
| 927 | free(first_line_buf); | ||
| 928 | return -1; | ||
| 929 | } | ||
| 930 | temp_string += 4; /* 400 SP */ | ||
| 931 | } | ||
| 932 | |||
| 933 | /* Human readable message: "Not Found" CRLF */ | ||
| 934 | |||
| 935 | temp_string = strtok(temp_string, ""); | ||
| 936 | if (temp_string == NULL) { | ||
| 937 | status_line->msg = ""; | ||
| 938 | return 0; | ||
| 939 | } | ||
| 940 | status_line->msg = status_line->first_line + (temp_string - first_line_buf); | ||
| 941 | free(first_line_buf); | ||
| 942 | |||
| 943 | return 0; | ||
| 944 | } | ||
| 945 | |||
| 946 | /* TODO: where to put this, it's actually part of sstrings2 (logically)? | ||
| 947 | */ | ||
| 948 | const char *strrstr2(const char *haystack, const char *needle) { | ||
| 949 | if (haystack == NULL || needle == NULL) { | ||
| 950 | return NULL; | ||
| 951 | } | ||
| 952 | |||
| 953 | if (haystack[0] == '\0' || needle[0] == '\0') { | ||
| 954 | return NULL; | ||
| 955 | } | ||
| 956 | |||
| 957 | int counter = 0; | ||
| 958 | const char *prev_pos = NULL; | ||
| 959 | const char *pos = haystack; | ||
| 960 | size_t len = strlen(needle); | ||
| 961 | for (;;) { | ||
| 962 | pos = strstr(pos, needle); | ||
| 963 | if (pos == NULL) { | ||
| 964 | if (counter == 0) { | ||
| 965 | return NULL; | ||
| 966 | } | ||
| 967 | return prev_pos; | ||
| 968 | } | ||
| 969 | counter++; | ||
| 970 | prev_pos = pos; | ||
| 971 | pos += len; | ||
| 972 | if (*pos == '\0') { | ||
| 973 | return prev_pos; | ||
| 974 | } | ||
| 975 | } | ||
| 976 | } | ||
| 977 | |||
| 978 | void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { | ||
| 979 | free(buf->buf); | ||
| 980 | buf->buf = NULL; | ||
| 981 | } | ||
| 982 | |||
| 983 | void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { | ||
| 984 | free(buf->buf); | ||
| 985 | buf->buf = NULL; | ||
| 986 | } | ||
| 987 | |||
| 988 | int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char *data, size_t datalen) { | ||
| 989 | if ((*buf = calloc(1, sizeof(curlhelp_read_curlbuf))) == NULL) { | ||
| 990 | return 1; | ||
| 991 | } | ||
| 992 | |||
| 993 | (*buf)->buflen = datalen; | ||
| 994 | (*buf)->buf = (char *)calloc((*buf)->buflen, sizeof(char)); | ||
| 995 | if ((*buf)->buf == NULL) { | ||
| 996 | return -1; | ||
| 997 | } | ||
| 998 | memcpy((*buf)->buf, data, datalen); | ||
| 999 | (*buf)->pos = 0; | ||
| 1000 | return 0; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { | ||
| 1004 | curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; | ||
| 1005 | |||
| 1006 | size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos); | ||
| 1007 | |||
| 1008 | memcpy(buffer, buf->buf + buf->pos, minimalSize); | ||
| 1009 | buf->pos += minimalSize; | ||
| 1010 | |||
| 1011 | return minimalSize; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf) { | ||
| 1015 | if ((*buf = calloc(1, sizeof(curlhelp_write_curlbuf))) == NULL) { | ||
| 1016 | return 1; | ||
| 1017 | } | ||
| 1018 | (*buf)->bufsize = DEFAULT_BUFFER_SIZE * sizeof(char); | ||
| 1019 | (*buf)->buflen = 0; | ||
| 1020 | (*buf)->buf = (char *)calloc((*buf)->bufsize, sizeof(char)); | ||
| 1021 | if ((*buf)->buf == NULL) { | ||
| 1022 | return -1; | ||
| 1023 | } | ||
| 1024 | return 0; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) { | ||
| 1028 | curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; | ||
| 1029 | |||
| 1030 | while (buf->bufsize < buf->buflen + size * nmemb + 1) { | ||
| 1031 | buf->bufsize = buf->bufsize * 2; | ||
| 1032 | buf->buf = (char *)realloc(buf->buf, buf->bufsize); | ||
| 1033 | if (buf->buf == NULL) { | ||
| 1034 | fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); | ||
| 1035 | return 0; | ||
| 1036 | } | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | memcpy(buf->buf + buf->buflen, buffer, size * nmemb); | ||
| 1040 | buf->buflen += size * nmemb; | ||
| 1041 | buf->buf[buf->buflen] = '\0'; | ||
| 1042 | |||
| 1043 | return size * nmemb; | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | void cleanup(check_curl_global_state global_state) { | ||
| 1047 | if (global_state.status_line_initialized) { | ||
| 1048 | curlhelp_free_statusline(global_state.status_line); | ||
| 1049 | } | ||
| 1050 | global_state.status_line_initialized = false; | ||
| 1051 | |||
| 1052 | if (global_state.curl_easy_initialized) { | ||
| 1053 | curl_easy_cleanup(global_state.curl); | ||
| 1054 | } | ||
| 1055 | global_state.curl_easy_initialized = false; | ||
| 1056 | |||
| 1057 | if (global_state.curl_global_initialized) { | ||
| 1058 | curl_global_cleanup(); | ||
| 1059 | } | ||
| 1060 | global_state.curl_global_initialized = false; | ||
| 1061 | |||
| 1062 | if (global_state.body_buf_initialized) { | ||
| 1063 | curlhelp_freewritebuffer(global_state.body_buf); | ||
| 1064 | } | ||
| 1065 | global_state.body_buf_initialized = false; | ||
| 1066 | |||
| 1067 | if (global_state.header_buf_initialized) { | ||
| 1068 | curlhelp_freewritebuffer(global_state.header_buf); | ||
| 1069 | } | ||
| 1070 | global_state.header_buf_initialized = false; | ||
| 1071 | |||
| 1072 | if (global_state.put_buf_initialized) { | ||
| 1073 | curlhelp_freereadbuffer(global_state.put_buf); | ||
| 1074 | } | ||
| 1075 | global_state.put_buf_initialized = false; | ||
| 1076 | |||
| 1077 | if (global_state.header_list) { | ||
| 1078 | curl_slist_free_all(global_state.header_list); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | if (global_state.host) { | ||
| 1082 | curl_slist_free_all(global_state.host); | ||
| 1083 | } | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { | ||
| 1087 | struct addrinfo hints = { | ||
| 1088 | .ai_family = addr_family, | ||
| 1089 | .ai_socktype = SOCK_STREAM, | ||
| 1090 | .ai_flags = AI_CANONNAME, | ||
| 1091 | }; | ||
| 1092 | |||
| 1093 | struct addrinfo *result; | ||
| 1094 | int errcode = getaddrinfo(host, NULL, &hints, &result); | ||
| 1095 | if (errcode != 0) { | ||
| 1096 | return errcode; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | strcpy(buf, ""); | ||
| 1100 | struct addrinfo *res = result; | ||
| 1101 | |||
| 1102 | size_t buflen_remaining = buflen - 1; | ||
| 1103 | size_t addrstr_len; | ||
| 1104 | char addrstr[100]; | ||
| 1105 | void *ptr = {0}; | ||
| 1106 | while (res) { | ||
| 1107 | switch (res->ai_family) { | ||
| 1108 | case AF_INET: | ||
| 1109 | ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; | ||
| 1110 | break; | ||
| 1111 | case AF_INET6: | ||
| 1112 | ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; | ||
| 1113 | break; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | inet_ntop(res->ai_family, ptr, addrstr, 100); | ||
| 1117 | if (verbose >= 1) { | ||
| 1118 | printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, | ||
| 1119 | addrstr); | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | // Append all IPs to buf as a comma-separated string | ||
| 1123 | addrstr_len = strlen(addrstr); | ||
| 1124 | if (buflen_remaining > addrstr_len + 1) { | ||
| 1125 | if (buf[0] != '\0') { | ||
| 1126 | strncat(buf, ",", buflen_remaining); | ||
| 1127 | buflen_remaining -= 1; | ||
| 1128 | } | ||
| 1129 | strncat(buf, addrstr, buflen_remaining); | ||
| 1130 | buflen_remaining -= addrstr_len; | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | res = res->ai_next; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | freeaddrinfo(result); | ||
| 1137 | |||
| 1138 | return 0; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | /* Checks if the server 'reply' is one of the expected 'statuscodes' */ | ||
| 1142 | bool expected_statuscode(const char *reply, const char *statuscodes) { | ||
| 1143 | char *expected; | ||
| 1144 | |||
| 1145 | if ((expected = strdup(statuscodes)) == NULL) { | ||
| 1146 | die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | bool result = false; | ||
| 1150 | for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { | ||
| 1151 | if (strstr(reply, code) != NULL) { | ||
| 1152 | result = true; | ||
| 1153 | break; | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | free(expected); | ||
| 1158 | return result; | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | /* returns a string "HTTP/1.x" or "HTTP/2" */ | ||
| 1162 | char *string_statuscode(int major, int minor) { | ||
| 1163 | static char buf[10]; | ||
| 1164 | |||
| 1165 | switch (major) { | ||
| 1166 | case 1: | ||
| 1167 | snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); | ||
| 1168 | break; | ||
| 1169 | case 2: | ||
| 1170 | case 3: | ||
| 1171 | snprintf(buf, sizeof(buf), "HTTP/%d", major); | ||
| 1172 | break; | ||
| 1173 | default: | ||
| 1174 | /* assuming here HTTP/N with N>=4 */ | ||
| 1175 | snprintf(buf, sizeof(buf), "HTTP/%d", major); | ||
| 1176 | break; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | return buf; | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | /* check whether a file exists */ | ||
| 1183 | void test_file(char *path) { | ||
| 1184 | if (access(path, R_OK) == 0) { | ||
| 1185 | return; | ||
| 1186 | } | ||
| 1187 | usage2(_("file does not exist or is not readable"), path); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, | ||
| 1191 | int days_till_exp_crit); | ||
| 1192 | |||
| 1193 | mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, | ||
| 1194 | int crit_days_till_exp) { | ||
| 1195 | mp_subcheck sc_cert_result = mp_subcheck_init(); | ||
| 1196 | sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK); | ||
| 1197 | |||
| 1198 | #ifdef LIBCURL_FEATURE_SSL | ||
| 1199 | if (is_openssl_callback) { | ||
| 1200 | # ifdef USE_OPENSSL | ||
| 1201 | /* check certificate with OpenSSL functions, curl has been built against OpenSSL | ||
| 1202 | * and we actually have OpenSSL in the monitoring tools | ||
| 1203 | */ | ||
| 1204 | return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); | ||
| 1205 | # else /* USE_OPENSSL */ | ||
| 1206 | xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " | ||
| 1207 | "callback used and not linked against OpenSSL\n"); | ||
| 1208 | mp_set_subcheck_state(result, STATE_CRITICAL); | ||
| 1209 | # endif /* USE_OPENSSL */ | ||
| 1210 | } else { | ||
| 1211 | struct curl_slist *slist; | ||
| 1212 | |||
| 1213 | cert_ptr_union cert_ptr = {0}; | ||
| 1214 | cert_ptr.to_info = NULL; | ||
| 1215 | CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); | ||
| 1216 | if (!res && cert_ptr.to_info) { | ||
| 1217 | # ifdef USE_OPENSSL | ||
| 1218 | /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert | ||
| 1219 | * parsing We only check the first certificate and assume it's the one of | ||
| 1220 | * the server | ||
| 1221 | */ | ||
| 1222 | char *raw_cert = NULL; | ||
| 1223 | bool got_first_cert = false; | ||
| 1224 | for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { | ||
| 1225 | if (got_first_cert) { | ||
| 1226 | break; | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { | ||
| 1230 | if (verbose >= 2) { | ||
| 1231 | printf("%d ** %s\n", i, slist->data); | ||
| 1232 | } | ||
| 1233 | if (strncmp(slist->data, "Cert:", 5) == 0) { | ||
| 1234 | raw_cert = &slist->data[5]; | ||
| 1235 | got_first_cert = true; | ||
| 1236 | break; | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | if (!raw_cert) { | ||
| 1242 | |||
| 1243 | xasprintf(&sc_cert_result.output, | ||
| 1244 | _("Cannot retrieve certificates from CERTINFO information - " | ||
| 1245 | "certificate data was empty")); | ||
| 1246 | sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); | ||
| 1247 | return sc_cert_result; | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | BIO *cert_BIO = BIO_new(BIO_s_mem()); | ||
| 1251 | BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert)); | ||
| 1252 | |||
| 1253 | cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); | ||
| 1254 | if (!cert) { | ||
| 1255 | xasprintf(&sc_cert_result.output, | ||
| 1256 | _("Cannot read certificate from CERTINFO information - BIO error")); | ||
| 1257 | sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); | ||
| 1258 | return sc_cert_result; | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | BIO_free(cert_BIO); | ||
| 1262 | return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); | ||
| 1263 | # else /* USE_OPENSSL */ | ||
| 1264 | /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our | ||
| 1265 | * disposal, so we use the libcurl CURLINFO data | ||
| 1266 | */ | ||
| 1267 | return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, | ||
| 1268 | days_till_exp_crit); | ||
| 1269 | # endif /* USE_OPENSSL */ | ||
| 1270 | } else { | ||
| 1271 | xasprintf(&sc_cert_result.output, | ||
| 1272 | _("Cannot retrieve certificates - cURL returned %d - %s"), res, | ||
| 1273 | curl_easy_strerror(res)); | ||
| 1274 | mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); | ||
| 1275 | } | ||
| 1276 | } | ||
| 1277 | #endif /* LIBCURL_FEATURE_SSL */ | ||
| 1278 | |||
| 1279 | return sc_cert_result; | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | char *fmt_url(check_curl_working_state workingState) { | ||
| 1283 | char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char)); | ||
| 1284 | if (url == NULL) { | ||
| 1285 | die(STATE_UNKNOWN, "memory allocation failed"); | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http", | ||
| 1289 | (workingState.use_ssl & (workingState.host_name != NULL)) | ||
| 1290 | ? workingState.host_name | ||
| 1291 | : workingState.server_address, | ||
| 1292 | workingState.serverPort, workingState.server_url); | ||
| 1293 | |||
| 1294 | return url; | ||
| 1295 | } | ||
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h new file mode 100644 index 00000000..e7b80f7e --- /dev/null +++ b/plugins/check_curl.d/check_curl_helpers.h | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | #include "./config.h" | ||
| 2 | #include <curl/curl.h> | ||
| 3 | #include "../picohttpparser/picohttpparser.h" | ||
| 4 | #include "output.h" | ||
| 5 | |||
| 6 | #if defined(HAVE_SSL) && defined(USE_OPENSSL) | ||
| 7 | # include <openssl/opensslv.h> | ||
| 8 | #endif | ||
| 9 | |||
| 10 | enum { | ||
| 11 | MAX_IPV4_HOSTLENGTH = 255, | ||
| 12 | }; | ||
| 13 | |||
| 14 | /* for buffers for header and body */ | ||
| 15 | typedef struct { | ||
| 16 | size_t buflen; | ||
| 17 | size_t bufsize; | ||
| 18 | char *buf; | ||
| 19 | } curlhelp_write_curlbuf; | ||
| 20 | |||
| 21 | /* for buffering the data sent in PUT */ | ||
| 22 | typedef struct { | ||
| 23 | size_t buflen; | ||
| 24 | off_t pos; | ||
| 25 | char *buf; | ||
| 26 | } curlhelp_read_curlbuf; | ||
| 27 | |||
| 28 | /* for parsing the HTTP status line */ | ||
| 29 | typedef struct { | ||
| 30 | int http_major; /* major version of the protocol, always 1 (HTTP/0.9 | ||
| 31 | * never reached the big internet most likely) */ | ||
| 32 | int http_minor; /* minor version of the protocol, usually 0 or 1 */ | ||
| 33 | int http_code; /* HTTP return code as in RFC 2145 */ | ||
| 34 | int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see | ||
| 35 | * http://support.microsoft.com/kb/318380/en-us */ | ||
| 36 | const char *msg; /* the human readable message */ | ||
| 37 | char *first_line; /* a copy of the first line */ | ||
| 38 | } curlhelp_statusline; | ||
| 39 | |||
| 40 | typedef struct { | ||
| 41 | bool curl_global_initialized; | ||
| 42 | bool curl_easy_initialized; | ||
| 43 | |||
| 44 | bool body_buf_initialized; | ||
| 45 | curlhelp_write_curlbuf *body_buf; | ||
| 46 | |||
| 47 | bool header_buf_initialized; | ||
| 48 | curlhelp_write_curlbuf *header_buf; | ||
| 49 | |||
| 50 | bool status_line_initialized; | ||
| 51 | curlhelp_statusline *status_line; | ||
| 52 | |||
| 53 | bool put_buf_initialized; | ||
| 54 | curlhelp_read_curlbuf *put_buf; | ||
| 55 | |||
| 56 | CURL *curl; | ||
| 57 | |||
| 58 | struct curl_slist *header_list; | ||
| 59 | struct curl_slist *host; | ||
| 60 | } check_curl_global_state; | ||
| 61 | |||
| 62 | /* to know the underlying SSL library used by libcurl */ | ||
| 63 | typedef enum curlhelp_ssl_library { | ||
| 64 | CURLHELP_SSL_LIBRARY_UNKNOWN, | ||
| 65 | CURLHELP_SSL_LIBRARY_OPENSSL, | ||
| 66 | CURLHELP_SSL_LIBRARY_LIBRESSL, | ||
| 67 | CURLHELP_SSL_LIBRARY_GNUTLS, | ||
| 68 | CURLHELP_SSL_LIBRARY_NSS | ||
| 69 | } curlhelp_ssl_library; | ||
| 70 | |||
| 71 | #define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) | ||
| 72 | |||
| 73 | typedef struct { | ||
| 74 | int errorcode; | ||
| 75 | check_curl_global_state curl_state; | ||
| 76 | check_curl_working_state working_state; | ||
| 77 | } check_curl_configure_curl_wrapper; | ||
| 78 | |||
| 79 | check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_curl_config config, | ||
| 80 | check_curl_working_state working_state, | ||
| 81 | bool check_cert, | ||
| 82 | bool on_redirect_dependent, | ||
| 83 | int follow_method, int max_depth); | ||
| 84 | |||
| 85 | void handle_curl_option_return_code(CURLcode res, const char *option); | ||
| 86 | |||
| 87 | int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf); | ||
| 88 | size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, | ||
| 89 | void * /*stream*/); | ||
| 90 | void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); | ||
| 91 | |||
| 92 | int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char * /*data*/, size_t /*datalen*/); | ||
| 93 | size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, | ||
| 94 | void * /*stream*/); | ||
| 95 | void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); | ||
| 96 | |||
| 97 | curlhelp_ssl_library curlhelp_get_ssl_library(void); | ||
| 98 | const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); | ||
| 99 | |||
| 100 | typedef union { | ||
| 101 | struct curl_slist *to_info; | ||
| 102 | struct curl_certinfo *to_certinfo; | ||
| 103 | } cert_ptr_union; | ||
| 104 | int net_noopenssl_check_certificate(cert_ptr_union *, int, int); | ||
| 105 | |||
| 106 | int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); | ||
| 107 | void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); | ||
| 108 | |||
| 109 | char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); | ||
| 110 | mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, | ||
| 111 | int /*maximum_age*/); | ||
| 112 | size_t get_content_length(const curlhelp_write_curlbuf *header_buf, | ||
| 113 | const curlhelp_write_curlbuf *body_buf); | ||
| 114 | int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); | ||
| 115 | CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm); | ||
| 116 | |||
| 117 | #define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN | ||
| 118 | const char *strrstr2(const char *haystack, const char *needle); | ||
| 119 | |||
| 120 | void cleanup(check_curl_global_state global_state); | ||
| 121 | |||
| 122 | bool expected_statuscode(const char *reply, const char *statuscodes); | ||
| 123 | char *string_statuscode(int major, int minor); | ||
| 124 | |||
| 125 | void test_file(char *path); | ||
| 126 | mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, | ||
| 127 | int crit_days_till_exp); | ||
| 128 | char *fmt_url(check_curl_working_state workingState); | ||
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h new file mode 100644 index 00000000..f51b2ee9 --- /dev/null +++ b/plugins/check_curl.d/config.h | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "../common.h" | ||
| 5 | #include "../../lib/states.h" | ||
| 6 | #include "../../lib/thresholds.h" | ||
| 7 | #include <stddef.h> | ||
| 8 | #include <string.h> | ||
| 9 | #include <sys/socket.h> | ||
| 10 | #include "curl/curl.h" | ||
| 11 | #include "perfdata.h" | ||
| 12 | #include "regex.h" | ||
| 13 | |||
| 14 | enum { | ||
| 15 | MAX_RE_SIZE = 1024, | ||
| 16 | HTTP_PORT = 80, | ||
| 17 | HTTPS_PORT = 443, | ||
| 18 | MAX_PORT = 65535, | ||
| 19 | DEFAULT_MAX_REDIRS = 15 | ||
| 20 | }; | ||
| 21 | |||
| 22 | enum { | ||
| 23 | FOLLOW_HTTP_CURL = 0, | ||
| 24 | FOLLOW_LIBCURL = 1 | ||
| 25 | }; | ||
| 26 | |||
| 27 | enum { | ||
| 28 | STICKY_NONE = 0, | ||
| 29 | STICKY_HOST = 1, | ||
| 30 | STICKY_PORT = 2 | ||
| 31 | }; | ||
| 32 | |||
| 33 | #define HTTP_EXPECT "HTTP/" | ||
| 34 | #define DEFAULT_BUFFER_SIZE 2048 | ||
| 35 | #define DEFAULT_SERVER_URL "/" | ||
| 36 | |||
| 37 | typedef struct { | ||
| 38 | char *server_address; | ||
| 39 | char *server_url; | ||
| 40 | char *host_name; | ||
| 41 | |||
| 42 | char *http_method; | ||
| 43 | |||
| 44 | char *http_post_data; | ||
| 45 | |||
| 46 | unsigned short virtualPort; | ||
| 47 | unsigned short serverPort; | ||
| 48 | |||
| 49 | bool use_ssl; | ||
| 50 | bool no_body; | ||
| 51 | } check_curl_working_state; | ||
| 52 | |||
| 53 | check_curl_working_state check_curl_working_state_init(); | ||
| 54 | |||
| 55 | typedef struct { | ||
| 56 | bool automatic_decompression; | ||
| 57 | bool haproxy_protocol; | ||
| 58 | long socket_timeout; | ||
| 59 | sa_family_t sin_family; | ||
| 60 | int curl_http_version; | ||
| 61 | char **http_opt_headers; | ||
| 62 | size_t http_opt_headers_count; | ||
| 63 | int ssl_version; | ||
| 64 | char *client_cert; | ||
| 65 | char *client_privkey; | ||
| 66 | char *ca_cert; | ||
| 67 | bool verify_peer_and_host; | ||
| 68 | char user_agent[DEFAULT_BUFFER_SIZE]; | ||
| 69 | char proxy_auth[MAX_INPUT_BUFFER]; | ||
| 70 | char user_auth[MAX_INPUT_BUFFER]; | ||
| 71 | char *http_content_type; | ||
| 72 | char *cookie_jar_file; | ||
| 73 | } check_curl_static_curl_config; | ||
| 74 | |||
| 75 | typedef struct { | ||
| 76 | check_curl_working_state initial_config; | ||
| 77 | |||
| 78 | check_curl_static_curl_config curl_config; | ||
| 79 | int max_depth; | ||
| 80 | int followmethod; | ||
| 81 | int followsticky; | ||
| 82 | |||
| 83 | int maximum_age; | ||
| 84 | |||
| 85 | // the original regex string from the command line | ||
| 86 | char regexp[MAX_RE_SIZE]; | ||
| 87 | |||
| 88 | // the compiled regex for usage later | ||
| 89 | regex_t compiled_regex; | ||
| 90 | |||
| 91 | mp_state_enum state_regex; | ||
| 92 | bool invert_regex; | ||
| 93 | bool check_cert; | ||
| 94 | bool continue_after_check_cert; | ||
| 95 | int days_till_exp_warn; | ||
| 96 | int days_till_exp_crit; | ||
| 97 | mp_thresholds thlds; | ||
| 98 | mp_range page_length_limits; | ||
| 99 | bool page_length_limits_is_set; | ||
| 100 | struct { | ||
| 101 | char string[MAX_INPUT_BUFFER]; | ||
| 102 | bool is_present; | ||
| 103 | } server_expect; | ||
| 104 | char string_expect[MAX_INPUT_BUFFER]; | ||
| 105 | char header_expect[MAX_INPUT_BUFFER]; | ||
| 106 | mp_state_enum on_redirect_result_state; | ||
| 107 | bool on_redirect_dependent; | ||
| 108 | |||
| 109 | bool show_extended_perfdata; | ||
| 110 | bool show_body; | ||
| 111 | |||
| 112 | bool output_format_is_set; | ||
| 113 | mp_output_format output_format; | ||
| 114 | } check_curl_config; | ||
| 115 | |||
| 116 | check_curl_config check_curl_config_init(); | ||
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 96575672..81d92952 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c | |||
| @@ -33,6 +33,12 @@ const char *progname = "check_dbi"; | |||
| 33 | const char *copyright = "2011-2024"; | 33 | const char *copyright = "2011-2024"; |
| 34 | const char *email = "devel@monitoring-plugins.org"; | 34 | const char *email = "devel@monitoring-plugins.org"; |
| 35 | 35 | ||
| 36 | #include "../lib/monitoringplug.h" | ||
| 37 | #include "thresholds.h" | ||
| 38 | #include "perfdata.h" | ||
| 39 | #include "output.h" | ||
| 40 | #include "states.h" | ||
| 41 | #include "check_dbi.d/config.h" | ||
| 36 | #include "common.h" | 42 | #include "common.h" |
| 37 | #include "utils.h" | 43 | #include "utils.h" |
| 38 | #include "utils_cmd.h" | 44 | #include "utils_cmd.h" |
| @@ -53,74 +59,33 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 53 | 59 | ||
| 54 | #include <stdarg.h> | 60 | #include <stdarg.h> |
| 55 | 61 | ||
| 56 | typedef enum { | ||
| 57 | METRIC_CONN_TIME, | ||
| 58 | METRIC_SERVER_VERSION, | ||
| 59 | METRIC_QUERY_RESULT, | ||
| 60 | METRIC_QUERY_TIME, | ||
| 61 | } np_dbi_metric_t; | ||
| 62 | |||
| 63 | typedef enum { | ||
| 64 | TYPE_NUMERIC, | ||
| 65 | TYPE_STRING, | ||
| 66 | } np_dbi_type_t; | ||
| 67 | |||
| 68 | typedef struct { | ||
| 69 | char *key; | ||
| 70 | char *value; | ||
| 71 | } driver_option_t; | ||
| 72 | |||
| 73 | static char *host = NULL; | ||
| 74 | static int verbose = 0; | 62 | static int verbose = 0; |
| 75 | 63 | ||
| 76 | static char *warning_range = NULL; | 64 | typedef struct { |
| 77 | static char *critical_range = NULL; | 65 | int errorcode; |
| 78 | static thresholds *dbi_thresholds = NULL; | 66 | check_dbi_config config; |
| 79 | 67 | } check_dbi_config_wrapper; | |
| 80 | static char *expect = NULL; | ||
| 81 | |||
| 82 | static regex_t expect_re; | ||
| 83 | static char *expect_re_str = NULL; | ||
| 84 | static int expect_re_cflags = 0; | ||
| 85 | |||
| 86 | static np_dbi_metric_t metric = METRIC_QUERY_RESULT; | ||
| 87 | static np_dbi_type_t type = TYPE_NUMERIC; | ||
| 88 | |||
| 89 | static char *np_dbi_driver = NULL; | ||
| 90 | static driver_option_t *np_dbi_options = NULL; | ||
| 91 | static int np_dbi_options_num = 0; | ||
| 92 | static char *np_dbi_database = NULL; | ||
| 93 | static char *np_dbi_query = NULL; | ||
| 94 | 68 | ||
| 95 | static int process_arguments(int, char **); | 69 | static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 96 | static int validate_arguments(void); | ||
| 97 | void print_usage(void); | 70 | void print_usage(void); |
| 98 | static void print_help(void); | 71 | static void print_help(void); |
| 99 | 72 | ||
| 100 | static double timediff(struct timeval, struct timeval); | 73 | static double timediff(struct timeval /*start*/, struct timeval /*end*/); |
| 101 | 74 | ||
| 102 | static void np_dbi_print_error(dbi_conn, char *, ...); | 75 | static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); |
| 103 | 76 | ||
| 104 | static int do_query(dbi_conn, const char **, double *, double *); | 77 | typedef struct { |
| 78 | char *result_string; | ||
| 79 | double result_number; | ||
| 80 | double query_duration; | ||
| 81 | int error_code; | ||
| 82 | const char *error_string; | ||
| 83 | mp_state_enum query_processing_status; | ||
| 84 | } do_query_result; | ||
| 85 | static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type, | ||
| 86 | char *query); | ||
| 105 | 87 | ||
| 106 | int main(int argc, char **argv) { | 88 | int main(int argc, char **argv) { |
| 107 | int status = STATE_UNKNOWN; | ||
| 108 | |||
| 109 | dbi_driver driver; | ||
| 110 | dbi_conn conn; | ||
| 111 | |||
| 112 | unsigned int server_version; | ||
| 113 | |||
| 114 | struct timeval start_timeval; | ||
| 115 | struct timeval end_timeval; | ||
| 116 | double conn_time = 0.0; | ||
| 117 | double query_time = 0.0; | ||
| 118 | |||
| 119 | const char *query_val_str = NULL; | ||
| 120 | double query_val = 0.0; | ||
| 121 | |||
| 122 | int i; | ||
| 123 | |||
| 124 | setlocale(LC_ALL, ""); | 89 | setlocale(LC_ALL, ""); |
| 125 | bindtextdomain(PACKAGE, LOCALEDIR); | 90 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 126 | textdomain(PACKAGE); | 91 | textdomain(PACKAGE); |
| @@ -128,8 +93,17 @@ int main(int argc, char **argv) { | |||
| 128 | /* Parse extra opts if any */ | 93 | /* Parse extra opts if any */ |
| 129 | argv = np_extra_opts(&argc, argv, progname); | 94 | argv = np_extra_opts(&argc, argv, progname); |
| 130 | 95 | ||
| 131 | if (process_arguments(argc, argv) == ERROR) | 96 | check_dbi_config_wrapper tmp = process_arguments(argc, argv); |
| 97 | |||
| 98 | if (tmp.errorcode == ERROR) { | ||
| 132 | usage4(_("Could not parse arguments")); | 99 | usage4(_("Could not parse arguments")); |
| 100 | } | ||
| 101 | |||
| 102 | const check_dbi_config config = tmp.config; | ||
| 103 | |||
| 104 | if (config.output_format_is_set) { | ||
| 105 | mp_set_format(config.output_format); | ||
| 106 | } | ||
| 133 | 107 | ||
| 134 | /* Set signal handling and alarm */ | 108 | /* Set signal handling and alarm */ |
| 135 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 109 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| @@ -137,69 +111,82 @@ int main(int argc, char **argv) { | |||
| 137 | } | 111 | } |
| 138 | alarm(timeout_interval); | 112 | alarm(timeout_interval); |
| 139 | 113 | ||
| 140 | if (verbose > 2) | 114 | if (verbose > 2) { |
| 141 | printf("Initializing DBI\n"); | 115 | printf("Initializing DBI\n"); |
| 116 | } | ||
| 142 | 117 | ||
| 143 | dbi_inst *instance_p = {0}; | 118 | dbi_inst instance_p = NULL; |
| 144 | 119 | if (dbi_initialize_r(NULL, &instance_p) < 0) { | |
| 145 | if (dbi_initialize_r(NULL, instance_p) < 0) { | 120 | printf("failed to initialize DBI; possibly you don't have any drivers installed.\n"); |
| 146 | printf("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); | 121 | exit(STATE_UNKNOWN); |
| 147 | return STATE_UNKNOWN; | ||
| 148 | } | 122 | } |
| 149 | 123 | ||
| 124 | // Try to prevent libdbi from printing stuff on stderr | ||
| 125 | // Who thought that would be a good idea anyway? | ||
| 126 | dbi_set_verbosity_r(0, instance_p); | ||
| 127 | |||
| 150 | if (instance_p == NULL) { | 128 | if (instance_p == NULL) { |
| 151 | printf("UNKNOWN - failed to initialize DBI.\n"); | 129 | printf("failed to initialize DBI.\n"); |
| 152 | return STATE_UNKNOWN; | 130 | exit(STATE_UNKNOWN); |
| 153 | } | 131 | } |
| 154 | 132 | ||
| 155 | if (verbose) | 133 | if (verbose) { |
| 156 | printf("Opening DBI driver '%s'\n", np_dbi_driver); | 134 | printf("Opening DBI driver '%s'\n", config.dbi_driver); |
| 135 | } | ||
| 157 | 136 | ||
| 158 | driver = dbi_driver_open_r(np_dbi_driver, instance_p); | 137 | dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p); |
| 159 | if (!driver) { | 138 | if (!driver) { |
| 160 | printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", np_dbi_driver); | 139 | printf("failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver); |
| 161 | 140 | ||
| 162 | printf("Known drivers:\n"); | 141 | printf("Known drivers:\n"); |
| 163 | for (driver = dbi_driver_list_r(NULL, instance_p); driver; driver = dbi_driver_list_r(driver, instance_p)) { | 142 | for (driver = dbi_driver_list_r(NULL, instance_p); driver; |
| 143 | driver = dbi_driver_list_r(driver, instance_p)) { | ||
| 164 | printf(" - %s\n", dbi_driver_get_name(driver)); | 144 | printf(" - %s\n", dbi_driver_get_name(driver)); |
| 165 | } | 145 | } |
| 166 | return STATE_UNKNOWN; | 146 | exit(STATE_UNKNOWN); |
| 167 | } | 147 | } |
| 168 | 148 | ||
| 169 | /* make a connection to the database */ | 149 | /* make a connection to the database */ |
| 150 | struct timeval start_timeval; | ||
| 170 | gettimeofday(&start_timeval, NULL); | 151 | gettimeofday(&start_timeval, NULL); |
| 171 | 152 | ||
| 172 | conn = dbi_conn_open(driver); | 153 | dbi_conn conn = dbi_conn_open(driver); |
| 173 | if (!conn) { | 154 | if (!conn) { |
| 174 | printf("UNKNOWN - failed top open connection object.\n"); | 155 | printf("UNKNOWN - failed top open connection object.\n"); |
| 175 | dbi_conn_close(conn); | 156 | dbi_conn_close(conn); |
| 176 | return STATE_UNKNOWN; | 157 | exit(STATE_UNKNOWN); |
| 177 | } | 158 | } |
| 178 | 159 | ||
| 179 | for (i = 0; i < np_dbi_options_num; ++i) { | 160 | for (size_t i = 0; i < config.dbi_options_num; ++i) { |
| 180 | const char *opt; | 161 | const char *opt; |
| 181 | 162 | ||
| 182 | if (verbose > 1) | 163 | if (verbose > 1) { |
| 183 | printf("Setting DBI driver option '%s' to '%s'\n", np_dbi_options[i].key, np_dbi_options[i].value); | 164 | printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key, |
| 165 | config.dbi_options[i].value); | ||
| 166 | } | ||
| 184 | 167 | ||
| 185 | if (!dbi_conn_set_option(conn, np_dbi_options[i].key, np_dbi_options[i].value)) | 168 | if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { |
| 186 | continue; | 169 | continue; |
| 187 | /* else: status != 0 */ | 170 | } |
| 188 | 171 | ||
| 189 | np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", np_dbi_options[i].key, np_dbi_options[i].value); | 172 | // Failing to set option |
| 173 | np_dbi_print_error(conn, "failed to set option '%s' to '%s'", config.dbi_options[i].key, | ||
| 174 | config.dbi_options[i].value); | ||
| 190 | printf("Known driver options:\n"); | 175 | printf("Known driver options:\n"); |
| 191 | 176 | ||
| 192 | for (opt = dbi_conn_get_option_list(conn, NULL); opt; opt = dbi_conn_get_option_list(conn, opt)) { | 177 | for (opt = dbi_conn_get_option_list(conn, NULL); opt; |
| 178 | opt = dbi_conn_get_option_list(conn, opt)) { | ||
| 193 | printf(" - %s\n", opt); | 179 | printf(" - %s\n", opt); |
| 194 | } | 180 | } |
| 195 | dbi_conn_close(conn); | 181 | dbi_conn_close(conn); |
| 196 | return STATE_UNKNOWN; | 182 | exit(STATE_UNKNOWN); |
| 197 | } | 183 | } |
| 198 | 184 | ||
| 199 | if (host) { | 185 | if (config.host) { |
| 200 | if (verbose > 1) | 186 | if (verbose > 1) { |
| 201 | printf("Setting DBI driver option 'host' to '%s'\n", host); | 187 | printf("Setting DBI driver option 'host' to '%s'\n", config.host); |
| 202 | dbi_conn_set_option(conn, "host", host); | 188 | } |
| 189 | dbi_conn_set_option(conn, "host", config.host); | ||
| 203 | } | 190 | } |
| 204 | 191 | ||
| 205 | if (verbose) { | 192 | if (verbose) { |
| @@ -209,130 +196,245 @@ int main(int argc, char **argv) { | |||
| 209 | dbname = dbi_conn_get_option(conn, "dbname"); | 196 | dbname = dbi_conn_get_option(conn, "dbname"); |
| 210 | host = dbi_conn_get_option(conn, "host"); | 197 | host = dbi_conn_get_option(conn, "host"); |
| 211 | 198 | ||
| 212 | if (!dbname) | 199 | if (!dbname) { |
| 213 | dbname = "<unspecified>"; | 200 | dbname = "<unspecified>"; |
| 214 | if (!host) | 201 | } |
| 202 | if (!host) { | ||
| 215 | host = "<unspecified>"; | 203 | host = "<unspecified>"; |
| 204 | } | ||
| 216 | 205 | ||
| 217 | printf("Connecting to database '%s' at host '%s'\n", dbname, host); | 206 | printf("Connecting to database '%s' at host '%s'\n", dbname, host); |
| 218 | } | 207 | } |
| 219 | 208 | ||
| 220 | if (dbi_conn_connect(conn) < 0) { | 209 | if (dbi_conn_connect(conn) < 0) { |
| 221 | np_dbi_print_error(conn, "UNKNOWN - failed to connect to database"); | 210 | np_dbi_print_error(conn, "failed to connect to database"); |
| 222 | return STATE_UNKNOWN; | 211 | exit(STATE_UNKNOWN); |
| 223 | } | 212 | } |
| 224 | 213 | ||
| 214 | struct timeval end_timeval; | ||
| 225 | gettimeofday(&end_timeval, NULL); | 215 | gettimeofday(&end_timeval, NULL); |
| 226 | conn_time = timediff(start_timeval, end_timeval); | 216 | double conn_time = timediff(start_timeval, end_timeval); |
| 217 | if (verbose) { | ||
| 218 | printf("Time elapsed: %f\n", conn_time); | ||
| 219 | } | ||
| 220 | |||
| 221 | mp_check overall = mp_check_init(); | ||
| 227 | 222 | ||
| 228 | server_version = dbi_conn_get_engine_version(conn); | 223 | mp_subcheck sc_connection_time = mp_subcheck_init(); |
| 229 | if (verbose) | 224 | sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_OK); |
| 225 | xasprintf(&sc_connection_time.output, "Connection time: %f", conn_time); | ||
| 226 | |||
| 227 | mp_perfdata pd_conn_duration = perfdata_init(); | ||
| 228 | pd_conn_duration.label = "conntime"; | ||
| 229 | pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time); | ||
| 230 | |||
| 231 | if (config.metric == METRIC_CONN_TIME) { | ||
| 232 | pd_conn_duration = mp_pd_set_thresholds(pd_conn_duration, config.thresholds); | ||
| 233 | mp_state_enum status = mp_get_pd_status(pd_conn_duration); | ||
| 234 | sc_connection_time = mp_set_subcheck_state(sc_connection_time, status); | ||
| 235 | if (status != STATE_OK) { | ||
| 236 | xasprintf(&sc_connection_time.output, "%s violates thresholds", | ||
| 237 | sc_connection_time.output); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | mp_add_perfdata_to_subcheck(&sc_connection_time, pd_conn_duration); | ||
| 242 | mp_add_subcheck_to_check(&overall, sc_connection_time); | ||
| 243 | |||
| 244 | unsigned int server_version = dbi_conn_get_engine_version(conn); | ||
| 245 | if (verbose) { | ||
| 230 | printf("Connected to server version %u\n", server_version); | 246 | printf("Connected to server version %u\n", server_version); |
| 247 | } | ||
| 231 | 248 | ||
| 232 | if (metric == METRIC_SERVER_VERSION) | 249 | mp_subcheck sc_server_version = mp_subcheck_init(); |
| 233 | status = get_status(server_version, dbi_thresholds); | 250 | sc_server_version = mp_set_subcheck_default_state(sc_server_version, STATE_OK); |
| 251 | xasprintf(&sc_server_version.output, "Connected to server version %u", server_version); | ||
| 234 | 252 | ||
| 235 | if (verbose) | 253 | if (config.metric == METRIC_SERVER_VERSION) { |
| 236 | printf("Time elapsed: %f\n", conn_time); | 254 | mp_perfdata pd_server_version = perfdata_init(); |
| 255 | pd_server_version = mp_set_pd_value(pd_server_version, server_version); | ||
| 256 | pd_server_version = mp_pd_set_thresholds(pd_server_version, config.thresholds); | ||
| 257 | mp_state_enum status = mp_get_pd_status(pd_server_version); | ||
| 258 | mp_add_perfdata_to_subcheck(&sc_server_version, pd_server_version); | ||
| 237 | 259 | ||
| 238 | if (metric == METRIC_CONN_TIME) | 260 | sc_server_version = mp_set_subcheck_state(sc_server_version, status); |
| 239 | status = get_status(conn_time, dbi_thresholds); | 261 | |
| 262 | if (status != STATE_OK) { | ||
| 263 | xasprintf(&sc_server_version.output, "%s violates thresholds", | ||
| 264 | sc_server_version.output); | ||
| 265 | } | ||
| 266 | }; | ||
| 267 | mp_add_subcheck_to_check(&overall, sc_server_version); | ||
| 240 | 268 | ||
| 241 | /* select a database */ | 269 | /* select a database */ |
| 242 | if (np_dbi_database) { | 270 | if (config.database) { |
| 243 | if (verbose > 1) | 271 | if (verbose > 1) { |
| 244 | printf("Selecting database '%s'\n", np_dbi_database); | 272 | printf("Selecting database '%s'\n", config.database); |
| 273 | } | ||
| 274 | |||
| 275 | mp_subcheck sc_select_db = mp_subcheck_init(); | ||
| 276 | sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK); | ||
| 245 | 277 | ||
| 246 | if (dbi_conn_select_db(conn, np_dbi_database)) { | 278 | if (dbi_conn_select_db(conn, config.database)) { |
| 247 | np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", np_dbi_database); | 279 | np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.database); |
| 248 | return STATE_UNKNOWN; | 280 | exit(STATE_UNKNOWN); |
| 281 | } else { | ||
| 282 | mp_add_subcheck_to_check(&overall, sc_select_db); | ||
| 249 | } | 283 | } |
| 250 | } | 284 | } |
| 251 | 285 | ||
| 252 | if (np_dbi_query) { | 286 | // Do a query (if configured) |
| 287 | if (config.query) { | ||
| 288 | mp_subcheck sc_query = mp_subcheck_init(); | ||
| 289 | sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN); | ||
| 290 | |||
| 253 | /* execute query */ | 291 | /* execute query */ |
| 254 | status = do_query(conn, &query_val_str, &query_val, &query_time); | 292 | do_query_result query_res = do_query(conn, config.metric, config.type, config.query); |
| 255 | if (status != STATE_OK) | 293 | |
| 256 | /* do_query prints an error message in this case */ | 294 | if (query_res.error_code != 0) { |
| 257 | return status; | 295 | xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string); |
| 258 | 296 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | |
| 259 | if (metric == METRIC_QUERY_RESULT) { | 297 | } else if (query_res.query_processing_status != STATE_OK) { |
| 260 | if (expect) { | 298 | if (query_res.error_string) { |
| 261 | if ((!query_val_str) || strcmp(query_val_str, expect)) | 299 | xasprintf(&sc_query.output, "Failed to process query: %s", query_res.error_string); |
| 262 | status = STATE_CRITICAL; | 300 | } else { |
| 263 | else | 301 | xasprintf(&sc_query.output, "Failed to process query"); |
| 264 | status = STATE_OK; | 302 | } |
| 265 | } else if (expect_re_str) { | 303 | sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status); |
| 266 | int err; | 304 | } else { |
| 267 | 305 | // query succeeded in general | |
| 268 | err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); | 306 | xasprintf(&sc_query.output, "Query '%s' succeeded", config.query); |
| 269 | if (!err) | 307 | |
| 270 | status = STATE_OK; | 308 | // that's a OK by default now |
| 271 | else if (err == REG_NOMATCH) | 309 | sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK); |
| 272 | status = STATE_CRITICAL; | 310 | |
| 273 | else { | 311 | // query duration first |
| 274 | char errmsg[1024]; | 312 | mp_perfdata pd_query_duration = perfdata_init(); |
| 275 | regerror(err, &expect_re, errmsg, sizeof(errmsg)); | 313 | pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration); |
| 276 | printf("ERROR - failed to execute regular expression: %s\n", errmsg); | 314 | pd_query_duration.label = "querytime"; |
| 277 | status = STATE_CRITICAL; | 315 | if (config.metric == METRIC_QUERY_TIME) { |
| 316 | pd_query_duration = mp_pd_set_thresholds(pd_query_duration, config.thresholds); | ||
| 317 | } | ||
| 318 | |||
| 319 | mp_add_perfdata_to_subcheck(&sc_query, pd_query_duration); | ||
| 320 | |||
| 321 | if (config.metric == METRIC_QUERY_RESULT) { | ||
| 322 | if (config.expect) { | ||
| 323 | if ((!query_res.result_string) || | ||
| 324 | strcmp(query_res.result_string, config.expect)) { | ||
| 325 | xasprintf(&sc_query.output, "Found string '%s' in query result", | ||
| 326 | config.expect); | ||
| 327 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 328 | } else { | ||
| 329 | xasprintf(&sc_query.output, "Did not find string '%s' in query result", | ||
| 330 | config.expect); | ||
| 331 | sc_query = mp_set_subcheck_state(sc_query, STATE_OK); | ||
| 332 | } | ||
| 333 | } else if (config.expect_re_str) { | ||
| 334 | int comp_err; | ||
| 335 | regex_t expect_re = {}; | ||
| 336 | comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags); | ||
| 337 | if (comp_err != 0) { | ||
| 338 | // TODO error, failed to compile regex | ||
| 339 | // TODO move this to config sanitatisation | ||
| 340 | printf("Failed to compile regex from string '%s'", config.expect_re_str); | ||
| 341 | exit(STATE_UNKNOWN); | ||
| 342 | } | ||
| 343 | |||
| 344 | int err = | ||
| 345 | regexec(&expect_re, query_res.result_string, 0, NULL, /* flags = */ 0); | ||
| 346 | if (!err) { | ||
| 347 | sc_query = mp_set_subcheck_state(sc_query, STATE_OK); | ||
| 348 | xasprintf(&sc_query.output, "Found regular expression '%s' in query result", | ||
| 349 | config.expect_re_str); | ||
| 350 | } else if (err == REG_NOMATCH) { | ||
| 351 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 352 | xasprintf(&sc_query.output, | ||
| 353 | "Did not find regular expression '%s' in query result", | ||
| 354 | config.expect_re_str); | ||
| 355 | } else { | ||
| 356 | char errmsg[1024]; | ||
| 357 | regerror(err, &expect_re, errmsg, sizeof(errmsg)); | ||
| 358 | xasprintf(&sc_query.output, | ||
| 359 | "ERROR - failed to execute regular expression: %s\n", errmsg); | ||
| 360 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 361 | } | ||
| 362 | } else { | ||
| 363 | // no string matching | ||
| 364 | if (isnan(query_res.result_number)) { | ||
| 365 | // The query result is not a number, but no string checking was configured | ||
| 366 | // so we expected a number | ||
| 367 | // this is a CRITICAL | ||
| 368 | xasprintf(&sc_query.output, "Query '%s' result is not numeric", | ||
| 369 | config.query); | ||
| 370 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 371 | |||
| 372 | } else { | ||
| 373 | |||
| 374 | mp_perfdata pd_query_val = perfdata_init(); | ||
| 375 | pd_query_val = mp_set_pd_value(pd_query_val, query_res.result_number); | ||
| 376 | pd_query_val.label = "query"; | ||
| 377 | pd_query_val = mp_pd_set_thresholds(pd_query_val, config.thresholds); | ||
| 378 | |||
| 379 | mp_add_perfdata_to_subcheck(&sc_query, pd_query_val); | ||
| 380 | mp_state_enum query_numerical_result = mp_get_pd_status(pd_query_val); | ||
| 381 | |||
| 382 | sc_query = mp_set_subcheck_state(sc_query, query_numerical_result); | ||
| 383 | // TODO set pd thresholds | ||
| 384 | // if (config.dbi_thresholds->warning) { | ||
| 385 | // pd_query_val.warn= config.dbi_thresholds->warning | ||
| 386 | // } else { | ||
| 387 | // } | ||
| 388 | |||
| 389 | if (query_numerical_result == STATE_OK) { | ||
| 390 | xasprintf(&sc_query.output, | ||
| 391 | "Query result '%f' is within given thresholds", | ||
| 392 | query_res.result_number); | ||
| 393 | } else { | ||
| 394 | xasprintf(&sc_query.output, | ||
| 395 | "Query result '%f' violates the given thresholds", | ||
| 396 | query_res.result_number); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | } | ||
| 400 | } else if (config.metric == METRIC_QUERY_TIME) { | ||
| 401 | mp_state_enum query_time_status = mp_get_pd_status(pd_query_duration); | ||
| 402 | mp_set_subcheck_state(sc_query, query_time_status); | ||
| 403 | |||
| 404 | if (query_time_status == STATE_OK) { | ||
| 405 | xasprintf(&sc_query.output, "Query duration '%f' is within given thresholds", | ||
| 406 | query_res.query_duration); | ||
| 407 | } else { | ||
| 408 | xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds", | ||
| 409 | query_res.query_duration); | ||
| 278 | } | 410 | } |
| 279 | } else | 411 | } else { |
| 280 | status = get_status(query_val, dbi_thresholds); | 412 | /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error |
| 281 | } else if (metric == METRIC_QUERY_TIME) | 413 | * which should have been reported and handled (abort) before |
| 282 | status = get_status(query_time, dbi_thresholds); | 414 | * ... unless we expected a string to be returned */ |
| 415 | assert((!isnan(query_res.result_number)) || (config.type == TYPE_STRING)); | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | mp_add_subcheck_to_check(&overall, sc_query); | ||
| 283 | } | 420 | } |
| 284 | 421 | ||
| 285 | if (verbose) | 422 | if (verbose) { |
| 286 | printf("Closing connection\n"); | 423 | printf("Closing connection\n"); |
| 424 | } | ||
| 287 | dbi_conn_close(conn); | 425 | dbi_conn_close(conn); |
| 288 | 426 | ||
| 289 | /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error | 427 | mp_exit(overall); |
| 290 | * which should have been reported and handled (abort) before | ||
| 291 | * ... unless we expected a string to be returned */ | ||
| 292 | assert((metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || (type == TYPE_STRING)); | ||
| 293 | |||
| 294 | assert((type != TYPE_STRING) || (expect || expect_re_str)); | ||
| 295 | |||
| 296 | printf("%s - connection time: %fs", state_text(status), conn_time); | ||
| 297 | if (np_dbi_query) { | ||
| 298 | if (type == TYPE_STRING) { | ||
| 299 | assert(expect || expect_re_str); | ||
| 300 | printf(", '%s' returned '%s' in %fs", np_dbi_query, query_val_str ? query_val_str : "<nothing>", query_time); | ||
| 301 | if (status != STATE_OK) { | ||
| 302 | if (expect) | ||
| 303 | printf(" (expected '%s')", expect); | ||
| 304 | else if (expect_re_str) | ||
| 305 | printf(" (expected regex /%s/%s)", expect_re_str, ((expect_re_cflags & REG_ICASE) ? "i" : "")); | ||
| 306 | } | ||
| 307 | } else if (isnan(query_val)) | ||
| 308 | printf(", '%s' query execution time: %fs", np_dbi_query, query_time); | ||
| 309 | else | ||
| 310 | printf(", '%s' returned %f in %fs", np_dbi_query, query_val, query_time); | ||
| 311 | } | ||
| 312 | |||
| 313 | printf(" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, | ||
| 314 | ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "", | ||
| 315 | ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "", server_version, | ||
| 316 | ((metric == METRIC_SERVER_VERSION) && warning_range) ? warning_range : "", | ||
| 317 | ((metric == METRIC_SERVER_VERSION) && critical_range) ? critical_range : ""); | ||
| 318 | if (np_dbi_query) { | ||
| 319 | if (!isnan(query_val)) /* this is also true when -e is used */ | ||
| 320 | printf(" query=%f;%s;%s;;", query_val, ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "", | ||
| 321 | ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : ""); | ||
| 322 | printf(" querytime=%fs;%s;%s;0;", query_time, ((metric == METRIC_QUERY_TIME) && warning_range) ? warning_range : "", | ||
| 323 | ((metric == METRIC_QUERY_TIME) && critical_range) ? critical_range : ""); | ||
| 324 | } | ||
| 325 | printf("\n"); | ||
| 326 | return status; | ||
| 327 | } | 428 | } |
| 328 | 429 | ||
| 329 | /* process command-line arguments */ | 430 | /* process command-line arguments */ |
| 330 | int process_arguments(int argc, char **argv) { | 431 | check_dbi_config_wrapper process_arguments(int argc, char **argv) { |
| 331 | int c; | 432 | enum { |
| 433 | output_format_index = CHAR_MAX + 1, | ||
| 434 | }; | ||
| 332 | 435 | ||
| 333 | int option = 0; | 436 | int option = 0; |
| 334 | static struct option longopts[] = {STD_LONG_OPTS, | 437 | static struct option longopts[] = {STD_LONG_OPTS, |
| 335 | |||
| 336 | {"expect", required_argument, 0, 'e'}, | 438 | {"expect", required_argument, 0, 'e'}, |
| 337 | {"regex", required_argument, 0, 'r'}, | 439 | {"regex", required_argument, 0, 'r'}, |
| 338 | {"regexi", required_argument, 0, 'R'}, | 440 | {"regexi", required_argument, 0, 'R'}, |
| @@ -341,15 +443,22 @@ int process_arguments(int argc, char **argv) { | |||
| 341 | {"option", required_argument, 0, 'o'}, | 443 | {"option", required_argument, 0, 'o'}, |
| 342 | {"query", required_argument, 0, 'q'}, | 444 | {"query", required_argument, 0, 'q'}, |
| 343 | {"database", required_argument, 0, 'D'}, | 445 | {"database", required_argument, 0, 'D'}, |
| 446 | {"output-format", required_argument, 0, output_format_index}, | ||
| 344 | {0, 0, 0, 0}}; | 447 | {0, 0, 0, 0}}; |
| 345 | 448 | ||
| 346 | while (1) { | 449 | check_dbi_config_wrapper result = { |
| 347 | c = getopt_long(argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:", longopts, &option); | 450 | .config = check_dbi_config_init(), |
| 451 | .errorcode = OK, | ||
| 452 | }; | ||
| 453 | int option_char; | ||
| 454 | while (true) { | ||
| 455 | option_char = getopt_long(argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:", longopts, &option); | ||
| 348 | 456 | ||
| 349 | if (c == EOF) | 457 | if (option_char == EOF) { |
| 350 | break; | 458 | break; |
| 459 | } | ||
| 351 | 460 | ||
| 352 | switch (c) { | 461 | switch (option_char) { |
| 353 | case '?': /* usage */ | 462 | case '?': /* usage */ |
| 354 | usage5(); | 463 | usage5(); |
| 355 | case 'h': /* help */ | 464 | case 'h': /* help */ |
| @@ -359,136 +468,171 @@ int process_arguments(int argc, char **argv) { | |||
| 359 | print_revision(progname, NP_VERSION); | 468 | print_revision(progname, NP_VERSION); |
| 360 | exit(STATE_UNKNOWN); | 469 | exit(STATE_UNKNOWN); |
| 361 | 470 | ||
| 362 | case 'c': /* critical range */ | 471 | case 'c': /* critical range */ { |
| 363 | critical_range = optarg; | 472 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 364 | type = TYPE_NUMERIC; | 473 | if (tmp.error != MP_PARSING_SUCCES) { |
| 365 | break; | 474 | die(STATE_UNKNOWN, "failed to parse critical threshold"); |
| 366 | case 'w': /* warning range */ | 475 | } |
| 367 | warning_range = optarg; | 476 | result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range); |
| 368 | type = TYPE_NUMERIC; | 477 | result.config.type = TYPE_NUMERIC; |
| 369 | break; | 478 | } break; |
| 479 | case 'w': /* warning range */ { | ||
| 480 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 481 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 482 | die(STATE_UNKNOWN, "failed to parse warning threshold"); | ||
| 483 | } | ||
| 484 | result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); | ||
| 485 | result.config.type = TYPE_NUMERIC; | ||
| 486 | } break; | ||
| 370 | case 'e': | 487 | case 'e': |
| 371 | expect = optarg; | 488 | result.config.expect = optarg; |
| 372 | type = TYPE_STRING; | 489 | result.config.type = TYPE_STRING; |
| 373 | break; | 490 | break; |
| 374 | case 'R': | 491 | case 'R': |
| 375 | expect_re_cflags = REG_ICASE; | 492 | result.config.expect_re_cflags = REG_ICASE; |
| 376 | /* fall through */ | 493 | /* fall through */ |
| 377 | case 'r': { | 494 | case 'r': { |
| 378 | int err; | 495 | int err; |
| 379 | 496 | ||
| 380 | expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; | 497 | result.config.expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; |
| 381 | expect_re_str = optarg; | 498 | result.config.expect_re_str = optarg; |
| 382 | type = TYPE_STRING; | 499 | result.config.type = TYPE_STRING; |
| 383 | 500 | ||
| 384 | err = regcomp(&expect_re, expect_re_str, expect_re_cflags); | 501 | regex_t expect_re = {}; |
| 502 | err = regcomp(&expect_re, result.config.expect_re_str, result.config.expect_re_cflags); | ||
| 385 | if (err) { | 503 | if (err) { |
| 386 | char errmsg[1024]; | 504 | char errmsg[1024]; |
| 387 | regerror(err, &expect_re, errmsg, sizeof(errmsg)); | 505 | regerror(err, &expect_re, errmsg, sizeof(errmsg)); |
| 388 | printf("ERROR - failed to compile regular expression: %s\n", errmsg); | 506 | printf("ERROR - failed to compile regular expression: %s\n", errmsg); |
| 389 | return ERROR; | 507 | |
| 508 | result.errorcode = ERROR; | ||
| 509 | return result; | ||
| 390 | } | 510 | } |
| 391 | break; | 511 | break; |
| 392 | } | 512 | } |
| 393 | |||
| 394 | case 'm': | 513 | case 'm': |
| 395 | if (!strcasecmp(optarg, "CONN_TIME")) | 514 | if (!strcasecmp(optarg, "CONN_TIME")) { |
| 396 | metric = METRIC_CONN_TIME; | 515 | result.config.metric = METRIC_CONN_TIME; |
| 397 | else if (!strcasecmp(optarg, "SERVER_VERSION")) | 516 | } else if (!strcasecmp(optarg, "SERVER_VERSION")) { |
| 398 | metric = METRIC_SERVER_VERSION; | 517 | result.config.metric = METRIC_SERVER_VERSION; |
| 399 | else if (!strcasecmp(optarg, "QUERY_RESULT")) | 518 | } else if (!strcasecmp(optarg, "QUERY_RESULT")) { |
| 400 | metric = METRIC_QUERY_RESULT; | 519 | result.config.metric = METRIC_QUERY_RESULT; |
| 401 | else if (!strcasecmp(optarg, "QUERY_TIME")) | 520 | } else if (!strcasecmp(optarg, "QUERY_TIME")) { |
| 402 | metric = METRIC_QUERY_TIME; | 521 | result.config.metric = METRIC_QUERY_TIME; |
| 403 | else | 522 | } else { |
| 404 | usage2(_("Invalid metric"), optarg); | 523 | usage2(_("Invalid metric"), optarg); |
| 524 | } | ||
| 405 | break; | 525 | break; |
| 406 | case 't': /* timeout */ | 526 | case 't': /* timeout */ |
| 407 | if (!is_intnonneg(optarg)) | 527 | if (!is_intnonneg(optarg)) { |
| 408 | usage2(_("Timeout interval must be a positive integer"), optarg); | 528 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 409 | else | 529 | } else { |
| 410 | timeout_interval = atoi(optarg); | 530 | timeout_interval = atoi(optarg); |
| 411 | 531 | } | |
| 412 | break; | 532 | break; |
| 413 | case 'H': /* host */ | 533 | case 'H': /* host */ |
| 414 | if (!is_host(optarg)) | 534 | if (!is_host(optarg)) { |
| 415 | usage2(_("Invalid hostname/address"), optarg); | 535 | usage2(_("Invalid hostname/address"), optarg); |
| 416 | else | 536 | } else { |
| 417 | host = optarg; | 537 | result.config.host = optarg; |
| 538 | } | ||
| 418 | break; | 539 | break; |
| 419 | case 'v': | 540 | case 'v': |
| 420 | verbose++; | 541 | verbose++; |
| 421 | break; | 542 | break; |
| 422 | |||
| 423 | case 'd': | 543 | case 'd': |
| 424 | np_dbi_driver = optarg; | 544 | result.config.dbi_driver = optarg; |
| 425 | break; | 545 | break; |
| 426 | case 'o': { | 546 | case 'o': { |
| 427 | driver_option_t *new; | 547 | driver_option_t *new = NULL; |
| 428 | |||
| 429 | char *k; | ||
| 430 | char *v; | ||
| 431 | 548 | ||
| 432 | k = optarg; | 549 | char *key = optarg; |
| 433 | v = strchr(k, (int)'='); | 550 | char *value = strchr(key, '='); |
| 434 | 551 | ||
| 435 | if (!v) | 552 | if (!value) { |
| 436 | usage2(_("Option must be '<key>=<value>'"), optarg); | 553 | usage2(_("Option must be '<key>=<value>'"), optarg); |
| 554 | } | ||
| 437 | 555 | ||
| 438 | *v = '\0'; | 556 | *value = '\0'; |
| 439 | ++v; | 557 | ++value; |
| 440 | 558 | ||
| 441 | new = realloc(np_dbi_options, (np_dbi_options_num + 1) * sizeof(*new)); | 559 | new = realloc(result.config.dbi_options, |
| 560 | (result.config.dbi_options_num + 1) * sizeof(*new)); | ||
| 442 | if (!new) { | 561 | if (!new) { |
| 443 | printf("UNKNOWN - failed to reallocate memory\n"); | 562 | printf("UNKNOWN - failed to reallocate memory\n"); |
| 444 | exit(STATE_UNKNOWN); | 563 | exit(STATE_UNKNOWN); |
| 445 | } | 564 | } |
| 446 | 565 | ||
| 447 | np_dbi_options = new; | 566 | result.config.dbi_options = new; |
| 448 | new = np_dbi_options + np_dbi_options_num; | 567 | new = result.config.dbi_options + result.config.dbi_options_num; |
| 449 | ++np_dbi_options_num; | 568 | result.config.dbi_options_num++; |
| 450 | 569 | ||
| 451 | new->key = k; | 570 | new->key = key; |
| 452 | new->value = v; | 571 | new->value = value; |
| 453 | } break; | 572 | } break; |
| 454 | case 'q': | 573 | case 'q': |
| 455 | np_dbi_query = optarg; | 574 | result.config.query = optarg; |
| 456 | break; | 575 | break; |
| 457 | case 'D': | 576 | case 'D': |
| 458 | np_dbi_database = optarg; | 577 | result.config.database = optarg; |
| 578 | break; | ||
| 579 | case output_format_index: { | ||
| 580 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 581 | if (!parser.parsing_success) { | ||
| 582 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 583 | printf("Invalid output format: %s\n", optarg); | ||
| 584 | exit(STATE_UNKNOWN); | ||
| 585 | } | ||
| 586 | |||
| 587 | result.config.output_format_is_set = true; | ||
| 588 | result.config.output_format = parser.output_format; | ||
| 459 | break; | 589 | break; |
| 460 | } | 590 | } |
| 591 | } | ||
| 461 | } | 592 | } |
| 462 | 593 | ||
| 463 | set_thresholds(&dbi_thresholds, warning_range, critical_range); | 594 | if (!result.config.dbi_driver) { |
| 464 | |||
| 465 | return validate_arguments(); | ||
| 466 | } | ||
| 467 | |||
| 468 | int validate_arguments(void) { | ||
| 469 | if (!np_dbi_driver) | ||
| 470 | usage("Must specify a DBI driver"); | 595 | usage("Must specify a DBI driver"); |
| 596 | } | ||
| 471 | 597 | ||
| 472 | if (((metric == METRIC_QUERY_RESULT) || (metric == METRIC_QUERY_TIME)) && (!np_dbi_query)) | 598 | if (((result.config.metric == METRIC_QUERY_RESULT) || |
| 599 | (result.config.metric == METRIC_QUERY_TIME)) && | ||
| 600 | (!result.config.query)) { | ||
| 473 | usage("Must specify a query to execute (metric == QUERY_RESULT)"); | 601 | usage("Must specify a query to execute (metric == QUERY_RESULT)"); |
| 602 | } | ||
| 474 | 603 | ||
| 475 | if ((metric != METRIC_CONN_TIME) && (metric != METRIC_SERVER_VERSION) && (metric != METRIC_QUERY_RESULT) && | 604 | if ((result.config.metric != METRIC_CONN_TIME) && |
| 476 | (metric != METRIC_QUERY_TIME)) | 605 | (result.config.metric != METRIC_SERVER_VERSION) && |
| 606 | (result.config.metric != METRIC_QUERY_RESULT) && | ||
| 607 | (result.config.metric != METRIC_QUERY_TIME)) { | ||
| 477 | usage("Invalid metric specified"); | 608 | usage("Invalid metric specified"); |
| 609 | } | ||
| 478 | 610 | ||
| 479 | if (expect && (warning_range || critical_range || expect_re_str)) | 611 | if (result.config.expect && |
| 612 | (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set || | ||
| 613 | result.config.expect_re_str)) { | ||
| 480 | usage("Do not mix -e and -w/-c/-r/-R"); | 614 | usage("Do not mix -e and -w/-c/-r/-R"); |
| 615 | } | ||
| 481 | 616 | ||
| 482 | if (expect_re_str && (warning_range || critical_range || expect)) | 617 | if (result.config.expect_re_str && |
| 618 | (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set || | ||
| 619 | result.config.expect)) { | ||
| 483 | usage("Do not mix -r/-R and -w/-c/-e"); | 620 | usage("Do not mix -r/-R and -w/-c/-e"); |
| 621 | } | ||
| 484 | 622 | ||
| 485 | if (expect && (metric != METRIC_QUERY_RESULT)) | 623 | if (result.config.expect && (result.config.metric != METRIC_QUERY_RESULT)) { |
| 486 | usage("Option -e requires metric QUERY_RESULT"); | 624 | usage("Option -e requires metric QUERY_RESULT"); |
| 625 | } | ||
| 487 | 626 | ||
| 488 | if (expect_re_str && (metric != METRIC_QUERY_RESULT)) | 627 | if (result.config.expect_re_str && (result.config.metric != METRIC_QUERY_RESULT)) { |
| 489 | usage("Options -r/-R require metric QUERY_RESULT"); | 628 | usage("Options -r/-R require metric QUERY_RESULT"); |
| 629 | } | ||
| 630 | |||
| 631 | if (result.config.type == TYPE_STRING) { | ||
| 632 | assert(result.config.expect || result.config.expect_re_str); | ||
| 633 | } | ||
| 490 | 634 | ||
| 491 | return OK; | 635 | return result; |
| 492 | } | 636 | } |
| 493 | 637 | ||
| 494 | void print_help(void) { | 638 | void print_help(void) { |
| @@ -518,6 +662,8 @@ void print_help(void) { | |||
| 518 | printf(" %s\n", _("DBI driver options")); | 662 | printf(" %s\n", _("DBI driver options")); |
| 519 | printf(" %s\n", "-q, --query=STRING"); | 663 | printf(" %s\n", "-q, --query=STRING"); |
| 520 | printf(" %s\n", _("query to execute")); | 664 | printf(" %s\n", _("query to execute")); |
| 665 | printf(" %s\n", "-H STRING"); | ||
| 666 | printf(" %s\n", _("target database host")); | ||
| 521 | printf("\n"); | 667 | printf("\n"); |
| 522 | 668 | ||
| 523 | printf(UT_WARN_CRIT_RANGE); | 669 | printf(UT_WARN_CRIT_RANGE); |
| @@ -542,6 +688,8 @@ void print_help(void) { | |||
| 542 | 688 | ||
| 543 | printf(UT_VERBOSE); | 689 | printf(UT_VERBOSE); |
| 544 | 690 | ||
| 691 | printf(UT_OUTPUT_FORMAT); | ||
| 692 | |||
| 545 | printf("\n"); | 693 | printf("\n"); |
| 546 | printf(" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates")); | 694 | printf(" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates")); |
| 547 | printf(" %s\n\n", _("on a query, one has to be specified (-q option).")); | 695 | printf(" %s\n\n", _("on a query, one has to be specified (-q option).")); |
| @@ -592,163 +740,193 @@ void print_usage(void) { | |||
| 592 | printf(" [-e <string>] [-r|-R <regex>]\n"); | 740 | printf(" [-e <string>] [-r|-R <regex>]\n"); |
| 593 | } | 741 | } |
| 594 | 742 | ||
| 595 | #define CHECK_IGNORE_ERROR(s) \ | 743 | const char *get_field_str(dbi_result res, check_dbi_metric metric, check_dbi_type type) { |
| 596 | do { \ | 744 | const char *str = dbi_result_get_string_idx(res, 1); |
| 597 | if (metric != METRIC_QUERY_RESULT) \ | ||
| 598 | return (s); \ | ||
| 599 | } while (0) | ||
| 600 | |||
| 601 | const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type) { | ||
| 602 | const char *str; | ||
| 603 | |||
| 604 | if (field_type != DBI_TYPE_STRING) { | ||
| 605 | printf("CRITICAL - result value is not a string\n"); | ||
| 606 | return NULL; | ||
| 607 | } | ||
| 608 | |||
| 609 | str = dbi_result_get_string_idx(res, 1); | ||
| 610 | if ((!str) || (strcmp(str, "ERROR") == 0)) { | 745 | if ((!str) || (strcmp(str, "ERROR") == 0)) { |
| 611 | CHECK_IGNORE_ERROR(NULL); | 746 | if (metric != METRIC_QUERY_RESULT) { |
| 612 | np_dbi_print_error(conn, "CRITICAL - failed to fetch string value"); | 747 | return NULL; |
| 748 | } | ||
| 613 | return NULL; | 749 | return NULL; |
| 614 | } | 750 | } |
| 615 | 751 | ||
| 616 | if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) | 752 | if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) { |
| 617 | printf("Query returned string '%s'\n", str); | 753 | printf("Query returned string '%s'\n", str); |
| 754 | } | ||
| 618 | return str; | 755 | return str; |
| 619 | } | 756 | } |
| 620 | 757 | ||
| 621 | double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type) { | 758 | typedef struct { |
| 622 | double val = NAN; | 759 | double value; |
| 623 | 760 | int error_code; | |
| 624 | if (*field_type == DBI_TYPE_INTEGER) { | 761 | int dbi_error_code; // not sure if useful |
| 625 | val = (double)dbi_result_get_longlong_idx(res, 1); | 762 | } get_field_wrapper; |
| 626 | } else if (*field_type == DBI_TYPE_DECIMAL) { | 763 | get_field_wrapper get_field(dbi_result res, check_dbi_metric metric, check_dbi_type type) { |
| 627 | val = dbi_result_get_double_idx(res, 1); | 764 | |
| 628 | } else if (*field_type == DBI_TYPE_STRING) { | 765 | unsigned short field_type = dbi_result_get_field_type_idx(res, 1); |
| 766 | get_field_wrapper result = { | ||
| 767 | .value = NAN, | ||
| 768 | .error_code = OK, | ||
| 769 | }; | ||
| 770 | |||
| 771 | if (field_type == DBI_TYPE_INTEGER) { | ||
| 772 | result.value = (double)dbi_result_get_longlong_idx(res, 1); | ||
| 773 | } else if (field_type == DBI_TYPE_DECIMAL) { | ||
| 774 | result.value = dbi_result_get_double_idx(res, 1); | ||
| 775 | } else if (field_type == DBI_TYPE_STRING) { | ||
| 629 | const char *val_str; | 776 | const char *val_str; |
| 630 | char *endptr = NULL; | 777 | char *endptr = NULL; |
| 631 | 778 | ||
| 632 | val_str = get_field_str(conn, res, *field_type); | 779 | val_str = get_field_str(res, metric, type); |
| 633 | if (!val_str) { | 780 | if (!val_str) { |
| 634 | CHECK_IGNORE_ERROR(NAN); | 781 | result.error_code = ERROR; |
| 635 | *field_type = DBI_TYPE_ERROR; | 782 | field_type = DBI_TYPE_ERROR; |
| 636 | return NAN; | 783 | return result; |
| 637 | } | 784 | } |
| 638 | 785 | ||
| 639 | val = strtod(val_str, &endptr); | 786 | result.value = strtod(val_str, &endptr); |
| 640 | if (endptr == val_str) { | 787 | if (endptr == val_str) { |
| 641 | CHECK_IGNORE_ERROR(NAN); | 788 | if (metric != METRIC_QUERY_RESULT) { |
| 642 | printf("CRITICAL - result value is not a numeric: %s\n", val_str); | 789 | result.error_code = ERROR; |
| 643 | *field_type = DBI_TYPE_ERROR; | 790 | return result; |
| 644 | return NAN; | 791 | } |
| 792 | |||
| 793 | if (verbose) { | ||
| 794 | printf("CRITICAL - result value is not a numeric: %s\n", val_str); | ||
| 795 | } | ||
| 796 | |||
| 797 | field_type = DBI_TYPE_ERROR; | ||
| 798 | result.error_code = ERROR; | ||
| 799 | return result; | ||
| 645 | } | 800 | } |
| 801 | |||
| 646 | if ((endptr != NULL) && (*endptr != '\0')) { | 802 | if ((endptr != NULL) && (*endptr != '\0')) { |
| 647 | if (verbose) | 803 | if (verbose) { |
| 648 | printf("Garbage after value: %s\n", endptr); | 804 | printf("Garbage after value: %s\n", endptr); |
| 805 | } | ||
| 649 | } | 806 | } |
| 650 | } else { | 807 | } else { |
| 651 | CHECK_IGNORE_ERROR(NAN); | 808 | if (metric != METRIC_QUERY_RESULT) { |
| 652 | printf("CRITICAL - cannot parse value of type %s (%i)\n", | 809 | result.error_code = ERROR; |
| 653 | (*field_type == DBI_TYPE_BINARY) ? "BINARY" | 810 | return result; |
| 654 | : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" | 811 | } |
| 655 | : "<unknown>", | 812 | // printf("CRITICAL - cannot parse value of type %s (%i)\n", |
| 656 | *field_type); | 813 | // (*field_type == DBI_TYPE_BINARY) ? "BINARY" |
| 657 | *field_type = DBI_TYPE_ERROR; | 814 | // : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" |
| 658 | return NAN; | 815 | // : "<unknown>", |
| 659 | } | 816 | // *field_type); |
| 660 | return val; | 817 | field_type = DBI_TYPE_ERROR; |
| 818 | result.error_code = ERROR; | ||
| 819 | } | ||
| 820 | return result; | ||
| 661 | } | 821 | } |
| 662 | 822 | ||
| 663 | double get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val) { | 823 | static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type, |
| 664 | unsigned short field_type; | 824 | char *query) { |
| 665 | double val = NAN; | 825 | assert(query); |
| 666 | 826 | ||
| 667 | if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { | 827 | if (verbose) { |
| 668 | CHECK_IGNORE_ERROR(STATE_OK); | 828 | printf("Executing query '%s'\n", query); |
| 669 | np_dbi_print_error(conn, "CRITICAL - failed to fetch rows"); | ||
| 670 | return STATE_CRITICAL; | ||
| 671 | } | 829 | } |
| 672 | 830 | ||
| 673 | if (dbi_result_get_numrows(res) < 1) { | 831 | do_query_result result = { |
| 674 | CHECK_IGNORE_ERROR(STATE_OK); | 832 | .query_duration = 0, |
| 675 | printf("WARNING - no rows returned\n"); | 833 | .result_string = NULL, |
| 676 | return STATE_WARNING; | 834 | .result_number = 0, |
| 677 | } | 835 | .error_code = 0, |
| 836 | .query_processing_status = STATE_UNKNOWN, | ||
| 837 | }; | ||
| 678 | 838 | ||
| 679 | if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) { | 839 | struct timeval timeval_start; |
| 680 | CHECK_IGNORE_ERROR(STATE_OK); | 840 | gettimeofday(&timeval_start, NULL); |
| 681 | np_dbi_print_error(conn, "CRITICAL - failed to fetch fields"); | ||
| 682 | return STATE_CRITICAL; | ||
| 683 | } | ||
| 684 | 841 | ||
| 685 | if (dbi_result_get_numfields(res) < 1) { | 842 | dbi_result res = dbi_conn_query(conn, query); |
| 686 | CHECK_IGNORE_ERROR(STATE_OK); | 843 | if (!res) { |
| 687 | printf("WARNING - no fields returned\n"); | 844 | dbi_conn_error(conn, &result.error_string); |
| 688 | return STATE_WARNING; | 845 | result.error_code = 1; |
| 846 | return result; | ||
| 689 | } | 847 | } |
| 690 | 848 | ||
| 691 | if (dbi_result_first_row(res) != 1) { | 849 | struct timeval timeval_end; |
| 692 | CHECK_IGNORE_ERROR(STATE_OK); | 850 | gettimeofday(&timeval_end, NULL); |
| 693 | np_dbi_print_error(conn, "CRITICAL - failed to fetch first row"); | 851 | result.query_duration = timediff(timeval_start, timeval_end); |
| 694 | return STATE_CRITICAL; | ||
| 695 | } | ||
| 696 | 852 | ||
| 697 | field_type = dbi_result_get_field_type_idx(res, 1); | 853 | if (verbose) { |
| 698 | if (field_type != DBI_TYPE_ERROR) { | 854 | printf("Query duration: %f\n", result.query_duration); |
| 699 | if (type == TYPE_STRING) | ||
| 700 | /* the value will be freed in dbi_result_free */ | ||
| 701 | *res_val_str = strdup(get_field_str(conn, res, field_type)); | ||
| 702 | else | ||
| 703 | val = get_field(conn, res, &field_type); | ||
| 704 | } | 855 | } |
| 705 | 856 | ||
| 706 | *res_val = val; | 857 | // Default state is OK, all error will be set explicitly |
| 858 | mp_state_enum query_processing_state = STATE_OK; | ||
| 859 | { | ||
| 707 | 860 | ||
| 708 | if (field_type == DBI_TYPE_ERROR) { | 861 | if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { |
| 709 | CHECK_IGNORE_ERROR(STATE_OK); | 862 | if (metric != METRIC_QUERY_RESULT) { |
| 710 | np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); | 863 | query_processing_state = STATE_OK; |
| 711 | return STATE_CRITICAL; | 864 | } else { |
| 865 | dbi_conn_error(conn, &result.error_string); | ||
| 866 | query_processing_state = STATE_CRITICAL; | ||
| 867 | } | ||
| 868 | } else if (dbi_result_get_numrows(res) < 1) { | ||
| 869 | if (metric != METRIC_QUERY_RESULT) { | ||
| 870 | query_processing_state = STATE_OK; | ||
| 871 | } else { | ||
| 872 | result.error_string = "no rows returned"; | ||
| 873 | // printf("WARNING - no rows returned\n"); | ||
| 874 | query_processing_state = STATE_WARNING; | ||
| 875 | } | ||
| 876 | } else if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) { | ||
| 877 | if (metric != METRIC_QUERY_RESULT) { | ||
| 878 | query_processing_state = STATE_OK; | ||
| 879 | } else { | ||
| 880 | dbi_conn_error(conn, &result.error_string); | ||
| 881 | // np_dbi_print_error(conn, "CRITICAL - failed to fetch fields"); | ||
| 882 | query_processing_state = STATE_CRITICAL; | ||
| 883 | } | ||
| 884 | } else if (dbi_result_get_numfields(res) < 1) { | ||
| 885 | if (metric != METRIC_QUERY_RESULT) { | ||
| 886 | query_processing_state = STATE_OK; | ||
| 887 | } else { | ||
| 888 | result.error_string = "no fields returned"; | ||
| 889 | // printf("WARNING - no fields returned\n"); | ||
| 890 | query_processing_state = STATE_WARNING; | ||
| 891 | } | ||
| 892 | } else if (dbi_result_first_row(res) != 1) { | ||
| 893 | if (metric != METRIC_QUERY_RESULT) { | ||
| 894 | query_processing_state = STATE_OK; | ||
| 895 | } else { | ||
| 896 | dbi_conn_error(conn, &result.error_string); | ||
| 897 | // np_dbi_print_error(conn, "CRITICAL - failed to fetch first row"); | ||
| 898 | query_processing_state = STATE_CRITICAL; | ||
| 899 | } | ||
| 900 | } else { | ||
| 901 | unsigned short field_type = dbi_result_get_field_type_idx(res, 1); | ||
| 902 | if (field_type != DBI_TYPE_ERROR) { | ||
| 903 | if (type == TYPE_STRING) { | ||
| 904 | result.result_string = strdup(get_field_str(res, metric, type)); | ||
| 905 | } else { | ||
| 906 | get_field_wrapper gfw = get_field(res, metric, type); | ||
| 907 | result.result_number = gfw.value; | ||
| 908 | } | ||
| 909 | } else { | ||
| 910 | // Error when retrieving the field, that is OK if the Query result is not of | ||
| 911 | // interest | ||
| 912 | if (metric != METRIC_QUERY_RESULT) { | ||
| 913 | query_processing_state = STATE_OK; | ||
| 914 | } else { | ||
| 915 | dbi_conn_error(conn, &result.error_string); | ||
| 916 | // np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); | ||
| 917 | query_processing_state = STATE_CRITICAL; | ||
| 918 | } | ||
| 919 | } | ||
| 920 | } | ||
| 712 | } | 921 | } |
| 713 | |||
| 714 | dbi_result_free(res); | 922 | dbi_result_free(res); |
| 715 | return STATE_OK; | ||
| 716 | } | ||
| 717 | |||
| 718 | #undef CHECK_IGNORE_ERROR | ||
| 719 | |||
| 720 | int do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time) { | ||
| 721 | dbi_result res; | ||
| 722 | |||
| 723 | struct timeval timeval_start; | ||
| 724 | struct timeval timeval_end; | ||
| 725 | int status = STATE_OK; | ||
| 726 | |||
| 727 | assert(np_dbi_query); | ||
| 728 | |||
| 729 | if (verbose) | ||
| 730 | printf("Executing query '%s'\n", np_dbi_query); | ||
| 731 | |||
| 732 | gettimeofday(&timeval_start, NULL); | ||
| 733 | |||
| 734 | res = dbi_conn_query(conn, np_dbi_query); | ||
| 735 | if (!res) { | ||
| 736 | np_dbi_print_error(conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); | ||
| 737 | return STATE_CRITICAL; | ||
| 738 | } | ||
| 739 | |||
| 740 | status = get_query_result(conn, res, res_val_str, res_val); | ||
| 741 | |||
| 742 | gettimeofday(&timeval_end, NULL); | ||
| 743 | *res_time = timediff(timeval_start, timeval_end); | ||
| 744 | 923 | ||
| 745 | if (verbose) | 924 | result.query_processing_status = query_processing_state; |
| 746 | printf("Time elapsed: %f\n", *res_time); | ||
| 747 | 925 | ||
| 748 | return status; | 926 | return result; |
| 749 | } | 927 | } |
| 750 | 928 | ||
| 751 | double timediff(struct timeval start, struct timeval end) { | 929 | static double timediff(struct timeval start, struct timeval end) { |
| 752 | double diff; | 930 | double diff; |
| 753 | 931 | ||
| 754 | while (start.tv_usec > end.tv_usec) { | 932 | while (start.tv_usec > end.tv_usec) { |
| @@ -759,7 +937,7 @@ double timediff(struct timeval start, struct timeval end) { | |||
| 759 | return diff; | 937 | return diff; |
| 760 | } | 938 | } |
| 761 | 939 | ||
| 762 | void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { | 940 | static void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { |
| 763 | const char *errmsg = NULL; | 941 | const char *errmsg = NULL; |
| 764 | va_list ap; | 942 | va_list ap; |
| 765 | 943 | ||
diff --git a/plugins/check_dbi.d/config.h b/plugins/check_dbi.d/config.h new file mode 100644 index 00000000..25d74a12 --- /dev/null +++ b/plugins/check_dbi.d/config.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | #include "../../lib/monitoringplug.h" | ||
| 6 | #include "thresholds.h" | ||
| 7 | |||
| 8 | typedef enum { | ||
| 9 | METRIC_CONN_TIME, | ||
| 10 | METRIC_SERVER_VERSION, | ||
| 11 | METRIC_QUERY_RESULT, | ||
| 12 | METRIC_QUERY_TIME, | ||
| 13 | } check_dbi_metric; | ||
| 14 | |||
| 15 | typedef enum { | ||
| 16 | TYPE_NUMERIC, | ||
| 17 | TYPE_STRING, | ||
| 18 | } check_dbi_type; | ||
| 19 | |||
| 20 | typedef struct { | ||
| 21 | char *key; | ||
| 22 | char *value; | ||
| 23 | } driver_option_t; | ||
| 24 | |||
| 25 | typedef struct { | ||
| 26 | char *dbi_driver; | ||
| 27 | char *host; | ||
| 28 | |||
| 29 | driver_option_t *dbi_options; | ||
| 30 | size_t dbi_options_num; | ||
| 31 | |||
| 32 | char *database; | ||
| 33 | char *query; | ||
| 34 | |||
| 35 | char *expect; | ||
| 36 | char *expect_re_str; | ||
| 37 | int expect_re_cflags; | ||
| 38 | check_dbi_metric metric; | ||
| 39 | check_dbi_type type; | ||
| 40 | mp_thresholds thresholds; | ||
| 41 | |||
| 42 | bool output_format_is_set; | ||
| 43 | mp_output_format output_format; | ||
| 44 | } check_dbi_config; | ||
| 45 | |||
| 46 | check_dbi_config check_dbi_config_init() { | ||
| 47 | check_dbi_config tmp = { | ||
| 48 | .dbi_driver = NULL, | ||
| 49 | .host = NULL, | ||
| 50 | .dbi_options = NULL, | ||
| 51 | .dbi_options_num = 0, | ||
| 52 | .database = NULL, | ||
| 53 | .query = NULL, | ||
| 54 | |||
| 55 | .expect = NULL, | ||
| 56 | .expect_re_str = NULL, | ||
| 57 | .expect_re_cflags = 0, | ||
| 58 | .metric = METRIC_QUERY_RESULT, | ||
| 59 | .type = TYPE_NUMERIC, | ||
| 60 | |||
| 61 | .thresholds = mp_thresholds_init(), | ||
| 62 | |||
| 63 | .output_format_is_set = false, | ||
| 64 | }; | ||
| 65 | return tmp; | ||
| 66 | } | ||
diff --git a/plugins/check_dig.c b/plugins/check_dig.c index 2bbd1e05..9ea19e6a 100644 --- a/plugins/check_dig.c +++ b/plugins/check_dig.c | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * Monitoring check_dig plugin | 3 | * Monitoring check_dig plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2002-2024 Monitoring Plugins Development Team | 6 | * Copyright (c) 2002-2025 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| @@ -33,105 +33,132 @@ | |||
| 33 | * because on some architectures those strings are in non-writable memory */ | 33 | * because on some architectures those strings are in non-writable memory */ |
| 34 | 34 | ||
| 35 | const char *progname = "check_dig"; | 35 | const char *progname = "check_dig"; |
| 36 | const char *copyright = "2002-2024"; | 36 | const char *copyright = "2002-2025"; |
| 37 | const char *email = "devel@monitoring-plugins.org"; | 37 | const char *email = "devel@monitoring-plugins.org"; |
| 38 | 38 | ||
| 39 | #include <ctype.h> | ||
| 39 | #include "common.h" | 40 | #include "common.h" |
| 40 | #include "netutils.h" | 41 | #include "netutils.h" |
| 41 | #include "utils.h" | 42 | #include "utils.h" |
| 42 | #include "runcmd.h" | 43 | #include "runcmd.h" |
| 43 | 44 | ||
| 44 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 45 | #include "check_dig.d/config.h" |
| 45 | static int validate_arguments(void); | 46 | #include "states.h" |
| 47 | |||
| 48 | typedef struct { | ||
| 49 | int errorcode; | ||
| 50 | check_dig_config config; | ||
| 51 | } check_dig_config_wrapper; | ||
| 52 | static check_dig_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 53 | static check_dig_config_wrapper validate_arguments(check_dig_config_wrapper /*config_wrapper*/); | ||
| 54 | |||
| 46 | static void print_help(void); | 55 | static void print_help(void); |
| 47 | void print_usage(void); | 56 | void print_usage(void); |
| 48 | 57 | ||
| 49 | #define UNDEFINED 0 | 58 | static int verbose = 0; |
| 50 | #define DEFAULT_PORT 53 | ||
| 51 | #define DEFAULT_TRIES 2 | ||
| 52 | |||
| 53 | static char *query_address = NULL; | ||
| 54 | static char *record_type = "A"; | ||
| 55 | static char *expected_address = NULL; | ||
| 56 | static char *dns_server = NULL; | ||
| 57 | static char *dig_args = ""; | ||
| 58 | static char *query_transport = ""; | ||
| 59 | static bool verbose = false; | ||
| 60 | static int server_port = DEFAULT_PORT; | ||
| 61 | static int number_tries = DEFAULT_TRIES; | ||
| 62 | static double warning_interval = UNDEFINED; | ||
| 63 | static double critical_interval = UNDEFINED; | ||
| 64 | static struct timeval tv; | ||
| 65 | 59 | ||
| 66 | int main(int argc, char **argv) { | 60 | /* helpers for flag parsing */ |
| 67 | char *command_line; | 61 | static flag_list parse_flags_line(const char *line); |
| 68 | output chld_out; | 62 | static flag_list split_csv_trim(const char *csv); |
| 69 | output chld_err; | 63 | static bool flag_list_contains(const flag_list *list, const char *needle); |
| 70 | char *msg = NULL; | 64 | static void free_flag_list(flag_list *list); |
| 71 | size_t i; | ||
| 72 | char *t; | ||
| 73 | long microsec; | ||
| 74 | double elapsed_time; | ||
| 75 | int result = STATE_UNKNOWN; | ||
| 76 | int timeout_interval_dig; | ||
| 77 | 65 | ||
| 66 | int main(int argc, char **argv) { | ||
| 78 | setlocale(LC_ALL, ""); | 67 | setlocale(LC_ALL, ""); |
| 79 | bindtextdomain(PACKAGE, LOCALEDIR); | 68 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 80 | textdomain(PACKAGE); | 69 | textdomain(PACKAGE); |
| 81 | 70 | ||
| 82 | /* Set signal handling and alarm */ | 71 | /* Set signal handling and alarm */ |
| 83 | if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) | 72 | if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { |
| 84 | usage_va(_("Cannot catch SIGALRM")); | 73 | usage_va(_("Cannot catch SIGALRM")); |
| 74 | } | ||
| 85 | 75 | ||
| 86 | /* Parse extra opts if any */ | 76 | /* Parse extra opts if any */ |
| 87 | argv = np_extra_opts(&argc, argv, progname); | 77 | argv = np_extra_opts(&argc, argv, progname); |
| 88 | 78 | ||
| 89 | if (process_arguments(argc, argv) == ERROR) | 79 | check_dig_config_wrapper tmp_config = process_arguments(argc, argv); |
| 80 | if (tmp_config.errorcode == ERROR) { | ||
| 90 | usage_va(_("Could not parse arguments")); | 81 | usage_va(_("Could not parse arguments")); |
| 82 | } | ||
| 83 | |||
| 84 | const check_dig_config config = tmp_config.config; | ||
| 91 | 85 | ||
| 92 | /* dig applies the timeout to each try, so we need to work around this */ | 86 | /* dig applies the timeout to each try, so we need to work around this */ |
| 93 | timeout_interval_dig = timeout_interval / number_tries + number_tries; | 87 | int timeout_interval_dig = ((int)timeout_interval / config.number_tries) + config.number_tries; |
| 94 | 88 | ||
| 89 | char *command_line; | ||
| 95 | /* get the command to run */ | 90 | /* get the command to run */ |
| 96 | xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG, dig_args, query_transport, server_port, dns_server, | 91 | xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG, |
| 97 | query_address, record_type, number_tries, timeout_interval_dig); | 92 | config.dig_args, config.query_transport, config.server_port, config.dns_server, |
| 93 | config.query_address, config.record_type, config.number_tries, timeout_interval_dig); | ||
| 98 | 94 | ||
| 99 | alarm(timeout_interval); | 95 | alarm(timeout_interval); |
| 100 | gettimeofday(&tv, NULL); | 96 | struct timeval start_time; |
| 97 | gettimeofday(&start_time, NULL); | ||
| 101 | 98 | ||
| 102 | if (verbose) { | 99 | if (verbose) { |
| 103 | printf("%s\n", command_line); | 100 | printf("%s\n", command_line); |
| 104 | if (expected_address != NULL) { | 101 | if (config.expected_address != NULL) { |
| 105 | printf(_("Looking for: '%s'\n"), expected_address); | 102 | printf(_("Looking for: '%s'\n"), config.expected_address); |
| 106 | } else { | 103 | } else { |
| 107 | printf(_("Looking for: '%s'\n"), query_address); | 104 | printf(_("Looking for: '%s'\n"), config.query_address); |
| 108 | } | 105 | } |
| 109 | } | 106 | } |
| 110 | 107 | ||
| 108 | output chld_out; | ||
| 109 | output chld_err; | ||
| 110 | char *msg = NULL; | ||
| 111 | flag_list dig_flags = {.items = NULL, .count = 0}; | ||
| 112 | mp_state_enum result = STATE_UNKNOWN; | ||
| 113 | |||
| 111 | /* run the command */ | 114 | /* run the command */ |
| 112 | if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { | 115 | if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { |
| 113 | result = STATE_WARNING; | 116 | result = STATE_WARNING; |
| 114 | msg = (char *)_("dig returned an error status"); | 117 | msg = (char *)_("dig returned an error status"); |
| 115 | } | 118 | } |
| 116 | 119 | ||
| 117 | for (i = 0; i < chld_out.lines; i++) { | 120 | /* extract ';; flags: ...' from stdout (first occurrence) */ |
| 121 | for (size_t i = 0; i < chld_out.lines; i++) { | ||
| 122 | if (strstr(chld_out.line[i], "flags:")) { | ||
| 123 | if (verbose) { | ||
| 124 | printf("Raw flags line: %s\n", chld_out.line[i]); | ||
| 125 | } | ||
| 126 | |||
| 127 | dig_flags = parse_flags_line(chld_out.line[i]); | ||
| 128 | |||
| 129 | if (verbose && dig_flags.count > 0) { | ||
| 130 | printf(_("Parsed flags:")); | ||
| 131 | for (size_t k = 0; k < dig_flags.count; k++) { | ||
| 132 | printf(" %s", dig_flags.items[k]); | ||
| 133 | } | ||
| 134 | printf("\n"); | ||
| 135 | } | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | for (size_t i = 0; i < chld_out.lines; i++) { | ||
| 118 | /* the server is responding, we just got the host name... */ | 141 | /* the server is responding, we just got the host name... */ |
| 119 | if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { | 142 | if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { |
| 120 | 143 | ||
| 121 | /* loop through the whole 'ANSWER SECTION' */ | 144 | /* loop through the whole 'ANSWER SECTION' */ |
| 122 | for (; i < chld_out.lines; i++) { | 145 | for (; i < chld_out.lines; i++) { |
| 123 | /* get the host address */ | 146 | /* get the host address */ |
| 124 | if (verbose) | 147 | if (verbose) { |
| 125 | printf("%s\n", chld_out.line[i]); | 148 | printf("%s\n", chld_out.line[i]); |
| 149 | } | ||
| 126 | 150 | ||
| 127 | if (strcasestr(chld_out.line[i], (expected_address == NULL ? query_address : expected_address)) != NULL) { | 151 | if (strcasestr(chld_out.line[i], (config.expected_address == NULL |
| 152 | ? config.query_address | ||
| 153 | : config.expected_address)) != NULL) { | ||
| 128 | msg = chld_out.line[i]; | 154 | msg = chld_out.line[i]; |
| 129 | result = STATE_OK; | 155 | result = STATE_OK; |
| 130 | 156 | ||
| 131 | /* Translate output TAB -> SPACE */ | 157 | /* Translate output TAB -> SPACE */ |
| 132 | t = msg; | 158 | char *temp = msg; |
| 133 | while ((t = strchr(t, '\t')) != NULL) | 159 | while ((temp = strchr(temp, '\t')) != NULL) { |
| 134 | *t = ' '; | 160 | *temp = ' '; |
| 161 | } | ||
| 135 | break; | 162 | break; |
| 136 | } | 163 | } |
| 137 | } | 164 | } |
| @@ -154,43 +181,84 @@ int main(int argc, char **argv) { | |||
| 154 | /* If we get anything on STDERR, at least set warning */ | 181 | /* If we get anything on STDERR, at least set warning */ |
| 155 | if (chld_err.buflen > 0) { | 182 | if (chld_err.buflen > 0) { |
| 156 | result = max_state(result, STATE_WARNING); | 183 | result = max_state(result, STATE_WARNING); |
| 157 | if (!msg) | 184 | if (!msg) { |
| 158 | for (i = 0; i < chld_err.lines; i++) { | 185 | for (size_t i = 0; i < chld_err.lines; i++) { |
| 159 | msg = strchr(chld_err.line[0], ':'); | 186 | msg = strchr(chld_err.line[0], ':'); |
| 160 | if (msg) { | 187 | if (msg) { |
| 161 | msg++; | 188 | msg++; |
| 162 | break; | 189 | break; |
| 163 | } | 190 | } |
| 164 | } | 191 | } |
| 192 | } | ||
| 165 | } | 193 | } |
| 166 | 194 | ||
| 167 | microsec = deltime(tv); | 195 | long microsec = deltime(start_time); |
| 168 | elapsed_time = (double)microsec / 1.0e6; | 196 | double elapsed_time = (double)microsec / 1.0e6; |
| 169 | 197 | ||
| 170 | if (critical_interval > UNDEFINED && elapsed_time > critical_interval) | 198 | if (config.critical_interval > UNDEFINED && elapsed_time > config.critical_interval) { |
| 171 | result = STATE_CRITICAL; | 199 | result = STATE_CRITICAL; |
| 200 | } | ||
| 172 | 201 | ||
| 173 | else if (warning_interval > UNDEFINED && elapsed_time > warning_interval) | 202 | else if (config.warning_interval > UNDEFINED && elapsed_time > config.warning_interval) { |
| 174 | result = STATE_WARNING; | 203 | result = STATE_WARNING; |
| 204 | } | ||
| 205 | |||
| 206 | /* Optional: evaluate dig flags only if -E/-X were provided */ | ||
| 207 | if ((config.require_flags.count > 0) || (config.forbid_flags.count > 0)) { | ||
| 208 | if (dig_flags.count > 0) { | ||
| 209 | for (size_t r = 0; r < config.require_flags.count; r++) { | ||
| 210 | if (!flag_list_contains(&dig_flags, config.require_flags.items[r])) { | ||
| 211 | result = STATE_CRITICAL; | ||
| 212 | if (!msg) { | ||
| 213 | xasprintf(&msg, _("Missing required DNS flag: %s"), | ||
| 214 | config.require_flags.items[r]); | ||
| 215 | } else { | ||
| 216 | char *newmsg = NULL; | ||
| 217 | xasprintf(&newmsg, _("%s; missing required DNS flag: %s"), msg, | ||
| 218 | config.require_flags.items[r]); | ||
| 219 | msg = newmsg; | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | for (size_t r = 0; r < config.forbid_flags.count; r++) { | ||
| 225 | if (flag_list_contains(&dig_flags, config.forbid_flags.items[r])) { | ||
| 226 | result = STATE_CRITICAL; | ||
| 227 | if (!msg) { | ||
| 228 | xasprintf(&msg, _("Forbidden DNS flag present: %s"), | ||
| 229 | config.forbid_flags.items[r]); | ||
| 230 | } else { | ||
| 231 | char *newmsg = NULL; | ||
| 232 | xasprintf(&newmsg, _("%s; forbidden DNS flag present: %s"), msg, | ||
| 233 | config.forbid_flags.items[r]); | ||
| 234 | msg = newmsg; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | /* cleanup flags buffer */ | ||
| 242 | free_flag_list(&dig_flags); | ||
| 175 | 243 | ||
| 176 | printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, | 244 | printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, |
| 177 | msg ? msg : _("Probably a non-existent host/domain"), | 245 | msg ? msg : _("Probably a non-existent host/domain"), |
| 178 | fperfdata("time", elapsed_time, "s", (warning_interval > UNDEFINED ? true : false), warning_interval, | 246 | fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED), |
| 179 | (critical_interval > UNDEFINED ? true : false), critical_interval, true, 0, false, 0)); | 247 | config.warning_interval, (config.critical_interval > UNDEFINED), |
| 180 | return result; | 248 | config.critical_interval, true, 0, false, 0)); |
| 249 | exit(result); | ||
| 181 | } | 250 | } |
| 182 | 251 | ||
| 183 | /* process command-line arguments */ | 252 | /* process command-line arguments */ |
| 184 | int process_arguments(int argc, char **argv) { | 253 | check_dig_config_wrapper process_arguments(int argc, char **argv) { |
| 185 | int c; | ||
| 186 | |||
| 187 | int option = 0; | ||
| 188 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, | 254 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 189 | {"query_address", required_argument, 0, 'l'}, | 255 | {"query_address", required_argument, 0, 'l'}, |
| 190 | {"warning", required_argument, 0, 'w'}, | 256 | {"warning", required_argument, 0, 'w'}, |
| 191 | {"critical", required_argument, 0, 'c'}, | 257 | {"critical", required_argument, 0, 'c'}, |
| 192 | {"timeout", required_argument, 0, 't'}, | 258 | {"timeout", required_argument, 0, 't'}, |
| 193 | {"dig-arguments", required_argument, 0, 'A'}, | 259 | {"dig-arguments", required_argument, 0, 'A'}, |
| 260 | {"require-flags", required_argument, 0, 'E'}, | ||
| 261 | {"forbid-flags", required_argument, 0, 'X'}, | ||
| 194 | {"verbose", no_argument, 0, 'v'}, | 262 | {"verbose", no_argument, 0, 'v'}, |
| 195 | {"version", no_argument, 0, 'V'}, | 263 | {"version", no_argument, 0, 'V'}, |
| 196 | {"help", no_argument, 0, 'h'}, | 264 | {"help", no_argument, 0, 'h'}, |
| @@ -201,16 +269,26 @@ int process_arguments(int argc, char **argv) { | |||
| 201 | {"use-ipv6", no_argument, 0, '6'}, | 269 | {"use-ipv6", no_argument, 0, '6'}, |
| 202 | {0, 0, 0, 0}}; | 270 | {0, 0, 0, 0}}; |
| 203 | 271 | ||
| 204 | if (argc < 2) | 272 | check_dig_config_wrapper result = { |
| 205 | return ERROR; | 273 | .errorcode = OK, |
| 274 | .config = check_dig_config_init(), | ||
| 275 | }; | ||
| 276 | |||
| 277 | if (argc < 2) { | ||
| 278 | result.errorcode = ERROR; | ||
| 279 | return result; | ||
| 280 | } | ||
| 206 | 281 | ||
| 207 | while (1) { | 282 | int option = 0; |
| 208 | c = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); | 283 | while (true) { |
| 284 | int option_index = | ||
| 285 | getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:E:X:46", longopts, &option); | ||
| 209 | 286 | ||
| 210 | if (c == -1 || c == EOF) | 287 | if (option_index == -1 || option_index == EOF) { |
| 211 | break; | 288 | break; |
| 289 | } | ||
| 212 | 290 | ||
| 213 | switch (c) { | 291 | switch (option_index) { |
| 214 | case 'h': /* help */ | 292 | case 'h': /* help */ |
| 215 | print_help(); | 293 | print_help(); |
| 216 | exit(STATE_UNKNOWN); | 294 | exit(STATE_UNKNOWN); |
| @@ -219,28 +297,28 @@ int process_arguments(int argc, char **argv) { | |||
| 219 | exit(STATE_UNKNOWN); | 297 | exit(STATE_UNKNOWN); |
| 220 | case 'H': /* hostname */ | 298 | case 'H': /* hostname */ |
| 221 | host_or_die(optarg); | 299 | host_or_die(optarg); |
| 222 | dns_server = optarg; | 300 | result.config.dns_server = optarg; |
| 223 | break; | 301 | break; |
| 224 | case 'p': /* server port */ | 302 | case 'p': /* server port */ |
| 225 | if (is_intpos(optarg)) { | 303 | if (is_intpos(optarg)) { |
| 226 | server_port = atoi(optarg); | 304 | result.config.server_port = atoi(optarg); |
| 227 | } else { | 305 | } else { |
| 228 | usage_va(_("Port must be a positive integer - %s"), optarg); | 306 | usage_va(_("Port must be a positive integer - %s"), optarg); |
| 229 | } | 307 | } |
| 230 | break; | 308 | break; |
| 231 | case 'l': /* address to lookup */ | 309 | case 'l': /* address to lookup */ |
| 232 | query_address = optarg; | 310 | result.config.query_address = optarg; |
| 233 | break; | 311 | break; |
| 234 | case 'w': /* warning */ | 312 | case 'w': /* warning */ |
| 235 | if (is_nonnegative(optarg)) { | 313 | if (is_nonnegative(optarg)) { |
| 236 | warning_interval = strtod(optarg, NULL); | 314 | result.config.warning_interval = strtod(optarg, NULL); |
| 237 | } else { | 315 | } else { |
| 238 | usage_va(_("Warning interval must be a positive integer - %s"), optarg); | 316 | usage_va(_("Warning interval must be a positive integer - %s"), optarg); |
| 239 | } | 317 | } |
| 240 | break; | 318 | break; |
| 241 | case 'c': /* critical */ | 319 | case 'c': /* critical */ |
| 242 | if (is_nonnegative(optarg)) { | 320 | if (is_nonnegative(optarg)) { |
| 243 | critical_interval = strtod(optarg, NULL); | 321 | result.config.critical_interval = strtod(optarg, NULL); |
| 244 | } else { | 322 | } else { |
| 245 | usage_va(_("Critical interval must be a positive integer - %s"), optarg); | 323 | usage_va(_("Critical interval must be a positive integer - %s"), optarg); |
| 246 | } | 324 | } |
| @@ -253,48 +331,56 @@ int process_arguments(int argc, char **argv) { | |||
| 253 | } | 331 | } |
| 254 | break; | 332 | break; |
| 255 | case 'A': /* dig arguments */ | 333 | case 'A': /* dig arguments */ |
| 256 | dig_args = strdup(optarg); | 334 | result.config.dig_args = strdup(optarg); |
| 335 | break; | ||
| 336 | case 'E': /* require flags */ | ||
| 337 | result.config.require_flags = split_csv_trim(optarg); | ||
| 338 | break; | ||
| 339 | case 'X': /* forbid flags */ | ||
| 340 | result.config.forbid_flags = split_csv_trim(optarg); | ||
| 257 | break; | 341 | break; |
| 258 | case 'v': /* verbose */ | 342 | case 'v': /* verbose */ |
| 259 | verbose = true; | 343 | verbose++; |
| 260 | break; | 344 | break; |
| 261 | case 'T': | 345 | case 'T': |
| 262 | record_type = optarg; | 346 | result.config.record_type = optarg; |
| 263 | break; | 347 | break; |
| 264 | case 'a': | 348 | case 'a': |
| 265 | expected_address = optarg; | 349 | result.config.expected_address = optarg; |
| 266 | break; | 350 | break; |
| 267 | case '4': | 351 | case '4': |
| 268 | query_transport = "-4"; | 352 | result.config.query_transport = "-4"; |
| 269 | break; | 353 | break; |
| 270 | case '6': | 354 | case '6': |
| 271 | query_transport = "-6"; | 355 | result.config.query_transport = "-6"; |
| 272 | break; | 356 | break; |
| 273 | default: /* usage5 */ | 357 | default: /* usage5 */ |
| 274 | usage5(); | 358 | usage5(); |
| 275 | } | 359 | } |
| 276 | } | 360 | } |
| 277 | 361 | ||
| 278 | c = optind; | 362 | int index = optind; |
| 279 | if (dns_server == NULL) { | 363 | if (result.config.dns_server == NULL) { |
| 280 | if (c < argc) { | 364 | if (index < argc) { |
| 281 | host_or_die(argv[c]); | 365 | host_or_die(argv[index]); |
| 282 | dns_server = argv[c]; | 366 | result.config.dns_server = argv[index]; |
| 283 | } else { | 367 | } else { |
| 284 | if (strcmp(query_transport, "-6") == 0) | 368 | if (strcmp(result.config.query_transport, "-6") == 0) { |
| 285 | dns_server = strdup("::1"); | 369 | result.config.dns_server = strdup("::1"); |
| 286 | else | 370 | } else { |
| 287 | dns_server = strdup("127.0.0.1"); | 371 | result.config.dns_server = strdup("127.0.0.1"); |
| 372 | } | ||
| 288 | } | 373 | } |
| 289 | } | 374 | } |
| 290 | 375 | ||
| 291 | return validate_arguments(); | 376 | return validate_arguments(result); |
| 292 | } | 377 | } |
| 293 | 378 | ||
| 294 | int validate_arguments(void) { | 379 | check_dig_config_wrapper validate_arguments(check_dig_config_wrapper config_wrapper) { |
| 295 | if (query_address != NULL) | 380 | if (config_wrapper.config.query_address == NULL) { |
| 296 | return OK; | 381 | config_wrapper.errorcode = ERROR; |
| 297 | return ERROR; | 382 | } |
| 383 | return config_wrapper; | ||
| 298 | } | 384 | } |
| 299 | 385 | ||
| 300 | void print_help(void) { | 386 | void print_help(void) { |
| @@ -328,10 +414,15 @@ void print_help(void) { | |||
| 328 | printf(" %s\n", "-T, --record_type=STRING"); | 414 | printf(" %s\n", "-T, --record_type=STRING"); |
| 329 | printf(" %s\n", _("Record type to lookup (default: A)")); | 415 | printf(" %s\n", _("Record type to lookup (default: A)")); |
| 330 | printf(" %s\n", "-a, --expected_address=STRING"); | 416 | printf(" %s\n", "-a, --expected_address=STRING"); |
| 331 | printf(" %s\n", _("An address expected to be in the answer section. If not set, uses whatever")); | 417 | printf(" %s\n", |
| 418 | _("An address expected to be in the answer section. If not set, uses whatever")); | ||
| 332 | printf(" %s\n", _("was in -l")); | 419 | printf(" %s\n", _("was in -l")); |
| 333 | printf(" %s\n", "-A, --dig-arguments=STRING"); | 420 | printf(" %s\n", "-A, --dig-arguments=STRING"); |
| 334 | printf(" %s\n", _("Pass STRING as argument(s) to dig")); | 421 | printf(" %s\n", _("Pass STRING as argument(s) to dig")); |
| 422 | printf(" %s\n", "-E, --require-flags=LIST"); | ||
| 423 | printf(" %s\n", _("Comma-separated dig flags that must be present (e.g. 'aa,qr')")); | ||
| 424 | printf(" %s\n", "-X, --forbid-flags=LIST"); | ||
| 425 | printf(" %s\n", _("Comma-separated dig flags that must NOT be present")); | ||
| 335 | printf(UT_WARN_CRIT); | 426 | printf(UT_WARN_CRIT); |
| 336 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 427 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 337 | printf(UT_VERBOSE); | 428 | printf(UT_VERBOSE); |
| @@ -348,5 +439,185 @@ void print_usage(void) { | |||
| 348 | printf("%s\n", _("Usage:")); | 439 | printf("%s\n", _("Usage:")); |
| 349 | printf("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname); | 440 | printf("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname); |
| 350 | printf(" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n"); | 441 | printf(" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n"); |
| 351 | printf(" [-t <timeout>] [-a <expected answer address>] [-v]\n"); | 442 | printf(" [-t <timeout>] [-a <expected answer address>] [-E <flags>] [-X <flags>] [-v]\n"); |
| 443 | } | ||
| 444 | |||
| 445 | /* helpers */ | ||
| 446 | |||
| 447 | /** | ||
| 448 | * parse_flags_line - Parse a dig output line and extract DNS header flags. | ||
| 449 | * | ||
| 450 | * Input: | ||
| 451 | * line - NUL terminated dig output line, e.g. ";; flags: qr rd ra; ..." | ||
| 452 | * | ||
| 453 | * Returns: | ||
| 454 | * flag_list where: | ||
| 455 | * - items: array of NUL terminated flag strings (heap allocated) | ||
| 456 | * - count: number of entries in items | ||
| 457 | * On parse failure or if no flags were found, count is 0 and items is NULL. | ||
| 458 | */ | ||
| 459 | static flag_list parse_flags_line(const char *line) { | ||
| 460 | flag_list result = {.items = NULL, .count = 0}; | ||
| 461 | |||
| 462 | if (!line) { | ||
| 463 | return result; | ||
| 464 | } | ||
| 465 | |||
| 466 | /* Locate start of DNS header flags in dig output */ | ||
| 467 | const char *p = strstr(line, "flags:"); | ||
| 468 | if (!p) { | ||
| 469 | return result; | ||
| 470 | } | ||
| 471 | p += 6; /* skip literal "flags:" */ | ||
| 472 | |||
| 473 | /* Skip whitespace after "flags:" */ | ||
| 474 | while (*p && isspace((unsigned char)*p)) { | ||
| 475 | p++; | ||
| 476 | } | ||
| 477 | |||
| 478 | /* Flags are terminated by the next semicolon e.g. "qr rd ra;" */ | ||
| 479 | const char *q = strchr(p, ';'); | ||
| 480 | if (!q) { | ||
| 481 | return result; | ||
| 482 | } | ||
| 483 | |||
| 484 | /* Extract substring containing the flag block */ | ||
| 485 | size_t len = (size_t)(q - p); | ||
| 486 | if (len == 0) { | ||
| 487 | return result; | ||
| 488 | } | ||
| 489 | |||
| 490 | char *buf = (char *)malloc(len + 1); | ||
| 491 | if (!buf) { | ||
| 492 | return result; | ||
| 493 | } | ||
| 494 | memcpy(buf, p, len); | ||
| 495 | buf[len] = '\0'; | ||
| 496 | |||
| 497 | /* Tokenize flags separated by whitespace */ | ||
| 498 | char **arr = NULL; | ||
| 499 | size_t cnt = 0; | ||
| 500 | char *saveptr = NULL; | ||
| 501 | char *tok = strtok_r(buf, " \t", &saveptr); | ||
| 502 | |||
| 503 | while (tok) { | ||
| 504 | /* Expand array for the next flag token */ | ||
| 505 | char **tmp = (char **)realloc(arr, (cnt + 1) * sizeof(char *)); | ||
| 506 | if (!tmp) { | ||
| 507 | /* On allocation failure keep what we have and return it */ | ||
| 508 | break; | ||
| 509 | } | ||
| 510 | arr = tmp; | ||
| 511 | arr[cnt++] = strdup(tok); | ||
| 512 | tok = strtok_r(NULL, " \t", &saveptr); | ||
| 513 | } | ||
| 514 | |||
| 515 | free(buf); | ||
| 516 | |||
| 517 | result.items = arr; | ||
| 518 | result.count = cnt; | ||
| 519 | return result; | ||
| 520 | } | ||
| 521 | |||
| 522 | /** | ||
| 523 | * split_csv_trim - Split a comma separated string into trimmed tokens. | ||
| 524 | * | ||
| 525 | * Input: | ||
| 526 | * csv - NUL terminated string, e.g. "aa, qr , rd" | ||
| 527 | * | ||
| 528 | * Returns: | ||
| 529 | * flag_list where: | ||
| 530 | * - items: array of NUL terminated tokens (heap allocated, whitespace trimmed) | ||
| 531 | * - count: number of tokens | ||
| 532 | * On empty input, count is 0 and items is NULL | ||
| 533 | */ | ||
| 534 | static flag_list split_csv_trim(const char *csv) { | ||
| 535 | flag_list result = {.items = NULL, .count = 0}; | ||
| 536 | |||
| 537 | if (!csv || !*csv) { | ||
| 538 | return result; | ||
| 539 | } | ||
| 540 | |||
| 541 | char *tmp = strdup(csv); | ||
| 542 | if (!tmp) { | ||
| 543 | return result; | ||
| 544 | } | ||
| 545 | |||
| 546 | char *s = tmp; | ||
| 547 | char *token = NULL; | ||
| 548 | |||
| 549 | /* Split CSV by commas, trimming whitespace on each token */ | ||
| 550 | while ((token = strsep(&s, ",")) != NULL) { | ||
| 551 | /* trim leading whitespace */ | ||
| 552 | while (*token && isspace((unsigned char)*token)) { | ||
| 553 | token++; | ||
| 554 | } | ||
| 555 | |||
| 556 | /* trim trailing whitespace */ | ||
| 557 | char *end = token + strlen(token); | ||
| 558 | while (end > token && isspace((unsigned char)end[-1])) { | ||
| 559 | *--end = '\0'; | ||
| 560 | } | ||
| 561 | |||
| 562 | if (*token) { | ||
| 563 | /* Expand the items array and append the token */ | ||
| 564 | char **arr = (char **)realloc(result.items, (result.count + 1) * sizeof(char *)); | ||
| 565 | if (!arr) { | ||
| 566 | /* Allocation failed, stop and return what we have */ | ||
| 567 | break; | ||
| 568 | } | ||
| 569 | result.items = arr; | ||
| 570 | result.items[result.count++] = strdup(token); | ||
| 571 | } | ||
| 572 | } | ||
| 573 | |||
| 574 | free(tmp); | ||
| 575 | return result; | ||
| 576 | } | ||
| 577 | |||
| 578 | /** | ||
| 579 | * flag_list_contains - Case-insensitive membership test in a flag_list. | ||
| 580 | * | ||
| 581 | * Input: | ||
| 582 | * list - pointer to a flag_list | ||
| 583 | * needle - NUL terminated string to search for | ||
| 584 | * | ||
| 585 | * Returns: | ||
| 586 | * true if needle is contained in list (strcasecmp) | ||
| 587 | * false otherwise | ||
| 588 | */ | ||
| 589 | static bool flag_list_contains(const flag_list *list, const char *needle) { | ||
| 590 | if (!list || !needle || !*needle) { | ||
| 591 | return false; | ||
| 592 | } | ||
| 593 | |||
| 594 | for (size_t i = 0; i < list->count; i++) { | ||
| 595 | if (strcasecmp(list->items[i], needle) == 0) { | ||
| 596 | return true; | ||
| 597 | } | ||
| 598 | } | ||
| 599 | return false; | ||
| 600 | } | ||
| 601 | |||
| 602 | /** | ||
| 603 | * free_flag_list - Release all heap allocations held by a flag_list. | ||
| 604 | * | ||
| 605 | * Input: | ||
| 606 | * list - pointer to a flag_list whose items were allocated by | ||
| 607 | * parse_flags_line() or split_csv_trim(). | ||
| 608 | * | ||
| 609 | * After this call list->items is NULL and list->count is 0. | ||
| 610 | */ | ||
| 611 | static void free_flag_list(flag_list *list) { | ||
| 612 | if (!list || !list->items) { | ||
| 613 | return; | ||
| 614 | } | ||
| 615 | |||
| 616 | for (size_t i = 0; i < list->count; i++) { | ||
| 617 | free(list->items[i]); | ||
| 618 | } | ||
| 619 | free(list->items); | ||
| 620 | |||
| 621 | list->items = NULL; | ||
| 622 | list->count = 0; | ||
| 352 | } | 623 | } |
diff --git a/plugins/check_dig.d/config.h b/plugins/check_dig.d/config.h new file mode 100644 index 00000000..dd1f58b5 --- /dev/null +++ b/plugins/check_dig.d/config.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | #define UNDEFINED 0 | ||
| 7 | #define DEFAULT_PORT 53 | ||
| 8 | #define DEFAULT_TRIES 2 | ||
| 9 | |||
| 10 | typedef struct { | ||
| 11 | char **items; | ||
| 12 | size_t count; | ||
| 13 | } flag_list; | ||
| 14 | |||
| 15 | typedef struct { | ||
| 16 | char *query_address; | ||
| 17 | char *record_type; | ||
| 18 | char *expected_address; | ||
| 19 | char *dns_server; | ||
| 20 | char *query_transport; | ||
| 21 | int server_port; | ||
| 22 | char *dig_args; | ||
| 23 | int number_tries; | ||
| 24 | |||
| 25 | double warning_interval; | ||
| 26 | double critical_interval; | ||
| 27 | flag_list require_flags; | ||
| 28 | flag_list forbid_flags; | ||
| 29 | } check_dig_config; | ||
| 30 | |||
| 31 | check_dig_config check_dig_config_init() { | ||
| 32 | check_dig_config tmp = { | ||
| 33 | .query_address = NULL, | ||
| 34 | .record_type = "A", | ||
| 35 | .expected_address = NULL, | ||
| 36 | .dns_server = NULL, | ||
| 37 | .query_transport = "", | ||
| 38 | .server_port = DEFAULT_PORT, | ||
| 39 | .dig_args = "", | ||
| 40 | .number_tries = DEFAULT_TRIES, | ||
| 41 | |||
| 42 | .warning_interval = UNDEFINED, | ||
| 43 | .critical_interval = UNDEFINED, | ||
| 44 | .require_flags = {.count = 0, .items = NULL}, | ||
| 45 | .forbid_flags = {.count = 0, .items = NULL}, | ||
| 46 | }; | ||
| 47 | return tmp; | ||
| 48 | } | ||
diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 037a6f7a..d42b5486 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c | |||
| @@ -31,24 +31,35 @@ const char *program_name = "check_disk"; /* Required for coreutils libs */ | |||
| 31 | const char *copyright = "1999-2024"; | 31 | const char *copyright = "1999-2024"; |
| 32 | const char *email = "devel@monitoring-plugins.org"; | 32 | const char *email = "devel@monitoring-plugins.org"; |
| 33 | 33 | ||
| 34 | #include "states.h" | ||
| 34 | #include "common.h" | 35 | #include "common.h" |
| 36 | #include "output.h" | ||
| 37 | #include "perfdata.h" | ||
| 38 | #include "utils_base.h" | ||
| 39 | #include "lib/thresholds.h" | ||
| 40 | |||
| 35 | #ifdef HAVE_SYS_STAT_H | 41 | #ifdef HAVE_SYS_STAT_H |
| 36 | # include <sys/stat.h> | 42 | # include <sys/stat.h> |
| 37 | #endif | 43 | #endif |
| 44 | |||
| 38 | #if HAVE_INTTYPES_H | 45 | #if HAVE_INTTYPES_H |
| 39 | # include <inttypes.h> | 46 | # include <inttypes.h> |
| 40 | #endif | 47 | #endif |
| 48 | |||
| 41 | #include <assert.h> | 49 | #include <assert.h> |
| 42 | #include "popen.h" | ||
| 43 | #include "utils.h" | ||
| 44 | #include "utils_disk.h" | ||
| 45 | #include <stdarg.h> | 50 | #include <stdarg.h> |
| 46 | #include "fsusage.h" | 51 | #include <stdint.h> |
| 47 | #include "mountlist.h" | ||
| 48 | #include <float.h> | 52 | #include <float.h> |
| 53 | #include "./popen.h" | ||
| 54 | #include "./utils.h" | ||
| 55 | #include "../gl/fsusage.h" | ||
| 56 | #include "../gl/mountlist.h" | ||
| 57 | #include "./check_disk.d/utils_disk.h" | ||
| 58 | |||
| 49 | #if HAVE_LIMITS_H | 59 | #if HAVE_LIMITS_H |
| 50 | # include <limits.h> | 60 | # include <limits.h> |
| 51 | #endif | 61 | #endif |
| 62 | |||
| 52 | #include "regex.h" | 63 | #include "regex.h" |
| 53 | 64 | ||
| 54 | #ifdef __CYGWIN__ | 65 | #ifdef __CYGWIN__ |
| @@ -57,424 +68,322 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 57 | # define ERROR -1 | 68 | # define ERROR -1 |
| 58 | #endif | 69 | #endif |
| 59 | 70 | ||
| 60 | /* If nonzero, show even filesystems with zero size or | ||
| 61 | uninteresting types. */ | ||
| 62 | static int show_all_fs = 1; | ||
| 63 | |||
| 64 | /* If nonzero, show only local filesystems. */ | ||
| 65 | static int show_local_fs = 0; | ||
| 66 | |||
| 67 | /* If nonzero, show only local filesystems but call stat() on remote ones. */ | ||
| 68 | static int stat_remote_fs = 0; | ||
| 69 | |||
| 70 | /* If positive, the units to use when printing sizes; | ||
| 71 | if negative, the human-readable base. */ | ||
| 72 | /* static int output_block_size; */ | ||
| 73 | |||
| 74 | /* If nonzero, invoke the `sync' system call before getting any usage data. | ||
| 75 | Using this option can make df very slow, especially with many or very | ||
| 76 | busy disks. Note that this may make a difference on some systems -- | ||
| 77 | SunOs4.1.3, for one. It is *not* necessary on Linux. */ | ||
| 78 | /* static int require_sync = 0; */ | ||
| 79 | |||
| 80 | /* Linked list of filesystem types to display. | ||
| 81 | If `fs_select_list' is NULL, list all types. | ||
| 82 | This table is generated dynamically from command-line options, | ||
| 83 | rather than hardcoding into the program what it thinks are the | ||
| 84 | valid filesystem types; let the user specify any filesystem type | ||
| 85 | they want to, and if there are any filesystems of that type, they | ||
| 86 | will be shown. | ||
| 87 | |||
| 88 | Some filesystem types: | ||
| 89 | 4.2 4.3 ufs nfs swap ignore io vm efs dbg */ | ||
| 90 | |||
| 91 | /* static struct parameter_list *fs_select_list; */ | ||
| 92 | |||
| 93 | /* Linked list of filesystem types to omit. | ||
| 94 | If the list is empty, don't exclude any types. */ | ||
| 95 | static struct regex_list *fs_exclude_list = NULL; | ||
| 96 | |||
| 97 | /* Linked list of filesystem types to check. | ||
| 98 | If the list is empty, include all types. */ | ||
| 99 | static struct regex_list *fs_include_list; | ||
| 100 | |||
| 101 | static struct name_list *dp_exclude_list; | ||
| 102 | |||
| 103 | static struct parameter_list *path_select_list = NULL; | ||
| 104 | |||
| 105 | /* Linked list of mounted filesystems. */ | ||
| 106 | static struct mount_entry *mount_list; | ||
| 107 | |||
| 108 | /* For long options that have no equivalent short option, use a | ||
| 109 | non-character as a pseudo short option, starting with CHAR_MAX + 1. */ | ||
| 110 | enum { | ||
| 111 | SYNC_OPTION = CHAR_MAX + 1, | ||
| 112 | NO_SYNC_OPTION, | ||
| 113 | BLOCK_SIZE_OPTION | ||
| 114 | }; | ||
| 115 | |||
| 116 | #ifdef _AIX | 71 | #ifdef _AIX |
| 117 | # pragma alloca | 72 | # pragma alloca |
| 118 | #endif | 73 | #endif |
| 119 | 74 | ||
| 120 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 75 | typedef struct { |
| 121 | static void set_all_thresholds(struct parameter_list *path); | 76 | int errorcode; |
| 122 | static void print_help(void); | 77 | check_disk_config config; |
| 78 | } check_disk_config_wrapper; | ||
| 79 | static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 80 | |||
| 81 | static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, | ||
| 82 | char *crit_freespace_units, char *warn_freespace_percent, | ||
| 83 | char *crit_freespace_percent, char *warn_freeinodes_percent, | ||
| 84 | char *crit_freeinodes_percent); | ||
| 85 | static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/); | ||
| 86 | static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/); | ||
| 87 | |||
| 88 | /* | ||
| 89 | * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control | ||
| 90 | * how reserved and inodes should be judged (ignored or not) | ||
| 91 | */ | ||
| 92 | static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp, | ||
| 93 | bool freespace_ignore_reserved); | ||
| 94 | static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, | ||
| 95 | bool display_inodes_perfdata, byte_unit unit); | ||
| 96 | |||
| 123 | void print_usage(void); | 97 | void print_usage(void); |
| 124 | static double calculate_percent(uintmax_t, uintmax_t); | 98 | static void print_help(void); |
| 125 | static bool stat_path(struct parameter_list *p); | ||
| 126 | static void get_stats(struct parameter_list *p, struct fs_usage *fsp); | ||
| 127 | static void get_path_stats(struct parameter_list *p, struct fs_usage *fsp); | ||
| 128 | 99 | ||
| 129 | static char *units; | ||
| 130 | static uintmax_t mult = 1024 * 1024; | ||
| 131 | static int verbose = 0; | 100 | static int verbose = 0; |
| 132 | static bool erronly = false; | ||
| 133 | static bool display_mntp = false; | ||
| 134 | static bool exact_match = false; | ||
| 135 | static bool ignore_missing = false; | ||
| 136 | static bool freespace_ignore_reserved = false; | ||
| 137 | static bool display_inodes_perfdata = false; | ||
| 138 | static char *warn_freespace_units = NULL; | ||
| 139 | static char *crit_freespace_units = NULL; | ||
| 140 | static char *warn_freespace_percent = NULL; | ||
| 141 | static char *crit_freespace_percent = NULL; | ||
| 142 | static char *warn_usedspace_units = NULL; | ||
| 143 | static char *crit_usedspace_units = NULL; | ||
| 144 | static char *warn_usedspace_percent = NULL; | ||
| 145 | static char *crit_usedspace_percent = NULL; | ||
| 146 | static char *warn_usedinodes_percent = NULL; | ||
| 147 | static char *crit_usedinodes_percent = NULL; | ||
| 148 | static char *warn_freeinodes_percent = NULL; | ||
| 149 | static char *crit_freeinodes_percent = NULL; | ||
| 150 | static bool path_selected = false; | ||
| 151 | static bool path_ignored = false; | ||
| 152 | static char *group = NULL; | ||
| 153 | static struct stat *stat_buf; | ||
| 154 | static struct name_list *seen = NULL; | ||
| 155 | 101 | ||
| 156 | int main(int argc, char **argv) { | 102 | // This would not be necessary in C23!! |
| 157 | int result = STATE_UNKNOWN; | 103 | const byte_unit Bytes_Factor = 1; |
| 158 | int disk_result = STATE_UNKNOWN; | 104 | const byte_unit KibiBytes_factor = 1024; |
| 159 | char *output = NULL; | 105 | const byte_unit MebiBytes_factor = 1048576; |
| 160 | char *ignored = NULL; | 106 | const byte_unit GibiBytes_factor = 1073741824; |
| 161 | char *details = NULL; | 107 | const byte_unit TebiBytes_factor = 1099511627776; |
| 162 | char *perf = NULL; | 108 | const byte_unit PebiBytes_factor = 1125899906842624; |
| 163 | char *perf_ilabel = NULL; | 109 | const byte_unit ExbiBytes_factor = 1152921504606846976; |
| 164 | char *preamble = " - free space:"; | 110 | const byte_unit KiloBytes_factor = 1000; |
| 165 | char *ignored_preamble = " - ignored paths:"; | 111 | const byte_unit MegaBytes_factor = 1000000; |
| 166 | char *flag_header = NULL; | 112 | const byte_unit GigaBytes_factor = 1000000000; |
| 167 | int temp_result = STATE_UNKNOWN; | 113 | const byte_unit TeraBytes_factor = 1000000000000; |
| 168 | 114 | const byte_unit PetaBytes_factor = 1000000000000000; | |
| 169 | struct mount_entry *me = NULL; | 115 | const byte_unit ExaBytes_factor = 1000000000000000000; |
| 170 | struct fs_usage fsp = {0}; | ||
| 171 | struct parameter_list *temp_list = NULL; | ||
| 172 | struct parameter_list *path = NULL; | ||
| 173 | |||
| 174 | #ifdef __CYGWIN__ | ||
| 175 | char mountdir[32]; | ||
| 176 | #endif | ||
| 177 | |||
| 178 | output = strdup(""); | ||
| 179 | ignored = strdup(""); | ||
| 180 | details = strdup(""); | ||
| 181 | perf = strdup(""); | ||
| 182 | perf_ilabel = strdup(""); | ||
| 183 | stat_buf = malloc(sizeof *stat_buf); | ||
| 184 | 116 | ||
| 117 | int main(int argc, char **argv) { | ||
| 185 | setlocale(LC_ALL, ""); | 118 | setlocale(LC_ALL, ""); |
| 186 | bindtextdomain(PACKAGE, LOCALEDIR); | 119 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 187 | textdomain(PACKAGE); | 120 | textdomain(PACKAGE); |
| 188 | 121 | ||
| 189 | mount_list = read_file_system_list(0); | 122 | #ifdef __CYGWIN__ |
| 123 | char mountdir[32]; | ||
| 124 | #endif | ||
| 190 | 125 | ||
| 191 | /* Parse extra opts if any */ | 126 | // Parse extra opts if any |
| 192 | argv = np_extra_opts(&argc, argv, progname); | 127 | argv = np_extra_opts(&argc, argv, progname); |
| 193 | 128 | ||
| 194 | if (process_arguments(argc, argv) == ERROR) | 129 | check_disk_config_wrapper tmp_config = process_arguments(argc, argv); |
| 130 | if (tmp_config.errorcode == ERROR) { | ||
| 195 | usage4(_("Could not parse arguments")); | 131 | usage4(_("Could not parse arguments")); |
| 132 | } | ||
| 196 | 133 | ||
| 197 | /* If a list of paths has not been selected, find entire | 134 | check_disk_config config = tmp_config.config; |
| 198 | mount list and create list of paths | 135 | |
| 199 | */ | 136 | if (config.output_format_is_set) { |
| 200 | if (path_selected == false && path_ignored == false) { | 137 | mp_set_format(config.output_format); |
| 201 | for (me = mount_list; me; me = me->me_next) { | ||
| 202 | if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { | ||
| 203 | path = np_add_parameter(&path_select_list, me->me_mountdir); | ||
| 204 | } | ||
| 205 | path->best_match = me; | ||
| 206 | path->group = group; | ||
| 207 | set_all_thresholds(path); | ||
| 208 | } | ||
| 209 | } | 138 | } |
| 210 | 139 | ||
| 211 | if (path_ignored == false) { | 140 | if (config.erronly) { |
| 212 | np_set_best_match(path_select_list, mount_list, exact_match); | 141 | mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY); |
| 213 | } | 142 | } |
| 214 | 143 | ||
| 215 | /* Error if no match found for specified paths */ | 144 | if (!config.path_ignored) { |
| 216 | temp_list = path_select_list; | 145 | mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list, |
| 146 | config.exact_match); | ||
| 147 | } | ||
| 217 | 148 | ||
| 218 | while (path_select_list) { | 149 | // Error if no match found for specified paths |
| 219 | if (!path_select_list->best_match && ignore_missing == true) { | 150 | for (parameter_list_elem *elem = config.path_select_list.first; elem;) { |
| 220 | /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ | 151 | if (!elem->best_match && config.ignore_missing) { |
| 221 | if (path_select_list == temp_list) { | ||
| 222 | temp_list = path_select_list->name_next; | ||
| 223 | } | ||
| 224 | /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */ | ||
| 225 | xasprintf(&ignored, "%s %s;", ignored, path_select_list->name); | ||
| 226 | /* Delete the path from the list so that it is not stat-checked later in the code. */ | 152 | /* Delete the path from the list so that it is not stat-checked later in the code. */ |
| 227 | path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev); | 153 | elem = mp_int_fs_list_del(&config.path_select_list, elem); |
| 228 | } else if (!path_select_list->best_match) { | 154 | continue; |
| 155 | } | ||
| 156 | if (!elem->best_match) { | ||
| 229 | /* Without --ignore-missing option, exit with Critical state. */ | 157 | /* Without --ignore-missing option, exit with Critical state. */ |
| 230 | die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name); | 158 | die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name); |
| 231 | } else { | ||
| 232 | /* Continue jumping through the list */ | ||
| 233 | path_select_list = path_select_list->name_next; | ||
| 234 | } | 159 | } |
| 235 | } | ||
| 236 | 160 | ||
| 237 | path_select_list = temp_list; | 161 | elem = mp_int_fs_list_get_next(elem); |
| 162 | } | ||
| 238 | 163 | ||
| 239 | if (!path_select_list && ignore_missing == true) { | 164 | mp_check overall = mp_check_init(); |
| 240 | result = STATE_OK; | 165 | if (config.path_select_list.length == 0) { |
| 241 | if (verbose >= 2) { | 166 | mp_subcheck none_sc = mp_subcheck_init(); |
| 242 | printf("None of the provided paths were found\n"); | 167 | xasprintf(&none_sc.output, "No filesystems were found for the provided parameters"); |
| 168 | if (config.ignore_missing) { | ||
| 169 | none_sc = mp_set_subcheck_state(none_sc, STATE_OK); | ||
| 170 | } else { | ||
| 171 | none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN); | ||
| 172 | if (verbose >= 2) { | ||
| 173 | printf("None of the provided paths were found\n"); | ||
| 174 | } | ||
| 243 | } | 175 | } |
| 176 | mp_add_subcheck_to_check(&overall, none_sc); | ||
| 177 | mp_exit(overall); | ||
| 244 | } | 178 | } |
| 245 | 179 | ||
| 246 | /* Process for every path in list */ | 180 | // Filter list first |
| 247 | for (path = path_select_list; path; path = path->name_next) { | 181 | for (parameter_list_elem *path = config.path_select_list.first; path;) { |
| 248 | if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) | 182 | if (!path->best_match) { |
| 249 | printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, | 183 | path = mp_int_fs_list_del(&config.path_select_list, path); |
| 250 | path->freespace_percent->critical->end); | ||
| 251 | |||
| 252 | if (verbose >= 3 && path->group != NULL) | ||
| 253 | printf("Group of %s: %s\n", path->name, path->group); | ||
| 254 | |||
| 255 | /* reset disk result */ | ||
| 256 | disk_result = STATE_UNKNOWN; | ||
| 257 | |||
| 258 | me = path->best_match; | ||
| 259 | |||
| 260 | if (!me) { | ||
| 261 | continue; | 184 | continue; |
| 262 | } | 185 | } |
| 263 | 186 | ||
| 187 | struct mount_entry *mount_entry = path->best_match; | ||
| 188 | |||
| 264 | #ifdef __CYGWIN__ | 189 | #ifdef __CYGWIN__ |
| 265 | if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) | 190 | if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) { |
| 191 | path = mp_int_fs_list_del(&config.path_select_list, path); | ||
| 266 | continue; | 192 | continue; |
| 193 | } | ||
| 194 | |||
| 195 | char *mountdir = NULL; | ||
| 267 | snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); | 196 | snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); |
| 268 | if (GetDriveType(mountdir) != DRIVE_FIXED) | 197 | if (GetDriveType(mountdir) != DRIVE_FIXED) { |
| 269 | me->me_remote = 1; | 198 | mount_entry->me_remote = 1; |
| 199 | } | ||
| 270 | #endif | 200 | #endif |
| 271 | /* Filters */ | ||
| 272 | 201 | ||
| 273 | /* Remove filesystems already seen */ | 202 | /* Remove filesystems already seen */ |
| 274 | if (np_seen_name(seen, me->me_mountdir)) { | 203 | if (np_seen_name(config.seen, mount_entry->me_mountdir)) { |
| 204 | path = mp_int_fs_list_del(&config.path_select_list, path); | ||
| 275 | continue; | 205 | continue; |
| 276 | } | 206 | } |
| 277 | np_add_name(&seen, me->me_mountdir); | ||
| 278 | 207 | ||
| 279 | if (path->group == NULL) { | 208 | if (path->group == NULL) { |
| 280 | /* Skip remote filesystems if we're not interested in them */ | 209 | if (config.fs_exclude_list && |
| 281 | if (me->me_remote && show_local_fs) { | 210 | np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) { |
| 282 | if (stat_remote_fs) { | 211 | // Skip excluded fs's |
| 283 | if (!stat_path(path) && ignore_missing == true) { | 212 | path = mp_int_fs_list_del(&config.path_select_list, path); |
| 284 | result = STATE_OK; | ||
| 285 | xasprintf(&ignored, "%s %s;", ignored, path->name); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | continue; | 213 | continue; |
| 289 | /* Skip pseudo fs's if we haven't asked for all fs's */ | ||
| 290 | } | 214 | } |
| 291 | if (me->me_dummy && !show_all_fs) { | 215 | |
| 292 | continue; | 216 | if (config.device_path_exclude_list && |
| 293 | /* Skip excluded fstypes */ | 217 | (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) || |
| 294 | } | 218 | np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) { |
| 295 | if (fs_exclude_list && np_find_regmatch(fs_exclude_list, me->me_type)) { | 219 | // Skip excluded device or mount paths |
| 220 | path = mp_int_fs_list_del(&config.path_select_list, path); | ||
| 296 | continue; | 221 | continue; |
| 297 | /* Skip excluded fs's */ | ||
| 298 | } | 222 | } |
| 299 | if (dp_exclude_list && (np_find_name(dp_exclude_list, me->me_devname) || np_find_name(dp_exclude_list, me->me_mountdir))) { | 223 | |
| 224 | if (config.fs_include_list && | ||
| 225 | !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) { | ||
| 226 | // Skip not included fstypes | ||
| 227 | path = mp_int_fs_list_del(&config.path_select_list, path); | ||
| 300 | continue; | 228 | continue; |
| 301 | /* Skip not included fstypes */ | ||
| 302 | } | 229 | } |
| 303 | if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) { | 230 | |
| 231 | /* Skip remote filesystems if we're not interested in them */ | ||
| 232 | if (mount_entry->me_remote && config.show_local_fs) { | ||
| 233 | if (config.stat_remote_fs) { | ||
| 234 | // TODO Stat here | ||
| 235 | if (!stat_path(path, config.ignore_missing) && config.ignore_missing) { | ||
| 236 | } | ||
| 237 | } | ||
| 304 | continue; | 238 | continue; |
| 305 | } | 239 | } |
| 306 | } | ||
| 307 | 240 | ||
| 308 | if (!stat_path(path)) { | 241 | // TODO why stat here? remove unstatable fs? |
| 309 | if (ignore_missing == true) { | 242 | if (!stat_path(path, config.ignore_missing)) { |
| 310 | result = STATE_OK; | 243 | // if (config.ignore_missing) { |
| 311 | xasprintf(&ignored, "%s %s;", ignored, path->name); | 244 | // xasprintf(&ignored, "%s %s;", ignored, path->name); |
| 245 | // } | ||
| 246 | // not accessible, remove from list | ||
| 247 | path = mp_int_fs_list_del(&config.path_select_list, path); | ||
| 248 | continue; | ||
| 312 | } | 249 | } |
| 313 | continue; | ||
| 314 | } | 250 | } |
| 315 | get_fs_usage(me->me_mountdir, me->me_devname, &fsp); | ||
| 316 | |||
| 317 | if (fsp.fsu_blocks && strcmp("none", me->me_mountdir)) { | ||
| 318 | get_stats(path, &fsp); | ||
| 319 | |||
| 320 | if (verbose >= 3) { | ||
| 321 | printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f " | ||
| 322 | "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n", | ||
| 323 | me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units, | ||
| 324 | path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult); | ||
| 325 | } | ||
| 326 | |||
| 327 | /* Threshold comparisons */ | ||
| 328 | |||
| 329 | temp_result = get_status(path->dfree_units, path->freespace_units); | ||
| 330 | if (verbose >= 3) | ||
| 331 | printf("Freespace_units result=%d\n", temp_result); | ||
| 332 | disk_result = max_state(disk_result, temp_result); | ||
| 333 | |||
| 334 | temp_result = get_status(path->dfree_pct, path->freespace_percent); | ||
| 335 | if (verbose >= 3) | ||
| 336 | printf("Freespace%% result=%d\n", temp_result); | ||
| 337 | disk_result = max_state(disk_result, temp_result); | ||
| 338 | 251 | ||
| 339 | temp_result = get_status(path->dused_units, path->usedspace_units); | 252 | path = mp_int_fs_list_get_next(path); |
| 340 | if (verbose >= 3) | 253 | } |
| 341 | printf("Usedspace_units result=%d\n", temp_result); | ||
| 342 | disk_result = max_state(disk_result, temp_result); | ||
| 343 | |||
| 344 | temp_result = get_status(path->dused_pct, path->usedspace_percent); | ||
| 345 | if (verbose >= 3) | ||
| 346 | printf("Usedspace_percent result=%d\n", temp_result); | ||
| 347 | disk_result = max_state(disk_result, temp_result); | ||
| 348 | |||
| 349 | temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent); | ||
| 350 | if (verbose >= 3) | ||
| 351 | printf("Usedinodes_percent result=%d\n", temp_result); | ||
| 352 | disk_result = max_state(disk_result, temp_result); | ||
| 353 | |||
| 354 | temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent); | ||
| 355 | if (verbose >= 3) | ||
| 356 | printf("Freeinodes_percent result=%d\n", temp_result); | ||
| 357 | disk_result = max_state(disk_result, temp_result); | ||
| 358 | |||
| 359 | result = max_state(result, disk_result); | ||
| 360 | |||
| 361 | /* What a mess of units. The output shows free space, the perf data shows used space. Yikes! | ||
| 362 | Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf | ||
| 363 | data. Assumption that start=0. Roll on new syntax... | ||
| 364 | */ | ||
| 365 | |||
| 366 | /* *_high_tide must be reinitialized at each run */ | ||
| 367 | uint64_t warning_high_tide = UINT64_MAX; | ||
| 368 | 254 | ||
| 369 | if (path->freespace_units->warning != NULL) { | 255 | // now get the actual measurements |
| 370 | warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult; | 256 | for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) { |
| 371 | } | 257 | // Get actual metrics here |
| 372 | if (path->freespace_percent->warning != NULL) { | 258 | struct mount_entry *mount_entry = filesystem->best_match; |
| 373 | warning_high_tide = | 259 | struct fs_usage fsp = {0}; |
| 374 | min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * (path->dtotal_units * mult))); | 260 | get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp); |
| 375 | } | ||
| 376 | 261 | ||
| 377 | uint64_t critical_high_tide = UINT64_MAX; | 262 | if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) { |
| 263 | *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved); | ||
| 378 | 264 | ||
| 379 | if (path->freespace_units->critical != NULL) { | 265 | if (verbose >= 3) { |
| 380 | critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult; | 266 | printf("For %s, used_units=%lu free_units=%lu total_units=%lu " |
| 381 | } | 267 | "fsp.fsu_blocksize=%lu\n", |
| 382 | if (path->freespace_percent->critical != NULL) { | 268 | mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes, |
| 383 | critical_high_tide = | 269 | filesystem->total_bytes, fsp.fsu_blocksize); |
| 384 | min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * (path->dtotal_units * mult))); | ||
| 385 | } | 270 | } |
| 271 | } else { | ||
| 272 | // failed to retrieve file system data or not mounted? | ||
| 273 | filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem); | ||
| 274 | continue; | ||
| 275 | } | ||
| 276 | filesystem = mp_int_fs_list_get_next(filesystem); | ||
| 277 | } | ||
| 386 | 278 | ||
| 387 | /* Nb: *_high_tide are unset when == UINT64_MAX */ | 279 | if (verbose > 2) { |
| 388 | xasprintf(&perf, "%s %s", perf, | 280 | for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; |
| 389 | perfdata_uint64((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, | 281 | filesystem = mp_int_fs_list_get_next(filesystem)) { |
| 390 | path->dused_units * mult, "B", (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide, | 282 | assert(filesystem->best_match != NULL); |
| 391 | (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide, true, 0, true, | 283 | if (filesystem->best_match == NULL) { |
| 392 | path->dtotal_units * mult)); | 284 | printf("Filesystem path %s has no mount_entry!\n", filesystem->name); |
| 393 | 285 | } else { | |
| 394 | if (display_inodes_perfdata) { | 286 | // printf("Filesystem path %s has a mount_entry!\n", filesystem->name); |
| 395 | /* *_high_tide must be reinitialized at each run */ | ||
| 396 | warning_high_tide = UINT64_MAX; | ||
| 397 | critical_high_tide = UINT64_MAX; | ||
| 398 | |||
| 399 | if (path->freeinodes_percent->warning != NULL) { | ||
| 400 | warning_high_tide = (uint64_t)fabs( | ||
| 401 | min((double)warning_high_tide, (double)(1.0 - path->freeinodes_percent->warning->end / 100) * path->inodes_total)); | ||
| 402 | } | ||
| 403 | if (path->freeinodes_percent->critical != NULL) { | ||
| 404 | critical_high_tide = (uint64_t)fabs(min( | ||
| 405 | (double)critical_high_tide, (double)(1.0 - path->freeinodes_percent->critical->end / 100) * path->inodes_total)); | ||
| 406 | } | ||
| 407 | |||
| 408 | xasprintf(&perf_ilabel, "%s (inodes)", | ||
| 409 | (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir); | ||
| 410 | /* Nb: *_high_tide are unset when == UINT64_MAX */ | ||
| 411 | xasprintf(&perf, "%s %s", perf, | ||
| 412 | perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX ? true : false), | ||
| 413 | warning_high_tide, (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide, true, 0, | ||
| 414 | true, path->inodes_total)); | ||
| 415 | } | 287 | } |
| 288 | } | ||
| 289 | } | ||
| 416 | 290 | ||
| 417 | if (disk_result == STATE_OK && erronly && !verbose) | 291 | measurement_unit_list *measurements = NULL; |
| 418 | continue; | 292 | measurement_unit_list *current = NULL; |
| 419 | 293 | // create measuring units, because of groups | |
| 420 | if (disk_result && verbose >= 1) { | 294 | for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; |
| 421 | xasprintf(&flag_header, " %s [", state_text(disk_result)); | 295 | filesystem = mp_int_fs_list_get_next(filesystem)) { |
| 296 | assert(filesystem->best_match != NULL); | ||
| 297 | |||
| 298 | if (filesystem->group == NULL) { | ||
| 299 | // create a measurement unit for the fs | ||
| 300 | measurement_unit unit = | ||
| 301 | create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); | ||
| 302 | if (measurements == NULL) { | ||
| 303 | measurements = current = add_measurement_list(NULL, unit); | ||
| 422 | } else { | 304 | } else { |
| 423 | xasprintf(&flag_header, ""); | 305 | current = add_measurement_list(measurements, unit); |
| 424 | } | 306 | } |
| 425 | xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, | 307 | } else { |
| 426 | (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, path->dfree_units, units, | 308 | // Grouped elements are consecutive |
| 427 | path->dfree_pct); | 309 | if (measurements == NULL) { |
| 428 | if (path->dused_inodes_percent < 0) { | 310 | // first entry |
| 429 | xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : "")); | 311 | measurement_unit unit = |
| 312 | create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); | ||
| 313 | unit.name = strdup(filesystem->group); | ||
| 314 | measurements = current = add_measurement_list(NULL, unit); | ||
| 430 | } else { | 315 | } else { |
| 431 | xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : "")); | 316 | // if this is the first element of a group, the name of the previous entry is |
| 317 | // different | ||
| 318 | if (strcmp(filesystem->group, current->unit.name) != 0) { | ||
| 319 | // so, this must be the first element of a group | ||
| 320 | measurement_unit unit = | ||
| 321 | create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); | ||
| 322 | unit.name = filesystem->group; | ||
| 323 | current = add_measurement_list(measurements, unit); | ||
| 324 | |||
| 325 | } else { | ||
| 326 | // NOT the first entry of a group, add info to the other one | ||
| 327 | current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem); | ||
| 328 | } | ||
| 432 | } | 329 | } |
| 433 | free(flag_header); | ||
| 434 | } | 330 | } |
| 435 | } | 331 | } |
| 436 | 332 | ||
| 437 | if (verbose >= 2) | 333 | /* Process for every path in list */ |
| 438 | xasprintf(&output, "%s%s", output, details); | 334 | if (measurements != NULL) { |
| 335 | for (measurement_unit_list *unit = measurements; unit; unit = unit->next) { | ||
| 336 | mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata, | ||
| 337 | config.display_unit); | ||
| 338 | mp_add_subcheck_to_check(&overall, unit_sc); | ||
| 339 | } | ||
| 340 | } else { | ||
| 341 | // Apparently no machting fs found | ||
| 342 | mp_subcheck none_sc = mp_subcheck_init(); | ||
| 343 | xasprintf(&none_sc.output, "No filesystems were found for the provided parameters"); | ||
| 439 | 344 | ||
| 440 | if (strcmp(output, "") == 0 && !erronly) { | 345 | if (config.ignore_missing) { |
| 441 | preamble = ""; | 346 | none_sc = mp_set_subcheck_state(none_sc, STATE_OK); |
| 442 | xasprintf(&output, " - No disks were found for provided parameters"); | 347 | } else { |
| 348 | none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN); | ||
| 349 | } | ||
| 350 | mp_add_subcheck_to_check(&overall, none_sc); | ||
| 443 | } | 351 | } |
| 444 | 352 | ||
| 445 | printf("DISK %s%s%s%s%s|%s\n", state_text(result), ((erronly && result == STATE_OK)) ? "" : preamble, output, | 353 | mp_exit(overall); |
| 446 | (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf); | ||
| 447 | return result; | ||
| 448 | } | 354 | } |
| 449 | 355 | ||
| 450 | double calculate_percent(uintmax_t value, uintmax_t total) { | 356 | double calculate_percent(uintmax_t value, uintmax_t total) { |
| 451 | double pct = -1; | 357 | double pct = -1; |
| 452 | if (value <= DBL_MAX && total != 0) { | 358 | if (value <= DBL_MAX && total != 0) { |
| 453 | pct = (double)value / total * 100.0; | 359 | pct = (double)value / (double)total * 100.0; |
| 454 | } | 360 | } |
| 361 | |||
| 455 | return pct; | 362 | return pct; |
| 456 | } | 363 | } |
| 457 | 364 | ||
| 458 | /* process command-line arguments */ | 365 | /* process command-line arguments */ |
| 459 | int process_arguments(int argc, char **argv) { | 366 | check_disk_config_wrapper process_arguments(int argc, char **argv) { |
| 460 | int c; | 367 | |
| 461 | int err; | 368 | check_disk_config_wrapper result = { |
| 462 | struct parameter_list *se; | 369 | .errorcode = OK, |
| 463 | struct parameter_list *temp_list = NULL; | 370 | .config = check_disk_config_init(), |
| 464 | struct parameter_list *previous = NULL; | 371 | }; |
| 465 | struct mount_entry *me; | 372 | |
| 466 | regex_t re; | 373 | if (argc < 2) { |
| 467 | int cflags = REG_NOSUB | REG_EXTENDED; | 374 | result.errorcode = ERROR; |
| 468 | int default_cflags = cflags; | 375 | return result; |
| 469 | char errbuf[MAX_INPUT_BUFFER]; | 376 | } |
| 470 | int fnd = 0; | 377 | |
| 378 | enum { | ||
| 379 | output_format_index = CHAR_MAX + 1, | ||
| 380 | display_unit_index, | ||
| 381 | }; | ||
| 471 | 382 | ||
| 472 | int option = 0; | ||
| 473 | static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, | 383 | static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, |
| 474 | {"warning", required_argument, 0, 'w'}, | 384 | {"warning", required_argument, 0, 'w'}, |
| 475 | {"critical", required_argument, 0, 'c'}, | 385 | {"critical", required_argument, 0, 'c'}, |
| 476 | {"iwarning", required_argument, 0, 'W'}, | 386 | {"iwarning", required_argument, 0, 'W'}, |
| 477 | /* Dang, -C is taken. We might want to reshuffle this. */ | ||
| 478 | {"icritical", required_argument, 0, 'K'}, | 387 | {"icritical", required_argument, 0, 'K'}, |
| 479 | {"kilobytes", no_argument, 0, 'k'}, | 388 | {"kilobytes", no_argument, 0, 'k'}, |
| 480 | {"megabytes", no_argument, 0, 'm'}, | 389 | {"megabytes", no_argument, 0, 'm'}, |
| @@ -507,24 +416,43 @@ int process_arguments(int argc, char **argv) { | |||
| 507 | {"clear", no_argument, 0, 'C'}, | 416 | {"clear", no_argument, 0, 'C'}, |
| 508 | {"version", no_argument, 0, 'V'}, | 417 | {"version", no_argument, 0, 'V'}, |
| 509 | {"help", no_argument, 0, 'h'}, | 418 | {"help", no_argument, 0, 'h'}, |
| 419 | {"output-format", required_argument, 0, output_format_index}, | ||
| 420 | {"display-unit", required_argument, 0, display_unit_index}, | ||
| 510 | {0, 0, 0, 0}}; | 421 | {0, 0, 0, 0}}; |
| 511 | 422 | ||
| 512 | if (argc < 2) | 423 | for (int index = 1; index < argc; index++) { |
| 513 | return ERROR; | 424 | if (strcmp("-to", argv[index]) == 0) { |
| 425 | strcpy(argv[index], "-t"); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | int cflags = REG_NOSUB | REG_EXTENDED; | ||
| 430 | int default_cflags = cflags; | ||
| 431 | char *warn_freespace_units = NULL; | ||
| 432 | char *crit_freespace_units = NULL; | ||
| 433 | char *warn_freespace_percent = NULL; | ||
| 434 | char *crit_freespace_percent = NULL; | ||
| 435 | char *warn_freeinodes_percent = NULL; | ||
| 436 | char *crit_freeinodes_percent = NULL; | ||
| 514 | 437 | ||
| 515 | np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); | 438 | bool path_selected = false; |
| 439 | char *group = NULL; | ||
| 440 | byte_unit unit = MebiBytes_factor; | ||
| 516 | 441 | ||
| 517 | for (c = 1; c < argc; c++) | 442 | result.config.mount_list = read_file_system_list(false); |
| 518 | if (strcmp("-to", argv[c]) == 0) | ||
| 519 | strcpy(argv[c], "-t"); | ||
| 520 | 443 | ||
| 521 | while (1) { | 444 | np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED); |
| 522 | c = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); | ||
| 523 | 445 | ||
| 524 | if (c == -1 || c == EOF) | 446 | while (true) { |
| 447 | int option = 0; | ||
| 448 | int option_index = getopt_long( | ||
| 449 | argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); | ||
| 450 | |||
| 451 | if (option_index == -1 || option_index == EOF) { | ||
| 525 | break; | 452 | break; |
| 453 | } | ||
| 526 | 454 | ||
| 527 | switch (c) { | 455 | switch (option_index) { |
| 528 | case 't': /* timeout period */ | 456 | case 't': /* timeout period */ |
| 529 | if (is_integer(optarg)) { | 457 | if (is_integer(optarg)) { |
| 530 | timeout_interval = atoi(optarg); | 458 | timeout_interval = atoi(optarg); |
| @@ -555,10 +483,10 @@ int process_arguments(int argc, char **argv) { | |||
| 555 | break; | 483 | break; |
| 556 | 484 | ||
| 557 | /* Awful mistake where the range values do not make sense. Normally, | 485 | /* Awful mistake where the range values do not make sense. Normally, |
| 558 | you alert if the value is within the range, but since we are using | 486 | * you alert if the value is within the range, but since we are using |
| 559 | freespace, we have to alert if outside the range. Thus we artificially | 487 | * freespace, we have to alert if outside the range. Thus we artificially |
| 560 | force @ at the beginning of the range, so that it is backwards compatible | 488 | * force @ at the beginning of the range, so that it is backwards compatible |
| 561 | */ | 489 | */ |
| 562 | case 'c': /* critical threshold */ | 490 | case 'c': /* critical threshold */ |
| 563 | if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { | 491 | if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { |
| 564 | die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); | 492 | die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); |
| @@ -594,181 +522,193 @@ int process_arguments(int argc, char **argv) { | |||
| 594 | } | 522 | } |
| 595 | break; | 523 | break; |
| 596 | case 'u': | 524 | case 'u': |
| 597 | if (units) | ||
| 598 | free(units); | ||
| 599 | if (!strcasecmp(optarg, "bytes")) { | 525 | if (!strcasecmp(optarg, "bytes")) { |
| 600 | mult = (uintmax_t)1; | 526 | unit = Bytes_Factor; |
| 601 | units = strdup("B"); | ||
| 602 | } else if (!strcmp(optarg, "KiB")) { | 527 | } else if (!strcmp(optarg, "KiB")) { |
| 603 | mult = (uintmax_t)1024; | 528 | unit = KibiBytes_factor; |
| 604 | units = strdup("KiB"); | ||
| 605 | } else if (!strcmp(optarg, "kB")) { | 529 | } else if (!strcmp(optarg, "kB")) { |
| 606 | mult = (uintmax_t)1000; | 530 | unit = KiloBytes_factor; |
| 607 | units = strdup("kB"); | ||
| 608 | } else if (!strcmp(optarg, "MiB")) { | 531 | } else if (!strcmp(optarg, "MiB")) { |
| 609 | mult = (uintmax_t)1024 * 1024; | 532 | unit = MebiBytes_factor; |
| 610 | units = strdup("MiB"); | ||
| 611 | } else if (!strcmp(optarg, "MB")) { | 533 | } else if (!strcmp(optarg, "MB")) { |
| 612 | mult = (uintmax_t)1000 * 1000; | 534 | unit = MegaBytes_factor; |
| 613 | units = strdup("MB"); | ||
| 614 | } else if (!strcmp(optarg, "GiB")) { | 535 | } else if (!strcmp(optarg, "GiB")) { |
| 615 | mult = (uintmax_t)1024 * 1024 * 1024; | 536 | unit = GibiBytes_factor; |
| 616 | units = strdup("GiB"); | ||
| 617 | } else if (!strcmp(optarg, "GB")) { | 537 | } else if (!strcmp(optarg, "GB")) { |
| 618 | mult = (uintmax_t)1000 * 1000 * 1000; | 538 | unit = GigaBytes_factor; |
| 619 | units = strdup("GB"); | ||
| 620 | } else if (!strcmp(optarg, "TiB")) { | 539 | } else if (!strcmp(optarg, "TiB")) { |
| 621 | mult = (uintmax_t)1024 * 1024 * 1024 * 1024; | 540 | unit = TebiBytes_factor; |
| 622 | units = strdup("TiB"); | ||
| 623 | } else if (!strcmp(optarg, "TB")) { | 541 | } else if (!strcmp(optarg, "TB")) { |
| 624 | mult = (uintmax_t)1000 * 1000 * 1000 * 1000; | 542 | unit = TeraBytes_factor; |
| 625 | units = strdup("TB"); | ||
| 626 | } else if (!strcmp(optarg, "PiB")) { | 543 | } else if (!strcmp(optarg, "PiB")) { |
| 627 | mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; | 544 | unit = PebiBytes_factor; |
| 628 | units = strdup("PiB"); | ||
| 629 | } else if (!strcmp(optarg, "PB")) { | 545 | } else if (!strcmp(optarg, "PB")) { |
| 630 | mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; | 546 | unit = PetaBytes_factor; |
| 631 | units = strdup("PB"); | ||
| 632 | } else { | 547 | } else { |
| 633 | die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); | 548 | die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); |
| 634 | } | 549 | } |
| 635 | if (units == NULL) | ||
| 636 | die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units"); | ||
| 637 | break; | 550 | break; |
| 638 | case 'k': /* display mountpoint */ | 551 | case 'k': |
| 639 | mult = 1024; | 552 | unit = KibiBytes_factor; |
| 640 | if (units) | ||
| 641 | free(units); | ||
| 642 | units = strdup("kiB"); | ||
| 643 | break; | 553 | break; |
| 644 | case 'm': /* display mountpoint */ | 554 | case 'm': |
| 645 | mult = 1024 * 1024; | 555 | unit = MebiBytes_factor; |
| 646 | if (units) | 556 | break; |
| 647 | free(units); | 557 | case display_unit_index: |
| 648 | units = strdup("MiB"); | 558 | if (!strcasecmp(optarg, "bytes")) { |
| 559 | result.config.display_unit = Bytes; | ||
| 560 | } else if (!strcmp(optarg, "KiB")) { | ||
| 561 | result.config.display_unit = KibiBytes; | ||
| 562 | } else if (!strcmp(optarg, "kB")) { | ||
| 563 | result.config.display_unit = KiloBytes; | ||
| 564 | } else if (!strcmp(optarg, "MiB")) { | ||
| 565 | result.config.display_unit = MebiBytes; | ||
| 566 | } else if (!strcmp(optarg, "MB")) { | ||
| 567 | result.config.display_unit = MegaBytes; | ||
| 568 | } else if (!strcmp(optarg, "GiB")) { | ||
| 569 | result.config.display_unit = GibiBytes; | ||
| 570 | } else if (!strcmp(optarg, "GB")) { | ||
| 571 | result.config.display_unit = GigaBytes; | ||
| 572 | } else if (!strcmp(optarg, "TiB")) { | ||
| 573 | result.config.display_unit = TebiBytes; | ||
| 574 | } else if (!strcmp(optarg, "TB")) { | ||
| 575 | result.config.display_unit = TeraBytes; | ||
| 576 | } else if (!strcmp(optarg, "PiB")) { | ||
| 577 | result.config.display_unit = PebiBytes; | ||
| 578 | } else if (!strcmp(optarg, "PB")) { | ||
| 579 | result.config.display_unit = PetaBytes; | ||
| 580 | } else { | ||
| 581 | die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); | ||
| 582 | } | ||
| 649 | break; | 583 | break; |
| 650 | case 'L': | 584 | case 'L': |
| 651 | stat_remote_fs = 1; | 585 | result.config.stat_remote_fs = true; |
| 652 | /* fallthrough */ | 586 | /* fallthrough */ |
| 653 | case 'l': | 587 | case 'l': |
| 654 | show_local_fs = 1; | 588 | result.config.show_local_fs = true; |
| 655 | break; | 589 | break; |
| 656 | case 'P': | 590 | case 'P': |
| 657 | display_inodes_perfdata = 1; | 591 | result.config.display_inodes_perfdata = true; |
| 658 | break; | 592 | break; |
| 659 | case 'p': /* select path */ | 593 | case 'p': /* select path */ { |
| 660 | if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || | 594 | if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || |
| 661 | warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || | 595 | crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { |
| 662 | warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { | 596 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), |
| 663 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); | 597 | _("Must set a threshold value before using -p\n")); |
| 664 | } | 598 | } |
| 665 | 599 | ||
| 666 | /* add parameter if not found. overwrite thresholds if path has already been added */ | 600 | /* add parameter if not found. overwrite thresholds if path has already been added */ |
| 667 | if (!(se = np_find_parameter(path_select_list, optarg))) { | 601 | parameter_list_elem *search_entry; |
| 668 | se = np_add_parameter(&path_select_list, optarg); | 602 | if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) { |
| 669 | 603 | search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg); | |
| 670 | if (stat(optarg, &stat_buf[0]) && ignore_missing == true) { | 604 | |
| 671 | path_ignored = true; | 605 | // struct stat stat_buf = {}; |
| 672 | break; | 606 | // if (stat(optarg, &stat_buf) && result.config.ignore_missing) { |
| 673 | } | 607 | // result.config.path_ignored = true; |
| 608 | // break; | ||
| 609 | // } | ||
| 674 | } | 610 | } |
| 675 | se->group = group; | 611 | search_entry->group = group; |
| 676 | set_all_thresholds(se); | 612 | set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units, |
| 613 | warn_freespace_percent, crit_freespace_percent, | ||
| 614 | |||
| 615 | warn_freeinodes_percent, crit_freeinodes_percent); | ||
| 677 | 616 | ||
| 678 | /* With autofs, it is required to stat() the path before re-populating the mount_list */ | 617 | /* With autofs, it is required to stat() the path before re-populating the mount_list */ |
| 679 | if (!stat_path(se)) { | 618 | // if (!stat_path(se, result.config.ignore_missing)) { |
| 680 | break; | 619 | // break; |
| 681 | } | 620 | // } |
| 682 | /* NB: We can't free the old mount_list "just like that": both list pointers and struct | 621 | mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, |
| 683 | * pointers are copied around. One of the reason it wasn't done yet is that other parts | 622 | result.config.exact_match); |
| 684 | * of check_disk need the same kind of cleanup so it'd better be done as a whole */ | ||
| 685 | mount_list = read_file_system_list(0); | ||
| 686 | np_set_best_match(se, mount_list, exact_match); | ||
| 687 | 623 | ||
| 688 | path_selected = true; | 624 | path_selected = true; |
| 689 | break; | 625 | } break; |
| 690 | case 'x': /* exclude path or partition */ | 626 | case 'x': /* exclude path or partition */ |
| 691 | np_add_name(&dp_exclude_list, optarg); | 627 | np_add_name(&result.config.device_path_exclude_list, optarg); |
| 692 | break; | 628 | break; |
| 693 | case 'X': /* exclude file system type */ | 629 | case 'X': /* exclude file system type */ { |
| 694 | err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); | 630 | int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED); |
| 695 | if (err != 0) { | 631 | if (err != 0) { |
| 696 | regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); | 632 | char errbuf[MAX_INPUT_BUFFER]; |
| 697 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); | 633 | regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); |
| 634 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), | ||
| 635 | _("Could not compile regular expression"), errbuf); | ||
| 698 | } | 636 | } |
| 699 | break; | 637 | break; |
| 700 | case 'N': /* include file system type */ | 638 | case 'N': /* include file system type */ |
| 701 | err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED); | 639 | err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED); |
| 702 | if (err != 0) { | 640 | if (err != 0) { |
| 703 | regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); | 641 | char errbuf[MAX_INPUT_BUFFER]; |
| 704 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); | 642 | regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); |
| 643 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), | ||
| 644 | _("Could not compile regular expression"), errbuf); | ||
| 705 | } | 645 | } |
| 706 | break; | 646 | } break; |
| 707 | case 'v': /* verbose */ | 647 | case 'v': /* verbose */ |
| 708 | verbose++; | 648 | verbose++; |
| 709 | break; | 649 | break; |
| 710 | case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ | 650 | case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ |
| 711 | /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ | 651 | /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ |
| 712 | erronly = true; | 652 | result.config.erronly = true; |
| 713 | break; | 653 | break; |
| 714 | case 'e': | 654 | case 'e': |
| 715 | erronly = true; | 655 | result.config.erronly = true; |
| 716 | break; | 656 | break; |
| 717 | case 'E': | 657 | case 'E': |
| 718 | if (path_selected) | 658 | if (path_selected) { |
| 719 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); | 659 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), |
| 720 | exact_match = true; | 660 | _("Must set -E before selecting paths\n")); |
| 661 | } | ||
| 662 | result.config.exact_match = true; | ||
| 721 | break; | 663 | break; |
| 722 | case 'f': | 664 | case 'f': |
| 723 | freespace_ignore_reserved = true; | 665 | result.config.freespace_ignore_reserved = true; |
| 724 | break; | 666 | break; |
| 725 | case 'g': | 667 | case 'g': |
| 726 | if (path_selected) | 668 | if (path_selected) { |
| 727 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); | 669 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), |
| 670 | _("Must set group value before selecting paths\n")); | ||
| 671 | } | ||
| 728 | group = optarg; | 672 | group = optarg; |
| 729 | break; | 673 | break; |
| 730 | case 'I': | 674 | case 'I': |
| 731 | cflags |= REG_ICASE; | 675 | cflags |= REG_ICASE; |
| 732 | // Intentional fallthrough | 676 | // Intentional fallthrough |
| 733 | case 'i': | 677 | case 'i': { |
| 734 | if (!path_selected) | 678 | if (!path_selected) { |
| 735 | die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), | 679 | die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), |
| 736 | _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); | 680 | _("Paths need to be selected before using -i/-I. Use -A to select all paths " |
| 737 | err = regcomp(&re, optarg, cflags); | 681 | "explicitly")); |
| 682 | } | ||
| 683 | regex_t regex; | ||
| 684 | int err = regcomp(®ex, optarg, cflags); | ||
| 738 | if (err != 0) { | 685 | if (err != 0) { |
| 739 | regerror(err, &re, errbuf, MAX_INPUT_BUFFER); | 686 | char errbuf[MAX_INPUT_BUFFER]; |
| 740 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); | 687 | regerror(err, ®ex, errbuf, MAX_INPUT_BUFFER); |
| 688 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), | ||
| 689 | _("Could not compile regular expression"), errbuf); | ||
| 741 | } | 690 | } |
| 742 | 691 | ||
| 743 | temp_list = path_select_list; | 692 | for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) { |
| 693 | if (elem->best_match) { | ||
| 694 | if (np_regex_match_mount_entry(elem->best_match, ®ex)) { | ||
| 744 | 695 | ||
| 745 | previous = NULL; | 696 | if (verbose >= 3) { |
| 746 | while (temp_list) { | 697 | printf("ignoring %s matching regex\n", elem->name); |
| 747 | if (temp_list->best_match) { | 698 | } |
| 748 | if (np_regex_match_mount_entry(temp_list->best_match, &re)) { | ||
| 749 | 699 | ||
| 750 | if (verbose >= 3) | 700 | elem = mp_int_fs_list_del(&result.config.path_select_list, elem); |
| 751 | printf("ignoring %s matching regex\n", temp_list->name); | 701 | continue; |
| 752 | |||
| 753 | temp_list = np_del_parameter(temp_list, previous); | ||
| 754 | /* pointer to first element needs to be updated if first item gets deleted */ | ||
| 755 | if (previous == NULL) | ||
| 756 | path_select_list = temp_list; | ||
| 757 | } else { | ||
| 758 | previous = temp_list; | ||
| 759 | temp_list = temp_list->name_next; | ||
| 760 | } | 702 | } |
| 761 | } else { | ||
| 762 | previous = temp_list; | ||
| 763 | temp_list = temp_list->name_next; | ||
| 764 | } | 703 | } |
| 704 | |||
| 705 | elem = mp_int_fs_list_get_next(elem); | ||
| 765 | } | 706 | } |
| 766 | 707 | ||
| 767 | cflags = default_cflags; | 708 | cflags = default_cflags; |
| 768 | break; | 709 | } break; |
| 769 | |||
| 770 | case 'n': | 710 | case 'n': |
| 771 | ignore_missing = true; | 711 | result.config.ignore_missing = true; |
| 772 | break; | 712 | break; |
| 773 | case 'A': | 713 | case 'A': |
| 774 | optarg = strdup(".*"); | 714 | optarg = strdup(".*"); |
| @@ -776,80 +716,96 @@ int process_arguments(int argc, char **argv) { | |||
| 776 | case 'R': | 716 | case 'R': |
| 777 | cflags |= REG_ICASE; | 717 | cflags |= REG_ICASE; |
| 778 | // Intentional fallthrough | 718 | // Intentional fallthrough |
| 779 | case 'r': | 719 | case 'r': { |
| 780 | if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || | 720 | if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || |
| 781 | warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || | 721 | crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { |
| 782 | warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { | ||
| 783 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), | 722 | die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), |
| 784 | _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); | 723 | _("Must set a threshold value before using -r/-R/-A " |
| 724 | "(--ereg-path/--eregi-path/--all)\n")); | ||
| 785 | } | 725 | } |
| 786 | 726 | ||
| 787 | err = regcomp(&re, optarg, cflags); | 727 | regex_t regex; |
| 728 | int err = regcomp(®ex, optarg, cflags); | ||
| 788 | if (err != 0) { | 729 | if (err != 0) { |
| 789 | regerror(err, &re, errbuf, MAX_INPUT_BUFFER); | 730 | char errbuf[MAX_INPUT_BUFFER]; |
| 790 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); | 731 | regerror(err, ®ex, errbuf, MAX_INPUT_BUFFER); |
| 732 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), | ||
| 733 | _("Could not compile regular expression"), errbuf); | ||
| 791 | } | 734 | } |
| 792 | 735 | ||
| 793 | for (me = mount_list; me; me = me->me_next) { | 736 | bool found = false; |
| 794 | if (np_regex_match_mount_entry(me, &re)) { | 737 | for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { |
| 795 | fnd = true; | 738 | if (np_regex_match_mount_entry(me, ®ex)) { |
| 796 | if (verbose >= 3) | 739 | found = true; |
| 797 | printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); | 740 | if (verbose >= 3) { |
| 741 | printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, | ||
| 742 | optarg); | ||
| 743 | } | ||
| 798 | 744 | ||
| 799 | /* add parameter if not found. overwrite thresholds if path has already been added */ | 745 | /* add parameter if not found. overwrite thresholds if path has already been |
| 800 | if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { | 746 | * added */ |
| 801 | se = np_add_parameter(&path_select_list, me->me_mountdir); | 747 | parameter_list_elem *se = NULL; |
| 748 | if (!(se = mp_int_fs_list_find(result.config.path_select_list, | ||
| 749 | me->me_mountdir))) { | ||
| 750 | se = | ||
| 751 | mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); | ||
| 802 | } | 752 | } |
| 803 | se->group = group; | 753 | se->group = group; |
| 804 | set_all_thresholds(se); | 754 | set_all_thresholds(se, warn_freespace_units, crit_freespace_units, |
| 755 | warn_freespace_percent, crit_freespace_percent, | ||
| 756 | warn_freeinodes_percent, crit_freeinodes_percent); | ||
| 805 | } | 757 | } |
| 806 | } | 758 | } |
| 807 | 759 | ||
| 808 | if (!fnd && ignore_missing == true) { | 760 | if (!found) { |
| 809 | path_ignored = true; | 761 | if (result.config.ignore_missing) { |
| 810 | path_selected = true; | 762 | result.config.path_ignored = true; |
| 811 | break; | 763 | path_selected = true; |
| 764 | break; | ||
| 765 | } | ||
| 766 | |||
| 767 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), | ||
| 768 | _("Regular expression did not match any path or disk"), optarg); | ||
| 812 | } | 769 | } |
| 813 | if (!fnd) | ||
| 814 | die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); | ||
| 815 | 770 | ||
| 816 | fnd = false; | ||
| 817 | path_selected = true; | 771 | path_selected = true; |
| 818 | np_set_best_match(path_select_list, mount_list, exact_match); | 772 | mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, |
| 773 | result.config.exact_match); | ||
| 819 | cflags = default_cflags; | 774 | cflags = default_cflags; |
| 820 | 775 | ||
| 821 | break; | 776 | } break; |
| 822 | case 'M': /* display mountpoint */ | 777 | case 'M': /* display mountpoint */ |
| 823 | display_mntp = true; | 778 | result.config.display_mntp = true; |
| 824 | break; | 779 | break; |
| 825 | case 'C': | 780 | case 'C': { |
| 826 | /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ | 781 | /* add all mount entries to path_select list if no partitions have been explicitly |
| 827 | if (path_selected == false) { | 782 | * defined using -p */ |
| 828 | struct parameter_list *path; | 783 | if (!path_selected) { |
| 829 | for (me = mount_list; me; me = me->me_next) { | 784 | parameter_list_elem *path; |
| 830 | if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) | 785 | for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { |
| 831 | path = np_add_parameter(&path_select_list, me->me_mountdir); | 786 | if (!(path = mp_int_fs_list_find(result.config.path_select_list, |
| 787 | me->me_mountdir))) { | ||
| 788 | path = | ||
| 789 | mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); | ||
| 790 | } | ||
| 832 | path->best_match = me; | 791 | path->best_match = me; |
| 833 | path->group = group; | 792 | path->group = group; |
| 834 | set_all_thresholds(path); | 793 | set_all_thresholds(path, warn_freespace_units, crit_freespace_units, |
| 794 | warn_freespace_percent, crit_freespace_percent, | ||
| 795 | warn_freeinodes_percent, crit_freeinodes_percent); | ||
| 835 | } | 796 | } |
| 836 | } | 797 | } |
| 798 | |||
| 837 | warn_freespace_units = NULL; | 799 | warn_freespace_units = NULL; |
| 838 | crit_freespace_units = NULL; | 800 | crit_freespace_units = NULL; |
| 839 | warn_usedspace_units = NULL; | ||
| 840 | crit_usedspace_units = NULL; | ||
| 841 | warn_freespace_percent = NULL; | 801 | warn_freespace_percent = NULL; |
| 842 | crit_freespace_percent = NULL; | 802 | crit_freespace_percent = NULL; |
| 843 | warn_usedspace_percent = NULL; | ||
| 844 | crit_usedspace_percent = NULL; | ||
| 845 | warn_usedinodes_percent = NULL; | ||
| 846 | crit_usedinodes_percent = NULL; | ||
| 847 | warn_freeinodes_percent = NULL; | 803 | warn_freeinodes_percent = NULL; |
| 848 | crit_freeinodes_percent = NULL; | 804 | crit_freeinodes_percent = NULL; |
| 849 | 805 | ||
| 850 | path_selected = false; | 806 | path_selected = false; |
| 851 | group = NULL; | 807 | group = NULL; |
| 852 | break; | 808 | } break; |
| 853 | case 'V': /* version */ | 809 | case 'V': /* version */ |
| 854 | print_revision(progname, NP_VERSION); | 810 | print_revision(progname, NP_VERSION); |
| 855 | exit(STATE_UNKNOWN); | 811 | exit(STATE_UNKNOWN); |
| @@ -858,50 +814,152 @@ int process_arguments(int argc, char **argv) { | |||
| 858 | exit(STATE_UNKNOWN); | 814 | exit(STATE_UNKNOWN); |
| 859 | case '?': /* help */ | 815 | case '?': /* help */ |
| 860 | usage(_("Unknown argument")); | 816 | usage(_("Unknown argument")); |
| 817 | case output_format_index: { | ||
| 818 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 819 | if (!parser.parsing_success) { | ||
| 820 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 821 | printf("Invalid output format: %s\n", optarg); | ||
| 822 | exit(STATE_UNKNOWN); | ||
| 823 | } | ||
| 824 | |||
| 825 | result.config.output_format_is_set = true; | ||
| 826 | result.config.output_format = parser.output_format; | ||
| 827 | break; | ||
| 828 | } | ||
| 861 | } | 829 | } |
| 862 | } | 830 | } |
| 863 | 831 | ||
| 864 | /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ | 832 | /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ |
| 865 | c = optind; | 833 | int index = optind; |
| 866 | if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) | 834 | |
| 867 | warn_usedspace_percent = argv[c++]; | 835 | if (argc > index && is_intnonneg(argv[index])) { |
| 836 | if (verbose > 0) { | ||
| 837 | printf("Got an positional warn threshold: %s\n", argv[index]); | ||
| 838 | } | ||
| 839 | char *range = argv[index++]; | ||
| 840 | mp_range_parsed tmp = mp_parse_range_string(range); | ||
| 841 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 842 | die(STATE_UNKNOWN, "failed to parse warning threshold"); | ||
| 843 | } | ||
| 844 | |||
| 845 | mp_range tmp_range = tmp.range; | ||
| 846 | // Invert range to use it for free instead of used | ||
| 847 | // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range; | ||
| 868 | 848 | ||
| 869 | if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) | 849 | warn_freespace_percent = mp_range_to_string(tmp_range); |
| 870 | crit_usedspace_percent = argv[c++]; | ||
| 871 | 850 | ||
| 872 | if (argc > c) { | 851 | if (verbose > 0) { |
| 873 | se = np_add_parameter(&path_select_list, strdup(argv[c++])); | 852 | printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent); |
| 853 | } | ||
| 854 | } | ||
| 855 | |||
| 856 | if (argc > index && is_intnonneg(argv[index])) { | ||
| 857 | if (verbose > 0) { | ||
| 858 | printf("Got an positional crit threshold: %s\n", argv[index]); | ||
| 859 | } | ||
| 860 | char *range = argv[index++]; | ||
| 861 | mp_range_parsed tmp = mp_parse_range_string(range); | ||
| 862 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 863 | die(STATE_UNKNOWN, "failed to parse warning threshold"); | ||
| 864 | } | ||
| 865 | |||
| 866 | mp_range tmp_range = tmp.range; | ||
| 867 | // Invert range to use it for free instead of used | ||
| 868 | // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range; | ||
| 869 | |||
| 870 | crit_freespace_percent = mp_range_to_string(tmp_range); | ||
| 871 | |||
| 872 | if (verbose > 0) { | ||
| 873 | printf("Positional critical threshold transformed to: %s\n", crit_freespace_percent); | ||
| 874 | } | ||
| 875 | } | ||
| 876 | |||
| 877 | if (argc > index) { | ||
| 878 | if (verbose > 0) { | ||
| 879 | printf("Got an positional filesystem: %s\n", argv[index]); | ||
| 880 | } | ||
| 881 | struct parameter_list *se = | ||
| 882 | mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++])); | ||
| 874 | path_selected = true; | 883 | path_selected = true; |
| 875 | set_all_thresholds(se); | 884 | set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, |
| 885 | crit_freespace_percent, warn_freeinodes_percent, | ||
| 886 | crit_freeinodes_percent); | ||
| 876 | } | 887 | } |
| 877 | 888 | ||
| 878 | if (units == NULL) { | 889 | // If a list of paths has not been explicitly selected, find entire |
| 879 | units = strdup("MiB"); | 890 | // mount list and create list of paths |
| 880 | mult = (uintmax_t)1024 * 1024; | 891 | if (!path_selected && !result.config.path_ignored) { |
| 892 | for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { | ||
| 893 | if (me->me_dummy != 0) { | ||
| 894 | // just do not add dummy filesystems | ||
| 895 | continue; | ||
| 896 | } | ||
| 897 | |||
| 898 | parameter_list_elem *path = NULL; | ||
| 899 | if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { | ||
| 900 | path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); | ||
| 901 | } | ||
| 902 | path->best_match = me; | ||
| 903 | path->group = group; | ||
| 904 | set_all_thresholds(path, warn_freespace_units, crit_freespace_units, | ||
| 905 | warn_freespace_percent, crit_freespace_percent, | ||
| 906 | warn_freeinodes_percent, crit_freeinodes_percent); | ||
| 907 | } | ||
| 881 | } | 908 | } |
| 882 | 909 | ||
| 883 | return true; | 910 | // Set thresholds to the appropriate unit |
| 911 | for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp; | ||
| 912 | tmp = mp_int_fs_list_get_next(tmp)) { | ||
| 913 | |||
| 914 | mp_perfdata_value factor = mp_create_pd_value(unit); | ||
| 915 | |||
| 916 | if (tmp->freespace_units.critical_is_set) { | ||
| 917 | tmp->freespace_units.critical = | ||
| 918 | mp_range_multiply(tmp->freespace_units.critical, factor); | ||
| 919 | } | ||
| 920 | if (tmp->freespace_units.warning_is_set) { | ||
| 921 | tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor); | ||
| 922 | } | ||
| 923 | } | ||
| 924 | |||
| 925 | return result; | ||
| 884 | } | 926 | } |
| 885 | 927 | ||
| 886 | void set_all_thresholds(struct parameter_list *path) { | 928 | void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, |
| 887 | if (path->freespace_units != NULL) | 929 | char *crit_freespace_units, char *warn_freespace_percent, |
| 888 | free(path->freespace_units); | 930 | char *crit_freespace_percent, char *warn_freeinodes_percent, |
| 889 | set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); | 931 | char *crit_freeinodes_percent) { |
| 890 | if (path->freespace_percent != NULL) | 932 | mp_range_parsed tmp; |
| 891 | free(path->freespace_percent); | 933 | |
| 892 | set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); | 934 | if (warn_freespace_units) { |
| 893 | if (path->usedspace_units != NULL) | 935 | tmp = mp_parse_range_string(warn_freespace_units); |
| 894 | free(path->usedspace_units); | 936 | path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range); |
| 895 | set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); | 937 | } |
| 896 | if (path->usedspace_percent != NULL) | 938 | |
| 897 | free(path->usedspace_percent); | 939 | if (crit_freespace_units) { |
| 898 | set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); | 940 | tmp = mp_parse_range_string(crit_freespace_units); |
| 899 | if (path->usedinodes_percent != NULL) | 941 | path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range); |
| 900 | free(path->usedinodes_percent); | 942 | } |
| 901 | set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); | 943 | |
| 902 | if (path->freeinodes_percent != NULL) | 944 | if (warn_freespace_percent) { |
| 903 | free(path->freeinodes_percent); | 945 | tmp = mp_parse_range_string(warn_freespace_percent); |
| 904 | set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); | 946 | path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range); |
| 947 | } | ||
| 948 | |||
| 949 | if (crit_freespace_percent) { | ||
| 950 | tmp = mp_parse_range_string(crit_freespace_percent); | ||
| 951 | path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range); | ||
| 952 | } | ||
| 953 | |||
| 954 | if (warn_freeinodes_percent) { | ||
| 955 | tmp = mp_parse_range_string(warn_freeinodes_percent); | ||
| 956 | path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range); | ||
| 957 | } | ||
| 958 | |||
| 959 | if (crit_freeinodes_percent) { | ||
| 960 | tmp = mp_parse_range_string(crit_freeinodes_percent); | ||
| 961 | path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range); | ||
| 962 | } | ||
| 905 | } | 963 | } |
| 906 | 964 | ||
| 907 | void print_help(void) { | 965 | void print_help(void) { |
| @@ -911,7 +969,8 @@ void print_help(void) { | |||
| 911 | printf(COPYRIGHT, copyright, email); | 969 | printf(COPYRIGHT, copyright, email); |
| 912 | 970 | ||
| 913 | printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system")); | 971 | printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system")); |
| 914 | printf("%s\n", _("and generates an alert if free space is less than one of the threshold values")); | 972 | printf("%s\n", |
| 973 | _("and generates an alert if free space is less than one of the threshold values")); | ||
| 915 | 974 | ||
| 916 | printf("\n\n"); | 975 | printf("\n\n"); |
| 917 | 976 | ||
| @@ -933,7 +992,8 @@ void print_help(void) { | |||
| 933 | printf(" %s\n", "-K, --icritical=PERCENT%"); | 992 | printf(" %s\n", "-K, --icritical=PERCENT%"); |
| 934 | printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); | 993 | printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); |
| 935 | printf(" %s\n", "-p, --path=PATH, --partition=PARTITION"); | 994 | printf(" %s\n", "-p, --path=PATH, --partition=PARTITION"); |
| 936 | printf(" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)")); | 995 | printf(" %s\n", |
| 996 | _("Mount point or block device as emitted by the mount(8) command (may be repeated)")); | ||
| 937 | printf(" %s\n", "-x, --exclude_device=PATH <STRING>"); | 997 | printf(" %s\n", "-x, --exclude_device=PATH <STRING>"); |
| 938 | printf(" %s\n", _("Ignore device (only works if -p unspecified)")); | 998 | printf(" %s\n", _("Ignore device (only works if -p unspecified)")); |
| 939 | printf(" %s\n", "-C, --clear"); | 999 | printf(" %s\n", "-C, --clear"); |
| @@ -947,167 +1007,298 @@ void print_help(void) { | |||
| 947 | printf(" %s\n", "-P, --iperfdata"); | 1007 | printf(" %s\n", "-P, --iperfdata"); |
| 948 | printf(" %s\n", _("Display inode usage in perfdata")); | 1008 | printf(" %s\n", _("Display inode usage in perfdata")); |
| 949 | printf(" %s\n", "-g, --group=NAME"); | 1009 | printf(" %s\n", "-g, --group=NAME"); |
| 950 | printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); | 1010 | printf(" %s\n", |
| 951 | printf(" %s\n", "-k, --kilobytes"); | 1011 | _("Group paths. Thresholds apply to (free-)space of all partitions together")); |
| 952 | printf(" %s\n", _("Same as '--units kB'")); | ||
| 953 | printf(" %s\n", "-l, --local"); | 1012 | printf(" %s\n", "-l, --local"); |
| 954 | printf(" %s\n", _("Only check local filesystems")); | 1013 | printf(" %s\n", _("Only check local filesystems")); |
| 955 | printf(" %s\n", "-L, --stat-remote-fs"); | 1014 | printf(" %s\n", "-L, --stat-remote-fs"); |
| 956 | printf(" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); | 1015 | printf( |
| 1016 | " %s\n", | ||
| 1017 | _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); | ||
| 957 | printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); | 1018 | printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); |
| 958 | printf(" %s\n", "-M, --mountpoint"); | 1019 | printf(" %s\n", "-M, --mountpoint"); |
| 959 | printf(" %s\n", _("Display the (block) device instead of the mount point")); | 1020 | printf(" %s\n", _("Display the (block) device instead of the mount point")); |
| 960 | printf(" %s\n", "-m, --megabytes"); | ||
| 961 | printf(" %s\n", _("Same as '--units MB'")); | ||
| 962 | printf(" %s\n", "-A, --all"); | 1021 | printf(" %s\n", "-A, --all"); |
| 963 | printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); | 1022 | printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); |
| 964 | printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); | 1023 | printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); |
| 965 | printf(" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)")); | 1024 | printf(" %s\n", |
| 1025 | _("Case insensitive regular expression for path/partition (may be repeated)")); | ||
| 966 | printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION"); | 1026 | printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION"); |
| 967 | printf(" %s\n", _("Regular expression for path or partition (may be repeated)")); | 1027 | printf(" %s\n", _("Regular expression for path or partition (may be repeated)")); |
| 968 | printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION"); | 1028 | printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION"); |
| 969 | printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)")); | 1029 | printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) " |
| 1030 | "(may be repeated)")); | ||
| 970 | printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION"); | 1031 | printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION"); |
| 971 | printf(" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)")); | 1032 | printf(" %s\n", |
| 1033 | _("Regular expression to ignore selected path or partition (may be repeated)")); | ||
| 972 | printf(" %s\n", "-n, --ignore-missing"); | 1034 | printf(" %s\n", "-n, --ignore-missing"); |
| 973 | printf(" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible.")); | 1035 | printf(" %s\n", |
| 1036 | _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible.")); | ||
| 974 | printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); | 1037 | printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); |
| 975 | printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 1038 | printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 976 | printf(" %s\n", "-u, --units=STRING"); | 1039 | printf(" %s\n", "-u, --units=STRING"); |
| 977 | printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); | 1040 | printf(" %s\n", _("Select the unit used for the absolute value thresholds")); |
| 1041 | printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", " | ||
| 1042 | "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); | ||
| 1043 | printf(" %s\n", "-k, --kilobytes"); | ||
| 1044 | printf(" %s\n", _("Same as '--units kB'")); | ||
| 1045 | printf(" %s\n", "--display-unit"); | ||
| 1046 | printf(" %s\n", _("Select the unit used for in the output")); | ||
| 1047 | printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", " | ||
| 1048 | "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); | ||
| 1049 | printf(" %s\n", "-m, --megabytes"); | ||
| 1050 | printf(" %s\n", _("Same as '--units MB'")); | ||
| 978 | printf(UT_VERBOSE); | 1051 | printf(UT_VERBOSE); |
| 979 | printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); | 1052 | printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); |
| 980 | printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); | 1053 | printf(" %s\n", |
| 1054 | _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); | ||
| 981 | printf(" %s\n", "-N, --include-type=TYPE_REGEX"); | 1055 | printf(" %s\n", "-N, --include-type=TYPE_REGEX"); |
| 982 | printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); | 1056 | printf( |
| 1057 | " %s\n", | ||
| 1058 | _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); | ||
| 1059 | printf(UT_OUTPUT_FORMAT); | ||
| 983 | 1060 | ||
| 984 | printf("\n"); | 1061 | printf("\n"); |
| 985 | printf("%s\n", _("General usage hints:")); | 1062 | printf("%s\n", _("General usage hints:")); |
| 986 | printf(" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as")); | 1063 | printf( |
| 1064 | " %s\n", | ||
| 1065 | _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as")); | ||
| 987 | printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\".")); | 1066 | printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\".")); |
| 988 | printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\"")); | 1067 | printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} " |
| 1068 | "{thresholds b} ...\"")); | ||
| 989 | 1069 | ||
| 990 | printf("\n"); | 1070 | printf("\n"); |
| 991 | printf("%s\n", _("Examples:")); | 1071 | printf("%s\n", _("Examples:")); |
| 992 | printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /"); | 1072 | printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /"); |
| 993 | printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB")); | 1073 | printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB")); |
| 994 | printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'"); | 1074 | printf(" %s\n", |
| 995 | printf(" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); | 1075 | "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'"); |
| 996 | printf(" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together")); | 1076 | printf( |
| 1077 | " %s\n", | ||
| 1078 | _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); | ||
| 1079 | printf(" %s\n\n", | ||
| 1080 | _("are grouped which means the freespace thresholds are applied to all disks together")); | ||
| 997 | printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar"); | 1081 | printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar"); |
| 998 | printf(" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); | 1082 | printf(" %s\n", |
| 1083 | _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); | ||
| 999 | 1084 | ||
| 1000 | printf(UT_SUPPORT); | 1085 | printf(UT_SUPPORT); |
| 1001 | } | 1086 | } |
| 1002 | 1087 | ||
| 1003 | void print_usage(void) { | 1088 | void print_usage(void) { |
| 1004 | printf("%s\n", _("Usage:")); | 1089 | printf("%s\n", _("Usage:")); |
| 1005 | printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K " | 1090 | printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c " |
| 1091 | "absolute_limit|-c percentage_limit%% | -K " | ||
| 1006 | "inode_percentage_limit } {-p path | -x device}\n", | 1092 | "inode_percentage_limit } {-p path | -x device}\n", |
| 1007 | progname); | 1093 | progname); |
| 1008 | printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); | 1094 | printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); |
| 1009 | printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); | 1095 | printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); |
| 1010 | } | 1096 | } |
| 1011 | 1097 | ||
| 1012 | bool stat_path(struct parameter_list *p) { | 1098 | bool stat_path(parameter_list_elem *parameters, bool ignore_missing) { |
| 1013 | /* Stat entry to check that dir exists and is accessible */ | 1099 | /* Stat entry to check that dir exists and is accessible */ |
| 1014 | if (verbose >= 3) | 1100 | if (verbose >= 3) { |
| 1015 | printf("calling stat on %s\n", p->name); | 1101 | printf("calling stat on %s\n", parameters->name); |
| 1016 | if (stat(p->name, &stat_buf[0])) { | 1102 | } |
| 1017 | if (verbose >= 3) | 1103 | |
| 1018 | printf("stat failed on %s\n", p->name); | 1104 | struct stat stat_buf = {0}; |
| 1019 | if (ignore_missing == true) { | 1105 | if (stat(parameters->name, &stat_buf)) { |
| 1106 | if (verbose >= 3) { | ||
| 1107 | printf("stat failed on %s\n", parameters->name); | ||
| 1108 | } | ||
| 1109 | if (ignore_missing) { | ||
| 1020 | return false; | 1110 | return false; |
| 1021 | } | 1111 | } |
| 1022 | printf("DISK %s - ", _("CRITICAL")); | 1112 | printf("DISK %s - ", _("CRITICAL")); |
| 1023 | die(STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); | 1113 | die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"), |
| 1114 | strerror(errno)); | ||
| 1024 | } | 1115 | } |
| 1116 | |||
| 1025 | return true; | 1117 | return true; |
| 1026 | } | 1118 | } |
| 1027 | 1119 | ||
| 1028 | void get_stats(struct parameter_list *p, struct fs_usage *fsp) { | 1120 | static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp, |
| 1029 | struct parameter_list *p_list; | 1121 | bool freespace_ignore_reserved) { |
| 1030 | struct fs_usage tmpfsp; | 1122 | uintmax_t available = fsp.fsu_bavail; |
| 1031 | int first = 1; | 1123 | uintmax_t available_to_root = fsp.fsu_bfree; |
| 1124 | uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree; | ||
| 1125 | uintmax_t total; | ||
| 1032 | 1126 | ||
| 1033 | if (p->group == NULL) { | ||
| 1034 | get_path_stats(p, fsp); | ||
| 1035 | } else { | ||
| 1036 | /* find all group members */ | ||
| 1037 | for (p_list = path_select_list; p_list; p_list = p_list->name_next) { | ||
| 1038 | #ifdef __CYGWIN__ | ||
| 1039 | if (strncmp(p_list->name, "/cygdrive/", 10) != 0) | ||
| 1040 | continue; | ||
| 1041 | #endif | ||
| 1042 | if (p_list->group && !(strcmp(p_list->group, p->group))) { | ||
| 1043 | if (!stat_path(p_list)) | ||
| 1044 | continue; | ||
| 1045 | get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp); | ||
| 1046 | get_path_stats(p_list, &tmpfsp); | ||
| 1047 | if (verbose >= 3) | ||
| 1048 | printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n", | ||
| 1049 | p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, | ||
| 1050 | p_list->dfree_units, p_list->dtotal_units, mult); | ||
| 1051 | |||
| 1052 | /* prevent counting the first FS of a group twice since its parameter_list entry | ||
| 1053 | * is used to carry the information of all file systems of the entire group */ | ||
| 1054 | if (!first) { | ||
| 1055 | p->total += p_list->total; | ||
| 1056 | p->available += p_list->available; | ||
| 1057 | p->available_to_root += p_list->available_to_root; | ||
| 1058 | p->used += p_list->used; | ||
| 1059 | |||
| 1060 | p->dused_units += p_list->dused_units; | ||
| 1061 | p->dfree_units += p_list->dfree_units; | ||
| 1062 | p->dtotal_units += p_list->dtotal_units; | ||
| 1063 | p->inodes_total += p_list->inodes_total; | ||
| 1064 | p->inodes_free += p_list->inodes_free; | ||
| 1065 | p->inodes_free_to_root += p_list->inodes_free_to_root; | ||
| 1066 | p->inodes_used += p_list->inodes_used; | ||
| 1067 | } | ||
| 1068 | first = 0; | ||
| 1069 | } | ||
| 1070 | if (verbose >= 3) | ||
| 1071 | printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p->group, | ||
| 1072 | p->dused_units, p->dfree_units, p->dtotal_units, tmpfsp.fsu_blocksize, mult); | ||
| 1073 | } | ||
| 1074 | /* modify devname and mountdir for output */ | ||
| 1075 | p->best_match->me_mountdir = p->best_match->me_devname = p->group; | ||
| 1076 | } | ||
| 1077 | /* finally calculate percentages for either plain FS or summed up group */ | ||
| 1078 | p->dused_pct = calculate_percent(p->used, p->used + p->available); /* used + available can never be > uintmax */ | ||
| 1079 | p->dfree_pct = 100.0 - p->dused_pct; | ||
| 1080 | p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total); | ||
| 1081 | p->dfree_inodes_percent = 100 - p->dused_inodes_percent; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | void get_path_stats(struct parameter_list *p, struct fs_usage *fsp) { | ||
| 1085 | p->available = fsp->fsu_bavail; | ||
| 1086 | p->available_to_root = fsp->fsu_bfree; | ||
| 1087 | p->used = fsp->fsu_blocks - fsp->fsu_bfree; | ||
| 1088 | if (freespace_ignore_reserved) { | 1127 | if (freespace_ignore_reserved) { |
| 1089 | /* option activated : we subtract the root-reserved space from the total */ | 1128 | /* option activated : we subtract the root-reserved space from the total */ |
| 1090 | p->total = fsp->fsu_blocks - p->available_to_root + p->available; | 1129 | total = fsp.fsu_blocks - available_to_root + available; |
| 1091 | } else { | 1130 | } else { |
| 1092 | /* default behaviour : take all the blocks into account */ | 1131 | /* default behaviour : take all the blocks into account */ |
| 1093 | p->total = fsp->fsu_blocks; | 1132 | total = fsp.fsu_blocks; |
| 1094 | } | 1133 | } |
| 1095 | 1134 | ||
| 1096 | p->dused_units = p->used * fsp->fsu_blocksize / mult; | 1135 | parameters.used_bytes = used * fsp.fsu_blocksize; |
| 1097 | p->dfree_units = p->available * fsp->fsu_blocksize / mult; | 1136 | parameters.free_bytes = available * fsp.fsu_blocksize; |
| 1098 | p->dtotal_units = p->total * fsp->fsu_blocksize / mult; | 1137 | parameters.total_bytes = total * fsp.fsu_blocksize; |
| 1138 | |||
| 1099 | /* Free file nodes. Not sure the workaround is required, but in case...*/ | 1139 | /* Free file nodes. Not sure the workaround is required, but in case...*/ |
| 1100 | p->inodes_free = fsp->fsu_ffree; | 1140 | parameters.inodes_free = fsp.fsu_ffree; |
| 1101 | p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ | 1141 | parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */ |
| 1102 | p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; | 1142 | parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree; |
| 1143 | |||
| 1103 | if (freespace_ignore_reserved) { | 1144 | if (freespace_ignore_reserved) { |
| 1104 | /* option activated : we subtract the root-reserved inodes from the total */ | 1145 | /* option activated : we subtract the root-reserved inodes from the total */ |
| 1105 | /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ | 1146 | /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ |
| 1106 | /* for others, fsp->fsu_ffree == fsp->fsu_favail */ | 1147 | /* for others, fsp->fsu_ffree == fsp->fsu_favail */ |
| 1107 | p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; | 1148 | parameters.inodes_total = |
| 1149 | fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free; | ||
| 1108 | } else { | 1150 | } else { |
| 1109 | /* default behaviour : take all the inodes into account */ | 1151 | /* default behaviour : take all the inodes into account */ |
| 1110 | p->inodes_total = fsp->fsu_files; | 1152 | parameters.inodes_total = fsp.fsu_files; |
| 1111 | } | 1153 | } |
| 1112 | np_add_name(&seen, p->best_match->me_mountdir); | 1154 | |
| 1155 | return parameters; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, | ||
| 1159 | byte_unit unit) { | ||
| 1160 | mp_subcheck result = mp_subcheck_init(); | ||
| 1161 | result = mp_set_subcheck_default_state(result, STATE_UNKNOWN); | ||
| 1162 | xasprintf(&result.output, "%s", measurement_unit.name); | ||
| 1163 | |||
| 1164 | if (!measurement_unit.is_group && measurement_unit.filesystem_type) { | ||
| 1165 | xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type); | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | /* Threshold comparisons */ | ||
| 1169 | |||
| 1170 | // =============================== | ||
| 1171 | // Free space absolute values test | ||
| 1172 | mp_subcheck freespace_bytes_sc = mp_subcheck_init(); | ||
| 1173 | freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK); | ||
| 1174 | |||
| 1175 | if (unit != Humanized) { | ||
| 1176 | xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)", | ||
| 1177 | (uintmax_t)(measurement_unit.free_bytes / unit), get_unit_string(unit), | ||
| 1178 | (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit)); | ||
| 1179 | } else { | ||
| 1180 | xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)", | ||
| 1181 | humanize_byte_value(measurement_unit.free_bytes, false), | ||
| 1182 | humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false)); | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | mp_perfdata used_space = perfdata_init(); | ||
| 1186 | used_space.label = measurement_unit.name; | ||
| 1187 | used_space.value = mp_create_pd_value(measurement_unit.free_bytes); | ||
| 1188 | used_space = mp_set_pd_max_value(used_space, mp_create_pd_value(measurement_unit.total_bytes)); | ||
| 1189 | used_space = mp_set_pd_min_value(used_space, mp_create_pd_value(0)); | ||
| 1190 | used_space.uom = "B"; | ||
| 1191 | used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); | ||
| 1192 | freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(used_space)); | ||
| 1193 | |||
| 1194 | // special case for absolute space thresholds here: | ||
| 1195 | // if absolute values are not set, compute the thresholds from percentage thresholds | ||
| 1196 | mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds; | ||
| 1197 | if (!temp_thlds.critical_is_set && | ||
| 1198 | measurement_unit.freespace_percent_thresholds.critical_is_set) { | ||
| 1199 | mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical; | ||
| 1200 | |||
| 1201 | if (!tmp_range.end_infinity) { | ||
| 1202 | tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * | ||
| 1203 | measurement_unit.total_bytes); | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | if (!tmp_range.start_infinity) { | ||
| 1207 | tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * | ||
| 1208 | measurement_unit.total_bytes); | ||
| 1209 | } | ||
| 1210 | measurement_unit.freespace_bytes_thresholds = | ||
| 1211 | mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range); | ||
| 1212 | used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | if (!temp_thlds.warning_is_set && | ||
| 1216 | measurement_unit.freespace_percent_thresholds.warning_is_set) { | ||
| 1217 | mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning; | ||
| 1218 | if (!tmp_range.end_infinity) { | ||
| 1219 | tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * | ||
| 1220 | measurement_unit.total_bytes); | ||
| 1221 | } | ||
| 1222 | if (!tmp_range.start_infinity) { | ||
| 1223 | tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * | ||
| 1224 | measurement_unit.total_bytes); | ||
| 1225 | } | ||
| 1226 | measurement_unit.freespace_bytes_thresholds = | ||
| 1227 | mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range); | ||
| 1228 | used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space); | ||
| 1232 | mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc); | ||
| 1233 | |||
| 1234 | // ========================== | ||
| 1235 | // Free space percentage test | ||
| 1236 | mp_subcheck freespace_percent_sc = mp_subcheck_init(); | ||
| 1237 | freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK); | ||
| 1238 | |||
| 1239 | double free_percentage = | ||
| 1240 | calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes); | ||
| 1241 | xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage); | ||
| 1242 | |||
| 1243 | // Using perfdata here just to get to the test result | ||
| 1244 | mp_perfdata free_space_percent_pd = perfdata_init(); | ||
| 1245 | free_space_percent_pd.value = mp_create_pd_value(free_percentage); | ||
| 1246 | free_space_percent_pd = | ||
| 1247 | mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds); | ||
| 1248 | |||
| 1249 | freespace_percent_sc = | ||
| 1250 | mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd)); | ||
| 1251 | mp_add_subcheck_to_subcheck(&result, freespace_percent_sc); | ||
| 1252 | |||
| 1253 | // ================ | ||
| 1254 | // Free inodes test | ||
| 1255 | // Only ever useful if the number of inodes is static (e.g. ext4), | ||
| 1256 | // not when it is dynamic (e.g btrfs) | ||
| 1257 | // Assumption: if the total number of inodes == 0, we have such a case and just skip the test | ||
| 1258 | if (measurement_unit.inodes_total > 0) { | ||
| 1259 | mp_subcheck freeindodes_percent_sc = mp_subcheck_init(); | ||
| 1260 | freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK); | ||
| 1261 | |||
| 1262 | double free_inode_percentage = | ||
| 1263 | calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total); | ||
| 1264 | |||
| 1265 | if (verbose > 0) { | ||
| 1266 | printf("free inode percentage computed: %g\n", free_inode_percentage); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)", | ||
| 1270 | free_inode_percentage, measurement_unit.inodes_free, | ||
| 1271 | measurement_unit.inodes_total); | ||
| 1272 | |||
| 1273 | mp_perfdata inodes_pd = perfdata_init(); | ||
| 1274 | xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name); | ||
| 1275 | inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used); | ||
| 1276 | inodes_pd = | ||
| 1277 | mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total)); | ||
| 1278 | inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0)); | ||
| 1279 | |||
| 1280 | mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds; | ||
| 1281 | |||
| 1282 | if (absolut_inode_thresholds.critical_is_set) { | ||
| 1283 | absolut_inode_thresholds.critical = | ||
| 1284 | mp_range_multiply(absolut_inode_thresholds.critical, | ||
| 1285 | mp_create_pd_value(measurement_unit.inodes_total / 100)); | ||
| 1286 | } | ||
| 1287 | if (absolut_inode_thresholds.warning_is_set) { | ||
| 1288 | absolut_inode_thresholds.warning = | ||
| 1289 | mp_range_multiply(absolut_inode_thresholds.warning, | ||
| 1290 | mp_create_pd_value(measurement_unit.inodes_total / 100)); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds); | ||
| 1294 | |||
| 1295 | freeindodes_percent_sc = | ||
| 1296 | mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd)); | ||
| 1297 | if (display_inodes_perfdata) { | ||
| 1298 | mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd); | ||
| 1299 | } | ||
| 1300 | mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | return result; | ||
| 1113 | } | 1304 | } |
diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c new file mode 100644 index 00000000..0b89018d --- /dev/null +++ b/plugins/check_disk.d/utils_disk.c | |||
| @@ -0,0 +1,528 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * Library for check_disk | ||
| 4 | * | ||
| 5 | * License: GPL | ||
| 6 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team | ||
| 7 | * | ||
| 8 | * Description: | ||
| 9 | * | ||
| 10 | * This file contains utilities for check_disk. These are tested by libtap | ||
| 11 | * | ||
| 12 | * | ||
| 13 | * This program is free software: you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation, either version 3 of the License, or | ||
| 16 | * (at your option) any later version. | ||
| 17 | * | ||
| 18 | * This program is distributed in the hope that it will be useful, | ||
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 21 | * GNU General Public License for more details. | ||
| 22 | * | ||
| 23 | * You should have received a copy of the GNU General Public License | ||
| 24 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 25 | * | ||
| 26 | * | ||
| 27 | *****************************************************************************/ | ||
| 28 | |||
| 29 | #include "../common.h" | ||
| 30 | #include "utils_disk.h" | ||
| 31 | #include "../../gl/fsusage.h" | ||
| 32 | #include "../../lib/thresholds.h" | ||
| 33 | #include "../../lib/states.h" | ||
| 34 | #include <stdint.h> | ||
| 35 | #include <stdio.h> | ||
| 36 | #include <string.h> | ||
| 37 | #include <assert.h> | ||
| 38 | |||
| 39 | void np_add_name(struct name_list **list, const char *name) { | ||
| 40 | struct name_list *new_entry; | ||
| 41 | new_entry = (struct name_list *)malloc(sizeof *new_entry); | ||
| 42 | new_entry->name = (char *)name; | ||
| 43 | new_entry->next = *list; | ||
| 44 | *list = new_entry; | ||
| 45 | } | ||
| 46 | |||
| 47 | /* @brief Initialises a new regex at the begin of list via regcomp(3) | ||
| 48 | * | ||
| 49 | * @details if the regex fails to compile the error code of regcomp(3) is returned | ||
| 50 | * and list is not modified, otherwise list is modified to point to the new | ||
| 51 | * element | ||
| 52 | * @param list Pointer to a linked list of regex_list elements | ||
| 53 | * @param regex the string containing the regex which should be inserted into the list | ||
| 54 | * @param clags the cflags parameter for regcomp(3) | ||
| 55 | */ | ||
| 56 | int np_add_regex(struct regex_list **list, const char *regex, int cflags) { | ||
| 57 | struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry); | ||
| 58 | |||
| 59 | if (new_entry == NULL) { | ||
| 60 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 61 | } | ||
| 62 | |||
| 63 | int regcomp_result = regcomp(&new_entry->regex, regex, cflags); | ||
| 64 | |||
| 65 | if (!regcomp_result) { | ||
| 66 | // regcomp succeeded | ||
| 67 | new_entry->next = *list; | ||
| 68 | *list = new_entry; | ||
| 69 | |||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | // regcomp failed | ||
| 73 | free(new_entry); | ||
| 74 | |||
| 75 | return regcomp_result; | ||
| 76 | } | ||
| 77 | |||
| 78 | parameter_list_elem parameter_list_init(const char *name) { | ||
| 79 | parameter_list_elem result = { | ||
| 80 | .name = strdup(name), | ||
| 81 | .best_match = NULL, | ||
| 82 | |||
| 83 | .freespace_units = mp_thresholds_init(), | ||
| 84 | .freespace_percent = mp_thresholds_init(), | ||
| 85 | .freeinodes_percent = mp_thresholds_init(), | ||
| 86 | |||
| 87 | .group = NULL, | ||
| 88 | |||
| 89 | .inodes_total = 0, | ||
| 90 | .inodes_free = 0, | ||
| 91 | .inodes_free_to_root = 0, | ||
| 92 | .inodes_used = 0, | ||
| 93 | |||
| 94 | .used_bytes = 0, | ||
| 95 | .free_bytes = 0, | ||
| 96 | .total_bytes = 0, | ||
| 97 | |||
| 98 | .next = NULL, | ||
| 99 | .prev = NULL, | ||
| 100 | }; | ||
| 101 | return result; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* Returns true if name is in list */ | ||
| 105 | bool np_find_name(struct name_list *list, const char *name) { | ||
| 106 | if (list == NULL || name == NULL) { | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | for (struct name_list *iterator = list; iterator; iterator = iterator->next) { | ||
| 110 | if (!strcmp(name, iterator->name)) { | ||
| 111 | return true; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | return false; | ||
| 115 | } | ||
| 116 | |||
| 117 | /* Returns true if name is in list */ | ||
| 118 | bool np_find_regmatch(struct regex_list *list, const char *name) { | ||
| 119 | if (name == NULL) { | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | |||
| 123 | size_t len = strlen(name); | ||
| 124 | |||
| 125 | for (; list; list = list->next) { | ||
| 126 | /* Emulate a full match as if surrounded with ^( )$ | ||
| 127 | by checking whether the match spans the whole name */ | ||
| 128 | regmatch_t dummy_match; | ||
| 129 | if (!regexec(&list->regex, name, 1, &dummy_match, 0) && dummy_match.rm_so == 0 && | ||
| 130 | dummy_match.rm_eo == len) { | ||
| 131 | return true; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | return false; | ||
| 136 | } | ||
| 137 | |||
| 138 | bool np_seen_name(struct name_list *list, const char *name) { | ||
| 139 | for (struct name_list *iterator = list; iterator; iterator = iterator->next) { | ||
| 140 | if (!strcmp(iterator->name, name)) { | ||
| 141 | return true; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | return false; | ||
| 145 | } | ||
| 146 | |||
| 147 | bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { | ||
| 148 | return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || | ||
| 149 | (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0)); | ||
| 150 | } | ||
| 151 | |||
| 152 | check_disk_config check_disk_config_init() { | ||
| 153 | check_disk_config tmp = { | ||
| 154 | .erronly = false, | ||
| 155 | .display_mntp = false, | ||
| 156 | .show_local_fs = false, | ||
| 157 | .stat_remote_fs = false, | ||
| 158 | .display_inodes_perfdata = false, | ||
| 159 | |||
| 160 | .exact_match = false, | ||
| 161 | .freespace_ignore_reserved = false, | ||
| 162 | |||
| 163 | .ignore_missing = false, | ||
| 164 | .path_ignored = false, | ||
| 165 | |||
| 166 | // FS Filters | ||
| 167 | .fs_exclude_list = NULL, | ||
| 168 | .fs_include_list = NULL, | ||
| 169 | .device_path_exclude_list = NULL, | ||
| 170 | |||
| 171 | // Actual filesystems paths to investigate | ||
| 172 | .path_select_list = filesystem_list_init(), | ||
| 173 | |||
| 174 | .mount_list = NULL, | ||
| 175 | .seen = NULL, | ||
| 176 | |||
| 177 | .display_unit = Humanized, | ||
| 178 | // .unit = MebiBytes, | ||
| 179 | |||
| 180 | .output_format_is_set = false, | ||
| 181 | }; | ||
| 182 | return tmp; | ||
| 183 | } | ||
| 184 | |||
| 185 | char *get_unit_string(byte_unit_enum unit) { | ||
| 186 | switch (unit) { | ||
| 187 | case Bytes: | ||
| 188 | return "Bytes"; | ||
| 189 | case KibiBytes: | ||
| 190 | return "KiB"; | ||
| 191 | case MebiBytes: | ||
| 192 | return "MiB"; | ||
| 193 | case GibiBytes: | ||
| 194 | return "GiB"; | ||
| 195 | case TebiBytes: | ||
| 196 | return "TiB"; | ||
| 197 | case PebiBytes: | ||
| 198 | return "PiB"; | ||
| 199 | case ExbiBytes: | ||
| 200 | return "EiB"; | ||
| 201 | case KiloBytes: | ||
| 202 | return "KB"; | ||
| 203 | case MegaBytes: | ||
| 204 | return "MB"; | ||
| 205 | case GigaBytes: | ||
| 206 | return "GB"; | ||
| 207 | case TeraBytes: | ||
| 208 | return "TB"; | ||
| 209 | case PetaBytes: | ||
| 210 | return "PB"; | ||
| 211 | case ExaBytes: | ||
| 212 | return "EB"; | ||
| 213 | default: | ||
| 214 | assert(false); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | measurement_unit measurement_unit_init() { | ||
| 219 | measurement_unit tmp = { | ||
| 220 | .name = NULL, | ||
| 221 | .filesystem_type = NULL, | ||
| 222 | .is_group = false, | ||
| 223 | |||
| 224 | .freeinodes_percent_thresholds = mp_thresholds_init(), | ||
| 225 | .freespace_percent_thresholds = mp_thresholds_init(), | ||
| 226 | .freespace_bytes_thresholds = mp_thresholds_init(), | ||
| 227 | |||
| 228 | .free_bytes = 0, | ||
| 229 | .used_bytes = 0, | ||
| 230 | .total_bytes = 0, | ||
| 231 | |||
| 232 | .inodes_total = 0, | ||
| 233 | .inodes_free = 0, | ||
| 234 | .inodes_free_to_root = 0, | ||
| 235 | .inodes_used = 0, | ||
| 236 | }; | ||
| 237 | return tmp; | ||
| 238 | } | ||
| 239 | |||
| 240 | // Add a given element to the list, memory for the new element is freshly allocated | ||
| 241 | // Returns a pointer to new element | ||
| 242 | measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem) { | ||
| 243 | // find last element | ||
| 244 | measurement_unit_list *new = NULL; | ||
| 245 | if (list == NULL) { | ||
| 246 | new = calloc(1, sizeof(measurement_unit_list)); | ||
| 247 | if (new == NULL) { | ||
| 248 | die(STATE_UNKNOWN, _("allocation failed")); | ||
| 249 | } | ||
| 250 | } else { | ||
| 251 | measurement_unit_list *list_elem = list; | ||
| 252 | while (list_elem->next != NULL) { | ||
| 253 | list_elem = list_elem->next; | ||
| 254 | } | ||
| 255 | |||
| 256 | new = calloc(1, sizeof(measurement_unit_list)); | ||
| 257 | if (new == NULL) { | ||
| 258 | die(STATE_UNKNOWN, _("allocation failed")); | ||
| 259 | } | ||
| 260 | |||
| 261 | list_elem->next = new; | ||
| 262 | } | ||
| 263 | |||
| 264 | new->unit = elem; | ||
| 265 | new->next = NULL; | ||
| 266 | return new; | ||
| 267 | } | ||
| 268 | |||
| 269 | measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, | ||
| 270 | parameter_list_elem filesystem) { | ||
| 271 | |||
| 272 | unit.free_bytes += filesystem.free_bytes; | ||
| 273 | unit.used_bytes += filesystem.used_bytes; | ||
| 274 | unit.total_bytes += filesystem.total_bytes; | ||
| 275 | |||
| 276 | unit.inodes_total += filesystem.inodes_total; | ||
| 277 | unit.inodes_free += filesystem.inodes_free; | ||
| 278 | unit.inodes_free_to_root += filesystem.inodes_free_to_root; | ||
| 279 | unit.inodes_used += filesystem.inodes_used; | ||
| 280 | return unit; | ||
| 281 | } | ||
| 282 | |||
| 283 | measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, | ||
| 284 | bool display_mntp) { | ||
| 285 | measurement_unit result = measurement_unit_init(); | ||
| 286 | if (!display_mntp) { | ||
| 287 | result.name = strdup(filesystem.best_match->me_mountdir); | ||
| 288 | } else { | ||
| 289 | result.name = strdup(filesystem.best_match->me_devname); | ||
| 290 | } | ||
| 291 | |||
| 292 | if (filesystem.group) { | ||
| 293 | result.is_group = true; | ||
| 294 | } else { | ||
| 295 | result.is_group = false; | ||
| 296 | if (filesystem.best_match) { | ||
| 297 | result.filesystem_type = filesystem.best_match->me_type; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | result.freeinodes_percent_thresholds = filesystem.freeinodes_percent; | ||
| 302 | result.freespace_percent_thresholds = filesystem.freespace_percent; | ||
| 303 | result.freespace_bytes_thresholds = filesystem.freespace_units; | ||
| 304 | result.free_bytes = filesystem.free_bytes; | ||
| 305 | result.total_bytes = filesystem.total_bytes; | ||
| 306 | result.used_bytes = filesystem.used_bytes; | ||
| 307 | result.inodes_total = filesystem.inodes_total; | ||
| 308 | result.inodes_used = filesystem.inodes_used; | ||
| 309 | result.inodes_free = filesystem.inodes_free; | ||
| 310 | result.inodes_free_to_root = filesystem.inodes_free_to_root; | ||
| 311 | return result; | ||
| 312 | } | ||
| 313 | |||
| 314 | #define RANDOM_STRING_LENGTH 64 | ||
| 315 | |||
| 316 | char *humanize_byte_value(unsigned long long value, bool use_si_units) { | ||
| 317 | // Idea: A reasonable output should have at most 3 orders of magnitude | ||
| 318 | // before the decimal separator | ||
| 319 | // 353GiB is ok, 2444 GiB should be 2.386 TiB | ||
| 320 | char *result = calloc(RANDOM_STRING_LENGTH, sizeof(char)); | ||
| 321 | if (result == NULL) { | ||
| 322 | die(STATE_UNKNOWN, _("allocation failed")); | ||
| 323 | } | ||
| 324 | const byte_unit KibiBytes_factor = 1024; | ||
| 325 | const byte_unit MebiBytes_factor = 1048576; | ||
| 326 | const byte_unit GibiBytes_factor = 1073741824; | ||
| 327 | const byte_unit TebiBytes_factor = 1099511627776; | ||
| 328 | const byte_unit PebiBytes_factor = 1125899906842624; | ||
| 329 | const byte_unit ExbiBytes_factor = 1152921504606846976; | ||
| 330 | const byte_unit KiloBytes_factor = 1000; | ||
| 331 | const byte_unit MegaBytes_factor = 1000000; | ||
| 332 | const byte_unit GigaBytes_factor = 1000000000; | ||
| 333 | const byte_unit TeraBytes_factor = 1000000000000; | ||
| 334 | const byte_unit PetaBytes_factor = 1000000000000000; | ||
| 335 | const byte_unit ExaBytes_factor = 1000000000000000000; | ||
| 336 | |||
| 337 | if (use_si_units) { | ||
| 338 | // SI units, powers of 10 | ||
| 339 | if (value < KiloBytes_factor) { | ||
| 340 | snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value); | ||
| 341 | } else if (value < MegaBytes_factor) { | ||
| 342 | snprintf(result, RANDOM_STRING_LENGTH, "%llu KB", value / KiloBytes_factor); | ||
| 343 | } else if (value < GigaBytes_factor) { | ||
| 344 | snprintf(result, RANDOM_STRING_LENGTH, "%llu MB", value / MegaBytes_factor); | ||
| 345 | } else if (value < TeraBytes_factor) { | ||
| 346 | snprintf(result, RANDOM_STRING_LENGTH, "%llu GB", value / GigaBytes_factor); | ||
| 347 | } else if (value < PetaBytes_factor) { | ||
| 348 | snprintf(result, RANDOM_STRING_LENGTH, "%llu TB", value / TeraBytes_factor); | ||
| 349 | } else if (value < ExaBytes_factor) { | ||
| 350 | snprintf(result, RANDOM_STRING_LENGTH, "%llu PB", value / PetaBytes_factor); | ||
| 351 | } else { | ||
| 352 | snprintf(result, RANDOM_STRING_LENGTH, "%llu EB", value / ExaBytes_factor); | ||
| 353 | } | ||
| 354 | } else { | ||
| 355 | // IEC units, powers of 2 ^ 10 | ||
| 356 | if (value < KibiBytes_factor) { | ||
| 357 | snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value); | ||
| 358 | } else if (value < MebiBytes_factor) { | ||
| 359 | snprintf(result, RANDOM_STRING_LENGTH, "%llu KiB", value / KibiBytes_factor); | ||
| 360 | } else if (value < GibiBytes_factor) { | ||
| 361 | snprintf(result, RANDOM_STRING_LENGTH, "%llu MiB", value / MebiBytes_factor); | ||
| 362 | } else if (value < TebiBytes_factor) { | ||
| 363 | snprintf(result, RANDOM_STRING_LENGTH, "%llu GiB", value / GibiBytes_factor); | ||
| 364 | } else if (value < PebiBytes_factor) { | ||
| 365 | snprintf(result, RANDOM_STRING_LENGTH, "%llu TiB", value / TebiBytes_factor); | ||
| 366 | } else if (value < ExbiBytes_factor) { | ||
| 367 | snprintf(result, RANDOM_STRING_LENGTH, "%llu PiB", value / PebiBytes_factor); | ||
| 368 | } else { | ||
| 369 | snprintf(result, RANDOM_STRING_LENGTH, "%llu EiB", value / ExbiBytes_factor); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | return result; | ||
| 374 | } | ||
| 375 | |||
| 376 | filesystem_list filesystem_list_init() { | ||
| 377 | filesystem_list tmp = { | ||
| 378 | .length = 0, | ||
| 379 | .first = NULL, | ||
| 380 | }; | ||
| 381 | return tmp; | ||
| 382 | } | ||
| 383 | |||
| 384 | parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name) { | ||
| 385 | parameter_list_elem *current = list->first; | ||
| 386 | parameter_list_elem *new_path = (struct parameter_list *)malloc(sizeof *new_path); | ||
| 387 | *new_path = parameter_list_init(name); | ||
| 388 | |||
| 389 | if (current == NULL) { | ||
| 390 | list->first = new_path; | ||
| 391 | new_path->prev = NULL; | ||
| 392 | list->length = 1; | ||
| 393 | } else { | ||
| 394 | while (current->next) { | ||
| 395 | current = current->next; | ||
| 396 | } | ||
| 397 | current->next = new_path; | ||
| 398 | new_path->prev = current; | ||
| 399 | list->length++; | ||
| 400 | } | ||
| 401 | return new_path; | ||
| 402 | } | ||
| 403 | |||
| 404 | parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name) { | ||
| 405 | if (list.length == 0) { | ||
| 406 | return NULL; | ||
| 407 | } | ||
| 408 | |||
| 409 | for (parameter_list_elem *temp_list = list.first; temp_list; temp_list = temp_list->next) { | ||
| 410 | if (!strcmp(temp_list->name, name)) { | ||
| 411 | return temp_list; | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | return NULL; | ||
| 416 | } | ||
| 417 | |||
| 418 | parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item) { | ||
| 419 | if (list->length == 0) { | ||
| 420 | return NULL; | ||
| 421 | } | ||
| 422 | |||
| 423 | if (item == NULL) { | ||
| 424 | // Got NULL for item, interpret this as "delete first element" | ||
| 425 | // as a kind of compatibility to the old function | ||
| 426 | item = list->first; | ||
| 427 | } | ||
| 428 | |||
| 429 | if (list->first == item) { | ||
| 430 | list->length--; | ||
| 431 | |||
| 432 | list->first = item->next; | ||
| 433 | if (list->first) { | ||
| 434 | list->first->prev = NULL; | ||
| 435 | } | ||
| 436 | return list->first; | ||
| 437 | } | ||
| 438 | |||
| 439 | // Was not the first element, continue | ||
| 440 | parameter_list_elem *prev = list->first; | ||
| 441 | parameter_list_elem *current = list->first->next; | ||
| 442 | |||
| 443 | while (current != item && current != NULL) { | ||
| 444 | prev = current; | ||
| 445 | current = current->next; | ||
| 446 | } | ||
| 447 | |||
| 448 | if (current == NULL) { | ||
| 449 | // didn't find that element .... | ||
| 450 | return NULL; | ||
| 451 | } | ||
| 452 | |||
| 453 | // remove the element | ||
| 454 | parameter_list_elem *next = current->next; | ||
| 455 | prev->next = next; | ||
| 456 | list->length--; | ||
| 457 | if (next) { | ||
| 458 | next->prev = prev; | ||
| 459 | } | ||
| 460 | |||
| 461 | if (item->name) { | ||
| 462 | free(item->name); | ||
| 463 | } | ||
| 464 | free(item); | ||
| 465 | |||
| 466 | return next; | ||
| 467 | } | ||
| 468 | |||
| 469 | parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current) { | ||
| 470 | if (!current) { | ||
| 471 | return NULL; | ||
| 472 | } | ||
| 473 | return current->next; | ||
| 474 | } | ||
| 475 | |||
| 476 | void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, | ||
| 477 | bool exact) { | ||
| 478 | for (parameter_list_elem *elem = list.first; elem; elem = mp_int_fs_list_get_next(elem)) { | ||
| 479 | if (!elem->best_match) { | ||
| 480 | size_t name_len = strlen(elem->name); | ||
| 481 | struct mount_entry *best_match = NULL; | ||
| 482 | |||
| 483 | /* set best match if path name exactly matches a mounted device name */ | ||
| 484 | for (struct mount_entry *mount_entry = mount_list; mount_entry; | ||
| 485 | mount_entry = mount_entry->me_next) { | ||
| 486 | if (strcmp(mount_entry->me_devname, elem->name) == 0) { | ||
| 487 | struct fs_usage fsp; | ||
| 488 | if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= | ||
| 489 | 0) { | ||
| 490 | best_match = mount_entry; | ||
| 491 | } | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | /* set best match by directory name if no match was found by devname */ | ||
| 496 | if (!best_match) { | ||
| 497 | size_t best_match_len = 0; | ||
| 498 | for (struct mount_entry *mount_entry = mount_list; mount_entry; | ||
| 499 | mount_entry = mount_entry->me_next) { | ||
| 500 | size_t len = strlen(mount_entry->me_mountdir); | ||
| 501 | |||
| 502 | if ((!exact && | ||
| 503 | (best_match_len <= len && len <= name_len && | ||
| 504 | (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) || | ||
| 505 | (exact && strcmp(mount_entry->me_mountdir, elem->name) == 0)) { | ||
| 506 | struct fs_usage fsp; | ||
| 507 | |||
| 508 | if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= | ||
| 509 | 0) { | ||
| 510 | best_match = mount_entry; | ||
| 511 | best_match_len = len; | ||
| 512 | } | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
| 516 | |||
| 517 | if (best_match) { | ||
| 518 | elem->best_match = best_match; | ||
| 519 | } else { | ||
| 520 | elem->best_match = | ||
| 521 | NULL; /* Not sure why this is needed as it should be null on initialisation */ | ||
| 522 | } | ||
| 523 | |||
| 524 | // No filesystem without a mount_entry! | ||
| 525 | // assert(elem->best_match != NULL); | ||
| 526 | } | ||
| 527 | } | ||
| 528 | } | ||
diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h new file mode 100644 index 00000000..c96d4296 --- /dev/null +++ b/plugins/check_disk.d/utils_disk.h | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | #pragma once | ||
| 2 | /* Header file for utils_disk */ | ||
| 3 | |||
| 4 | #include "../../config.h" | ||
| 5 | #include "../../gl/mountlist.h" | ||
| 6 | #include "../../lib/utils_base.h" | ||
| 7 | #include "../../lib/output.h" | ||
| 8 | #include "regex.h" | ||
| 9 | #include <stdint.h> | ||
| 10 | |||
| 11 | typedef unsigned long long byte_unit; | ||
| 12 | |||
| 13 | typedef enum { | ||
| 14 | Humanized, | ||
| 15 | Bytes, | ||
| 16 | KibiBytes, | ||
| 17 | MebiBytes, | ||
| 18 | GibiBytes, | ||
| 19 | TebiBytes, | ||
| 20 | PebiBytes, | ||
| 21 | ExbiBytes, | ||
| 22 | KiloBytes, | ||
| 23 | MegaBytes, | ||
| 24 | GigaBytes, | ||
| 25 | TeraBytes, | ||
| 26 | PetaBytes, | ||
| 27 | ExaBytes, | ||
| 28 | } byte_unit_enum; | ||
| 29 | |||
| 30 | typedef struct name_list string_list; | ||
| 31 | struct name_list { | ||
| 32 | char *name; | ||
| 33 | string_list *next; | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct regex_list { | ||
| 37 | regex_t regex; | ||
| 38 | struct regex_list *next; | ||
| 39 | }; | ||
| 40 | |||
| 41 | typedef struct parameter_list parameter_list_elem; | ||
| 42 | struct parameter_list { | ||
| 43 | char *name; | ||
| 44 | char *group; | ||
| 45 | |||
| 46 | mp_thresholds freespace_units; | ||
| 47 | mp_thresholds freespace_percent; | ||
| 48 | mp_thresholds freeinodes_percent; | ||
| 49 | |||
| 50 | struct mount_entry *best_match; | ||
| 51 | |||
| 52 | uintmax_t inodes_free_to_root; | ||
| 53 | uintmax_t inodes_free; | ||
| 54 | uintmax_t inodes_used; | ||
| 55 | uintmax_t inodes_total; | ||
| 56 | |||
| 57 | uint64_t used_bytes; | ||
| 58 | uint64_t free_bytes; | ||
| 59 | uint64_t total_bytes; | ||
| 60 | |||
| 61 | parameter_list_elem *next; | ||
| 62 | parameter_list_elem *prev; | ||
| 63 | }; | ||
| 64 | |||
| 65 | typedef struct { | ||
| 66 | size_t length; | ||
| 67 | parameter_list_elem *first; | ||
| 68 | } filesystem_list; | ||
| 69 | |||
| 70 | filesystem_list filesystem_list_init(); | ||
| 71 | |||
| 72 | typedef struct { | ||
| 73 | char *name; | ||
| 74 | char *filesystem_type; | ||
| 75 | bool is_group; | ||
| 76 | |||
| 77 | mp_thresholds freespace_bytes_thresholds; | ||
| 78 | mp_thresholds freespace_percent_thresholds; | ||
| 79 | mp_thresholds freeinodes_percent_thresholds; | ||
| 80 | |||
| 81 | uintmax_t inodes_free_to_root; | ||
| 82 | uintmax_t inodes_free; | ||
| 83 | uintmax_t inodes_used; | ||
| 84 | uintmax_t inodes_total; | ||
| 85 | |||
| 86 | uintmax_t used_bytes; | ||
| 87 | uintmax_t free_bytes; | ||
| 88 | uintmax_t total_bytes; | ||
| 89 | } measurement_unit; | ||
| 90 | |||
| 91 | typedef struct measurement_unit_list measurement_unit_list; | ||
| 92 | struct measurement_unit_list { | ||
| 93 | measurement_unit unit; | ||
| 94 | measurement_unit_list *next; | ||
| 95 | }; | ||
| 96 | |||
| 97 | typedef struct { | ||
| 98 | // Output options | ||
| 99 | bool erronly; | ||
| 100 | bool display_mntp; | ||
| 101 | /* show only local filesystems. */ | ||
| 102 | bool show_local_fs; | ||
| 103 | /* show only local filesystems but call stat() on remote ones. */ | ||
| 104 | bool stat_remote_fs; | ||
| 105 | bool display_inodes_perfdata; | ||
| 106 | |||
| 107 | bool exact_match; | ||
| 108 | bool freespace_ignore_reserved; | ||
| 109 | |||
| 110 | bool ignore_missing; | ||
| 111 | bool path_ignored; | ||
| 112 | |||
| 113 | /* Linked list of filesystem types to omit. | ||
| 114 | If the list is empty, don't exclude any types. */ | ||
| 115 | struct regex_list *fs_exclude_list; | ||
| 116 | /* Linked list of filesystem types to check. | ||
| 117 | If the list is empty, include all types. */ | ||
| 118 | struct regex_list *fs_include_list; | ||
| 119 | struct name_list *device_path_exclude_list; | ||
| 120 | filesystem_list path_select_list; | ||
| 121 | /* Linked list of mounted filesystems. */ | ||
| 122 | struct mount_entry *mount_list; | ||
| 123 | struct name_list *seen; | ||
| 124 | |||
| 125 | byte_unit_enum display_unit; | ||
| 126 | // byte_unit unit; | ||
| 127 | |||
| 128 | bool output_format_is_set; | ||
| 129 | mp_output_format output_format; | ||
| 130 | } check_disk_config; | ||
| 131 | |||
| 132 | void np_add_name(struct name_list **list, const char *name); | ||
| 133 | bool np_find_name(struct name_list *list, const char *name); | ||
| 134 | bool np_seen_name(struct name_list *list, const char *name); | ||
| 135 | int np_add_regex(struct regex_list **list, const char *regex, int cflags); | ||
| 136 | bool np_find_regmatch(struct regex_list *list, const char *name); | ||
| 137 | |||
| 138 | parameter_list_elem parameter_list_init(const char *); | ||
| 139 | |||
| 140 | parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name); | ||
| 141 | parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name); | ||
| 142 | parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item); | ||
| 143 | parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current); | ||
| 144 | void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, | ||
| 145 | bool exact); | ||
| 146 | |||
| 147 | measurement_unit measurement_unit_init(); | ||
| 148 | measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem); | ||
| 149 | measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, | ||
| 150 | parameter_list_elem filesystem); | ||
| 151 | measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, | ||
| 152 | bool display_mntp); | ||
| 153 | |||
| 154 | int search_parameter_list(parameter_list_elem *list, const char *name); | ||
| 155 | bool np_regex_match_mount_entry(struct mount_entry *, regex_t *); | ||
| 156 | |||
| 157 | char *get_unit_string(byte_unit_enum); | ||
| 158 | check_disk_config check_disk_config_init(); | ||
| 159 | |||
| 160 | char *humanize_byte_value(unsigned long long value, bool use_si_units); | ||
diff --git a/plugins/check_dns.c b/plugins/check_dns.c index e1e7c00e..56f91dae 100644 --- a/plugins/check_dns.c +++ b/plugins/check_dns.c | |||
| @@ -39,26 +39,23 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 39 | #include "netutils.h" | 39 | #include "netutils.h" |
| 40 | #include "runcmd.h" | 40 | #include "runcmd.h" |
| 41 | 41 | ||
| 42 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 42 | #include "states.h" |
| 43 | static int validate_arguments(void); | 43 | #include "check_dns.d/config.h" |
| 44 | static int error_scan(char * /*input_buffer*/, bool *); | 44 | |
| 45 | typedef struct { | ||
| 46 | int errorcode; | ||
| 47 | check_dns_config config; | ||
| 48 | } check_dns_config_wrapper; | ||
| 49 | static check_dns_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 50 | static check_dns_config_wrapper validate_arguments(check_dns_config_wrapper /*config_wrapper*/); | ||
| 51 | static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/, | ||
| 52 | const char /*dns_server*/[ADDRESS_LENGTH]); | ||
| 45 | static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/); | 53 | static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/); |
| 46 | static unsigned long ip2long(const char * /*src*/); | 54 | static unsigned long ip2long(const char * /*src*/); |
| 47 | static void print_help(void); | 55 | static void print_help(void); |
| 48 | void print_usage(void); | 56 | void print_usage(void); |
| 49 | 57 | ||
| 50 | #define ADDRESS_LENGTH 256 | ||
| 51 | static char query_address[ADDRESS_LENGTH] = ""; | ||
| 52 | static char dns_server[ADDRESS_LENGTH] = ""; | ||
| 53 | static char ptr_server[ADDRESS_LENGTH] = ""; | ||
| 54 | static bool verbose = false; | 58 | static bool verbose = false; |
| 55 | static char **expected_address = NULL; | ||
| 56 | static int expected_address_cnt = 0; | ||
| 57 | static bool expect_nxdomain = false; | ||
| 58 | |||
| 59 | static bool expect_authority = false; | ||
| 60 | static bool all_match = false; | ||
| 61 | static thresholds *time_thresholds = NULL; | ||
| 62 | 59 | ||
| 63 | static int qstrcmp(const void *p1, const void *p2) { | 60 | static int qstrcmp(const void *p1, const void *p2) { |
| 64 | /* The actual arguments to this function are "pointers to | 61 | /* The actual arguments to this function are "pointers to |
| @@ -68,23 +65,6 @@ static int qstrcmp(const void *p1, const void *p2) { | |||
| 68 | } | 65 | } |
| 69 | 66 | ||
| 70 | int main(int argc, char **argv) { | 67 | int main(int argc, char **argv) { |
| 71 | char *command_line = NULL; | ||
| 72 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 73 | char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */ | ||
| 74 | char **addresses = NULL; | ||
| 75 | int n_addresses = 0; | ||
| 76 | char *msg = NULL; | ||
| 77 | char *temp_buffer = NULL; | ||
| 78 | bool non_authoritative = false; | ||
| 79 | int result = STATE_UNKNOWN; | ||
| 80 | double elapsed_time; | ||
| 81 | long microsec; | ||
| 82 | struct timeval tv; | ||
| 83 | bool parse_address = false; /* This flag scans for Address: but only after Name: */ | ||
| 84 | output chld_out; | ||
| 85 | output chld_err; | ||
| 86 | bool is_nxdomain = false; | ||
| 87 | |||
| 88 | setlocale(LC_ALL, ""); | 68 | setlocale(LC_ALL, ""); |
| 89 | bindtextdomain(PACKAGE, LOCALEDIR); | 69 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 90 | textdomain(PACKAGE); | 70 | textdomain(PACKAGE); |
| @@ -97,39 +77,66 @@ int main(int argc, char **argv) { | |||
| 97 | /* Parse extra opts if any */ | 77 | /* Parse extra opts if any */ |
| 98 | argv = np_extra_opts(&argc, argv, progname); | 78 | argv = np_extra_opts(&argc, argv, progname); |
| 99 | 79 | ||
| 100 | if (process_arguments(argc, argv) == ERROR) { | 80 | check_dns_config_wrapper tmp = process_arguments(argc, argv); |
| 81 | |||
| 82 | if (tmp.errorcode == ERROR) { | ||
| 101 | usage_va(_("Could not parse arguments")); | 83 | usage_va(_("Could not parse arguments")); |
| 102 | } | 84 | } |
| 103 | 85 | ||
| 86 | const check_dns_config config = tmp.config; | ||
| 87 | |||
| 88 | char *command_line = NULL; | ||
| 104 | /* get the command to run */ | 89 | /* get the command to run */ |
| 105 | xasprintf(&command_line, "%s %s %s", NSLOOKUP_COMMAND, query_address, dns_server); | 90 | xasprintf(&command_line, "%s %s %s", NSLOOKUP_COMMAND, config.query_address, config.dns_server); |
| 106 | 91 | ||
| 92 | struct timeval tv; | ||
| 107 | alarm(timeout_interval); | 93 | alarm(timeout_interval); |
| 108 | gettimeofday(&tv, NULL); | 94 | gettimeofday(&tv, NULL); |
| 109 | 95 | ||
| 110 | if (verbose) | 96 | if (verbose) { |
| 111 | printf("%s\n", command_line); | 97 | printf("%s\n", command_line); |
| 98 | } | ||
| 112 | 99 | ||
| 100 | output chld_out; | ||
| 101 | output chld_err; | ||
| 102 | char *msg = NULL; | ||
| 103 | mp_state_enum result = STATE_UNKNOWN; | ||
| 113 | /* run the command */ | 104 | /* run the command */ |
| 114 | if ((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) { | 105 | if ((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) { |
| 115 | msg = (char *)_("nslookup returned an error status"); | 106 | msg = (char *)_("nslookup returned an error status"); |
| 116 | result = STATE_WARNING; | 107 | result = STATE_WARNING; |
| 117 | } | 108 | } |
| 118 | 109 | ||
| 119 | /* scan stdout */ | 110 | /* ===== |
| 111 | * scan stdout, main results get retrieved here | ||
| 112 | * ===== | ||
| 113 | */ | ||
| 114 | char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */ | ||
| 115 | char **addresses = NULL; // All addresses parsed from stdout | ||
| 116 | size_t n_addresses = 0; // counter for retrieved addresses | ||
| 117 | bool non_authoritative = false; | ||
| 118 | bool is_nxdomain = false; | ||
| 119 | bool parse_address = false; /* This flag scans for Address: but only after Name: */ | ||
| 120 | for (size_t i = 0; i < chld_out.lines; i++) { | 120 | for (size_t i = 0; i < chld_out.lines; i++) { |
| 121 | if (addresses == NULL) | 121 | if (addresses == NULL) { |
| 122 | addresses = malloc(sizeof(*addresses) * 10); | 122 | addresses = malloc(sizeof(*addresses) * 10); |
| 123 | else if (!(n_addresses % 10)) | 123 | } else if (!(n_addresses % 10)) { |
| 124 | addresses = realloc(addresses, sizeof(*addresses) * (n_addresses + 10)); | 124 | addresses = realloc(addresses, sizeof(*addresses) * (n_addresses + 10)); |
| 125 | } | ||
| 125 | 126 | ||
| 126 | if (verbose) | 127 | if (verbose) { |
| 127 | puts(chld_out.line[i]); | 128 | puts(chld_out.line[i]); |
| 129 | } | ||
| 128 | 130 | ||
| 129 | if (strcasestr(chld_out.line[i], ".in-addr.arpa") || strcasestr(chld_out.line[i], ".ip6.arpa")) { | 131 | if (strcasestr(chld_out.line[i], ".in-addr.arpa") || |
| 130 | if ((temp_buffer = strstr(chld_out.line[i], "name = "))) | 132 | strcasestr(chld_out.line[i], ".ip6.arpa")) { |
| 133 | if ((strstr(chld_out.line[i], "canonical name = ") != NULL)) { | ||
| 134 | continue; | ||
| 135 | } | ||
| 136 | char *temp_buffer = NULL; | ||
| 137 | if ((temp_buffer = strstr(chld_out.line[i], "name = "))) { | ||
| 131 | addresses[n_addresses++] = strdup(temp_buffer + 7); | 138 | addresses[n_addresses++] = strdup(temp_buffer + 7); |
| 132 | else { | 139 | } else { |
| 133 | msg = (char *)_("Warning plugin error"); | 140 | msg = (char *)_("Warning plugin error"); |
| 134 | result = STATE_WARNING; | 141 | result = STATE_WARNING; |
| 135 | } | 142 | } |
| @@ -137,38 +144,54 @@ int main(int argc, char **argv) { | |||
| 137 | 144 | ||
| 138 | /* bug ID: 2946553 - Older versions of bind will use all available dns | 145 | /* bug ID: 2946553 - Older versions of bind will use all available dns |
| 139 | servers, we have to match the one specified */ | 146 | servers, we have to match the one specified */ |
| 140 | if (strstr(chld_out.line[i], "Server:") && strlen(dns_server) > 0) { | 147 | if (strstr(chld_out.line[i], "Server:") && strlen(config.dns_server) > 0) { |
| 141 | temp_buffer = strchr(chld_out.line[i], ':'); | 148 | char *temp_buffer = strchr(chld_out.line[i], ':'); |
| 149 | if (temp_buffer == NULL) { | ||
| 150 | die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"), | ||
| 151 | NSLOOKUP_COMMAND); | ||
| 152 | } | ||
| 153 | |||
| 142 | temp_buffer++; | 154 | temp_buffer++; |
| 143 | 155 | ||
| 144 | /* Strip leading tabs */ | 156 | /* Strip leading tabs */ |
| 145 | for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) | 157 | for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) { |
| 146 | /* NOOP */; | 158 | /* NOOP */; |
| 159 | } | ||
| 147 | 160 | ||
| 148 | strip(temp_buffer); | 161 | strip(temp_buffer); |
| 149 | if (temp_buffer == NULL || strlen(temp_buffer) == 0) { | 162 | if (strlen(temp_buffer) == 0) { |
| 150 | die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"), NSLOOKUP_COMMAND); | 163 | die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"), |
| 164 | NSLOOKUP_COMMAND); | ||
| 151 | } | 165 | } |
| 152 | 166 | ||
| 153 | if (strcmp(temp_buffer, dns_server) != 0) { | 167 | if (strcmp(temp_buffer, config.dns_server) != 0) { |
| 154 | die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), dns_server); | 168 | die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), |
| 169 | config.dns_server); | ||
| 155 | } | 170 | } |
| 156 | } | 171 | } |
| 157 | 172 | ||
| 158 | /* the server is responding, we just got the host name... */ | 173 | /* the server is responding, we just got the host name... */ |
| 159 | if (strstr(chld_out.line[i], "Name:")) | 174 | if (strstr(chld_out.line[i], "Name:")) { |
| 160 | parse_address = true; | 175 | parse_address = true; |
| 161 | else if (parse_address && (strstr(chld_out.line[i], "Address:") || strstr(chld_out.line[i], "Addresses:"))) { | 176 | } else if (parse_address && (strstr(chld_out.line[i], "Address:") || |
| 162 | temp_buffer = index(chld_out.line[i], ':'); | 177 | strstr(chld_out.line[i], "Addresses:"))) { |
| 178 | char *temp_buffer = strchr(chld_out.line[i], ':'); | ||
| 179 | if (temp_buffer == NULL) { | ||
| 180 | die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"), | ||
| 181 | NSLOOKUP_COMMAND); | ||
| 182 | } | ||
| 183 | |||
| 163 | temp_buffer++; | 184 | temp_buffer++; |
| 164 | 185 | ||
| 165 | /* Strip leading spaces */ | 186 | /* Strip leading spaces */ |
| 166 | while (*temp_buffer == ' ') | 187 | while (*temp_buffer == ' ') { |
| 167 | temp_buffer++; | 188 | temp_buffer++; |
| 189 | } | ||
| 168 | 190 | ||
| 169 | strip(temp_buffer); | 191 | strip(temp_buffer); |
| 170 | if (temp_buffer == NULL || strlen(temp_buffer) == 0) { | 192 | if (strlen(temp_buffer) == 0) { |
| 171 | die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"), NSLOOKUP_COMMAND); | 193 | die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"), |
| 194 | NSLOOKUP_COMMAND); | ||
| 172 | } | 195 | } |
| 173 | 196 | ||
| 174 | addresses[n_addresses++] = strdup(temp_buffer); | 197 | addresses[n_addresses++] = strdup(temp_buffer); |
| @@ -176,65 +199,74 @@ int main(int argc, char **argv) { | |||
| 176 | non_authoritative = true; | 199 | non_authoritative = true; |
| 177 | } | 200 | } |
| 178 | 201 | ||
| 179 | result = error_scan(chld_out.line[i], &is_nxdomain); | 202 | result = error_scan(chld_out.line[i], &is_nxdomain, config.dns_server); |
| 180 | if (result != STATE_OK) { | 203 | if (result != STATE_OK) { |
| 181 | msg = strchr(chld_out.line[i], ':'); | 204 | msg = strchr(chld_out.line[i], ':'); |
| 182 | if (msg) | 205 | if (msg) { |
| 183 | msg++; | 206 | msg++; |
| 207 | } | ||
| 184 | break; | 208 | break; |
| 185 | } | 209 | } |
| 186 | } | 210 | } |
| 187 | 211 | ||
| 212 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 188 | /* scan stderr */ | 213 | /* scan stderr */ |
| 189 | for (size_t i = 0; i < chld_err.lines; i++) { | 214 | for (size_t i = 0; i < chld_err.lines; i++) { |
| 190 | if (verbose) | 215 | if (verbose) { |
| 191 | puts(chld_err.line[i]); | 216 | puts(chld_err.line[i]); |
| 217 | } | ||
| 192 | 218 | ||
| 193 | if (error_scan(chld_err.line[i], &is_nxdomain) != STATE_OK) { | 219 | if (error_scan(chld_err.line[i], &is_nxdomain, config.dns_server) != STATE_OK) { |
| 194 | result = max_state(result, error_scan(chld_err.line[i], &is_nxdomain)); | 220 | result = |
| 221 | max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server)); | ||
| 195 | msg = strchr(input_buffer, ':'); | 222 | msg = strchr(input_buffer, ':'); |
| 196 | if (msg) | 223 | if (msg) { |
| 197 | msg++; | 224 | msg++; |
| 198 | else | 225 | } else { |
| 199 | msg = input_buffer; | 226 | msg = input_buffer; |
| 227 | } | ||
| 200 | } | 228 | } |
| 201 | } | 229 | } |
| 202 | 230 | ||
| 203 | if (is_nxdomain && !expect_nxdomain) { | 231 | if (is_nxdomain && !config.expect_nxdomain) { |
| 204 | die(STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), query_address); | 232 | die(STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), config.query_address); |
| 205 | } | 233 | } |
| 206 | 234 | ||
| 207 | if (addresses) { | 235 | if (addresses) { |
| 208 | int i; | 236 | size_t slen = 1; |
| 209 | int slen; | 237 | char *adrp = NULL; |
| 210 | char *adrp; | ||
| 211 | qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp); | 238 | qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp); |
| 212 | for (i = 0, slen = 1; i < n_addresses; i++) { | 239 | for (size_t i = 0; i < n_addresses; i++) { |
| 213 | slen += strlen(addresses[i]) + 1; | 240 | slen += strlen(addresses[i]) + 1; |
| 214 | } | 241 | } |
| 242 | |||
| 243 | // Temporary pointer adrp gets moved, address stays on the beginning | ||
| 215 | adrp = address = malloc(slen); | 244 | adrp = address = malloc(slen); |
| 216 | for (i = 0; i < n_addresses; i++) { | 245 | for (size_t i = 0; i < n_addresses; i++) { |
| 217 | if (i) | 246 | if (i) { |
| 218 | *adrp++ = ','; | 247 | *adrp++ = ','; |
| 248 | } | ||
| 219 | strcpy(adrp, addresses[i]); | 249 | strcpy(adrp, addresses[i]); |
| 220 | adrp += strlen(addresses[i]); | 250 | adrp += strlen(addresses[i]); |
| 221 | } | 251 | } |
| 222 | *adrp = 0; | 252 | *adrp = 0; |
| 223 | } else | 253 | } else { |
| 224 | die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), NSLOOKUP_COMMAND); | 254 | die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), |
| 255 | NSLOOKUP_COMMAND); | ||
| 256 | } | ||
| 225 | 257 | ||
| 226 | /* compare to expected address */ | 258 | /* compare to expected address */ |
| 227 | if (result == STATE_OK && expected_address_cnt > 0) { | 259 | if (result == STATE_OK && config.expected_address_cnt > 0) { |
| 228 | result = STATE_CRITICAL; | 260 | result = STATE_CRITICAL; |
| 229 | temp_buffer = ""; | 261 | char *temp_buffer = ""; |
| 230 | unsigned long expect_match = (1 << expected_address_cnt) - 1; | 262 | unsigned long expect_match = (1 << config.expected_address_cnt) - 1; |
| 231 | unsigned long addr_match = (1 << n_addresses) - 1; | 263 | unsigned long addr_match = (1 << n_addresses) - 1; |
| 232 | 264 | ||
| 233 | for (int i = 0; i < expected_address_cnt; i++) { | 265 | for (size_t i = 0; i < config.expected_address_cnt; i++) { |
| 234 | int j; | ||
| 235 | /* check if we get a match on 'raw' ip or cidr */ | 266 | /* check if we get a match on 'raw' ip or cidr */ |
| 236 | for (j = 0; j < n_addresses; j++) { | 267 | for (size_t j = 0; j < n_addresses; j++) { |
| 237 | if (strcmp(addresses[j], expected_address[i]) == 0 || ip_match_cidr(addresses[j], expected_address[i])) { | 268 | if (strcmp(addresses[j], config.expected_address[i]) == 0 || |
| 269 | ip_match_cidr(addresses[j], config.expected_address[i])) { | ||
| 238 | result = STATE_OK; | 270 | result = STATE_OK; |
| 239 | addr_match &= ~(1 << j); | 271 | addr_match &= ~(1 << j); |
| 240 | expect_match &= ~(1 << i); | 272 | expect_match &= ~(1 << i); |
| @@ -242,11 +274,12 @@ int main(int argc, char **argv) { | |||
| 242 | } | 274 | } |
| 243 | 275 | ||
| 244 | /* prepare an error string */ | 276 | /* prepare an error string */ |
| 245 | xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]); | 277 | xasprintf(&temp_buffer, "%s%s; ", temp_buffer, config.expected_address[i]); |
| 246 | } | 278 | } |
| 247 | /* check if expected_address must cover all in addresses and none may be missing */ | 279 | /* check if expected_address must cover all in addresses and none may be missing */ |
| 248 | if (all_match && (expect_match != 0 || addr_match != 0)) | 280 | if (config.all_match && (expect_match != 0 || addr_match != 0)) { |
| 249 | result = STATE_CRITICAL; | 281 | result = STATE_CRITICAL; |
| 282 | } | ||
| 250 | if (result == STATE_CRITICAL) { | 283 | if (result == STATE_CRITICAL) { |
| 251 | /* Strip off last semicolon... */ | 284 | /* Strip off last semicolon... */ |
| 252 | temp_buffer[strlen(temp_buffer) - 2] = '\0'; | 285 | temp_buffer[strlen(temp_buffer) - 2] = '\0'; |
| @@ -254,28 +287,31 @@ int main(int argc, char **argv) { | |||
| 254 | } | 287 | } |
| 255 | } | 288 | } |
| 256 | 289 | ||
| 257 | if (expect_nxdomain) { | 290 | if (config.expect_nxdomain) { |
| 258 | if (!is_nxdomain) { | 291 | if (!is_nxdomain) { |
| 259 | result = STATE_CRITICAL; | 292 | result = STATE_CRITICAL; |
| 260 | xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), query_address, address); | 293 | xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address, |
| 294 | address); | ||
| 261 | } else { | 295 | } else { |
| 262 | if (address != NULL) | 296 | if (address != NULL) { |
| 263 | free(address); | 297 | free(address); |
| 298 | } | ||
| 264 | address = "NXDOMAIN"; | 299 | address = "NXDOMAIN"; |
| 265 | } | 300 | } |
| 266 | } | 301 | } |
| 267 | 302 | ||
| 268 | /* check if authoritative */ | 303 | /* check if authoritative */ |
| 269 | if (result == STATE_OK && expect_authority && non_authoritative) { | 304 | if (result == STATE_OK && config.expect_authority && non_authoritative) { |
| 270 | result = STATE_CRITICAL; | 305 | result = STATE_CRITICAL; |
| 271 | xasprintf(&msg, _("server %s is not authoritative for %s"), dns_server, query_address); | 306 | xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server, |
| 307 | config.query_address); | ||
| 272 | } | 308 | } |
| 273 | 309 | ||
| 274 | microsec = deltime(tv); | 310 | long microsec = deltime(tv); |
| 275 | elapsed_time = (double)microsec / 1.0e6; | 311 | double elapsed_time = (double)microsec / 1.0e6; |
| 276 | 312 | ||
| 277 | if (result == STATE_OK) { | 313 | if (result == STATE_OK) { |
| 278 | result = get_status(elapsed_time, time_thresholds); | 314 | result = get_status(elapsed_time, config.time_thresholds); |
| 279 | if (result == STATE_OK) { | 315 | if (result == STATE_OK) { |
| 280 | printf("DNS %s: ", _("OK")); | 316 | printf("DNS %s: ", _("OK")); |
| 281 | } else if (result == STATE_WARNING) { | 317 | } else if (result == STATE_WARNING) { |
| @@ -283,25 +319,39 @@ int main(int argc, char **argv) { | |||
| 283 | } else if (result == STATE_CRITICAL) { | 319 | } else if (result == STATE_CRITICAL) { |
| 284 | printf("DNS %s: ", _("CRITICAL")); | 320 | printf("DNS %s: ", _("CRITICAL")); |
| 285 | } | 321 | } |
| 286 | printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time); | 322 | printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), |
| 287 | printf(_(". %s returns %s"), query_address, address); | 323 | elapsed_time); |
| 288 | if ((time_thresholds->warning != NULL) && (time_thresholds->critical != NULL)) { | 324 | printf(_(". %s returns %s"), config.query_address, address); |
| 289 | printf("|%s\n", fperfdata("time", elapsed_time, "s", true, time_thresholds->warning->end, true, time_thresholds->critical->end, | 325 | if ((config.time_thresholds->warning != NULL) && |
| 290 | true, 0, false, 0)); | 326 | (config.time_thresholds->critical != NULL)) { |
| 291 | } else if ((time_thresholds->warning == NULL) && (time_thresholds->critical != NULL)) { | 327 | printf("|%s\n", |
| 292 | printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true, time_thresholds->critical->end, true, 0, false, 0)); | 328 | fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, |
| 293 | } else if ((time_thresholds->warning != NULL) && (time_thresholds->critical == NULL)) { | 329 | true, config.time_thresholds->critical->end, true, 0, false, 0)); |
| 294 | printf("|%s\n", fperfdata("time", elapsed_time, "s", true, time_thresholds->warning->end, false, 0, true, 0, false, 0)); | 330 | } else if ((config.time_thresholds->warning == NULL) && |
| 295 | } else | 331 | (config.time_thresholds->critical != NULL)) { |
| 296 | printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); | 332 | printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true, |
| 297 | } else if (result == STATE_WARNING) | 333 | config.time_thresholds->critical->end, true, 0, false, 0)); |
| 298 | printf(_("DNS WARNING - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); | 334 | } else if ((config.time_thresholds->warning != NULL) && |
| 299 | else if (result == STATE_CRITICAL) | 335 | (config.time_thresholds->critical == NULL)) { |
| 300 | printf(_("DNS CRITICAL - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); | 336 | printf("|%s\n", |
| 301 | else | 337 | fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, |
| 302 | printf(_("DNS UNKNOWN - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); | 338 | false, 0, true, 0, false, 0)); |
| 303 | 339 | } else { | |
| 304 | return result; | 340 | printf("|%s\n", |
| 341 | fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); | ||
| 342 | } | ||
| 343 | } else if (result == STATE_WARNING) { | ||
| 344 | printf(_("DNS WARNING - %s\n"), | ||
| 345 | !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); | ||
| 346 | } else if (result == STATE_CRITICAL) { | ||
| 347 | printf(_("DNS CRITICAL - %s\n"), | ||
| 348 | !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); | ||
| 349 | } else { | ||
| 350 | printf(_("DNS UNKNOWN - %s\n"), | ||
| 351 | !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); | ||
| 352 | } | ||
| 353 | |||
| 354 | exit(result); | ||
| 305 | } | 355 | } |
| 306 | 356 | ||
| 307 | bool ip_match_cidr(const char *addr, const char *cidr_ro) { | 357 | bool ip_match_cidr(const char *addr, const char *cidr_ro) { |
| @@ -317,76 +367,87 @@ bool ip_match_cidr(const char *addr, const char *cidr_ro) { | |||
| 317 | mask = atoi(mask_c); | 367 | mask = atoi(mask_c); |
| 318 | 368 | ||
| 319 | /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ | 369 | /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ |
| 320 | return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) << (32 - mask); | 370 | return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) |
| 371 | << (32 - mask); | ||
| 321 | } | 372 | } |
| 322 | 373 | ||
| 323 | unsigned long ip2long(const char *src) { | 374 | unsigned long ip2long(const char *src) { |
| 324 | unsigned long ip[4]; | 375 | unsigned long ip[4]; |
| 325 | /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ | 376 | /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ |
| 326 | return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && | 377 | return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && |
| 327 | ip[3] < 256) | 378 | ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && ip[3] < 256) |
| 328 | ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3] | 379 | ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3] |
| 329 | : 0; | 380 | : 0; |
| 330 | } | 381 | } |
| 331 | 382 | ||
| 332 | int error_scan(char *input_buffer, bool *is_nxdomain) { | 383 | mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain, |
| 384 | const char dns_server[ADDRESS_LENGTH]) { | ||
| 333 | 385 | ||
| 334 | const int nxdomain = strstr(input_buffer, "Non-existent") || strstr(input_buffer, "** server can't find") || | 386 | const int nxdomain = strstr(input_buffer, "Non-existent") || |
| 387 | strstr(input_buffer, "** server can't find") || | ||
| 335 | strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN"); | 388 | strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN"); |
| 336 | if (nxdomain) | 389 | if (nxdomain) { |
| 337 | *is_nxdomain = true; | 390 | *is_nxdomain = true; |
| 391 | } | ||
| 338 | 392 | ||
| 339 | /* the DNS lookup timed out */ | 393 | /* the DNS lookup timed out */ |
| 340 | if (strstr(input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) || | 394 | if (strstr(input_buffer, |
| 341 | strstr(input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || | 395 | _("Note: nslookup is deprecated and may be removed from future releases.")) || |
| 342 | strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) | 396 | strstr(input_buffer, |
| 397 | _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || | ||
| 398 | strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) { | ||
| 343 | return STATE_OK; | 399 | return STATE_OK; |
| 400 | } | ||
| 344 | 401 | ||
| 345 | /* DNS server is not running... */ | 402 | /* DNS server is not running... */ |
| 346 | else if (strstr(input_buffer, "No response from server")) | 403 | else if (strstr(input_buffer, "No response from server")) { |
| 347 | die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); | 404 | die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); |
| 348 | else if (strstr(input_buffer, "no servers could be reached")) | 405 | } else if (strstr(input_buffer, "no servers could be reached")) { |
| 349 | die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); | 406 | die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); |
| 407 | } | ||
| 350 | 408 | ||
| 351 | /* Host name is valid, but server doesn't have records... */ | 409 | /* Host name is valid, but server doesn't have records... */ |
| 352 | else if (strstr(input_buffer, "No records")) | 410 | else if (strstr(input_buffer, "No records")) { |
| 353 | die(STATE_CRITICAL, _("DNS %s has no records\n"), dns_server); | 411 | die(STATE_CRITICAL, _("DNS %s has no records\n"), dns_server); |
| 412 | } | ||
| 354 | 413 | ||
| 355 | /* Connection was refused */ | 414 | /* Connection was refused */ |
| 356 | else if (strstr(input_buffer, "Connection refused") || strstr(input_buffer, "Couldn't find server") || | 415 | else if (strstr(input_buffer, "Connection refused") || |
| 357 | strstr(input_buffer, "Refused") || (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) | 416 | strstr(input_buffer, "Couldn't find server") || strstr(input_buffer, "Refused") || |
| 417 | (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) { | ||
| 358 | die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server); | 418 | die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server); |
| 419 | } | ||
| 359 | 420 | ||
| 360 | /* Query refused (usually by an ACL in the namserver) */ | 421 | /* Query refused (usually by an ACL in the namserver) */ |
| 361 | else if (strstr(input_buffer, "Query refused")) | 422 | else if (strstr(input_buffer, "Query refused")) { |
| 362 | die(STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server); | 423 | die(STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server); |
| 424 | } | ||
| 363 | 425 | ||
| 364 | /* No information (e.g. nameserver IP has two PTR records) */ | 426 | /* No information (e.g. nameserver IP has two PTR records) */ |
| 365 | else if (strstr(input_buffer, "No information")) | 427 | else if (strstr(input_buffer, "No information")) { |
| 366 | die(STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server); | 428 | die(STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server); |
| 429 | } | ||
| 367 | 430 | ||
| 368 | /* Network is unreachable */ | 431 | /* Network is unreachable */ |
| 369 | else if (strstr(input_buffer, "Network is unreachable")) | 432 | else if (strstr(input_buffer, "Network is unreachable")) { |
| 370 | die(STATE_CRITICAL, _("Network is unreachable\n")); | 433 | die(STATE_CRITICAL, _("Network is unreachable\n")); |
| 434 | } | ||
| 371 | 435 | ||
| 372 | /* Internal server failure */ | 436 | /* Internal server failure */ |
| 373 | else if (strstr(input_buffer, "Server failure")) | 437 | else if (strstr(input_buffer, "Server failure")) { |
| 374 | die(STATE_CRITICAL, _("DNS failure for %s\n"), dns_server); | 438 | die(STATE_CRITICAL, _("DNS failure for %s\n"), dns_server); |
| 439 | } | ||
| 375 | 440 | ||
| 376 | /* Request error or the DNS lookup timed out */ | 441 | /* Request error or the DNS lookup timed out */ |
| 377 | else if (strstr(input_buffer, "Format error") || strstr(input_buffer, "Timed out")) | 442 | else if (strstr(input_buffer, "Format error") || strstr(input_buffer, "Timed out")) { |
| 378 | return STATE_WARNING; | 443 | return STATE_WARNING; |
| 444 | } | ||
| 379 | 445 | ||
| 380 | return STATE_OK; | 446 | return STATE_OK; |
| 381 | } | 447 | } |
| 382 | 448 | ||
| 383 | /* process command-line arguments */ | 449 | /* process command-line arguments */ |
| 384 | int process_arguments(int argc, char **argv) { | 450 | check_dns_config_wrapper process_arguments(int argc, char **argv) { |
| 385 | int c; | ||
| 386 | char *warning = NULL; | ||
| 387 | char *critical = NULL; | ||
| 388 | |||
| 389 | int opt_index = 0; | ||
| 390 | static struct option long_opts[] = {{"help", no_argument, 0, 'h'}, | 451 | static struct option long_opts[] = {{"help", no_argument, 0, 'h'}, |
| 391 | {"version", no_argument, 0, 'V'}, | 452 | {"version", no_argument, 0, 'V'}, |
| 392 | {"verbose", no_argument, 0, 'v'}, | 453 | {"verbose", no_argument, 0, 'v'}, |
| @@ -402,20 +463,34 @@ int process_arguments(int argc, char **argv) { | |||
| 402 | {"critical", required_argument, 0, 'c'}, | 463 | {"critical", required_argument, 0, 'c'}, |
| 403 | {0, 0, 0, 0}}; | 464 | {0, 0, 0, 0}}; |
| 404 | 465 | ||
| 405 | if (argc < 2) | 466 | check_dns_config_wrapper result = { |
| 406 | return ERROR; | 467 | .config = check_dns_config_init(), |
| 468 | .errorcode = OK, | ||
| 469 | }; | ||
| 407 | 470 | ||
| 408 | for (c = 1; c < argc; c++) | 471 | if (argc < 2) { |
| 409 | if (strcmp("-to", argv[c]) == 0) | 472 | result.errorcode = ERROR; |
| 410 | strcpy(argv[c], "-t"); | 473 | return result; |
| 474 | } | ||
| 411 | 475 | ||
| 412 | while (1) { | 476 | for (int index = 1; index < argc; index++) { |
| 413 | c = getopt_long(argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index); | 477 | if (strcmp("-to", argv[index]) == 0) { |
| 478 | strcpy(argv[index], "-t"); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | char *warning = NULL; | ||
| 483 | char *critical = NULL; | ||
| 484 | int opt_index = 0; | ||
| 485 | int index = 0; | ||
| 486 | while (true) { | ||
| 487 | index = getopt_long(argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index); | ||
| 414 | 488 | ||
| 415 | if (c == -1 || c == EOF) | 489 | if (index == -1 || index == EOF) { |
| 416 | break; | 490 | break; |
| 491 | } | ||
| 417 | 492 | ||
| 418 | switch (c) { | 493 | switch (index) { |
| 419 | case 'h': /* help */ | 494 | case 'h': /* help */ |
| 420 | print_help(); | 495 | print_help(); |
| 421 | exit(STATE_UNKNOWN); | 496 | exit(STATE_UNKNOWN); |
| @@ -429,54 +504,67 @@ int process_arguments(int argc, char **argv) { | |||
| 429 | timeout_interval = atoi(optarg); | 504 | timeout_interval = atoi(optarg); |
| 430 | break; | 505 | break; |
| 431 | case 'H': /* hostname */ | 506 | case 'H': /* hostname */ |
| 432 | if (strlen(optarg) >= ADDRESS_LENGTH) | 507 | if (strlen(optarg) >= ADDRESS_LENGTH) { |
| 433 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 508 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 434 | strcpy(query_address, optarg); | 509 | } |
| 510 | strcpy(result.config.query_address, optarg); | ||
| 435 | break; | 511 | break; |
| 436 | case 's': /* server name */ | 512 | case 's': /* server name */ |
| 437 | /* TODO: this host_or_die check is probably unnecessary. | 513 | /* TODO: this host_or_die check is probably unnecessary. |
| 438 | * Better to confirm nslookup response matches */ | 514 | * Better to confirm nslookup response matches */ |
| 439 | host_or_die(optarg); | 515 | host_or_die(optarg); |
| 440 | if (strlen(optarg) >= ADDRESS_LENGTH) | 516 | if (strlen(optarg) >= ADDRESS_LENGTH) { |
| 441 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 517 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 442 | strcpy(dns_server, optarg); | 518 | } |
| 519 | strcpy(result.config.dns_server, optarg); | ||
| 443 | break; | 520 | break; |
| 444 | case 'r': /* reverse server name */ | 521 | case 'r': /* reverse server name */ |
| 445 | /* TODO: Is this host_or_die necessary? */ | 522 | /* TODO: Is this host_or_die necessary? */ |
| 523 | // TODO This does not do anything!!! 2025-03-08 rincewind | ||
| 446 | host_or_die(optarg); | 524 | host_or_die(optarg); |
| 447 | if (strlen(optarg) >= ADDRESS_LENGTH) | 525 | if (strlen(optarg) >= ADDRESS_LENGTH) { |
| 448 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 526 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 527 | } | ||
| 528 | static char ptr_server[ADDRESS_LENGTH] = ""; | ||
| 449 | strcpy(ptr_server, optarg); | 529 | strcpy(ptr_server, optarg); |
| 450 | break; | 530 | break; |
| 451 | case 'a': /* expected address */ | 531 | case 'a': /* expected address */ |
| 452 | if (strlen(optarg) >= ADDRESS_LENGTH) | 532 | if (strlen(optarg) >= ADDRESS_LENGTH) { |
| 453 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 533 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 534 | } | ||
| 454 | if (strchr(optarg, ',') != NULL) { | 535 | if (strchr(optarg, ',') != NULL) { |
| 455 | char *comma = strchr(optarg, ','); | 536 | char *comma = strchr(optarg, ','); |
| 456 | while (comma != NULL) { | 537 | while (comma != NULL) { |
| 457 | expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **)); | 538 | result.config.expected_address = (char **)realloc( |
| 458 | expected_address[expected_address_cnt] = strndup(optarg, comma - optarg); | 539 | result.config.expected_address, |
| 459 | expected_address_cnt++; | 540 | (result.config.expected_address_cnt + 1) * sizeof(char **)); |
| 541 | result.config.expected_address[result.config.expected_address_cnt] = | ||
| 542 | strndup(optarg, comma - optarg); | ||
| 543 | result.config.expected_address_cnt++; | ||
| 460 | optarg = comma + 1; | 544 | optarg = comma + 1; |
| 461 | comma = strchr(optarg, ','); | 545 | comma = strchr(optarg, ','); |
| 462 | } | 546 | } |
| 463 | expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **)); | 547 | result.config.expected_address = |
| 464 | expected_address[expected_address_cnt] = strdup(optarg); | 548 | (char **)realloc(result.config.expected_address, |
| 465 | expected_address_cnt++; | 549 | (result.config.expected_address_cnt + 1) * sizeof(char **)); |
| 550 | result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg); | ||
| 551 | result.config.expected_address_cnt++; | ||
| 466 | } else { | 552 | } else { |
| 467 | expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **)); | 553 | result.config.expected_address = |
| 468 | expected_address[expected_address_cnt] = strdup(optarg); | 554 | (char **)realloc(result.config.expected_address, |
| 469 | expected_address_cnt++; | 555 | (result.config.expected_address_cnt + 1) * sizeof(char **)); |
| 556 | result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg); | ||
| 557 | result.config.expected_address_cnt++; | ||
| 470 | } | 558 | } |
| 471 | break; | 559 | break; |
| 472 | case 'n': /* expect NXDOMAIN */ | 560 | case 'n': /* expect NXDOMAIN */ |
| 473 | expect_nxdomain = true; | 561 | result.config.expect_nxdomain = true; |
| 474 | break; | 562 | break; |
| 475 | case 'A': /* expect authority */ | 563 | case 'A': /* expect authority */ |
| 476 | expect_authority = true; | 564 | result.config.expect_authority = true; |
| 477 | break; | 565 | break; |
| 478 | case 'L': /* all must match */ | 566 | case 'L': /* all must match */ |
| 479 | all_match = true; | 567 | result.config.all_match = true; |
| 480 | break; | 568 | break; |
| 481 | case 'w': | 569 | case 'w': |
| 482 | warning = optarg; | 570 | warning = optarg; |
| @@ -489,38 +577,42 @@ int process_arguments(int argc, char **argv) { | |||
| 489 | } | 577 | } |
| 490 | } | 578 | } |
| 491 | 579 | ||
| 492 | c = optind; | 580 | index = optind; |
| 493 | if (strlen(query_address) == 0 && c < argc) { | 581 | if (strlen(result.config.query_address) == 0 && index < argc) { |
| 494 | if (strlen(argv[c]) >= ADDRESS_LENGTH) | 582 | if (strlen(argv[index]) >= ADDRESS_LENGTH) { |
| 495 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 583 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 496 | strcpy(query_address, argv[c++]); | 584 | } |
| 585 | strcpy(result.config.query_address, argv[index++]); | ||
| 497 | } | 586 | } |
| 498 | 587 | ||
| 499 | if (strlen(dns_server) == 0 && c < argc) { | 588 | if (strlen(result.config.dns_server) == 0 && index < argc) { |
| 500 | /* TODO: See -s option */ | 589 | /* TODO: See -s option */ |
| 501 | host_or_die(argv[c]); | 590 | host_or_die(argv[index]); |
| 502 | if (strlen(argv[c]) >= ADDRESS_LENGTH) | 591 | if (strlen(argv[index]) >= ADDRESS_LENGTH) { |
| 503 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 592 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 504 | strcpy(dns_server, argv[c++]); | 593 | } |
| 594 | strcpy(result.config.dns_server, argv[index++]); | ||
| 505 | } | 595 | } |
| 506 | 596 | ||
| 507 | set_thresholds(&time_thresholds, warning, critical); | 597 | set_thresholds(&result.config.time_thresholds, warning, critical); |
| 508 | 598 | ||
| 509 | return validate_arguments(); | 599 | return validate_arguments(result); |
| 510 | } | 600 | } |
| 511 | 601 | ||
| 512 | int validate_arguments(void) { | 602 | check_dns_config_wrapper validate_arguments(check_dns_config_wrapper config_wrapper) { |
| 513 | if (query_address[0] == 0) { | 603 | if (config_wrapper.config.query_address[0] == 0) { |
| 514 | printf("missing --host argument\n"); | 604 | printf("missing --host argument\n"); |
| 515 | return ERROR; | 605 | config_wrapper.errorcode = ERROR; |
| 606 | return config_wrapper; | ||
| 516 | } | 607 | } |
| 517 | 608 | ||
| 518 | if (expected_address_cnt > 0 && expect_nxdomain) { | 609 | if (config_wrapper.config.expected_address_cnt > 0 && config_wrapper.config.expect_nxdomain) { |
| 519 | printf("--expected-address and --expect-nxdomain cannot be combined\n"); | 610 | printf("--expected-address and --expect-nxdomain cannot be combined\n"); |
| 520 | return ERROR; | 611 | config_wrapper.errorcode = ERROR; |
| 612 | return config_wrapper; | ||
| 521 | } | 613 | } |
| 522 | 614 | ||
| 523 | return OK; | 615 | return config_wrapper; |
| 524 | } | 616 | } |
| 525 | 617 | ||
| 526 | void print_help(void) { | 618 | void print_help(void) { |
| @@ -529,9 +621,11 @@ void print_help(void) { | |||
| 529 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); | 621 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); |
| 530 | printf(COPYRIGHT, copyright, email); | 622 | printf(COPYRIGHT, copyright, email); |
| 531 | 623 | ||
| 532 | printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query.")); | 624 | printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given " |
| 625 | "host/domain query.")); | ||
| 533 | printf("%s\n", _("An optional DNS server to use may be specified.")); | 626 | printf("%s\n", _("An optional DNS server to use may be specified.")); |
| 534 | printf("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used.")); | 627 | printf("%s\n", _("If no DNS server is specified, the default server(s) specified in " |
| 628 | "/etc/resolv.conf will be used.")); | ||
| 535 | 629 | ||
| 536 | printf("\n\n"); | 630 | printf("\n\n"); |
| 537 | 631 | ||
| @@ -545,11 +639,14 @@ void print_help(void) { | |||
| 545 | printf(" -s, --server=HOST\n"); | 639 | printf(" -s, --server=HOST\n"); |
| 546 | printf(" %s\n", _("Optional DNS server you want to use for the lookup")); | 640 | printf(" %s\n", _("Optional DNS server you want to use for the lookup")); |
| 547 | printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n"); | 641 | printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n"); |
| 548 | printf(" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end")); | 642 | printf(" %s\n", |
| 549 | printf(" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any")); | 643 | _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end")); |
| 644 | printf(" %s\n", | ||
| 645 | _("with a dot (.). This option can be repeated multiple times (Returns OK if any")); | ||
| 550 | printf(" %s\n", _("value matches).")); | 646 | printf(" %s\n", _("value matches).")); |
| 551 | printf(" -n, --expect-nxdomain\n"); | 647 | printf(" -n, --expect-nxdomain\n"); |
| 552 | printf(" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)")); | 648 | printf(" %s\n", |
| 649 | _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)")); | ||
| 553 | printf(" %s\n", _("Cannot be used together with -a")); | 650 | printf(" %s\n", _("Cannot be used together with -a")); |
| 554 | printf(" -A, --expect-authority\n"); | 651 | printf(" -A, --expect-authority\n"); |
| 555 | printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup")); | 652 | printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup")); |
| @@ -558,7 +655,8 @@ void print_help(void) { | |||
| 558 | printf(" -c, --critical=seconds\n"); | 655 | printf(" -c, --critical=seconds\n"); |
| 559 | printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off")); | 656 | printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off")); |
| 560 | printf(" -L, --all\n"); | 657 | printf(" -L, --all\n"); |
| 561 | printf(" %s\n", _("Return critical if the list of expected addresses does not match all addresses")); | 658 | printf(" %s\n", |
| 659 | _("Return critical if the list of expected addresses does not match all addresses")); | ||
| 562 | printf(" %s\n", _("returned. Default off")); | 660 | printf(" %s\n", _("returned. Default off")); |
| 563 | 661 | ||
| 564 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 662 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| @@ -568,5 +666,7 @@ void print_help(void) { | |||
| 568 | 666 | ||
| 569 | void print_usage(void) { | 667 | void print_usage(void) { |
| 570 | printf("%s\n", _("Usage:")); | 668 | printf("%s\n", _("Usage:")); |
| 571 | printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname); | 669 | printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c " |
| 670 | "crit] [-L]\n", | ||
| 671 | progname); | ||
| 572 | } | 672 | } |
diff --git a/plugins/check_dns.d/config.h b/plugins/check_dns.d/config.h new file mode 100644 index 00000000..9ec4eb82 --- /dev/null +++ b/plugins/check_dns.d/config.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "thresholds.h" | ||
| 5 | #include <stddef.h> | ||
| 6 | |||
| 7 | #define ADDRESS_LENGTH 256 | ||
| 8 | |||
| 9 | typedef struct { | ||
| 10 | bool all_match; | ||
| 11 | char dns_server[ADDRESS_LENGTH]; | ||
| 12 | char query_address[ADDRESS_LENGTH]; | ||
| 13 | bool expect_nxdomain; | ||
| 14 | bool expect_authority; | ||
| 15 | char **expected_address; | ||
| 16 | size_t expected_address_cnt; | ||
| 17 | |||
| 18 | thresholds *time_thresholds; | ||
| 19 | } check_dns_config; | ||
| 20 | |||
| 21 | check_dns_config check_dns_config_init() { | ||
| 22 | check_dns_config tmp = { | ||
| 23 | .all_match = false, | ||
| 24 | .dns_server = "", | ||
| 25 | .query_address = "", | ||
| 26 | .expect_nxdomain = false, | ||
| 27 | .expect_authority = false, | ||
| 28 | .expected_address = NULL, | ||
| 29 | .expected_address_cnt = 0, | ||
| 30 | |||
| 31 | .time_thresholds = NULL, | ||
| 32 | }; | ||
| 33 | return tmp; | ||
| 34 | } | ||
diff --git a/plugins/check_dummy.c b/plugins/check_dummy.c index 19f6c046..602d5868 100644 --- a/plugins/check_dummy.c +++ b/plugins/check_dummy.c | |||
| @@ -45,18 +45,19 @@ int main(int argc, char **argv) { | |||
| 45 | bindtextdomain(PACKAGE, LOCALEDIR); | 45 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 46 | textdomain(PACKAGE); | 46 | textdomain(PACKAGE); |
| 47 | 47 | ||
| 48 | if (argc < 2) | 48 | if (argc < 2) { |
| 49 | usage4(_("Could not parse arguments")); | 49 | usage4(_("Could not parse arguments")); |
| 50 | else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) { | 50 | } else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) { |
| 51 | print_revision(progname, NP_VERSION); | 51 | print_revision(progname, NP_VERSION); |
| 52 | exit(STATE_UNKNOWN); | 52 | exit(STATE_UNKNOWN); |
| 53 | } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { | 53 | } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { |
| 54 | print_help(); | 54 | print_help(); |
| 55 | exit(STATE_UNKNOWN); | 55 | exit(STATE_UNKNOWN); |
| 56 | } else if (!is_integer(argv[1])) | 56 | } else if (!is_integer(argv[1])) { |
| 57 | usage4(_("Arguments to check_dummy must be an integer")); | 57 | usage4(_("Arguments to check_dummy must be an integer")); |
| 58 | else | 58 | } else { |
| 59 | result = atoi(argv[1]); | 59 | result = atoi(argv[1]); |
| 60 | } | ||
| 60 | 61 | ||
| 61 | switch (result) { | 62 | switch (result) { |
| 62 | case STATE_OK: | 63 | case STATE_OK: |
| @@ -78,8 +79,9 @@ int main(int argc, char **argv) { | |||
| 78 | return STATE_UNKNOWN; | 79 | return STATE_UNKNOWN; |
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | if (argc >= 3) | 82 | if (argc >= 3) { |
| 82 | printf(": %s", argv[2]); | 83 | printf(": %s", argv[2]); |
| 84 | } | ||
| 83 | 85 | ||
| 84 | printf("\n"); | 86 | printf("\n"); |
| 85 | 87 | ||
| @@ -92,7 +94,8 @@ void print_help(void) { | |||
| 92 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); | 94 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); |
| 93 | printf(COPYRIGHT, copyright, email); | 95 | printf(COPYRIGHT, copyright, email); |
| 94 | 96 | ||
| 95 | printf("%s\n", _("This plugin will simply return the state corresponding to the numeric value")); | 97 | printf("%s\n", |
| 98 | _("This plugin will simply return the state corresponding to the numeric value")); | ||
| 96 | 99 | ||
| 97 | printf("%s\n", _("of the <state> argument with optional text")); | 100 | printf("%s\n", _("of the <state> argument with optional text")); |
| 98 | 101 | ||
diff --git a/plugins/check_fping.c b/plugins/check_fping.c index c1d03ece..6160c2cb 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c | |||
| @@ -38,52 +38,30 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 38 | #include "netutils.h" | 38 | #include "netutils.h" |
| 39 | #include "utils.h" | 39 | #include "utils.h" |
| 40 | #include <stdbool.h> | 40 | #include <stdbool.h> |
| 41 | #include "check_fping.d/config.h" | ||
| 42 | #include "states.h" | ||
| 41 | 43 | ||
| 42 | enum { | 44 | enum { |
| 43 | PACKET_COUNT = 1, | ||
| 44 | PACKET_SIZE = 56, | ||
| 45 | PL = 0, | 45 | PL = 0, |
| 46 | RTA = 1 | 46 | RTA = 1 |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | static int textscan(char *buf); | 49 | static mp_state_enum textscan(char *buf, const char * /*server_name*/, bool /*crta_p*/, |
| 50 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 50 | double /*crta*/, bool /*wrta_p*/, double /*wrta*/, bool /*cpl_p*/, |
| 51 | int /*cpl*/, bool /*wpl_p*/, int /*wpl*/, bool /*alive_p*/); | ||
| 52 | |||
| 53 | typedef struct { | ||
| 54 | int errorcode; | ||
| 55 | check_fping_config config; | ||
| 56 | } check_fping_config_wrapper; | ||
| 57 | static check_fping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 51 | static int get_threshold(char *arg, char *rv[2]); | 58 | static int get_threshold(char *arg, char *rv[2]); |
| 52 | static void print_help(void); | 59 | static void print_help(void); |
| 53 | void print_usage(void); | 60 | void print_usage(void); |
| 54 | 61 | ||
| 55 | static char *server_name = NULL; | ||
| 56 | static char *sourceip = NULL; | ||
| 57 | static char *sourceif = NULL; | ||
| 58 | static int packet_size = PACKET_SIZE; | ||
| 59 | static int packet_count = PACKET_COUNT; | ||
| 60 | static int target_timeout = 0; | ||
| 61 | static int packet_interval = 0; | ||
| 62 | static bool verbose = false; | 62 | static bool verbose = false; |
| 63 | static bool dontfrag = false; | ||
| 64 | static bool randomize_packet_data = false; | ||
| 65 | static int cpl; | ||
| 66 | static int wpl; | ||
| 67 | static double crta; | ||
| 68 | static double wrta; | ||
| 69 | static bool cpl_p = false; | ||
| 70 | static bool wpl_p = false; | ||
| 71 | static bool alive_p = false; | ||
| 72 | static bool crta_p = false; | ||
| 73 | static bool wrta_p = false; | ||
| 74 | 63 | ||
| 75 | int main(int argc, char **argv) { | 64 | int main(int argc, char **argv) { |
| 76 | /* normally should be int result = STATE_UNKNOWN; */ | ||
| 77 | |||
| 78 | int status = STATE_UNKNOWN; | ||
| 79 | int result = 0; | ||
| 80 | char *fping_prog = NULL; | ||
| 81 | char *server = NULL; | ||
| 82 | char *command_line = NULL; | ||
| 83 | char *input_buffer = NULL; | ||
| 84 | char *option_string = ""; | ||
| 85 | input_buffer = malloc(MAX_INPUT_BUFFER); | ||
| 86 | |||
| 87 | setlocale(LC_ALL, ""); | 65 | setlocale(LC_ALL, ""); |
| 88 | bindtextdomain(PACKAGE, LOCALEDIR); | 66 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 89 | textdomain(PACKAGE); | 67 | textdomain(PACKAGE); |
| @@ -91,39 +69,81 @@ int main(int argc, char **argv) { | |||
| 91 | /* Parse extra opts if any */ | 69 | /* Parse extra opts if any */ |
| 92 | argv = np_extra_opts(&argc, argv, progname); | 70 | argv = np_extra_opts(&argc, argv, progname); |
| 93 | 71 | ||
| 94 | if (process_arguments(argc, argv) == ERROR) | 72 | check_fping_config_wrapper tmp_config = process_arguments(argc, argv); |
| 73 | if (tmp_config.errorcode == ERROR) { | ||
| 95 | usage4(_("Could not parse arguments")); | 74 | usage4(_("Could not parse arguments")); |
| 75 | } | ||
| 76 | |||
| 77 | const check_fping_config config = tmp_config.config; | ||
| 78 | |||
| 79 | char *server = NULL; | ||
| 80 | server = strscpy(server, config.server_name); | ||
| 96 | 81 | ||
| 97 | server = strscpy(server, server_name); | 82 | char *option_string = ""; |
| 83 | char *fping_prog = NULL; | ||
| 84 | |||
| 85 | /* First determine if the target is dualstack or ipv6 only. */ | ||
| 86 | bool server_is_inet6_addr = is_inet6_addr(server); | ||
| 87 | |||
| 88 | /* | ||
| 89 | * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack | ||
| 90 | * -> we use ipv6 | ||
| 91 | * If the user requested -4 OR the user made no assertion and the address is v4 ONLY | ||
| 92 | * -> we use ipv4 | ||
| 93 | */ | ||
| 94 | if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) { | ||
| 95 | xasprintf(&option_string, "%s-6 ", option_string); | ||
| 96 | } else { | ||
| 97 | xasprintf(&option_string, "%s-4 ", option_string); | ||
| 98 | } | ||
| 99 | fping_prog = strdup(PATH_TO_FPING); | ||
| 98 | 100 | ||
| 99 | /* compose the command */ | 101 | /* compose the command */ |
| 100 | if (target_timeout) | 102 | if (config.target_timeout) { |
| 101 | xasprintf(&option_string, "%s-t %d ", option_string, target_timeout); | 103 | xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout); |
| 102 | if (packet_interval) | 104 | } |
| 103 | xasprintf(&option_string, "%s-p %d ", option_string, packet_interval); | 105 | if (config.packet_interval) { |
| 104 | if (sourceip) | 106 | xasprintf(&option_string, "%s-p %d ", option_string, config.packet_interval); |
| 105 | xasprintf(&option_string, "%s-S %s ", option_string, sourceip); | 107 | } |
| 106 | if (sourceif) | 108 | if (config.sourceip) { |
| 107 | xasprintf(&option_string, "%s-I %s ", option_string, sourceif); | 109 | xasprintf(&option_string, "%s-S %s ", option_string, config.sourceip); |
| 108 | if (dontfrag) | 110 | } |
| 111 | if (config.sourceif) { | ||
| 112 | xasprintf(&option_string, "%s-I %s ", option_string, config.sourceif); | ||
| 113 | } | ||
| 114 | if (config.dontfrag) { | ||
| 109 | xasprintf(&option_string, "%s-M ", option_string); | 115 | xasprintf(&option_string, "%s-M ", option_string); |
| 110 | if (randomize_packet_data) | 116 | } |
| 117 | if (config.randomize_packet_data) { | ||
| 111 | xasprintf(&option_string, "%s-R ", option_string); | 118 | xasprintf(&option_string, "%s-R ", option_string); |
| 119 | } | ||
| 112 | 120 | ||
| 121 | if (config.fwmark_set) { | ||
| 122 | xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark); | ||
| 123 | } | ||
| 113 | 124 | ||
| 114 | #ifdef PATH_TO_FPING6 | 125 | if (config.icmp_timestamp) { |
| 115 | if (address_family != AF_INET && is_inet6_addr(server)) | 126 | xasprintf(&option_string, "%s--icmp-timestamp ", option_string); |
| 116 | fping_prog = strdup(PATH_TO_FPING6); | 127 | } |
| 117 | else | ||
| 118 | fping_prog = strdup(PATH_TO_FPING); | ||
| 119 | #else | ||
| 120 | fping_prog = strdup(PATH_TO_FPING); | ||
| 121 | #endif | ||
| 122 | 128 | ||
| 123 | xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, packet_size, packet_count, server); | 129 | if (config.check_source) { |
| 130 | xasprintf(&option_string, "%s--check-source ", option_string); | ||
| 131 | } | ||
| 132 | |||
| 133 | char *command_line = NULL; | ||
| 124 | 134 | ||
| 125 | if (verbose) | 135 | if (config.icmp_timestamp) { |
| 136 | // no packet size settable for ICMP timestamp | ||
| 137 | xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count, | ||
| 138 | server); | ||
| 139 | } else { | ||
| 140 | xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, | ||
| 141 | config.packet_size, config.packet_count, server); | ||
| 142 | } | ||
| 143 | |||
| 144 | if (verbose) { | ||
| 126 | printf("%s\n", command_line); | 145 | printf("%s\n", command_line); |
| 146 | } | ||
| 127 | 147 | ||
| 128 | /* run the command */ | 148 | /* run the command */ |
| 129 | child_process = spopen(command_line); | 149 | child_process = spopen(command_line); |
| @@ -137,23 +157,31 @@ int main(int argc, char **argv) { | |||
| 137 | printf(_("Could not open stderr for %s\n"), command_line); | 157 | printf(_("Could not open stderr for %s\n"), command_line); |
| 138 | } | 158 | } |
| 139 | 159 | ||
| 160 | char *input_buffer = malloc(MAX_INPUT_BUFFER); | ||
| 161 | mp_state_enum status = STATE_UNKNOWN; | ||
| 140 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { | 162 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { |
| 141 | if (verbose) | 163 | if (verbose) { |
| 142 | printf("%s", input_buffer); | 164 | printf("%s", input_buffer); |
| 143 | status = max_state(status, textscan(input_buffer)); | 165 | } |
| 166 | status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p, | ||
| 167 | config.crta, config.wrta_p, config.wrta, config.cpl_p, | ||
| 168 | config.cpl, config.wpl_p, config.wpl, config.alive_p)); | ||
| 144 | } | 169 | } |
| 145 | 170 | ||
| 146 | /* If we get anything on STDERR, at least set warning */ | 171 | /* If we get anything on STDERR, at least set warning */ |
| 147 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { | 172 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { |
| 148 | status = max_state(status, STATE_WARNING); | 173 | status = max_state(status, STATE_WARNING); |
| 149 | if (verbose) | 174 | if (verbose) { |
| 150 | printf("%s", input_buffer); | 175 | printf("%s", input_buffer); |
| 151 | status = max_state(status, textscan(input_buffer)); | 176 | } |
| 177 | status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p, | ||
| 178 | config.crta, config.wrta_p, config.wrta, config.cpl_p, | ||
| 179 | config.cpl, config.wpl_p, config.wpl, config.alive_p)); | ||
| 152 | } | 180 | } |
| 153 | (void)fclose(child_stderr); | 181 | (void)fclose(child_stderr); |
| 154 | 182 | ||
| 155 | /* close the pipe */ | 183 | /* close the pipe */ |
| 156 | result = spclose(child_process); | 184 | int result = spclose(child_process); |
| 157 | if (result) { | 185 | if (result) { |
| 158 | /* need to use max_state not max */ | 186 | /* need to use max_state not max */ |
| 159 | status = max_state(status, STATE_WARNING); | 187 | status = max_state(status, STATE_WARNING); |
| @@ -172,21 +200,17 @@ int main(int argc, char **argv) { | |||
| 172 | } | 200 | } |
| 173 | } | 201 | } |
| 174 | 202 | ||
| 175 | printf("FPING %s - %s\n", state_text(status), server_name); | 203 | printf("FPING %s - %s\n", state_text(status), config.server_name); |
| 176 | 204 | ||
| 177 | return status; | 205 | return status; |
| 178 | } | 206 | } |
| 179 | 207 | ||
| 180 | int textscan(char *buf) { | 208 | mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double crta, bool wrta_p, |
| 181 | char *rtastr = NULL; | 209 | double wrta, bool cpl_p, int cpl, bool wpl_p, int wpl, bool alive_p) { |
| 182 | char *losstr = NULL; | ||
| 183 | char *xmtstr = NULL; | ||
| 184 | double loss; | ||
| 185 | double rta; | ||
| 186 | double xmt; | ||
| 187 | int status = STATE_UNKNOWN; | ||
| 188 | |||
| 189 | /* stops testing after the first successful reply. */ | 210 | /* stops testing after the first successful reply. */ |
| 211 | double rta; | ||
| 212 | double loss; | ||
| 213 | char *rtastr = NULL; | ||
| 190 | if (alive_p && strstr(buf, "avg, 0% loss)")) { | 214 | if (alive_p && strstr(buf, "avg, 0% loss)")) { |
| 191 | rtastr = strstr(buf, "ms ("); | 215 | rtastr = strstr(buf, "ms ("); |
| 192 | rtastr = 1 + index(rtastr, '('); | 216 | rtastr = 1 + index(rtastr, '('); |
| @@ -195,9 +219,14 @@ int textscan(char *buf) { | |||
| 195 | die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta, | 219 | die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta, |
| 196 | /* No loss since we only waited for the first reply | 220 | /* No loss since we only waited for the first reply |
| 197 | perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */ | 221 | perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */ |
| 198 | fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); | 222 | fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, |
| 223 | false, 0)); | ||
| 199 | } | 224 | } |
| 200 | 225 | ||
| 226 | mp_state_enum status = STATE_UNKNOWN; | ||
| 227 | char *xmtstr = NULL; | ||
| 228 | double xmt; | ||
| 229 | char *losstr = NULL; | ||
| 201 | if (strstr(buf, "not found")) { | 230 | if (strstr(buf, "not found")) { |
| 202 | die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name); | 231 | die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name); |
| 203 | 232 | ||
| @@ -221,19 +250,22 @@ int textscan(char *buf) { | |||
| 221 | rtastr = 1 + index(rtastr, '/'); | 250 | rtastr = 1 + index(rtastr, '/'); |
| 222 | loss = strtod(losstr, NULL); | 251 | loss = strtod(losstr, NULL); |
| 223 | rta = strtod(rtastr, NULL); | 252 | rta = strtod(rtastr, NULL); |
| 224 | if (cpl_p && loss > cpl) | 253 | if (cpl_p && loss > cpl) { |
| 225 | status = STATE_CRITICAL; | 254 | status = STATE_CRITICAL; |
| 226 | else if (crta_p && rta > crta) | 255 | } else if (crta_p && rta > crta) { |
| 227 | status = STATE_CRITICAL; | 256 | status = STATE_CRITICAL; |
| 228 | else if (wpl_p && loss > wpl) | 257 | } else if (wpl_p && loss > wpl) { |
| 229 | status = STATE_WARNING; | 258 | status = STATE_WARNING; |
| 230 | else if (wrta_p && rta > wrta) | 259 | } else if (wrta_p && rta > wrta) { |
| 231 | status = STATE_WARNING; | 260 | status = STATE_WARNING; |
| 232 | else | 261 | } else { |
| 233 | status = STATE_OK; | 262 | status = STATE_OK; |
| 234 | die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status), server_name, loss, rta, | 263 | } |
| 235 | perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), | 264 | die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status), |
| 236 | fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); | 265 | server_name, loss, rta, |
| 266 | perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0), | ||
| 267 | fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, | ||
| 268 | false, 0)); | ||
| 237 | 269 | ||
| 238 | } else if (strstr(buf, "xmt/rcv/%loss")) { | 270 | } else if (strstr(buf, "xmt/rcv/%loss")) { |
| 239 | /* no min/max/avg if host was unreachable in fping v2.2.b1 */ | 271 | /* no min/max/avg if host was unreachable in fping v2.2.b1 */ |
| @@ -241,22 +273,24 @@ int textscan(char *buf) { | |||
| 241 | losstr = strstr(buf, "="); | 273 | losstr = strstr(buf, "="); |
| 242 | xmtstr = 1 + losstr; | 274 | xmtstr = 1 + losstr; |
| 243 | xmt = strtod(xmtstr, NULL); | 275 | xmt = strtod(xmtstr, NULL); |
| 244 | if (xmt == 0) | 276 | if (xmt == 0) { |
| 245 | die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); | 277 | die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); |
| 278 | } | ||
| 246 | losstr = 1 + strstr(losstr, "/"); | 279 | losstr = 1 + strstr(losstr, "/"); |
| 247 | losstr = 1 + strstr(losstr, "/"); | 280 | losstr = 1 + strstr(losstr, "/"); |
| 248 | loss = strtod(losstr, NULL); | 281 | loss = strtod(losstr, NULL); |
| 249 | if (atoi(losstr) == 100) | 282 | if (atoi(losstr) == 100) { |
| 250 | status = STATE_CRITICAL; | 283 | status = STATE_CRITICAL; |
| 251 | else if (cpl_p && loss > cpl) | 284 | } else if (cpl_p && loss > cpl) { |
| 252 | status = STATE_CRITICAL; | 285 | status = STATE_CRITICAL; |
| 253 | else if (wpl_p && loss > wpl) | 286 | } else if (wpl_p && loss > wpl) { |
| 254 | status = STATE_WARNING; | 287 | status = STATE_WARNING; |
| 255 | else | 288 | } else { |
| 256 | status = STATE_OK; | 289 | status = STATE_OK; |
| 290 | } | ||
| 257 | /* loss=%.0f%%;%d;%d;0;100 */ | 291 | /* loss=%.0f%%;%d;%d;0;100 */ |
| 258 | die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss, | 292 | die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss, |
| 259 | perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100)); | 293 | perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0)); |
| 260 | 294 | ||
| 261 | } else { | 295 | } else { |
| 262 | status = max_state(status, STATE_WARNING); | 296 | status = max_state(status, STATE_WARNING); |
| @@ -266,11 +300,12 @@ int textscan(char *buf) { | |||
| 266 | } | 300 | } |
| 267 | 301 | ||
| 268 | /* process command-line arguments */ | 302 | /* process command-line arguments */ |
| 269 | int process_arguments(int argc, char **argv) { | 303 | check_fping_config_wrapper process_arguments(int argc, char **argv) { |
| 270 | int c; | 304 | enum { |
| 271 | char *rv[2]; | 305 | FWMARK_OPT = CHAR_MAX + 1, |
| 272 | 306 | ICMP_TIMESTAMP_OPT, | |
| 273 | int option = 0; | 307 | CHECK_SOURCE_OPT, |
| 308 | }; | ||
| 274 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, | 309 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 275 | {"sourceip", required_argument, 0, 'S'}, | 310 | {"sourceip", required_argument, 0, 'S'}, |
| 276 | {"sourceif", required_argument, 0, 'I'}, | 311 | {"sourceif", required_argument, 0, 'I'}, |
| @@ -288,32 +323,53 @@ int process_arguments(int argc, char **argv) { | |||
| 288 | {"use-ipv6", no_argument, 0, '6'}, | 323 | {"use-ipv6", no_argument, 0, '6'}, |
| 289 | {"dontfrag", no_argument, 0, 'M'}, | 324 | {"dontfrag", no_argument, 0, 'M'}, |
| 290 | {"random", no_argument, 0, 'R'}, | 325 | {"random", no_argument, 0, 'R'}, |
| 326 | #ifdef FPING_VERSION_5_2_OR_HIGHER | ||
| 327 | // only available with fping version >= 5.2 | ||
| 328 | {"fwmark", required_argument, NULL, FWMARK_OPT}, | ||
| 329 | # ifdef FPING_VERSION_5_3_OR_HIGHER | ||
| 330 | // only available with fping version >= 5.3 | ||
| 331 | {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT}, | ||
| 332 | {"check-source", no_argument, NULL, CHECK_SOURCE_OPT}, | ||
| 333 | # endif | ||
| 334 | #endif | ||
| 291 | {0, 0, 0, 0}}; | 335 | {0, 0, 0, 0}}; |
| 292 | 336 | ||
| 337 | char *rv[2]; | ||
| 293 | rv[PL] = NULL; | 338 | rv[PL] = NULL; |
| 294 | rv[RTA] = NULL; | 339 | rv[RTA] = NULL; |
| 295 | 340 | ||
| 296 | if (argc < 2) | 341 | int option = 0; |
| 297 | return ERROR; | 342 | |
| 343 | check_fping_config_wrapper result = { | ||
| 344 | .errorcode = OK, | ||
| 345 | .config = check_fping_config_init(), | ||
| 346 | }; | ||
| 347 | |||
| 348 | if (argc < 2) { | ||
| 349 | result.errorcode = ERROR; | ||
| 350 | return result; | ||
| 351 | } | ||
| 298 | 352 | ||
| 299 | if (!is_option(argv[1])) { | 353 | if (!is_option(argv[1])) { |
| 300 | server_name = argv[1]; | 354 | result.config.server_name = argv[1]; |
| 301 | argv[1] = argv[0]; | 355 | argv[1] = argv[0]; |
| 302 | argv = &argv[1]; | 356 | argv = &argv[1]; |
| 303 | argc--; | 357 | argc--; |
| 304 | } | 358 | } |
| 305 | 359 | ||
| 306 | while (1) { | 360 | while (true) { |
| 307 | c = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); | 361 | int option_index = |
| 362 | getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); | ||
| 308 | 363 | ||
| 309 | if (c == -1 || c == EOF || c == 1) | 364 | if (option_index == -1 || option_index == EOF || option_index == 1) { |
| 310 | break; | 365 | break; |
| 366 | } | ||
| 311 | 367 | ||
| 312 | switch (c) { | 368 | switch (option_index) { |
| 313 | case '?': /* print short usage statement if args not parsable */ | 369 | case '?': /* print short usage statement if args not parsable */ |
| 314 | usage5(); | 370 | usage5(); |
| 315 | case 'a': /* host alive mode */ | 371 | case 'a': /* host alive mode */ |
| 316 | alive_p = true; | 372 | result.config.alive_p = true; |
| 317 | break; | 373 | break; |
| 318 | case 'h': /* help */ | 374 | case 'h': /* help */ |
| 319 | print_help(); | 375 | print_help(); |
| @@ -325,109 +381,128 @@ int process_arguments(int argc, char **argv) { | |||
| 325 | verbose = true; | 381 | verbose = true; |
| 326 | break; | 382 | break; |
| 327 | case 'H': /* hostname */ | 383 | case 'H': /* hostname */ |
| 328 | if (is_host(optarg) == false) { | 384 | if (!is_host(optarg)) { |
| 329 | usage2(_("Invalid hostname/address"), optarg); | 385 | usage2(_("Invalid hostname/address"), optarg); |
| 330 | } | 386 | } |
| 331 | server_name = strscpy(server_name, optarg); | 387 | result.config.server_name = optarg; |
| 332 | break; | 388 | break; |
| 333 | case 'S': /* sourceip */ | 389 | case 'S': /* sourceip */ |
| 334 | if (is_host(optarg) == false) { | 390 | if (!is_host(optarg)) { |
| 335 | usage2(_("Invalid hostname/address"), optarg); | 391 | usage2(_("Invalid hostname/address"), optarg); |
| 336 | } | 392 | } |
| 337 | sourceip = strscpy(sourceip, optarg); | 393 | result.config.sourceip = optarg; |
| 338 | break; | 394 | break; |
| 339 | case 'I': /* sourceip */ | 395 | case 'I': /* sourceip */ |
| 340 | sourceif = strscpy(sourceif, optarg); | 396 | result.config.sourceif = optarg; |
| 341 | break; | 397 | break; |
| 342 | case '4': /* IPv4 only */ | 398 | case '4': /* IPv4 only */ |
| 343 | address_family = AF_INET; | 399 | address_family = AF_INET; |
| 344 | break; | 400 | break; |
| 345 | case '6': /* IPv6 only */ | 401 | case '6': /* IPv6 only */ |
| 346 | #ifdef USE_IPV6 | ||
| 347 | address_family = AF_INET6; | 402 | address_family = AF_INET6; |
| 348 | #else | ||
| 349 | usage(_("IPv6 support not available\n")); | ||
| 350 | #endif | ||
| 351 | break; | 403 | break; |
| 352 | case 'c': | 404 | case 'c': |
| 353 | get_threshold(optarg, rv); | 405 | get_threshold(optarg, rv); |
| 354 | if (rv[RTA]) { | 406 | if (rv[RTA]) { |
| 355 | crta = strtod(rv[RTA], NULL); | 407 | result.config.crta = strtod(rv[RTA], NULL); |
| 356 | crta_p = true; | 408 | result.config.crta_p = true; |
| 357 | rv[RTA] = NULL; | 409 | rv[RTA] = NULL; |
| 358 | } | 410 | } |
| 359 | if (rv[PL]) { | 411 | if (rv[PL]) { |
| 360 | cpl = atoi(rv[PL]); | 412 | result.config.cpl = atoi(rv[PL]); |
| 361 | cpl_p = true; | 413 | result.config.cpl_p = true; |
| 362 | rv[PL] = NULL; | 414 | rv[PL] = NULL; |
| 363 | } | 415 | } |
| 364 | break; | 416 | break; |
| 365 | case 'w': | 417 | case 'w': |
| 366 | get_threshold(optarg, rv); | 418 | get_threshold(optarg, rv); |
| 367 | if (rv[RTA]) { | 419 | if (rv[RTA]) { |
| 368 | wrta = strtod(rv[RTA], NULL); | 420 | result.config.wrta = strtod(rv[RTA], NULL); |
| 369 | wrta_p = true; | 421 | result.config.wrta_p = true; |
| 370 | rv[RTA] = NULL; | 422 | rv[RTA] = NULL; |
| 371 | } | 423 | } |
| 372 | if (rv[PL]) { | 424 | if (rv[PL]) { |
| 373 | wpl = atoi(rv[PL]); | 425 | result.config.wpl = atoi(rv[PL]); |
| 374 | wpl_p = true; | 426 | result.config.wpl_p = true; |
| 375 | rv[PL] = NULL; | 427 | rv[PL] = NULL; |
| 376 | } | 428 | } |
| 377 | break; | 429 | break; |
| 378 | case 'b': /* bytes per packet */ | 430 | case 'b': /* bytes per packet */ |
| 379 | if (is_intpos(optarg)) | 431 | if (is_intpos(optarg)) { |
| 380 | packet_size = atoi(optarg); | 432 | result.config.packet_size = atoi(optarg); |
| 381 | else | 433 | } else { |
| 382 | usage(_("Packet size must be a positive integer")); | 434 | usage(_("Packet size must be a positive integer")); |
| 435 | } | ||
| 383 | break; | 436 | break; |
| 384 | case 'n': /* number of packets */ | 437 | case 'n': /* number of packets */ |
| 385 | if (is_intpos(optarg)) | 438 | if (is_intpos(optarg)) { |
| 386 | packet_count = atoi(optarg); | 439 | result.config.packet_count = atoi(optarg); |
| 387 | else | 440 | } else { |
| 388 | usage(_("Packet count must be a positive integer")); | 441 | usage(_("Packet count must be a positive integer")); |
| 442 | } | ||
| 389 | break; | 443 | break; |
| 390 | case 'T': /* timeout in msec */ | 444 | case 'T': /* timeout in msec */ |
| 391 | if (is_intpos(optarg)) | 445 | if (is_intpos(optarg)) { |
| 392 | target_timeout = atoi(optarg); | 446 | result.config.target_timeout = atoi(optarg); |
| 393 | else | 447 | } else { |
| 394 | usage(_("Target timeout must be a positive integer")); | 448 | usage(_("Target timeout must be a positive integer")); |
| 449 | } | ||
| 395 | break; | 450 | break; |
| 396 | case 'i': /* interval in msec */ | 451 | case 'i': /* interval in msec */ |
| 397 | if (is_intpos(optarg)) | 452 | if (is_intpos(optarg)) { |
| 398 | packet_interval = atoi(optarg); | 453 | result.config.packet_interval = atoi(optarg); |
| 399 | else | 454 | } else { |
| 400 | usage(_("Interval must be a positive integer")); | 455 | usage(_("Interval must be a positive integer")); |
| 456 | } | ||
| 401 | break; | 457 | break; |
| 402 | case 'R': | 458 | case 'R': |
| 403 | randomize_packet_data = true; | 459 | result.config.randomize_packet_data = true; |
| 404 | break; | 460 | break; |
| 405 | case 'M': | 461 | case 'M': |
| 406 | dontfrag = true; | 462 | result.config.dontfrag = true; |
| 463 | break; | ||
| 464 | case FWMARK_OPT: | ||
| 465 | if (is_intpos(optarg)) { | ||
| 466 | result.config.fwmark = (unsigned int)atol(optarg); | ||
| 467 | result.config.fwmark_set = true; | ||
| 468 | } else { | ||
| 469 | usage(_("fwmark must be a positive integer")); | ||
| 470 | } | ||
| 471 | break; | ||
| 472 | case ICMP_TIMESTAMP_OPT: | ||
| 473 | result.config.icmp_timestamp = true; | ||
| 474 | break; | ||
| 475 | case CHECK_SOURCE_OPT: | ||
| 476 | result.config.check_source = true; | ||
| 407 | break; | 477 | break; |
| 408 | } | 478 | } |
| 409 | } | 479 | } |
| 410 | 480 | ||
| 411 | if (server_name == NULL) | 481 | if (result.config.server_name == NULL) { |
| 412 | usage4(_("Hostname was not supplied")); | 482 | usage4(_("Hostname was not supplied")); |
| 483 | } | ||
| 413 | 484 | ||
| 414 | return OK; | 485 | return result; |
| 415 | } | 486 | } |
| 416 | 487 | ||
| 417 | int get_threshold(char *arg, char *rv[2]) { | 488 | int get_threshold(char *arg, char *rv[2]) { |
| 418 | char *arg1 = NULL; | ||
| 419 | char *arg2 = NULL; | 489 | char *arg2 = NULL; |
| 420 | 490 | ||
| 421 | arg1 = strscpy(arg1, arg); | 491 | char *arg1 = strdup(arg); |
| 422 | if (strpbrk(arg1, ",:")) | 492 | if (strpbrk(arg1, ",:")) { |
| 423 | arg2 = 1 + strpbrk(arg1, ",:"); | 493 | arg2 = 1 + strpbrk(arg1, ",:"); |
| 494 | } | ||
| 424 | 495 | ||
| 425 | if (arg2) { | 496 | if (arg2) { |
| 426 | arg1[strcspn(arg1, ",:")] = 0; | 497 | arg1[strcspn(arg1, ",:")] = 0; |
| 427 | if (strstr(arg1, "%") && strstr(arg2, "%")) | 498 | if (strstr(arg1, "%") && strstr(arg2, "%")) { |
| 428 | die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname, arg); | 499 | die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname, |
| 429 | if (!strstr(arg1, "%") && !strstr(arg2, "%")) | 500 | arg); |
| 430 | die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname, arg); | 501 | } |
| 502 | if (!strstr(arg1, "%") && !strstr(arg2, "%")) { | ||
| 503 | die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname, | ||
| 504 | arg); | ||
| 505 | } | ||
| 431 | } | 506 | } |
| 432 | 507 | ||
| 433 | if (arg2 && strstr(arg2, "%")) { | 508 | if (arg2 && strstr(arg2, "%")) { |
| @@ -452,7 +527,8 @@ void print_help(void) { | |||
| 452 | printf("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n"); | 527 | printf("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n"); |
| 453 | printf(COPYRIGHT, copyright, email); | 528 | printf(COPYRIGHT, copyright, email); |
| 454 | 529 | ||
| 455 | printf("%s\n", _("This plugin will use the fping command to ping the specified host for a fast check")); | 530 | printf("%s\n", |
| 531 | _("This plugin will use the fping command to ping the specified host for a fast check")); | ||
| 456 | 532 | ||
| 457 | printf("%s\n", _("Note that it is necessary to set the suid flag on fping.")); | 533 | printf("%s\n", _("Note that it is necessary to set the suid flag on fping.")); |
| 458 | 534 | ||
| @@ -466,7 +542,8 @@ void print_help(void) { | |||
| 466 | printf(UT_IPv46); | 542 | printf(UT_IPv46); |
| 467 | 543 | ||
| 468 | printf(" %s\n", "-H, --hostname=HOST"); | 544 | printf(" %s\n", "-H, --hostname=HOST"); |
| 469 | printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, reducing system load)")); | 545 | printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, " |
| 546 | "reducing system load)")); | ||
| 470 | printf(" %s\n", "-w, --warning=THRESHOLD"); | 547 | printf(" %s\n", "-w, --warning=THRESHOLD"); |
| 471 | printf(" %s\n", _("warning threshold pair")); | 548 | printf(" %s\n", _("warning threshold pair")); |
| 472 | printf(" %s\n", "-c, --critical=THRESHOLD"); | 549 | printf(" %s\n", "-c, --critical=THRESHOLD"); |
| @@ -480,7 +557,8 @@ void print_help(void) { | |||
| 480 | printf(" %s\n", "-T, --target-timeout=INTEGER"); | 557 | printf(" %s\n", "-T, --target-timeout=INTEGER"); |
| 481 | printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)")); | 558 | printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)")); |
| 482 | printf(" %s\n", "-i, --interval=INTEGER"); | 559 | printf(" %s\n", "-i, --interval=INTEGER"); |
| 483 | printf(" %s (default: fping's default for -p)\n", _("Interval (ms) between sending packets")); | 560 | printf(" %s (default: fping's default for -p)\n", |
| 561 | _("Interval (ms) between sending packets")); | ||
| 484 | printf(" %s\n", "-S, --sourceip=HOST"); | 562 | printf(" %s\n", "-S, --sourceip=HOST"); |
| 485 | printf(" %s\n", _("name or IP Address of sourceip")); | 563 | printf(" %s\n", _("name or IP Address of sourceip")); |
| 486 | printf(" %s\n", "-I, --sourceif=IF"); | 564 | printf(" %s\n", "-I, --sourceif=IF"); |
| @@ -489,9 +567,20 @@ void print_help(void) { | |||
| 489 | printf(" %s\n", _("set the Don't Fragment flag")); | 567 | printf(" %s\n", _("set the Don't Fragment flag")); |
| 490 | printf(" %s\n", "-R, --random"); | 568 | printf(" %s\n", "-R, --random"); |
| 491 | printf(" %s\n", _("random packet data (to foil link data compression)")); | 569 | printf(" %s\n", _("random packet data (to foil link data compression)")); |
| 570 | #ifdef FPING_VERSION_5_2_OR_HIGHER | ||
| 571 | printf(" %s\n", "--fwmark=INTEGER"); | ||
| 572 | printf(" %s\n", _("set the routing mark to INTEGER (fping option)")); | ||
| 573 | # ifdef FPING_VERSION_5_3_OR_HIGHER | ||
| 574 | printf(" %s\n", "--icmp-timestamp"); | ||
| 575 | printf(" %s\n", _("use ICMP Timestamp instead of ICMP Echo (fping option)")); | ||
| 576 | printf(" %s\n", "--check-source"); | ||
| 577 | printf(" %s\n", _("discard replies not from target address (fping option)")); | ||
| 578 | # endif | ||
| 579 | #endif | ||
| 492 | printf(UT_VERBOSE); | 580 | printf(UT_VERBOSE); |
| 493 | printf("\n"); | 581 | printf("\n"); |
| 494 | printf(" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); | 582 | printf(" %s\n", |
| 583 | _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); | ||
| 495 | printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of")); | 584 | printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of")); |
| 496 | printf(" %s\n", _("packet loss to trigger an alarm state.")); | 585 | printf(" %s\n", _("packet loss to trigger an alarm state.")); |
| 497 | 586 | ||
| @@ -503,5 +592,6 @@ void print_help(void) { | |||
| 503 | 592 | ||
| 504 | void print_usage(void) { | 593 | void print_usage(void) { |
| 505 | printf("%s\n", _("Usage:")); | 594 | printf("%s\n", _("Usage:")); |
| 506 | printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", progname); | 595 | printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", |
| 596 | progname); | ||
| 507 | } | 597 | } |
diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h new file mode 100644 index 00000000..d3e50565 --- /dev/null +++ b/plugins/check_fping.d/config.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | enum { | ||
| 7 | PACKET_SIZE = 56, | ||
| 8 | PACKET_COUNT = 1, | ||
| 9 | }; | ||
| 10 | |||
| 11 | typedef struct { | ||
| 12 | char *server_name; | ||
| 13 | char *sourceip; | ||
| 14 | char *sourceif; | ||
| 15 | int packet_size; | ||
| 16 | int packet_count; | ||
| 17 | int target_timeout; | ||
| 18 | int packet_interval; | ||
| 19 | bool randomize_packet_data; | ||
| 20 | bool dontfrag; | ||
| 21 | bool alive_p; | ||
| 22 | |||
| 23 | double crta; | ||
| 24 | bool crta_p; | ||
| 25 | double wrta; | ||
| 26 | bool wrta_p; | ||
| 27 | |||
| 28 | int cpl; | ||
| 29 | bool cpl_p; | ||
| 30 | int wpl; | ||
| 31 | bool wpl_p; | ||
| 32 | |||
| 33 | // only available with fping version >= 5.2 | ||
| 34 | // for a given uint _fwmark_ fping sets _fwmark_ as a firewall mark | ||
| 35 | // in the packets | ||
| 36 | unsigned int fwmark; | ||
| 37 | bool fwmark_set; | ||
| 38 | |||
| 39 | // only available with fping version >= 5.3 | ||
| 40 | // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead | ||
| 41 | // of ICMP Echo | ||
| 42 | bool icmp_timestamp; | ||
| 43 | |||
| 44 | // Setting check_source lets fping discard replies which are not from the target address | ||
| 45 | bool check_source; | ||
| 46 | } check_fping_config; | ||
| 47 | |||
| 48 | check_fping_config check_fping_config_init() { | ||
| 49 | check_fping_config tmp = { | ||
| 50 | .server_name = NULL, | ||
| 51 | .sourceip = NULL, | ||
| 52 | .sourceif = NULL, | ||
| 53 | .packet_size = PACKET_SIZE, | ||
| 54 | .packet_count = PACKET_COUNT, | ||
| 55 | .target_timeout = 0, | ||
| 56 | .packet_interval = 0, | ||
| 57 | .randomize_packet_data = false, | ||
| 58 | .dontfrag = false, | ||
| 59 | .alive_p = false, | ||
| 60 | |||
| 61 | .crta = 0, | ||
| 62 | .crta_p = false, | ||
| 63 | .wrta = 0, | ||
| 64 | .wrta_p = false, | ||
| 65 | |||
| 66 | .cpl = 0, | ||
| 67 | .cpl_p = false, | ||
| 68 | .wpl = 0, | ||
| 69 | .wpl_p = false, | ||
| 70 | |||
| 71 | // only available with fping version >= 5.2 | ||
| 72 | .fwmark = 0, | ||
| 73 | .fwmark_set = false, // just to be deterministic | ||
| 74 | |||
| 75 | // only available with fping version >= 5.3 | ||
| 76 | .icmp_timestamp = false, | ||
| 77 | .check_source = false, | ||
| 78 | |||
| 79 | }; | ||
| 80 | return tmp; | ||
| 81 | } | ||
diff --git a/plugins/check_game.c b/plugins/check_game.c index 619277e7..974a7253 100644 --- a/plugins/check_game.c +++ b/plugins/check_game.c | |||
| @@ -36,9 +36,15 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 36 | #include "common.h" | 36 | #include "common.h" |
| 37 | #include "utils.h" | 37 | #include "utils.h" |
| 38 | #include "runcmd.h" | 38 | #include "runcmd.h" |
| 39 | #include "check_game.d/config.h" | ||
| 40 | #include "../lib/monitoringplug.h" | ||
| 39 | 41 | ||
| 40 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 42 | typedef struct { |
| 41 | static int validate_arguments(void); | 43 | int errorcode; |
| 44 | check_game_config config; | ||
| 45 | } check_game_config_wrapper; | ||
| 46 | |||
| 47 | static check_game_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 42 | static void print_help(void); | 48 | static void print_help(void); |
| 43 | void print_usage(void); | 49 | void print_usage(void); |
| 44 | 50 | ||
| @@ -49,26 +55,9 @@ void print_usage(void); | |||
| 49 | #define QSTAT_HOST_TIMEOUT "TIMEOUT" | 55 | #define QSTAT_HOST_TIMEOUT "TIMEOUT" |
| 50 | #define QSTAT_MAX_RETURN_ARGS 12 | 56 | #define QSTAT_MAX_RETURN_ARGS 12 |
| 51 | 57 | ||
| 52 | static char *server_ip; | ||
| 53 | static char *game_type; | ||
| 54 | static int port = 0; | ||
| 55 | |||
| 56 | static bool verbose = false; | 58 | static bool verbose = false; |
| 57 | 59 | ||
| 58 | static int qstat_game_players_max = -1; | ||
| 59 | static int qstat_game_players = -1; | ||
| 60 | static int qstat_game_field = -1; | ||
| 61 | static int qstat_map_field = -1; | ||
| 62 | static int qstat_ping_field = -1; | ||
| 63 | |||
| 64 | int main(int argc, char **argv) { | 60 | int main(int argc, char **argv) { |
| 65 | char *command_line; | ||
| 66 | int result = STATE_UNKNOWN; | ||
| 67 | char *p; | ||
| 68 | char *ret[QSTAT_MAX_RETURN_ARGS]; | ||
| 69 | size_t i = 0; | ||
| 70 | output chld_out; | ||
| 71 | |||
| 72 | setlocale(LC_ALL, ""); | 61 | setlocale(LC_ALL, ""); |
| 73 | bindtextdomain(PACKAGE, LOCALEDIR); | 62 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 74 | textdomain(PACKAGE); | 63 | textdomain(PACKAGE); |
| @@ -76,22 +65,32 @@ int main(int argc, char **argv) { | |||
| 76 | /* Parse extra opts if any */ | 65 | /* Parse extra opts if any */ |
| 77 | argv = np_extra_opts(&argc, argv, progname); | 66 | argv = np_extra_opts(&argc, argv, progname); |
| 78 | 67 | ||
| 79 | if (process_arguments(argc, argv) == ERROR) | 68 | check_game_config_wrapper tmp = process_arguments(argc, argv); |
| 69 | |||
| 70 | if (tmp.errorcode == ERROR) { | ||
| 80 | usage_va(_("Could not parse arguments")); | 71 | usage_va(_("Could not parse arguments")); |
| 72 | } | ||
| 73 | |||
| 74 | check_game_config config = tmp.config; | ||
| 81 | 75 | ||
| 82 | result = STATE_OK; | 76 | mp_state_enum result = STATE_OK; |
| 83 | 77 | ||
| 84 | /* create the command line to execute */ | 78 | /* create the command line to execute */ |
| 85 | xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, game_type, server_ip); | 79 | char *command_line = NULL; |
| 80 | xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, | ||
| 81 | config.game_type, config.server_ip); | ||
| 86 | 82 | ||
| 87 | if (port) | 83 | if (config.port) { |
| 88 | xasprintf(&command_line, "%s:%-d", command_line, port); | 84 | xasprintf(&command_line, "%s:%-d", command_line, config.port); |
| 85 | } | ||
| 89 | 86 | ||
| 90 | if (verbose) | 87 | if (verbose) { |
| 91 | printf("%s\n", command_line); | 88 | printf("%s\n", command_line); |
| 89 | } | ||
| 92 | 90 | ||
| 93 | /* run the command. historically, this plugin ignores output on stderr, | 91 | /* run the command. historically, this plugin ignores output on stderr, |
| 94 | * as well as return status of the qstat program */ | 92 | * as well as return status of the qstat program */ |
| 93 | output chld_out = {}; | ||
| 95 | (void)np_runcmd(command_line, &chld_out, NULL, 0); | 94 | (void)np_runcmd(command_line, &chld_out, NULL, 0); |
| 96 | 95 | ||
| 97 | /* sanity check */ | 96 | /* sanity check */ |
| @@ -104,19 +103,22 @@ int main(int argc, char **argv) { | |||
| 104 | In the end, I figured I'd simply let an error occur & then trap it | 103 | In the end, I figured I'd simply let an error occur & then trap it |
| 105 | */ | 104 | */ |
| 106 | 105 | ||
| 107 | if (!strncmp(chld_out.line[0], "unknown option", 14)) { | 106 | if (!strncmp(chld_out.line[0], "unknown option", strlen("unknown option"))) { |
| 108 | printf(_("CRITICAL - Host type parameter incorrect!\n")); | 107 | printf(_("CRITICAL - Host type parameter incorrect!\n")); |
| 109 | result = STATE_CRITICAL; | 108 | result = STATE_CRITICAL; |
| 110 | return result; | 109 | exit(result); |
| 111 | } | 110 | } |
| 112 | 111 | ||
| 113 | p = (char *)strtok(chld_out.line[0], QSTAT_DATA_DELIMITER); | 112 | char *ret[QSTAT_MAX_RETURN_ARGS]; |
| 114 | while (p != NULL) { | 113 | size_t i = 0; |
| 115 | ret[i] = p; | 114 | char *sequence = strtok(chld_out.line[0], QSTAT_DATA_DELIMITER); |
| 116 | p = (char *)strtok(NULL, QSTAT_DATA_DELIMITER); | 115 | while (sequence != NULL) { |
| 116 | ret[i] = sequence; | ||
| 117 | sequence = strtok(NULL, QSTAT_DATA_DELIMITER); | ||
| 117 | i++; | 118 | i++; |
| 118 | if (i >= QSTAT_MAX_RETURN_ARGS) | 119 | if (i >= QSTAT_MAX_RETURN_ARGS) { |
| 119 | break; | 120 | break; |
| 121 | } | ||
| 120 | } | 122 | } |
| 121 | 123 | ||
| 122 | if (strstr(ret[2], QSTAT_HOST_ERROR)) { | 124 | if (strstr(ret[2], QSTAT_HOST_ERROR)) { |
| @@ -129,52 +131,66 @@ int main(int argc, char **argv) { | |||
| 129 | printf(_("CRITICAL - Game server timeout\n")); | 131 | printf(_("CRITICAL - Game server timeout\n")); |
| 130 | result = STATE_CRITICAL; | 132 | result = STATE_CRITICAL; |
| 131 | } else { | 133 | } else { |
| 132 | printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[qstat_game_players], ret[qstat_game_players_max], ret[qstat_game_field], | 134 | printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players], |
| 133 | ret[qstat_map_field], ret[qstat_ping_field], | 135 | ret[config.qstat_game_players_max], ret[config.qstat_game_field], |
| 134 | perfdata("players", atol(ret[qstat_game_players]), "", false, 0, false, 0, true, 0, true, atol(ret[qstat_game_players_max])), | 136 | ret[config.qstat_map_field], ret[config.qstat_ping_field], |
| 135 | fperfdata("ping", strtod(ret[qstat_ping_field], NULL), "", false, 0, false, 0, true, 0, false, 0)); | 137 | perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0, |
| 138 | true, 0, true, atol(ret[config.qstat_game_players_max])), | ||
| 139 | fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0, | ||
| 140 | true, 0, false, 0)); | ||
| 136 | } | 141 | } |
| 137 | 142 | ||
| 138 | return result; | 143 | exit(result); |
| 139 | } | 144 | } |
| 140 | 145 | ||
| 141 | int process_arguments(int argc, char **argv) { | 146 | #define players_field_index 129 |
| 142 | int c; | 147 | #define max_players_field_index 130 |
| 148 | |||
| 149 | check_game_config_wrapper process_arguments(int argc, char **argv) { | ||
| 150 | static struct option long_opts[] = { | ||
| 151 | {"help", no_argument, 0, 'h'}, | ||
| 152 | {"version", no_argument, 0, 'V'}, | ||
| 153 | {"verbose", no_argument, 0, 'v'}, | ||
| 154 | {"timeout", required_argument, 0, 't'}, | ||
| 155 | {"hostname", required_argument, 0, 'H'}, | ||
| 156 | {"port", required_argument, 0, 'P'}, | ||
| 157 | {"game-type", required_argument, 0, 'G'}, | ||
| 158 | {"map-field", required_argument, 0, 'm'}, | ||
| 159 | {"ping-field", required_argument, 0, 'p'}, | ||
| 160 | {"game-field", required_argument, 0, 'g'}, | ||
| 161 | {"players-field", required_argument, 0, players_field_index}, | ||
| 162 | {"max-players-field", required_argument, 0, max_players_field_index}, | ||
| 163 | {0, 0, 0, 0}}; | ||
| 164 | |||
| 165 | check_game_config_wrapper result = { | ||
| 166 | .config = check_game_config_init(), | ||
| 167 | .errorcode = OK, | ||
| 168 | }; | ||
| 169 | |||
| 170 | if (argc < 2) { | ||
| 171 | result.errorcode = ERROR; | ||
| 172 | return result; | ||
| 173 | } | ||
| 143 | 174 | ||
| 144 | int opt_index = 0; | 175 | for (int option_counter = 1; option_counter < argc; option_counter++) { |
| 145 | static struct option long_opts[] = {{"help", no_argument, 0, 'h'}, | 176 | if (strcmp("-mf", argv[option_counter]) == 0) { |
| 146 | {"version", no_argument, 0, 'V'}, | 177 | strcpy(argv[option_counter], "-m"); |
| 147 | {"verbose", no_argument, 0, 'v'}, | 178 | } else if (strcmp("-pf", argv[option_counter]) == 0) { |
| 148 | {"timeout", required_argument, 0, 't'}, | 179 | strcpy(argv[option_counter], "-p"); |
| 149 | {"hostname", required_argument, 0, 'H'}, | 180 | } else if (strcmp("-gf", argv[option_counter]) == 0) { |
| 150 | {"port", required_argument, 0, 'P'}, | 181 | strcpy(argv[option_counter], "-g"); |
| 151 | {"game-type", required_argument, 0, 'G'}, | 182 | } |
| 152 | {"map-field", required_argument, 0, 'm'}, | ||
| 153 | {"ping-field", required_argument, 0, 'p'}, | ||
| 154 | {"game-field", required_argument, 0, 'g'}, | ||
| 155 | {"players-field", required_argument, 0, 129}, | ||
| 156 | {"max-players-field", required_argument, 0, 130}, | ||
| 157 | {0, 0, 0, 0}}; | ||
| 158 | |||
| 159 | if (argc < 2) | ||
| 160 | return ERROR; | ||
| 161 | |||
| 162 | for (c = 1; c < argc; c++) { | ||
| 163 | if (strcmp("-mf", argv[c]) == 0) | ||
| 164 | strcpy(argv[c], "-m"); | ||
| 165 | else if (strcmp("-pf", argv[c]) == 0) | ||
| 166 | strcpy(argv[c], "-p"); | ||
| 167 | else if (strcmp("-gf", argv[c]) == 0) | ||
| 168 | strcpy(argv[c], "-g"); | ||
| 169 | } | 183 | } |
| 170 | 184 | ||
| 171 | while (1) { | 185 | int opt_index = 0; |
| 172 | c = getopt_long(argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index); | 186 | while (true) { |
| 187 | int option_index = getopt_long(argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index); | ||
| 173 | 188 | ||
| 174 | if (c == -1 || c == EOF) | 189 | if (option_index == -1 || option_index == EOF) { |
| 175 | break; | 190 | break; |
| 191 | } | ||
| 176 | 192 | ||
| 177 | switch (c) { | 193 | switch (option_index) { |
| 178 | case 'h': /* help */ | 194 | case 'h': /* help */ |
| 179 | print_help(); | 195 | print_help(); |
| 180 | exit(STATE_UNKNOWN); | 196 | exit(STATE_UNKNOWN); |
| @@ -188,79 +204,80 @@ int process_arguments(int argc, char **argv) { | |||
| 188 | timeout_interval = atoi(optarg); | 204 | timeout_interval = atoi(optarg); |
| 189 | break; | 205 | break; |
| 190 | case 'H': /* hostname */ | 206 | case 'H': /* hostname */ |
| 191 | if (strlen(optarg) >= MAX_HOST_ADDRESS_LENGTH) | 207 | if (strlen(optarg) >= MAX_HOST_ADDRESS_LENGTH) { |
| 192 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 208 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 193 | server_ip = optarg; | 209 | } |
| 210 | result.config.server_ip = optarg; | ||
| 194 | break; | 211 | break; |
| 195 | case 'P': /* port */ | 212 | case 'P': /* port */ |
| 196 | port = atoi(optarg); | 213 | result.config.port = atoi(optarg); |
| 197 | break; | 214 | break; |
| 198 | case 'G': /* hostname */ | 215 | case 'G': /* hostname */ |
| 199 | if (strlen(optarg) >= MAX_INPUT_BUFFER) | 216 | if (strlen(optarg) >= MAX_INPUT_BUFFER) { |
| 200 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); | 217 | die(STATE_UNKNOWN, _("Input buffer overflow\n")); |
| 201 | game_type = optarg; | 218 | } |
| 219 | result.config.game_type = optarg; | ||
| 202 | break; | 220 | break; |
| 203 | case 'p': /* index of ping field */ | 221 | case 'p': /* index of ping field */ |
| 204 | qstat_ping_field = atoi(optarg); | 222 | result.config.qstat_ping_field = atoi(optarg); |
| 205 | if (qstat_ping_field < 0 || qstat_ping_field > QSTAT_MAX_RETURN_ARGS) | 223 | if (result.config.qstat_ping_field < 0 || |
| 206 | return ERROR; | 224 | result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) { |
| 225 | result.errorcode = ERROR; | ||
| 226 | return result; | ||
| 227 | } | ||
| 207 | break; | 228 | break; |
| 208 | case 'm': /* index on map field */ | 229 | case 'm': /* index on map field */ |
| 209 | qstat_map_field = atoi(optarg); | 230 | result.config.qstat_map_field = atoi(optarg); |
| 210 | if (qstat_map_field < 0 || qstat_map_field > QSTAT_MAX_RETURN_ARGS) | 231 | if (result.config.qstat_map_field < 0 || |
| 211 | return ERROR; | 232 | result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) { |
| 233 | result.errorcode = ERROR; | ||
| 234 | return result; | ||
| 235 | } | ||
| 212 | break; | 236 | break; |
| 213 | case 'g': /* index of game field */ | 237 | case 'g': /* index of game field */ |
| 214 | qstat_game_field = atoi(optarg); | 238 | result.config.qstat_game_field = atoi(optarg); |
| 215 | if (qstat_game_field < 0 || qstat_game_field > QSTAT_MAX_RETURN_ARGS) | 239 | if (result.config.qstat_game_field < 0 || |
| 216 | return ERROR; | 240 | result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) { |
| 241 | result.errorcode = ERROR; | ||
| 242 | return result; | ||
| 243 | } | ||
| 217 | break; | 244 | break; |
| 218 | case 129: /* index of player count field */ | 245 | case players_field_index: /* index of player count field */ |
| 219 | qstat_game_players = atoi(optarg); | 246 | result.config.qstat_game_players = atoi(optarg); |
| 220 | if (qstat_game_players_max == 0) | 247 | if (result.config.qstat_game_players_max == 0) { |
| 221 | qstat_game_players_max = qstat_game_players - 1; | 248 | result.config.qstat_game_players_max = result.config.qstat_game_players - 1; |
| 222 | if (qstat_game_players < 0 || qstat_game_players > QSTAT_MAX_RETURN_ARGS) | 249 | } |
| 223 | return ERROR; | 250 | if (result.config.qstat_game_players < 0 || |
| 251 | result.config.qstat_game_players > QSTAT_MAX_RETURN_ARGS) { | ||
| 252 | result.errorcode = ERROR; | ||
| 253 | return result; | ||
| 254 | } | ||
| 224 | break; | 255 | break; |
| 225 | case 130: /* index of max players field */ | 256 | case max_players_field_index: /* index of max players field */ |
| 226 | qstat_game_players_max = atoi(optarg); | 257 | result.config.qstat_game_players_max = atoi(optarg); |
| 227 | if (qstat_game_players_max < 0 || qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) | 258 | if (result.config.qstat_game_players_max < 0 || |
| 228 | return ERROR; | 259 | result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) { |
| 260 | result.errorcode = ERROR; | ||
| 261 | return result; | ||
| 262 | } | ||
| 229 | break; | 263 | break; |
| 230 | default: /* args not parsable */ | 264 | default: /* args not parsable */ |
| 231 | usage5(); | 265 | usage5(); |
| 232 | } | 266 | } |
| 233 | } | 267 | } |
| 234 | 268 | ||
| 235 | c = optind; | 269 | int option_counter = optind; |
| 236 | /* first option is the game type */ | 270 | /* first option is the game type */ |
| 237 | if (!game_type && c < argc) | 271 | if (!result.config.game_type && option_counter < argc) { |
| 238 | game_type = strdup(argv[c++]); | 272 | result.config.game_type = strdup(argv[option_counter++]); |
| 273 | } | ||
| 239 | 274 | ||
| 240 | /* Second option is the server name */ | 275 | /* Second option is the server name */ |
| 241 | if (!server_ip && c < argc) | 276 | if (!result.config.server_ip && option_counter < argc) { |
| 242 | server_ip = strdup(argv[c++]); | 277 | result.config.server_ip = strdup(argv[option_counter++]); |
| 243 | 278 | } | |
| 244 | return validate_arguments(); | ||
| 245 | } | ||
| 246 | |||
| 247 | int validate_arguments(void) { | ||
| 248 | if (qstat_game_players_max < 0) | ||
| 249 | qstat_game_players_max = 4; | ||
| 250 | |||
| 251 | if (qstat_game_players < 0) | ||
| 252 | qstat_game_players = 5; | ||
| 253 | |||
| 254 | if (qstat_game_field < 0) | ||
| 255 | qstat_game_field = 2; | ||
| 256 | |||
| 257 | if (qstat_map_field < 0) | ||
| 258 | qstat_map_field = 3; | ||
| 259 | |||
| 260 | if (qstat_ping_field < 0) | ||
| 261 | qstat_ping_field = 5; | ||
| 262 | 279 | ||
| 263 | return OK; | 280 | return result; |
| 264 | } | 281 | } |
| 265 | 282 | ||
| 266 | void print_help(void) { | 283 | void print_help(void) { |
| @@ -277,22 +294,25 @@ void print_help(void) { | |||
| 277 | 294 | ||
| 278 | printf(UT_HELP_VRSN); | 295 | printf(UT_HELP_VRSN); |
| 279 | printf(UT_EXTRA_OPTS); | 296 | printf(UT_EXTRA_OPTS); |
| 280 | 297 | printf(" -H, --hostname=ADDRESS\n" | |
| 281 | printf(" %s\n", "-p"); | 298 | " Host name, IP Address, or unix socket (must be an absolute path)\n"); |
| 282 | printf(" %s\n", _("Optional port of which to connect")); | 299 | printf(" %s\n", "-P"); |
| 283 | printf(" %s\n", "gf"); | 300 | printf(" %s\n", _("Optional port to connect to")); |
| 301 | printf(" %s\n", "-g"); | ||
| 284 | printf(" %s\n", _("Field number in raw qstat output that contains game name")); | 302 | printf(" %s\n", _("Field number in raw qstat output that contains game name")); |
| 285 | printf(" %s\n", "-mf"); | 303 | printf(" %s\n", "-m"); |
| 286 | printf(" %s\n", _("Field number in raw qstat output that contains map name")); | 304 | printf(" %s\n", _("Field number in raw qstat output that contains map name")); |
| 287 | printf(" %s\n", "-pf"); | 305 | printf(" %s\n", "-p"); |
| 288 | printf(" %s\n", _("Field number in raw qstat output that contains ping time")); | 306 | printf(" %s\n", _("Field number in raw qstat output that contains ping time")); |
| 289 | 307 | ||
| 290 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 308 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 291 | 309 | ||
| 292 | printf("\n"); | 310 | printf("\n"); |
| 293 | printf("%s\n", _("Notes:")); | 311 | printf("%s\n", _("Notes:")); |
| 294 | printf(" %s\n", _("This plugin uses the 'qstat' command, the popular game server status query tool.")); | 312 | printf(" %s\n", |
| 295 | printf(" %s\n", _("If you don't have the package installed, you will need to download it from")); | 313 | _("This plugin uses the 'qstat' command, the popular game server status query tool.")); |
| 314 | printf(" %s\n", | ||
| 315 | _("If you don't have the package installed, you will need to download it from")); | ||
| 296 | printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin.")); | 316 | printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin.")); |
| 297 | 317 | ||
| 298 | printf(UT_SUPPORT); | 318 | printf(UT_SUPPORT); |
| @@ -300,7 +320,8 @@ void print_help(void) { | |||
| 300 | 320 | ||
| 301 | void print_usage(void) { | 321 | void print_usage(void) { |
| 302 | printf("%s\n", _("Usage:")); | 322 | printf("%s\n", _("Usage:")); |
| 303 | printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G game-time] [-H hostname] <game> " | 323 | printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G " |
| 324 | "game-time] [-H hostname] <game> " | ||
| 304 | "<ip_address>\n", | 325 | "<ip_address>\n", |
| 305 | progname); | 326 | progname); |
| 306 | } | 327 | } |
diff --git a/plugins/check_game.d/config.h b/plugins/check_game.d/config.h new file mode 100644 index 00000000..c95a1ced --- /dev/null +++ b/plugins/check_game.d/config.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #pragma once | ||
| 2 | #include "../../config.h" | ||
| 3 | #include <stddef.h> | ||
| 4 | |||
| 5 | typedef struct { | ||
| 6 | char *server_ip; | ||
| 7 | char *game_type; | ||
| 8 | int port; | ||
| 9 | |||
| 10 | int qstat_game_players_max; | ||
| 11 | int qstat_game_players; | ||
| 12 | int qstat_game_field; | ||
| 13 | int qstat_map_field; | ||
| 14 | int qstat_ping_field; | ||
| 15 | } check_game_config; | ||
| 16 | |||
| 17 | check_game_config check_game_config_init() { | ||
| 18 | check_game_config tmp = { | ||
| 19 | .server_ip = NULL, | ||
| 20 | .game_type = NULL, | ||
| 21 | .port = 0, | ||
| 22 | |||
| 23 | .qstat_game_players_max = 4, | ||
| 24 | .qstat_game_players = 5, | ||
| 25 | .qstat_map_field = 3, | ||
| 26 | .qstat_game_field = 2, | ||
| 27 | .qstat_ping_field = 5, | ||
| 28 | }; | ||
| 29 | return tmp; | ||
| 30 | } | ||
diff --git a/plugins/check_hpjd.c b/plugins/check_hpjd.c index b39bccff..9907abc5 100644 --- a/plugins/check_hpjd.c +++ b/plugins/check_hpjd.c | |||
| @@ -37,9 +37,10 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 37 | #include "popen.h" | 37 | #include "popen.h" |
| 38 | #include "utils.h" | 38 | #include "utils.h" |
| 39 | #include "netutils.h" | 39 | #include "netutils.h" |
| 40 | #include "states.h" | ||
| 41 | #include "check_hpjd.d/config.h" | ||
| 40 | 42 | ||
| 41 | #define DEFAULT_COMMUNITY "public" | 43 | #define DEFAULT_COMMUNITY "public" |
| 42 | #define DEFAULT_PORT "161" | ||
| 43 | 44 | ||
| 44 | #define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1" | 45 | #define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1" |
| 45 | #define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2" | 46 | #define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2" |
| @@ -57,39 +58,15 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 57 | #define ONLINE 0 | 58 | #define ONLINE 0 |
| 58 | #define OFFLINE 1 | 59 | #define OFFLINE 1 |
| 59 | 60 | ||
| 60 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 61 | typedef struct { |
| 61 | static int validate_arguments(void); | 62 | int errorcode; |
| 63 | check_hpjd_config config; | ||
| 64 | } check_hpjd_config_wrapper; | ||
| 65 | static check_hpjd_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 62 | static void print_help(void); | 66 | static void print_help(void); |
| 63 | void print_usage(void); | 67 | void print_usage(void); |
| 64 | 68 | ||
| 65 | static char *community = NULL; | ||
| 66 | static char *address = NULL; | ||
| 67 | static unsigned int port = 0; | ||
| 68 | static int check_paper_out = 1; | ||
| 69 | |||
| 70 | int main(int argc, char **argv) { | 69 | int main(int argc, char **argv) { |
| 71 | char command_line[1024]; | ||
| 72 | int result = STATE_UNKNOWN; | ||
| 73 | int line; | ||
| 74 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 75 | char query_string[512]; | ||
| 76 | char *errmsg; | ||
| 77 | char *temp_buffer; | ||
| 78 | int line_status = ONLINE; | ||
| 79 | int paper_status = 0; | ||
| 80 | int intervention_required = 0; | ||
| 81 | int peripheral_error = 0; | ||
| 82 | int paper_jam = 0; | ||
| 83 | int paper_out = 0; | ||
| 84 | int toner_low = 0; | ||
| 85 | int page_punt = 0; | ||
| 86 | int memory_out = 0; | ||
| 87 | int door_open = 0; | ||
| 88 | int paper_output = 0; | ||
| 89 | char display_message[MAX_INPUT_BUFFER]; | ||
| 90 | |||
| 91 | errmsg = malloc(MAX_INPUT_BUFFER); | ||
| 92 | |||
| 93 | setlocale(LC_ALL, ""); | 70 | setlocale(LC_ALL, ""); |
| 94 | bindtextdomain(PACKAGE, LOCALEDIR); | 71 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 95 | textdomain(PACKAGE); | 72 | textdomain(PACKAGE); |
| @@ -97,17 +74,27 @@ int main(int argc, char **argv) { | |||
| 97 | /* Parse extra opts if any */ | 74 | /* Parse extra opts if any */ |
| 98 | argv = np_extra_opts(&argc, argv, progname); | 75 | argv = np_extra_opts(&argc, argv, progname); |
| 99 | 76 | ||
| 100 | if (process_arguments(argc, argv) == ERROR) | 77 | check_hpjd_config_wrapper tmp_config = process_arguments(argc, argv); |
| 78 | |||
| 79 | if (tmp_config.errorcode == ERROR) { | ||
| 101 | usage4(_("Could not parse arguments")); | 80 | usage4(_("Could not parse arguments")); |
| 81 | } | ||
| 82 | |||
| 83 | const check_hpjd_config config = tmp_config.config; | ||
| 102 | 84 | ||
| 85 | char query_string[512]; | ||
| 103 | /* removed ' 2>1' at end of command 10/27/1999 - EG */ | 86 | /* removed ' 2>1' at end of command 10/27/1999 - EG */ |
| 104 | /* create the query string */ | 87 | /* create the query string */ |
| 105 | sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0", HPJD_LINE_STATUS, HPJD_PAPER_STATUS, | 88 | sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0", |
| 106 | HPJD_INTERVENTION_REQUIRED, HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW, | 89 | HPJD_LINE_STATUS, HPJD_PAPER_STATUS, HPJD_INTERVENTION_REQUIRED, |
| 107 | HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, HPJD_GD_STATUS_DISPLAY); | 90 | HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW, |
| 91 | HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, | ||
| 92 | HPJD_GD_STATUS_DISPLAY); | ||
| 108 | 93 | ||
| 109 | /* get the command to run */ | 94 | /* get the command to run */ |
| 110 | sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, community, address, port, query_string); | 95 | char command_line[1024]; |
| 96 | sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, config.community, | ||
| 97 | config.address, config.port, query_string); | ||
| 111 | 98 | ||
| 112 | /* run the command */ | 99 | /* run the command */ |
| 113 | child_process = spopen(command_line); | 100 | child_process = spopen(command_line); |
| @@ -121,29 +108,41 @@ int main(int argc, char **argv) { | |||
| 121 | printf(_("Could not open stderr for %s\n"), command_line); | 108 | printf(_("Could not open stderr for %s\n"), command_line); |
| 122 | } | 109 | } |
| 123 | 110 | ||
| 124 | result = STATE_OK; | 111 | mp_state_enum result = STATE_OK; |
| 125 | 112 | ||
| 126 | line = 0; | 113 | int line_status = ONLINE; |
| 127 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { | 114 | int paper_status = 0; |
| 115 | int intervention_required = 0; | ||
| 116 | int peripheral_error = 0; | ||
| 117 | int paper_jam = 0; | ||
| 118 | int paper_out = 0; | ||
| 119 | int toner_low = 0; | ||
| 120 | int page_punt = 0; | ||
| 121 | int memory_out = 0; | ||
| 122 | int door_open = 0; | ||
| 123 | int paper_output = 0; | ||
| 124 | char display_message[MAX_INPUT_BUFFER]; | ||
| 128 | 125 | ||
| 126 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 127 | char *errmsg = malloc(MAX_INPUT_BUFFER); | ||
| 128 | int line = 0; | ||
| 129 | |||
| 130 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { | ||
| 129 | /* strip the newline character from the end of the input */ | 131 | /* strip the newline character from the end of the input */ |
| 130 | if (input_buffer[strlen(input_buffer) - 1] == '\n') | 132 | if (input_buffer[strlen(input_buffer) - 1] == '\n') { |
| 131 | input_buffer[strlen(input_buffer) - 1] = 0; | 133 | input_buffer[strlen(input_buffer) - 1] = 0; |
| 134 | } | ||
| 132 | 135 | ||
| 133 | line++; | 136 | line++; |
| 134 | 137 | ||
| 135 | temp_buffer = strtok(input_buffer, "="); | 138 | char *temp_buffer = strtok(input_buffer, "="); |
| 136 | temp_buffer = strtok(NULL, "="); | 139 | temp_buffer = strtok(NULL, "="); |
| 137 | 140 | ||
| 138 | if (temp_buffer == NULL && line < 13) { | 141 | if (temp_buffer == NULL && line < 13) { |
| 139 | |||
| 140 | result = STATE_UNKNOWN; | 142 | result = STATE_UNKNOWN; |
| 141 | strcpy(errmsg, input_buffer); | 143 | strcpy(errmsg, input_buffer); |
| 142 | |||
| 143 | } else { | 144 | } else { |
| 144 | |||
| 145 | switch (line) { | 145 | switch (line) { |
| 146 | |||
| 147 | case 1: /* 1st line should contain the line status */ | 146 | case 1: /* 1st line should contain the line status */ |
| 148 | line_status = atoi(temp_buffer); | 147 | line_status = atoi(temp_buffer); |
| 149 | break; | 148 | break; |
| @@ -181,21 +180,24 @@ int main(int argc, char **argv) { | |||
| 181 | strcpy(display_message, temp_buffer + 1); | 180 | strcpy(display_message, temp_buffer + 1); |
| 182 | break; | 181 | break; |
| 183 | default: /* fold multiline message */ | 182 | default: /* fold multiline message */ |
| 184 | strncat(display_message, input_buffer, sizeof(display_message) - strlen(display_message) - 1); | 183 | strncat(display_message, input_buffer, |
| 184 | sizeof(display_message) - strlen(display_message) - 1); | ||
| 185 | } | 185 | } |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | /* break out of the read loop if we encounter an error */ | 188 | /* break out of the read loop if we encounter an error */ |
| 189 | if (result != STATE_OK) | 189 | if (result != STATE_OK) { |
| 190 | break; | 190 | break; |
| 191 | } | ||
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | /* WARNING if output found on stderr */ | 194 | /* WARNING if output found on stderr */ |
| 194 | if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { | 195 | if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { |
| 195 | result = max_state(result, STATE_WARNING); | 196 | result = max_state(result, STATE_WARNING); |
| 196 | /* remove CRLF */ | 197 | /* remove CRLF */ |
| 197 | if (input_buffer[strlen(input_buffer) - 1] == '\n') | 198 | if (input_buffer[strlen(input_buffer) - 1] == '\n') { |
| 198 | input_buffer[strlen(input_buffer) - 1] = 0; | 199 | input_buffer[strlen(input_buffer) - 1] = 0; |
| 200 | } | ||
| 199 | sprintf(errmsg, "%s", input_buffer); | 201 | sprintf(errmsg, "%s", input_buffer); |
| 200 | } | 202 | } |
| 201 | 203 | ||
| @@ -203,15 +205,15 @@ int main(int argc, char **argv) { | |||
| 203 | (void)fclose(child_stderr); | 205 | (void)fclose(child_stderr); |
| 204 | 206 | ||
| 205 | /* close the pipe */ | 207 | /* close the pipe */ |
| 206 | if (spclose(child_process)) | 208 | if (spclose(child_process)) { |
| 207 | result = max_state(result, STATE_WARNING); | 209 | result = max_state(result, STATE_WARNING); |
| 210 | } | ||
| 208 | 211 | ||
| 209 | /* if there wasn't any output, display an error */ | 212 | /* if there wasn't any output, display an error */ |
| 210 | if (line == 0) { | 213 | if (line == 0) { |
| 211 | |||
| 212 | /* might not be the problem, but most likely is. */ | 214 | /* might not be the problem, but most likely is. */ |
| 213 | result = STATE_UNKNOWN; | 215 | result = STATE_UNKNOWN; |
| 214 | xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, address); | 216 | xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, config.address); |
| 215 | } | 217 | } |
| 216 | 218 | ||
| 217 | /* if we had no read errors, check the printer status results... */ | 219 | /* if we had no read errors, check the printer status results... */ |
| @@ -221,8 +223,9 @@ int main(int argc, char **argv) { | |||
| 221 | result = STATE_WARNING; | 223 | result = STATE_WARNING; |
| 222 | strcpy(errmsg, _("Paper Jam")); | 224 | strcpy(errmsg, _("Paper Jam")); |
| 223 | } else if (paper_out) { | 225 | } else if (paper_out) { |
| 224 | if (check_paper_out) | 226 | if (config.check_paper_out) { |
| 225 | result = STATE_WARNING; | 227 | result = STATE_WARNING; |
| 228 | } | ||
| 226 | strcpy(errmsg, _("Out of Paper")); | 229 | strcpy(errmsg, _("Out of Paper")); |
| 227 | } else if (line_status == OFFLINE) { | 230 | } else if (line_status == OFFLINE) { |
| 228 | if (strcmp(errmsg, "POWERSAVE ON") != 0) { | 231 | if (strcmp(errmsg, "POWERSAVE ON") != 0) { |
| @@ -256,29 +259,23 @@ int main(int argc, char **argv) { | |||
| 256 | } | 259 | } |
| 257 | } | 260 | } |
| 258 | 261 | ||
| 259 | if (result == STATE_OK) | 262 | if (result == STATE_OK) { |
| 260 | printf(_("Printer ok - (%s)\n"), display_message); | 263 | printf(_("Printer ok - (%s)\n"), display_message); |
| 261 | 264 | } else if (result == STATE_UNKNOWN) { | |
| 262 | else if (result == STATE_UNKNOWN) { | ||
| 263 | |||
| 264 | printf("%s\n", errmsg); | 265 | printf("%s\n", errmsg); |
| 265 | |||
| 266 | /* if printer could not be reached, escalate to critical */ | 266 | /* if printer could not be reached, escalate to critical */ |
| 267 | if (strstr(errmsg, "Timeout")) | 267 | if (strstr(errmsg, "Timeout")) { |
| 268 | result = STATE_CRITICAL; | 268 | result = STATE_CRITICAL; |
| 269 | } | 269 | } |
| 270 | 270 | } else if (result == STATE_WARNING) { | |
| 271 | else if (result == STATE_WARNING) | ||
| 272 | printf("%s (%s)\n", errmsg, display_message); | 271 | printf("%s (%s)\n", errmsg, display_message); |
| 272 | } | ||
| 273 | 273 | ||
| 274 | return result; | 274 | exit(result); |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | /* process command-line arguments */ | 277 | /* process command-line arguments */ |
| 278 | int process_arguments(int argc, char **argv) { | 278 | check_hpjd_config_wrapper process_arguments(int argc, char **argv) { |
| 279 | int c; | ||
| 280 | |||
| 281 | int option = 0; | ||
| 282 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, | 279 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 283 | {"community", required_argument, 0, 'C'}, | 280 | {"community", required_argument, 0, 'C'}, |
| 284 | /* {"critical", required_argument,0,'c'}, */ | 281 | /* {"critical", required_argument,0,'c'}, */ |
| @@ -288,34 +285,44 @@ int process_arguments(int argc, char **argv) { | |||
| 288 | {"help", no_argument, 0, 'h'}, | 285 | {"help", no_argument, 0, 'h'}, |
| 289 | {0, 0, 0, 0}}; | 286 | {0, 0, 0, 0}}; |
| 290 | 287 | ||
| 291 | if (argc < 2) | 288 | check_hpjd_config_wrapper result = { |
| 292 | return ERROR; | 289 | .errorcode = OK, |
| 290 | .config = check_hpjd_config_init(), | ||
| 291 | }; | ||
| 292 | |||
| 293 | if (argc < 2) { | ||
| 294 | result.errorcode = ERROR; | ||
| 295 | return result; | ||
| 296 | } | ||
| 293 | 297 | ||
| 294 | while (1) { | 298 | int option = 0; |
| 295 | c = getopt_long(argc, argv, "+hVH:C:p:D", longopts, &option); | 299 | while (true) { |
| 300 | int option_index = getopt_long(argc, argv, "+hVH:C:p:D", longopts, &option); | ||
| 296 | 301 | ||
| 297 | if (c == -1 || c == EOF || c == 1) | 302 | if (option_index == -1 || option_index == EOF || option_index == 1) { |
| 298 | break; | 303 | break; |
| 304 | } | ||
| 299 | 305 | ||
| 300 | switch (c) { | 306 | switch (option_index) { |
| 301 | case 'H': /* hostname */ | 307 | case 'H': /* hostname */ |
| 302 | if (is_host(optarg)) { | 308 | if (is_host(optarg)) { |
| 303 | address = strscpy(address, optarg); | 309 | result.config.address = strscpy(result.config.address, optarg); |
| 304 | } else { | 310 | } else { |
| 305 | usage2(_("Invalid hostname/address"), optarg); | 311 | usage2(_("Invalid hostname/address"), optarg); |
| 306 | } | 312 | } |
| 307 | break; | 313 | break; |
| 308 | case 'C': /* community */ | 314 | case 'C': /* community */ |
| 309 | community = strscpy(community, optarg); | 315 | result.config.community = strscpy(result.config.community, optarg); |
| 310 | break; | 316 | break; |
| 311 | case 'p': | 317 | case 'p': |
| 312 | if (!is_intpos(optarg)) | 318 | if (!is_intpos(optarg)) { |
| 313 | usage2(_("Port must be a positive short integer"), optarg); | 319 | usage2(_("Port must be a positive short integer"), optarg); |
| 314 | else | 320 | } else { |
| 315 | port = atoi(optarg); | 321 | result.config.port = atoi(optarg); |
| 322 | } | ||
| 316 | break; | 323 | break; |
| 317 | case 'D': /* disable paper out check*/ | 324 | case 'D': /* disable paper out check*/ |
| 318 | check_paper_out = 0; | 325 | result.config.check_paper_out = false; |
| 319 | break; | 326 | break; |
| 320 | case 'V': /* version */ | 327 | case 'V': /* version */ |
| 321 | print_revision(progname, NP_VERSION); | 328 | print_revision(progname, NP_VERSION); |
| @@ -328,31 +335,26 @@ int process_arguments(int argc, char **argv) { | |||
| 328 | } | 335 | } |
| 329 | } | 336 | } |
| 330 | 337 | ||
| 331 | c = optind; | 338 | int c = optind; |
| 332 | if (address == NULL) { | 339 | if (result.config.address == NULL) { |
| 333 | if (is_host(argv[c])) { | 340 | if (is_host(argv[c])) { |
| 334 | address = argv[c++]; | 341 | result.config.address = argv[c++]; |
| 335 | } else { | 342 | } else { |
| 336 | usage2(_("Invalid hostname/address"), argv[c]); | 343 | usage2(_("Invalid hostname/address"), argv[c]); |
| 337 | } | 344 | } |
| 338 | } | 345 | } |
| 339 | 346 | ||
| 340 | if (community == NULL) { | 347 | if (result.config.community == NULL) { |
| 341 | if (argv[c] != NULL) | 348 | if (argv[c] != NULL) { |
| 342 | community = argv[c]; | 349 | result.config.community = argv[c]; |
| 343 | else | 350 | } else { |
| 344 | community = strdup(DEFAULT_COMMUNITY); | 351 | result.config.community = strdup(DEFAULT_COMMUNITY); |
| 345 | } | 352 | } |
| 346 | |||
| 347 | if (port == 0) { | ||
| 348 | port = atoi(DEFAULT_PORT); | ||
| 349 | } | 353 | } |
| 350 | 354 | ||
| 351 | return validate_arguments(); | 355 | return result; |
| 352 | } | 356 | } |
| 353 | 357 | ||
| 354 | int validate_arguments(void) { return OK; } | ||
| 355 | |||
| 356 | void print_help(void) { | 358 | void print_help(void) { |
| 357 | print_revision(progname, NP_VERSION); | 359 | print_revision(progname, NP_VERSION); |
| 358 | 360 | ||
diff --git a/plugins/check_hpjd.d/config.h b/plugins/check_hpjd.d/config.h new file mode 100644 index 00000000..e36b7972 --- /dev/null +++ b/plugins/check_hpjd.d/config.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | |||
| 7 | #define DEFAULT_PORT "161" | ||
| 8 | |||
| 9 | typedef struct { | ||
| 10 | char *address; | ||
| 11 | char *community; | ||
| 12 | unsigned int port; | ||
| 13 | bool check_paper_out; | ||
| 14 | |||
| 15 | } check_hpjd_config; | ||
| 16 | |||
| 17 | check_hpjd_config check_hpjd_config_init() { | ||
| 18 | check_hpjd_config tmp = { | ||
| 19 | .address = NULL, | ||
| 20 | .community = NULL, | ||
| 21 | .port = (unsigned int)atoi(DEFAULT_PORT), | ||
| 22 | .check_paper_out = true, | ||
| 23 | }; | ||
| 24 | return tmp; | ||
| 25 | } | ||
diff --git a/plugins/check_http.c b/plugins/check_http.c index baff682a..d264b95d 100644 --- a/plugins/check_http.c +++ b/plugins/check_http.c | |||
| @@ -1,35 +1,35 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_http plugin | 3 | * Monitoring check_http plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team | 6 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the check_http plugin | 10 | * This file contains the check_http plugin |
| 11 | * | 11 | * |
| 12 | * This plugin tests the HTTP service on the specified host. It can test | 12 | * This plugin tests the HTTP service on the specified host. It can test |
| 13 | * normal (http) and secure (https) servers, follow redirects, search for | 13 | * normal (http) and secure (https) servers, follow redirects, search for |
| 14 | * strings and regular expressions, check connection times, and report on | 14 | * strings and regular expressions, check connection times, and report on |
| 15 | * certificate expiration times. | 15 | * certificate expiration times. |
| 16 | * | 16 | * |
| 17 | * | 17 | * |
| 18 | * This program is free software: you can redistribute it and/or modify | 18 | * This program is free software: you can redistribute it and/or modify |
| 19 | * it under the terms of the GNU General Public License as published by | 19 | * it under the terms of the GNU General Public License as published by |
| 20 | * the Free Software Foundation, either version 3 of the License, or | 20 | * the Free Software Foundation, either version 3 of the License, or |
| 21 | * (at your option) any later version. | 21 | * (at your option) any later version. |
| 22 | * | 22 | * |
| 23 | * This program is distributed in the hope that it will be useful, | 23 | * This program is distributed in the hope that it will be useful, |
| 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 26 | * GNU General Public License for more details. | 26 | * GNU General Public License for more details. |
| 27 | * | 27 | * |
| 28 | * You should have received a copy of the GNU General Public License | 28 | * You should have received a copy of the GNU General Public License |
| 29 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 29 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 30 | * | 30 | * |
| 31 | * | 31 | * |
| 32 | *****************************************************************************/ | 32 | *****************************************************************************/ |
| 33 | 33 | ||
| 34 | const char *progname = "check_http"; | 34 | const char *progname = "check_http"; |
| 35 | const char *copyright = "1999-2024"; | 35 | const 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." |
| 52 | enum { | 51 | enum { |
| 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 |
| 61 | bool check_cert = false; | 60 | static bool check_cert = false; |
| 62 | bool continue_after_check_cert = false; | 61 | static bool continue_after_check_cert = false; |
| 63 | int ssl_version = 0; | 62 | static int ssl_version = 0; |
| 64 | int days_till_exp_warn, days_till_exp_crit; | 63 | static int days_till_exp_warn, days_till_exp_crit; |
| 65 | char *randbuff; | 64 | # define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) |
| 66 | X509 *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 */ |
| 73 | bool no_body = false; | 70 | static bool no_body = false; |
| 74 | int maximum_age = -1; | 71 | static int maximum_age = -1; |
| 75 | 72 | ||
| 76 | enum { | 73 | enum { |
| 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" |
| 81 | regex_t preg; | 78 | static regex_t preg; |
| 82 | regmatch_t pmatch[REGS]; | 79 | static regmatch_t pmatch[REGS]; |
| 83 | char regexp[MAX_RE_SIZE]; | 80 | static char regexp[MAX_RE_SIZE]; |
| 84 | char errbuf[MAX_INPUT_BUFFER]; | 81 | static char errbuf[MAX_INPUT_BUFFER]; |
| 85 | int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; | 82 | static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; |
| 86 | int errcode; | 83 | static int errcode; |
| 87 | int invert_regex = 0; | 84 | static int invert_regex = 0; |
| 88 | int state_regex = STATE_CRITICAL; | 85 | static int state_regex = STATE_CRITICAL; |
| 89 | 86 | ||
| 90 | struct timeval tv; | 87 | static struct timeval tv; |
| 91 | struct timeval tv_temp; | 88 | static 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 | ||
| 96 | bool specify_port = false; | 93 | static bool specify_port = false; |
| 97 | int server_port = HTTP_PORT; | 94 | static int server_port = HTTP_PORT; |
| 98 | int virtual_port = 0; | 95 | static int virtual_port = 0; |
| 99 | char server_port_text[6] = ""; | 96 | static char server_type[6] = "http"; |
| 100 | char server_type[6] = "http"; | 97 | static char *server_address; |
| 101 | char *server_address; | 98 | static char *host_name; |
| 102 | char *host_name; | 99 | static int host_name_length; |
| 103 | int host_name_length; | 100 | static char *server_url; |
| 104 | char *server_url; | 101 | static char *user_agent; |
| 105 | char *user_agent; | 102 | static int server_url_length; |
| 106 | int server_url_length; | 103 | static int server_expect_yn = 0; |
| 107 | int server_expect_yn = 0; | 104 | static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; |
| 108 | char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; | 105 | static char header_expect[MAX_INPUT_BUFFER] = ""; |
| 109 | char header_expect[MAX_INPUT_BUFFER] = ""; | 106 | static char string_expect[MAX_INPUT_BUFFER] = ""; |
| 110 | char string_expect[MAX_INPUT_BUFFER] = ""; | 107 | static char *warning_thresholds = NULL; |
| 111 | char *warning_thresholds = NULL; | 108 | static char *critical_thresholds = NULL; |
| 112 | char *critical_thresholds = NULL; | 109 | static thresholds *thlds; |
| 113 | thresholds *thlds; | 110 | static char user_auth[MAX_INPUT_BUFFER] = ""; |
| 114 | char user_auth[MAX_INPUT_BUFFER] = ""; | 111 | static char proxy_auth[MAX_INPUT_BUFFER] = ""; |
| 115 | char proxy_auth[MAX_INPUT_BUFFER] = ""; | 112 | static bool display_html = false; |
| 116 | bool display_html = false; | 113 | static char **http_opt_headers; |
| 117 | char **http_opt_headers; | 114 | static int http_opt_headers_count = 0; |
| 118 | int http_opt_headers_count = 0; | 115 | static int onredirect = STATE_OK; |
| 119 | int onredirect = STATE_OK; | 116 | static int followsticky = STICKY_NONE; |
| 120 | int followsticky = STICKY_NONE; | 117 | static bool use_ssl = false; |
| 121 | bool use_ssl = false; | 118 | static bool use_sni = false; |
| 122 | bool use_sni = false; | 119 | static bool verbose = false; |
| 123 | bool verbose = false; | 120 | static bool show_extended_perfdata = false; |
| 124 | bool show_extended_perfdata = false; | 121 | static bool show_body = false; |
| 125 | bool show_body = false; | 122 | static int sd; |
| 126 | int sd; | 123 | static int min_page_len = 0; |
| 127 | int min_page_len = 0; | 124 | static int max_page_len = 0; |
| 128 | int max_page_len = 0; | 125 | static int redir_depth = 0; |
| 129 | int redir_depth = 0; | 126 | static int max_depth = DEFAULT_MAX_REDIRS; |
| 130 | int max_depth = DEFAULT_MAX_REDIRS; | 127 | static char *http_method; |
| 131 | char *http_method; | 128 | static char *http_method_proxy; |
| 132 | char *http_method_proxy; | 129 | static char *http_post_data; |
| 133 | char *http_post_data; | 130 | static char *http_content_type; |
| 134 | char *http_content_type; | 131 | static char buffer[MAX_INPUT_BUFFER]; |
| 135 | char buffer[MAX_INPUT_BUFFER]; | 132 | static char *client_cert = NULL; |
| 136 | char *client_cert = NULL; | 133 | static char *client_privkey = NULL; |
| 137 | char *client_privkey = NULL; | ||
| 138 | 134 | ||
| 139 | // Forward function declarations | 135 | // Forward function declarations |
| 140 | bool process_arguments (int, char **); | 136 | static bool process_arguments(int /*argc*/, char ** /*argv*/); |
| 141 | int check_http (void); | 137 | static int check_http(void); |
| 142 | void redir (char *pos, char *status_line); | 138 | static void redir(char *pos, char *status_line); |
| 143 | bool server_type_check(const char *type); | 139 | static bool server_type_check(const char *type); |
| 144 | int server_port_check(int ssl_flag); | 140 | static int server_port_check(int ssl_flag); |
| 145 | char *perfd_time (double microsec); | 141 | static char *perfd_time(double elapsed_time); |
| 146 | char *perfd_time_connect (double microsec); | 142 | static char *perfd_time_connect(double elapsed_time_connect); |
| 147 | char *perfd_time_ssl (double microsec); | 143 | static char *perfd_time_ssl(double elapsed_time_ssl); |
| 148 | char *perfd_time_firstbyte (double microsec); | 144 | static char *perfd_time_firstbyte(double elapsed_time_firstbyte); |
| 149 | char *perfd_time_headers (double microsec); | 145 | static char *perfd_time_headers(double elapsed_time_headers); |
| 150 | char *perfd_time_transfer (double microsec); | 146 | static char *perfd_time_transfer(double elapsed_time_transfer); |
| 151 | char *perfd_size (int page_len); | 147 | static char *perfd_size(int page_len); |
| 152 | void print_help (void); | 148 | void print_help(void); |
| 153 | void print_usage (void); | 149 | void print_usage(void); |
| 154 | char *unchunk_content(const char *content); | 150 | static char *unchunk_content(const char *content); |
| 155 | 151 | ||
| 156 | int | 152 | int main(int argc, char **argv) { |
| 157 | main (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 */ |
| 192 | void | 187 | void test_file(char *path) { |
| 193 | test_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 | */ |
| 204 | bool process_arguments (int argc, char **argv) | 198 | bool 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 */ |
| 627 | static int | 653 | static int document_headers_done(char *full_page) { |
| 628 | document_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 | ||
| 644 | static time_t | 670 | static time_t parse_time_string(const char *string) { |
| 645 | parse_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' */ |
| 736 | static int | 744 | static int expected_statuscode(const char *reply, const char *statuscodes) { |
| 737 | expected_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 | ||
| 755 | static int | 764 | static int check_document_dates(const char *headers, char **msg) { |
| 756 | check_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 | ||
| 849 | int | 870 | int get_content_length(const char *headers) { |
| 850 | get_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 | ||
| 902 | char * | 928 | char *prepend_slash(char *path) { |
| 903 | prepend_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 | ||
| 918 | int | 944 | int check_http(void) { |
| 919 | check_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 | */ |
| 1400 | char *unchunk_content(const char *content) { | 1447 | char *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 | ||
| 1497 | void | 1545 | void redir(char *pos, char *status_line) { |
| 1498 | redir (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 | ||
| 1655 | bool | 1694 | if (verbose) { |
| 1656 | server_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 | ||
| 1664 | int | 1703 | bool server_type_check(const char *type) { return (!(bool)strcmp(type, "https")); } |
| 1665 | server_port_check (int ssl_flag) | 1704 | |
| 1666 | { | 1705 | int 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 | ||
| 1673 | char *perfd_time (double elapsed_time) | 1712 | char *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 | ||
| 1681 | char *perfd_time_connect (double elapsed_time_connect) | 1718 | char *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 | ||
| 1686 | char *perfd_time_ssl (double elapsed_time_ssl) | 1723 | char *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 | ||
| 1691 | char *perfd_time_headers (double elapsed_time_headers) | 1728 | char *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 | ||
| 1696 | char *perfd_time_firstbyte (double elapsed_time_firstbyte) | 1733 | char *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 | ||
| 1701 | char *perfd_time_transfer (double elapsed_time_transfer) | 1738 | char *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 | ||
| 1706 | char *perfd_size (int page_len) | 1743 | char *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 | ||
| 1714 | void | 1748 | void print_help(void) { |
| 1715 | print_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 | 1959 | void print_usage(void) { | |
| 1892 | 1960 | printf("%s\n", _("Usage:")); | |
| 1893 | void | 1961 | printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); |
| 1894 | print_usage (void) | 1962 | printf(" [-J <client certificate file>] [-K <private key>]\n"); |
| 1895 | { | 1963 | printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); |
| 1896 | printf ("%s\n", _("Usage:")); | 1964 | printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n"); |
| 1897 | printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname); | 1965 | printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive " |
| 1898 | printf (" [-J <client certificate file>] [-K <private key>]\n"); | 1966 | "regex>]\n"); |
| 1899 | printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); | 1967 | printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); |
| 1900 | printf (" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n"); | 1968 | printf(" [-A string] [-k string] [-S <version>] [--sni]\n"); |
| 1901 | printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); | 1969 | printf(" [-T <content-type>] [-j method]\n"); |
| 1902 | printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); | 1970 | printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname); |
| 1903 | printf (" [-A string] [-k string] [-S <version>] [--sni]\n"); | 1971 | printf(" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n"); |
| 1904 | printf (" [-T <content-type>] [-j method]\n"); | ||
| 1905 | printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname); | ||
| 1906 | printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n"); | ||
| 1907 | } | 1972 | } |
diff --git a/plugins/check_ide_smart.c b/plugins/check_ide_smart.c index 9640ef70..16fe3d01 100644 --- a/plugins/check_ide_smart.c +++ b/plugins/check_ide_smart.c | |||
| @@ -56,7 +56,6 @@ void print_usage(void); | |||
| 56 | # include <sys/device.h> | 56 | # include <sys/device.h> |
| 57 | # include <sys/param.h> | 57 | # include <sys/param.h> |
| 58 | # include <sys/sysctl.h> | 58 | # include <sys/sysctl.h> |
| 59 | # include <sys/videoio.h> /* for __u8 and friends */ | ||
| 60 | # include <sys/scsiio.h> | 59 | # include <sys/scsiio.h> |
| 61 | # include <sys/ataio.h> | 60 | # include <sys/ataio.h> |
| 62 | # include <dev/ata/atareg.h> | 61 | # include <dev/ata/atareg.h> |
| @@ -79,48 +78,47 @@ void print_usage(void); | |||
| 79 | #define UNKNOWN -1 | 78 | #define UNKNOWN -1 |
| 80 | 79 | ||
| 81 | typedef struct threshold_s { | 80 | typedef struct threshold_s { |
| 82 | __u8 id; | 81 | uint8_t id; |
| 83 | __u8 threshold; | 82 | uint8_t threshold; |
| 84 | __u8 reserved[10]; | 83 | uint8_t reserved[10]; |
| 85 | } __attribute__((packed)) threshold_t; | 84 | } __attribute__((packed)) threshold_t; |
| 86 | 85 | ||
| 87 | typedef struct thresholds_s { | 86 | typedef struct thresholds_s { |
| 88 | __u16 revision; | 87 | uint16_t revision; |
| 89 | threshold_t thresholds[NR_ATTRIBUTES]; | 88 | threshold_t thresholds[NR_ATTRIBUTES]; |
| 90 | __u8 reserved[18]; | 89 | uint8_t reserved[18]; |
| 91 | __u8 vendor[131]; | 90 | uint8_t vendor[131]; |
| 92 | __u8 checksum; | 91 | uint8_t checksum; |
| 93 | } __attribute__((packed)) thresholds_t; | 92 | } __attribute__((packed)) thresholds_t; |
| 94 | 93 | ||
| 95 | typedef struct value_s { | 94 | typedef struct value_s { |
| 96 | __u8 id; | 95 | uint8_t id; |
| 97 | __u16 status; | 96 | uint16_t status; |
| 98 | __u8 value; | 97 | uint8_t value; |
| 99 | __u8 vendor[8]; | 98 | uint8_t vendor[8]; |
| 100 | } __attribute__((packed)) value_t; | 99 | } __attribute__((packed)) value_t; |
| 101 | 100 | ||
| 102 | typedef struct values_s { | 101 | typedef struct values_s { |
| 103 | __u16 revision; | 102 | uint16_t revision; |
| 104 | value_t values[NR_ATTRIBUTES]; | 103 | value_t values[NR_ATTRIBUTES]; |
| 105 | __u8 offline_status; | 104 | uint8_t offline_status; |
| 106 | __u8 vendor1; | 105 | uint8_t vendor1; |
| 107 | __u16 offline_timeout; | 106 | uint16_t offline_timeout; |
| 108 | __u8 vendor2; | 107 | uint8_t vendor2; |
| 109 | __u8 offline_capability; | 108 | uint8_t offline_capability; |
| 110 | __u16 smart_capability; | 109 | uint16_t smart_capability; |
| 111 | __u8 reserved[16]; | 110 | uint8_t reserved[16]; |
| 112 | __u8 vendor[125]; | 111 | uint8_t vendor[125]; |
| 113 | __u8 checksum; | 112 | uint8_t checksum; |
| 114 | } __attribute__((packed)) values_t; | 113 | } __attribute__((packed)) values_t; |
| 115 | 114 | ||
| 116 | static struct { | 115 | static struct { |
| 117 | __u8 value; | 116 | uint8_t value; |
| 118 | char *text; | 117 | char *text; |
| 119 | } offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, | 118 | } offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}}; |
| 120 | {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}}; | ||
| 121 | 119 | ||
| 122 | static struct { | 120 | static struct { |
| 123 | __u8 value; | 121 | uint8_t value; |
| 124 | char *text; | 122 | char *text; |
| 125 | } smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"}, | 123 | } smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"}, |
| 126 | {SMART_DISABLE, "SMART_DISABLE"}, | 124 | {SMART_DISABLE, "SMART_DISABLE"}, |
| @@ -140,7 +138,7 @@ static int smart_read_values(int, values_t *); | |||
| 140 | static int nagios(values_t *, thresholds_t *); | 138 | static int nagios(values_t *, thresholds_t *); |
| 141 | static void print_value(value_t *, threshold_t *); | 139 | static void print_value(value_t *, threshold_t *); |
| 142 | static void print_values(values_t *, thresholds_t *); | 140 | static void print_values(values_t *, thresholds_t *); |
| 143 | static int smart_cmd_simple(int, enum SmartCommand, __u8, bool); | 141 | static int smart_cmd_simple(int, enum SmartCommand, uint8_t, bool); |
| 144 | static int smart_read_thresholds(int, thresholds_t *); | 142 | static int smart_read_thresholds(int, thresholds_t *); |
| 145 | static bool verbose = false; | 143 | static bool verbose = false; |
| 146 | 144 | ||
| @@ -175,8 +173,9 @@ int main(int argc, char *argv[]) { | |||
| 175 | 173 | ||
| 176 | o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex); | 174 | o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex); |
| 177 | 175 | ||
| 178 | if (o == -1 || o == EOF || o == 1) | 176 | if (o == -1 || o == EOF || o == 1) { |
| 179 | break; | 177 | break; |
| 178 | } | ||
| 180 | 179 | ||
| 181 | switch (o) { | 180 | switch (o) { |
| 182 | case 'd': | 181 | case 'd': |
| @@ -234,8 +233,9 @@ int main(int argc, char *argv[]) { | |||
| 234 | smart_read_values(fd, &values); | 233 | smart_read_values(fd, &values); |
| 235 | smart_read_thresholds(fd, &thresholds); | 234 | smart_read_thresholds(fd, &thresholds); |
| 236 | retval = nagios(&values, &thresholds); | 235 | retval = nagios(&values, &thresholds); |
| 237 | if (verbose) | 236 | if (verbose) { |
| 238 | print_values(&values, &thresholds); | 237 | print_values(&values, &thresholds); |
| 238 | } | ||
| 239 | 239 | ||
| 240 | close(fd); | 240 | close(fd); |
| 241 | return retval; | 241 | return retval; |
| @@ -254,7 +254,7 @@ char *get_offline_text(int status) { | |||
| 254 | int smart_read_values(int fd, values_t *values) { | 254 | int smart_read_values(int fd, values_t *values) { |
| 255 | #ifdef __linux__ | 255 | #ifdef __linux__ |
| 256 | int e; | 256 | int e; |
| 257 | __u8 args[4 + 512]; | 257 | uint8_t args[4 + 512]; |
| 258 | args[0] = WIN_SMART; | 258 | args[0] = WIN_SMART; |
| 259 | args[1] = 0; | 259 | args[1] = 0; |
| 260 | args[2] = SMART_READ_VALUES; | 260 | args[2] = SMART_READ_VALUES; |
| @@ -282,8 +282,9 @@ int smart_read_values(int fd, values_t *values) { | |||
| 282 | req.cylinder = WDSMART_CYL; | 282 | req.cylinder = WDSMART_CYL; |
| 283 | 283 | ||
| 284 | if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { | 284 | if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { |
| 285 | if (req.retsts != ATACMD_OK) | 285 | if (req.retsts != ATACMD_OK) { |
| 286 | errno = ENODEV; | 286 | errno = ENODEV; |
| 287 | } | ||
| 287 | } | 288 | } |
| 288 | 289 | ||
| 289 | if (errno != 0) { | 290 | if (errno != 0) { |
| @@ -370,22 +371,24 @@ void print_values(values_t *p, thresholds_t *t) { | |||
| 370 | p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : ""); | 371 | p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : ""); |
| 371 | } | 372 | } |
| 372 | 373 | ||
| 373 | int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_error) { | 374 | int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_error) { |
| 374 | int e = STATE_UNKNOWN; | 375 | int e = STATE_UNKNOWN; |
| 375 | #ifdef __linux__ | 376 | #ifdef __linux__ |
| 376 | __u8 args[4]; | 377 | uint8_t args[4]; |
| 377 | args[0] = WIN_SMART; | 378 | args[0] = WIN_SMART; |
| 378 | args[1] = val0; | 379 | args[1] = val0; |
| 379 | args[2] = smart_command[command].value; | 380 | args[2] = smart_command[command].value; |
| 380 | args[3] = 0; | 381 | args[3] = 0; |
| 381 | if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { | 382 | if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { |
| 382 | e = STATE_CRITICAL; | 383 | e = STATE_CRITICAL; |
| 383 | if (show_error) | 384 | if (show_error) { |
| 384 | printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); | 385 | printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); |
| 386 | } | ||
| 385 | } else { | 387 | } else { |
| 386 | e = STATE_OK; | 388 | e = STATE_OK; |
| 387 | if (show_error) | 389 | if (show_error) { |
| 388 | printf(_("OK - Command sent (%s)\n"), smart_command[command].text); | 390 | printf(_("OK - Command sent (%s)\n"), smart_command[command].text); |
| 391 | } | ||
| 389 | } | 392 | } |
| 390 | 393 | ||
| 391 | #endif /* __linux__ */ | 394 | #endif /* __linux__ */ |
| @@ -401,20 +404,24 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err | |||
| 401 | req.sec_count = val0; | 404 | req.sec_count = val0; |
| 402 | 405 | ||
| 403 | if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { | 406 | if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { |
| 404 | if (req.retsts != ATACMD_OK) | 407 | if (req.retsts != ATACMD_OK) { |
| 405 | errno = ENODEV; | 408 | errno = ENODEV; |
| 406 | if (req.cylinder != WDSMART_CYL) | 409 | } |
| 410 | if (req.cylinder != WDSMART_CYL) { | ||
| 407 | errno = ENODEV; | 411 | errno = ENODEV; |
| 412 | } | ||
| 408 | } | 413 | } |
| 409 | 414 | ||
| 410 | if (errno != 0) { | 415 | if (errno != 0) { |
| 411 | e = STATE_CRITICAL; | 416 | e = STATE_CRITICAL; |
| 412 | if (show_error) | 417 | if (show_error) { |
| 413 | printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); | 418 | printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); |
| 419 | } | ||
| 414 | } else { | 420 | } else { |
| 415 | e = STATE_OK; | 421 | e = STATE_OK; |
| 416 | if (show_error) | 422 | if (show_error) { |
| 417 | printf(_("OK - Command sent (%s)\n"), smart_command[command].text); | 423 | printf(_("OK - Command sent (%s)\n"), smart_command[command].text); |
| 424 | } | ||
| 418 | } | 425 | } |
| 419 | 426 | ||
| 420 | #endif /* __NetBSD__ */ | 427 | #endif /* __NetBSD__ */ |
| @@ -424,7 +431,7 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err | |||
| 424 | int smart_read_thresholds(int fd, thresholds_t *thresholds) { | 431 | int smart_read_thresholds(int fd, thresholds_t *thresholds) { |
| 425 | #ifdef __linux__ | 432 | #ifdef __linux__ |
| 426 | int e; | 433 | int e; |
| 427 | __u8 args[4 + 512]; | 434 | uint8_t args[4 + 512]; |
| 428 | args[0] = WIN_SMART; | 435 | args[0] = WIN_SMART; |
| 429 | args[1] = 0; | 436 | args[1] = 0; |
| 430 | args[2] = SMART_READ_THRESHOLDS; | 437 | args[2] = SMART_READ_THRESHOLDS; |
| @@ -452,8 +459,9 @@ int smart_read_thresholds(int fd, thresholds_t *thresholds) { | |||
| 452 | req.cylinder = WDSMART_CYL; | 459 | req.cylinder = WDSMART_CYL; |
| 453 | 460 | ||
| 454 | if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { | 461 | if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { |
| 455 | if (req.retsts != ATACMD_OK) | 462 | if (req.retsts != ATACMD_OK) { |
| 456 | errno = ENODEV; | 463 | errno = ENODEV; |
| 464 | } | ||
| 457 | } | 465 | } |
| 458 | 466 | ||
| 459 | if (errno != 0) { | 467 | if (errno != 0) { |
diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c index 87818da6..1b2e2826 100644 --- a/plugins/check_ldap.c +++ b/plugins/check_ldap.c | |||
| @@ -1,517 +1,604 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_ldap plugin | 3 | * Monitoring check_ldap plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team | 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the check_ldap plugin | 10 | * This file contains the check_ldap plugin |
| 11 | * | 11 | * |
| 12 | * | 12 | * |
| 13 | * This program is free software: you can redistribute it and/or modify | 13 | * This program is free software: you can redistribute it and/or modify |
| 14 | * it under the terms of the GNU General Public License as published by | 14 | * it under the terms of the GNU General Public License as published by |
| 15 | * the Free Software Foundation, either version 3 of the License, or | 15 | * the Free Software Foundation, either version 3 of the License, or |
| 16 | * (at your option) any later version. | 16 | * (at your option) any later version. |
| 17 | * | 17 | * |
| 18 | * This program is distributed in the hope that it will be useful, | 18 | * This program is distributed in the hope that it will be useful, |
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 21 | * GNU General Public License for more details. | 21 | * GNU General Public License for more details. |
| 22 | * | 22 | * |
| 23 | * You should have received a copy of the GNU General Public License | 23 | * You should have received a copy of the GNU General Public License |
| 24 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 24 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 25 | * | 25 | * |
| 26 | * | 26 | * |
| 27 | *****************************************************************************/ | 27 | *****************************************************************************/ |
| 28 | 28 | ||
| 29 | /* progname may be check_ldaps */ | 29 | /* progname may be check_ldaps */ |
| 30 | char *progname = "check_ldap"; | 30 | #include "output.h" |
| 31 | const char *copyright = "2000-2024"; | ||
| 32 | const char *email = "devel@monitoring-plugins.org"; | ||
| 33 | |||
| 34 | #include "common.h" | 31 | #include "common.h" |
| 35 | #include "netutils.h" | 32 | #include "netutils.h" |
| 33 | #include "perfdata.h" | ||
| 34 | #include "thresholds.h" | ||
| 36 | #include "utils.h" | 35 | #include "utils.h" |
| 36 | #include "check_ldap.d/config.h" | ||
| 37 | 37 | ||
| 38 | #include "states.h" | ||
| 38 | #include <lber.h> | 39 | #include <lber.h> |
| 39 | #define LDAP_DEPRECATED 1 | 40 | #define LDAP_DEPRECATED 1 |
| 40 | #include <ldap.h> | 41 | #include <ldap.h> |
| 41 | 42 | ||
| 43 | char *progname = "check_ldap"; | ||
| 44 | const char *copyright = "2000-2024"; | ||
| 45 | const char *email = "devel@monitoring-plugins.org"; | ||
| 46 | |||
| 42 | enum { | 47 | enum { |
| 43 | UNDEFINED = 0, | ||
| 44 | #ifdef HAVE_LDAP_SET_OPTION | ||
| 45 | DEFAULT_PROTOCOL = 2, | ||
| 46 | #endif | ||
| 47 | DEFAULT_PORT = 389 | 48 | DEFAULT_PORT = 389 |
| 48 | }; | 49 | }; |
| 49 | 50 | ||
| 50 | static int process_arguments (int, char **); | 51 | typedef struct { |
| 51 | static int validate_arguments (void); | 52 | int errorcode; |
| 52 | static void print_help (void); | 53 | check_ldap_config config; |
| 53 | void print_usage (void); | 54 | } check_ldap_config_wrapper; |
| 54 | 55 | static check_ldap_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | |
| 55 | static char ld_defattr[] = "(objectclass=*)"; | 56 | static check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper /*config_wrapper*/); |
| 56 | static char *ld_attr = ld_defattr; | ||
| 57 | static char *ld_host = NULL; | ||
| 58 | static char *ld_base = NULL; | ||
| 59 | static char *ld_passwd = NULL; | ||
| 60 | static char *ld_binddn = NULL; | ||
| 61 | static int ld_port = -1; | ||
| 62 | #ifdef HAVE_LDAP_SET_OPTION | ||
| 63 | static int ld_protocol = DEFAULT_PROTOCOL; | ||
| 64 | #endif | ||
| 65 | #ifndef LDAP_OPT_SUCCESS | ||
| 66 | # define LDAP_OPT_SUCCESS LDAP_SUCCESS | ||
| 67 | #endif | ||
| 68 | static double warn_time = UNDEFINED; | ||
| 69 | static double crit_time = UNDEFINED; | ||
| 70 | static thresholds *entries_thresholds = NULL; | ||
| 71 | static struct timeval tv; | ||
| 72 | static char* warn_entries = NULL; | ||
| 73 | static char* crit_entries = NULL; | ||
| 74 | static bool starttls = false; | ||
| 75 | static bool ssl_on_connect = false; | ||
| 76 | static bool verbose = false; | ||
| 77 | |||
| 78 | /* for ldap tls */ | ||
| 79 | 57 | ||
| 80 | static char *SERVICE = "LDAP"; | 58 | static void print_help(void); |
| 81 | 59 | void print_usage(void); | |
| 82 | int | ||
| 83 | main (int argc, char *argv[]) | ||
| 84 | { | ||
| 85 | |||
| 86 | LDAP *ld; | ||
| 87 | LDAPMessage *result; | ||
| 88 | |||
| 89 | /* should be int result = STATE_UNKNOWN; */ | ||
| 90 | |||
| 91 | int status = STATE_UNKNOWN; | ||
| 92 | long microsec; | ||
| 93 | double elapsed_time; | ||
| 94 | |||
| 95 | /* for ldap tls */ | ||
| 96 | |||
| 97 | int tls; | ||
| 98 | int version=3; | ||
| 99 | 60 | ||
| 100 | int status_entries = STATE_OK; | 61 | #ifndef LDAP_OPT_SUCCESS |
| 101 | int num_entries = 0; | 62 | # define LDAP_OPT_SUCCESS LDAP_SUCCESS |
| 63 | #endif | ||
| 64 | static int verbose = 0; | ||
| 102 | 65 | ||
| 103 | setlocale (LC_ALL, ""); | 66 | int main(int argc, char *argv[]) { |
| 104 | bindtextdomain (PACKAGE, LOCALEDIR); | 67 | setlocale(LC_ALL, ""); |
| 105 | textdomain (PACKAGE); | 68 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 69 | textdomain(PACKAGE); | ||
| 106 | 70 | ||
| 107 | if (strstr(argv[0],"check_ldaps")) { | 71 | if (strstr(argv[0], "check_ldaps")) { |
| 108 | xasprintf (&progname, "check_ldaps"); | 72 | xasprintf(&progname, "check_ldaps"); |
| 109 | } | 73 | } |
| 110 | 74 | ||
| 111 | /* Parse extra opts if any */ | 75 | /* Parse extra opts if any */ |
| 112 | argv=np_extra_opts (&argc, argv, progname); | 76 | argv = np_extra_opts(&argc, argv, progname); |
| 113 | 77 | ||
| 114 | if (process_arguments (argc, argv) == ERROR) | 78 | check_ldap_config_wrapper tmp_config = process_arguments(argc, argv); |
| 115 | usage4 (_("Could not parse arguments")); | 79 | if (tmp_config.errorcode == ERROR) { |
| 80 | usage4(_("Could not parse arguments")); | ||
| 81 | } | ||
| 82 | |||
| 83 | const check_ldap_config config = tmp_config.config; | ||
| 116 | 84 | ||
| 117 | if (strstr(argv[0],"check_ldaps") && ! starttls && ! ssl_on_connect) | 85 | if (config.output_format_is_set) { |
| 118 | starttls = true; | 86 | mp_set_format(config.output_format); |
| 87 | } | ||
| 119 | 88 | ||
| 120 | /* initialize alarm signal handling */ | 89 | /* initialize alarm signal handling */ |
| 121 | signal (SIGALRM, socket_timeout_alarm_handler); | 90 | signal(SIGALRM, socket_timeout_alarm_handler); |
| 122 | 91 | ||
| 123 | /* set socket timeout */ | 92 | /* set socket timeout */ |
| 124 | alarm (socket_timeout); | 93 | alarm(socket_timeout); |
| 125 | 94 | ||
| 126 | /* get the start time */ | 95 | /* get the start time */ |
| 127 | gettimeofday (&tv, NULL); | 96 | struct timeval start_time; |
| 97 | gettimeofday(&start_time, NULL); | ||
| 128 | 98 | ||
| 99 | mp_check overall = mp_check_init(); | ||
| 100 | |||
| 101 | LDAP *ldap_connection; | ||
| 129 | /* initialize ldap */ | 102 | /* initialize ldap */ |
| 103 | { | ||
| 130 | #ifdef HAVE_LDAP_INIT | 104 | #ifdef HAVE_LDAP_INIT |
| 131 | if (!(ld = ldap_init (ld_host, ld_port))) { | 105 | mp_subcheck sc_ldap_init = mp_subcheck_init(); |
| 132 | printf ("Could not connect to the server at port %i\n", ld_port); | 106 | if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) { |
| 133 | return STATE_CRITICAL; | 107 | xasprintf(&sc_ldap_init.output, "could not connect to the server at port %i", |
| 134 | } | 108 | config.ld_port); |
| 109 | sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL); | ||
| 110 | mp_add_subcheck_to_check(&overall, sc_ldap_init); | ||
| 111 | mp_exit(overall); | ||
| 112 | } else { | ||
| 113 | xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port); | ||
| 114 | sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK); | ||
| 115 | mp_add_subcheck_to_check(&overall, sc_ldap_init); | ||
| 116 | } | ||
| 135 | #else | 117 | #else |
| 136 | if (!(ld = ldap_open (ld_host, ld_port))) { | 118 | mp_subcheck sc_ldap_init = mp_subcheck_init(); |
| 137 | if (verbose) | 119 | if (!(ld = ldap_open(config.ld_host, config.ld_port))) { |
| 138 | ldap_perror(ld, "ldap_open"); | 120 | if (verbose) { |
| 139 | printf (_("Could not connect to the server at port %i\n"), ld_port); | 121 | ldap_perror(ldap_connection, "ldap_open"); |
| 140 | return STATE_CRITICAL; | 122 | } |
| 141 | } | 123 | xasprintf(&sc_ldap_init.output, "Could not connect to the server at port %i"), config.ld_port); |
| 124 | sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL); | ||
| 125 | mp_add_subcheck_to_check(&overall, sc_ldap_init); | ||
| 126 | mp_exit(overall); | ||
| 127 | } else { | ||
| 128 | xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port); | ||
| 129 | sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK); | ||
| 130 | mp_add_subcheck_to_check(&overall, sc_ldap_init); | ||
| 131 | } | ||
| 142 | #endif /* HAVE_LDAP_INIT */ | 132 | #endif /* HAVE_LDAP_INIT */ |
| 133 | } | ||
| 143 | 134 | ||
| 144 | #ifdef HAVE_LDAP_SET_OPTION | 135 | #ifdef HAVE_LDAP_SET_OPTION |
| 145 | /* set ldap options */ | 136 | /* set ldap options */ |
| 146 | if (ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &ld_protocol) != | 137 | mp_subcheck sc_ldap_set_opts = mp_subcheck_init(); |
| 147 | LDAP_OPT_SUCCESS ) { | 138 | if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) != |
| 148 | printf(_("Could not set protocol version %d\n"), ld_protocol); | 139 | LDAP_OPT_SUCCESS) { |
| 149 | return STATE_CRITICAL; | 140 | xasprintf(&sc_ldap_set_opts.output, "Could not set protocol version %d", |
| 141 | config.ld_protocol); | ||
| 142 | sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_CRITICAL); | ||
| 143 | mp_add_subcheck_to_check(&overall, sc_ldap_set_opts); | ||
| 144 | mp_exit(overall); | ||
| 145 | } else { | ||
| 146 | xasprintf(&sc_ldap_set_opts.output, "set protocol version %d", config.ld_protocol); | ||
| 147 | sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_OK); | ||
| 148 | mp_add_subcheck_to_check(&overall, sc_ldap_set_opts); | ||
| 150 | } | 149 | } |
| 151 | #endif | 150 | #endif |
| 152 | 151 | ||
| 153 | if (ld_port == LDAPS_PORT || ssl_on_connect) { | 152 | int version = 3; |
| 154 | xasprintf (&SERVICE, "LDAPS"); | 153 | int tls; |
| 154 | { | ||
| 155 | if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) { | ||
| 155 | #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) | 156 | #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) |
| 156 | /* ldaps: set option tls */ | 157 | /* ldaps: set option tls */ |
| 157 | tls = LDAP_OPT_X_TLS_HARD; | 158 | tls = LDAP_OPT_X_TLS_HARD; |
| 158 | 159 | ||
| 159 | if (ldap_set_option (ld, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) | 160 | mp_subcheck sc_ldap_tls_init = mp_subcheck_init(); |
| 160 | { | 161 | if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) { |
| 161 | if (verbose) | 162 | if (verbose) { |
| 162 | ldap_perror(ld, "ldaps_option"); | 163 | ldap_perror(ldap_connection, "ldaps_option"); |
| 163 | printf (_("Could not init TLS at port %i!\n"), ld_port); | 164 | } |
| 164 | return STATE_CRITICAL; | 165 | xasprintf(&sc_ldap_tls_init.output, "could not init TLS at port %i!", |
| 165 | } | 166 | config.ld_port); |
| 167 | sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_CRITICAL); | ||
| 168 | mp_add_subcheck_to_check(&overall, sc_ldap_tls_init); | ||
| 169 | mp_exit(overall); | ||
| 170 | } else { | ||
| 171 | xasprintf(&sc_ldap_tls_init.output, "initiated TLS at port %i!", config.ld_port); | ||
| 172 | sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_OK); | ||
| 173 | mp_add_subcheck_to_check(&overall, sc_ldap_tls_init); | ||
| 174 | } | ||
| 166 | #else | 175 | #else |
| 167 | printf (_("TLS not supported by the libraries!\n")); | 176 | printf(_("TLS not supported by the libraries!\n")); |
| 168 | return STATE_CRITICAL; | 177 | exit(STATE_CRITICAL); |
| 169 | #endif /* LDAP_OPT_X_TLS */ | 178 | #endif /* LDAP_OPT_X_TLS */ |
| 170 | } else if (starttls) { | 179 | } else if (config.starttls) { |
| 171 | xasprintf (&SERVICE, "LDAP-TLS"); | ||
| 172 | #if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) | 180 | #if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) |
| 173 | /* ldap with startTLS: set option version */ | 181 | /* ldap with startTLS: set option version */ |
| 174 | if (ldap_get_option(ld,LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS ) | 182 | if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) == |
| 175 | { | 183 | LDAP_OPT_SUCCESS) { |
| 176 | if (version < LDAP_VERSION3) | 184 | if (version < LDAP_VERSION3) { |
| 177 | { | 185 | version = LDAP_VERSION3; |
| 178 | version = LDAP_VERSION3; | 186 | ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version); |
| 179 | ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version); | 187 | } |
| 188 | } | ||
| 189 | /* call start_tls */ | ||
| 190 | mp_subcheck sc_ldap_starttls = mp_subcheck_init(); | ||
| 191 | if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) { | ||
| 192 | if (verbose) { | ||
| 193 | ldap_perror(ldap_connection, "ldap_start_tls"); | ||
| 194 | } | ||
| 195 | xasprintf(&sc_ldap_starttls.output, "could not init STARTTLS at port %i!", | ||
| 196 | config.ld_port); | ||
| 197 | sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_CRITICAL); | ||
| 198 | mp_add_subcheck_to_check(&overall, sc_ldap_starttls); | ||
| 199 | mp_exit(overall); | ||
| 200 | } else { | ||
| 201 | xasprintf(&sc_ldap_starttls.output, "initiated STARTTLS at port %i!", | ||
| 202 | config.ld_port); | ||
| 203 | sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_OK); | ||
| 204 | mp_add_subcheck_to_check(&overall, sc_ldap_starttls); | ||
| 180 | } | 205 | } |
| 181 | } | ||
| 182 | /* call start_tls */ | ||
| 183 | if (ldap_start_tls_s(ld, NULL, NULL) != LDAP_SUCCESS) | ||
| 184 | { | ||
| 185 | if (verbose) | ||
| 186 | ldap_perror(ld, "ldap_start_tls"); | ||
| 187 | printf (_("Could not init startTLS at port %i!\n"), ld_port); | ||
| 188 | return STATE_CRITICAL; | ||
| 189 | } | ||
| 190 | #else | 206 | #else |
| 191 | printf (_("startTLS not supported by the library, needs LDAPv3!\n")); | 207 | printf(_("startTLS not supported by the library, needs LDAPv3!\n")); |
| 192 | return STATE_CRITICAL; | 208 | exit(STATE_CRITICAL); |
| 193 | #endif /* HAVE_LDAP_START_TLS_S */ | 209 | #endif /* HAVE_LDAP_START_TLS_S */ |
| 210 | } | ||
| 194 | } | 211 | } |
| 195 | 212 | ||
| 196 | /* bind to the ldap server */ | 213 | /* bind to the ldap server */ |
| 197 | if (ldap_bind_s (ld, ld_binddn, ld_passwd, LDAP_AUTH_SIMPLE) != | 214 | { |
| 198 | LDAP_SUCCESS) { | 215 | mp_subcheck sc_ldap_bind = mp_subcheck_init(); |
| 199 | if (verbose) | 216 | int ldap_error = |
| 200 | ldap_perror(ld, "ldap_bind"); | 217 | ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE); |
| 201 | printf (_("Could not bind to the LDAP server\n")); | 218 | if (ldap_error != LDAP_SUCCESS) { |
| 202 | return STATE_CRITICAL; | 219 | if (verbose) { |
| 220 | ldap_perror(ldap_connection, "ldap_bind"); | ||
| 221 | } | ||
| 222 | |||
| 223 | xasprintf(&sc_ldap_bind.output, "could not bind to the LDAP server: %s", | ||
| 224 | ldap_err2string(ldap_error)); | ||
| 225 | sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_CRITICAL); | ||
| 226 | mp_add_subcheck_to_check(&overall, sc_ldap_bind); | ||
| 227 | mp_exit(overall); | ||
| 228 | } else { | ||
| 229 | xasprintf(&sc_ldap_bind.output, "execute bind to the LDAP server"); | ||
| 230 | sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_OK); | ||
| 231 | mp_add_subcheck_to_check(&overall, sc_ldap_bind); | ||
| 232 | } | ||
| 203 | } | 233 | } |
| 204 | 234 | ||
| 235 | LDAPMessage *result; | ||
| 205 | /* do a search of all objectclasses in the base dn */ | 236 | /* do a search of all objectclasses in the base dn */ |
| 206 | if (ldap_search_s (ld, ld_base, (crit_entries!=NULL || warn_entries!=NULL) ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_BASE, ld_attr, NULL, 0, &result) | 237 | { |
| 207 | != LDAP_SUCCESS) { | 238 | mp_subcheck sc_ldap_search = mp_subcheck_init(); |
| 208 | if (verbose) | 239 | int ldap_error = ldap_search_s( |
| 209 | ldap_perror(ld, "ldap_search"); | 240 | ldap_connection, config.ld_base, |
| 210 | printf (_("Could not search/find objectclasses in %s\n"), ld_base); | 241 | (config.entries_thresholds.warning_is_set || config.entries_thresholds.critical_is_set) |
| 211 | return STATE_CRITICAL; | 242 | ? LDAP_SCOPE_SUBTREE |
| 212 | } else if (crit_entries!=NULL || warn_entries!=NULL) { | 243 | : LDAP_SCOPE_BASE, |
| 213 | num_entries = ldap_count_entries(ld, result); | 244 | config.ld_attr, NULL, 0, &result); |
| 245 | |||
| 246 | if (ldap_error != LDAP_SUCCESS) { | ||
| 247 | if (verbose) { | ||
| 248 | ldap_perror(ldap_connection, "ldap_search"); | ||
| 249 | } | ||
| 250 | xasprintf(&sc_ldap_search.output, "could not search/find objectclasses in %s: %s", | ||
| 251 | config.ld_base, ldap_err2string(ldap_error)); | ||
| 252 | sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_CRITICAL); | ||
| 253 | mp_add_subcheck_to_check(&overall, sc_ldap_search); | ||
| 254 | mp_exit(overall); | ||
| 255 | } else { | ||
| 256 | xasprintf(&sc_ldap_search.output, "search/find objectclasses in %s", config.ld_base); | ||
| 257 | sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_OK); | ||
| 258 | mp_add_subcheck_to_check(&overall, sc_ldap_search); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | int num_entries = ldap_count_entries(ldap_connection, result); | ||
| 263 | if (verbose) { | ||
| 264 | printf("entries found: %d\n", num_entries); | ||
| 214 | } | 265 | } |
| 215 | 266 | ||
| 216 | /* unbind from the ldap server */ | 267 | /* unbind from the ldap server */ |
| 217 | ldap_unbind (ld); | 268 | ldap_unbind(ldap_connection); |
| 218 | 269 | ||
| 219 | /* reset the alarm handler */ | 270 | /* reset the alarm handler */ |
| 220 | alarm (0); | 271 | alarm(0); |
| 221 | 272 | ||
| 222 | /* calculate the elapsed time and compare to thresholds */ | 273 | /* calculate the elapsed time and compare to thresholds */ |
| 274 | long microsec = deltime(start_time); | ||
| 275 | double elapsed_time = (double)microsec / 1.0e6; | ||
| 276 | mp_perfdata pd_connection_time = perfdata_init(); | ||
| 277 | pd_connection_time.label = "time"; | ||
| 278 | pd_connection_time.value = mp_create_pd_value(elapsed_time); | ||
| 279 | pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.connection_time_threshold); | ||
| 280 | |||
| 281 | mp_subcheck sc_connection_time = mp_subcheck_init(); | ||
| 282 | mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time); | ||
| 283 | |||
| 284 | mp_state_enum connection_time_state = mp_get_pd_status(pd_connection_time); | ||
| 285 | sc_connection_time = mp_set_subcheck_state(sc_connection_time, connection_time_state); | ||
| 286 | |||
| 287 | if (connection_time_state == STATE_OK) { | ||
| 288 | xasprintf(&sc_connection_time.output, "connection time %.3fs is within thresholds", | ||
| 289 | elapsed_time); | ||
| 290 | } else { | ||
| 291 | xasprintf(&sc_connection_time.output, "connection time %.3fs is violating thresholds", | ||
| 292 | elapsed_time); | ||
| 293 | } | ||
| 223 | 294 | ||
| 224 | microsec = deltime (tv); | 295 | mp_add_subcheck_to_check(&overall, sc_connection_time); |
| 225 | elapsed_time = (double)microsec / 1.0e6; | ||
| 226 | 296 | ||
| 227 | if (crit_time!=UNDEFINED && elapsed_time>crit_time) | 297 | mp_perfdata pd_num_entries = perfdata_init(); |
| 228 | status = STATE_CRITICAL; | 298 | pd_num_entries.label = "entries"; |
| 229 | else if (warn_time!=UNDEFINED && elapsed_time>warn_time) | 299 | pd_num_entries.value = mp_create_pd_value(num_entries); |
| 230 | status = STATE_WARNING; | 300 | pd_num_entries = mp_pd_set_thresholds(pd_num_entries, config.entries_thresholds); |
| 231 | else | ||
| 232 | status = STATE_OK; | ||
| 233 | 301 | ||
| 234 | if(entries_thresholds != NULL) { | 302 | mp_subcheck sc_num_entries = mp_subcheck_init(); |
| 235 | if (verbose) { | 303 | mp_add_perfdata_to_subcheck(&sc_num_entries, pd_num_entries); |
| 236 | printf ("entries found: %d\n", num_entries); | 304 | xasprintf(&sc_num_entries.output, "found %d entries", num_entries); |
| 237 | print_thresholds("entry thresholds", entries_thresholds); | 305 | sc_num_entries = mp_set_subcheck_state(sc_num_entries, mp_get_pd_status(pd_num_entries)); |
| 238 | } | ||
| 239 | status_entries = get_status(num_entries, entries_thresholds); | ||
| 240 | if (status_entries == STATE_CRITICAL) { | ||
| 241 | status = STATE_CRITICAL; | ||
| 242 | } else if (status != STATE_CRITICAL) { | ||
| 243 | status = status_entries; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | 306 | ||
| 247 | /* print out the result */ | 307 | mp_add_subcheck_to_check(&overall, sc_num_entries); |
| 248 | if (crit_entries!=NULL || warn_entries!=NULL) { | ||
| 249 | printf (_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), | ||
| 250 | state_text (status), | ||
| 251 | num_entries, | ||
| 252 | elapsed_time, | ||
| 253 | fperfdata ("time", elapsed_time, "s", | ||
| 254 | (int)warn_time, warn_time, | ||
| 255 | (int)crit_time, crit_time, | ||
| 256 | true, 0, false, 0), | ||
| 257 | sperfdata ("entries", (double)num_entries, "", | ||
| 258 | warn_entries, | ||
| 259 | crit_entries, | ||
| 260 | true, 0.0, false, 0.0)); | ||
| 261 | } else { | ||
| 262 | printf (_("LDAP %s - %.3f seconds response time|%s\n"), | ||
| 263 | state_text (status), | ||
| 264 | elapsed_time, | ||
| 265 | fperfdata ("time", elapsed_time, "s", | ||
| 266 | (int)warn_time, warn_time, | ||
| 267 | (int)crit_time, crit_time, | ||
| 268 | true, 0, false, 0)); | ||
| 269 | } | ||
| 270 | 308 | ||
| 271 | return status; | 309 | mp_exit(overall); |
| 272 | } | 310 | } |
| 273 | 311 | ||
| 274 | /* process command-line arguments */ | 312 | /* process command-line arguments */ |
| 275 | int | 313 | check_ldap_config_wrapper process_arguments(int argc, char **argv) { |
| 276 | process_arguments (int argc, char **argv) | 314 | enum { |
| 277 | { | 315 | output_format_index = CHAR_MAX + 1, |
| 278 | int c; | 316 | }; |
| 279 | 317 | ||
| 280 | int option = 0; | ||
| 281 | /* initialize the long option struct */ | 318 | /* initialize the long option struct */ |
| 282 | static struct option longopts[] = { | 319 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, |
| 283 | {"help", no_argument, 0, 'h'}, | 320 | {"version", no_argument, 0, 'V'}, |
| 284 | {"version", no_argument, 0, 'V'}, | 321 | {"timeout", required_argument, 0, 't'}, |
| 285 | {"timeout", required_argument, 0, 't'}, | 322 | {"hostname", required_argument, 0, 'H'}, |
| 286 | {"hostname", required_argument, 0, 'H'}, | 323 | {"base", required_argument, 0, 'b'}, |
| 287 | {"base", required_argument, 0, 'b'}, | 324 | {"attr", required_argument, 0, 'a'}, |
| 288 | {"attr", required_argument, 0, 'a'}, | 325 | {"bind", required_argument, 0, 'D'}, |
| 289 | {"bind", required_argument, 0, 'D'}, | 326 | {"pass", required_argument, 0, 'P'}, |
| 290 | {"pass", required_argument, 0, 'P'}, | ||
| 291 | #ifdef HAVE_LDAP_SET_OPTION | 327 | #ifdef HAVE_LDAP_SET_OPTION |
| 292 | {"ver2", no_argument, 0, '2'}, | 328 | {"ver2", no_argument, 0, '2'}, |
| 293 | {"ver3", no_argument, 0, '3'}, | 329 | {"ver3", no_argument, 0, '3'}, |
| 294 | #endif | 330 | #endif |
| 295 | {"starttls", no_argument, 0, 'T'}, | 331 | {"starttls", no_argument, 0, 'T'}, |
| 296 | {"ssl", no_argument, 0, 'S'}, | 332 | {"ssl", no_argument, 0, 'S'}, |
| 297 | {"use-ipv4", no_argument, 0, '4'}, | 333 | {"use-ipv4", no_argument, 0, '4'}, |
| 298 | {"use-ipv6", no_argument, 0, '6'}, | 334 | {"use-ipv6", no_argument, 0, '6'}, |
| 299 | {"port", required_argument, 0, 'p'}, | 335 | {"port", required_argument, 0, 'p'}, |
| 300 | {"warn", required_argument, 0, 'w'}, | 336 | {"warn", required_argument, 0, 'w'}, |
| 301 | {"crit", required_argument, 0, 'c'}, | 337 | {"crit", required_argument, 0, 'c'}, |
| 302 | {"warn-entries", required_argument, 0, 'W'}, | 338 | {"warn-entries", required_argument, 0, 'W'}, |
| 303 | {"crit-entries", required_argument, 0, 'C'}, | 339 | {"crit-entries", required_argument, 0, 'C'}, |
| 304 | {"verbose", no_argument, 0, 'v'}, | 340 | {"verbose", no_argument, 0, 'v'}, |
| 305 | {0, 0, 0, 0} | 341 | {"output-format", required_argument, 0, output_format_index}, |
| 342 | {0, 0, 0, 0}}; | ||
| 343 | |||
| 344 | check_ldap_config_wrapper result = { | ||
| 345 | .errorcode = OK, | ||
| 346 | .config = check_ldap_config_init(), | ||
| 306 | }; | 347 | }; |
| 307 | 348 | ||
| 308 | if (argc < 2) | 349 | if (argc < 2) { |
| 309 | return ERROR; | 350 | result.errorcode = ERROR; |
| 351 | return result; | ||
| 352 | } | ||
| 310 | 353 | ||
| 311 | for (c = 1; c < argc; c++) { | 354 | for (int index = 1; index < argc; index++) { |
| 312 | if (strcmp ("-to", argv[c]) == 0) | 355 | if (strcmp("-to", argv[index]) == 0) { |
| 313 | strcpy (argv[c], "-t"); | 356 | strcpy(argv[index], "-t"); |
| 357 | } | ||
| 314 | } | 358 | } |
| 315 | 359 | ||
| 360 | int option = 0; | ||
| 316 | while (true) { | 361 | while (true) { |
| 317 | c = getopt_long (argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option); | 362 | int option_index = |
| 363 | getopt_long(argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option); | ||
| 318 | 364 | ||
| 319 | if (c == -1 || c == EOF) | 365 | if (option_index == -1 || option_index == EOF) { |
| 320 | break; | 366 | break; |
| 367 | } | ||
| 321 | 368 | ||
| 322 | switch (c) { | 369 | switch (option_index) { |
| 323 | case 'h': /* help */ | 370 | case 'h': /* help */ |
| 324 | print_help (); | 371 | print_help(); |
| 325 | exit (STATE_UNKNOWN); | 372 | exit(STATE_UNKNOWN); |
| 326 | case 'V': /* version */ | 373 | case 'V': /* version */ |
| 327 | print_revision (progname, NP_VERSION); | 374 | print_revision(progname, NP_VERSION); |
| 328 | exit (STATE_UNKNOWN); | 375 | exit(STATE_UNKNOWN); |
| 329 | case 't': /* timeout period */ | 376 | case 't': /* timeout period */ |
| 330 | if (!is_intnonneg (optarg)) | 377 | if (!is_intnonneg(optarg)) { |
| 331 | usage2 (_("Timeout interval must be a positive integer"), optarg); | 378 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 332 | else | 379 | } else { |
| 333 | socket_timeout = atoi (optarg); | 380 | socket_timeout = atoi(optarg); |
| 381 | } | ||
| 334 | break; | 382 | break; |
| 335 | case 'H': | 383 | case 'H': |
| 336 | ld_host = optarg; | 384 | result.config.ld_host = optarg; |
| 337 | break; | 385 | break; |
| 338 | case 'b': | 386 | case 'b': |
| 339 | ld_base = optarg; | 387 | result.config.ld_base = optarg; |
| 340 | break; | 388 | break; |
| 341 | case 'p': | 389 | case 'p': |
| 342 | ld_port = atoi (optarg); | 390 | result.config.ld_port = atoi(optarg); |
| 343 | break; | 391 | break; |
| 344 | case 'a': | 392 | case 'a': |
| 345 | ld_attr = optarg; | 393 | result.config.ld_attr = optarg; |
| 346 | break; | 394 | break; |
| 347 | case 'D': | 395 | case 'D': |
| 348 | ld_binddn = optarg; | 396 | result.config.ld_binddn = optarg; |
| 349 | break; | 397 | break; |
| 350 | case 'P': | 398 | case 'P': |
| 351 | ld_passwd = optarg; | 399 | result.config.ld_passwd = optarg; |
| 352 | break; | ||
| 353 | case 'w': | ||
| 354 | warn_time = strtod (optarg, NULL); | ||
| 355 | break; | ||
| 356 | case 'c': | ||
| 357 | crit_time = strtod (optarg, NULL); | ||
| 358 | break; | ||
| 359 | case 'W': | ||
| 360 | warn_entries = optarg; | ||
| 361 | break; | ||
| 362 | case 'C': | ||
| 363 | crit_entries = optarg; | ||
| 364 | break; | 400 | break; |
| 401 | case 'w': { | ||
| 402 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 403 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 404 | die(STATE_UNKNOWN, "failed to parse warning connection time threshold"); | ||
| 405 | } | ||
| 406 | result.config.connection_time_threshold = | ||
| 407 | mp_thresholds_set_warn(result.config.connection_time_threshold, tmp.range); | ||
| 408 | } break; | ||
| 409 | case 'c': { | ||
| 410 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 411 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 412 | die(STATE_UNKNOWN, "failed to parse critical connection time threshold"); | ||
| 413 | } | ||
| 414 | result.config.connection_time_threshold = | ||
| 415 | mp_thresholds_set_crit(result.config.connection_time_threshold, tmp.range); | ||
| 416 | } break; | ||
| 417 | case 'W': { | ||
| 418 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 419 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 420 | die(STATE_UNKNOWN, "failed to parse number of entries warning threshold"); | ||
| 421 | } | ||
| 422 | result.config.entries_thresholds = | ||
| 423 | mp_thresholds_set_warn(result.config.entries_thresholds, tmp.range); | ||
| 424 | } break; | ||
| 425 | case 'C': { | ||
| 426 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 427 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 428 | die(STATE_UNKNOWN, "failed to parse number of entries critical threshold"); | ||
| 429 | } | ||
| 430 | result.config.entries_thresholds = | ||
| 431 | mp_thresholds_set_crit(result.config.entries_thresholds, tmp.range); | ||
| 432 | } break; | ||
| 365 | #ifdef HAVE_LDAP_SET_OPTION | 433 | #ifdef HAVE_LDAP_SET_OPTION |
| 366 | case '2': | 434 | case '2': |
| 367 | ld_protocol = 2; | 435 | result.config.ld_protocol = 2; |
| 368 | break; | 436 | break; |
| 369 | case '3': | 437 | case '3': |
| 370 | ld_protocol = 3; | 438 | result.config.ld_protocol = 3; |
| 371 | break; | 439 | break; |
| 372 | #endif | 440 | #endif // HAVE_LDAP_SET_OPTION |
| 373 | case '4': | 441 | case '4': |
| 374 | address_family = AF_INET; | 442 | address_family = AF_INET; |
| 375 | break; | 443 | break; |
| 376 | case 'v': | 444 | case 'v': |
| 377 | verbose = true; | 445 | verbose++; |
| 378 | break; | 446 | break; |
| 379 | case 'T': | 447 | case 'T': |
| 380 | if (! ssl_on_connect) | 448 | if (!result.config.ssl_on_connect) { |
| 381 | starttls = true; | 449 | result.config.starttls = true; |
| 382 | else | 450 | } else { |
| 383 | usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl"); | 451 | usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl"); |
| 452 | } | ||
| 384 | break; | 453 | break; |
| 385 | case 'S': | 454 | case 'S': |
| 386 | if (! starttls) { | 455 | if (!result.config.starttls) { |
| 387 | ssl_on_connect = true; | 456 | result.config.ssl_on_connect = true; |
| 388 | if (ld_port == -1) | 457 | if (result.config.ld_port == -1) { |
| 389 | ld_port = LDAPS_PORT; | 458 | result.config.ld_port = LDAPS_PORT; |
| 390 | } else | 459 | } |
| 460 | } else { | ||
| 391 | usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls"); | 461 | usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls"); |
| 462 | } | ||
| 392 | break; | 463 | break; |
| 393 | case '6': | 464 | case '6': |
| 394 | #ifdef USE_IPV6 | 465 | #ifdef USE_IPV6 |
| 395 | address_family = AF_INET6; | 466 | address_family = AF_INET6; |
| 396 | #else | 467 | #else |
| 397 | usage (_("IPv6 support not available\n")); | 468 | usage(_("IPv6 support not available\n")); |
| 398 | #endif | 469 | #endif |
| 399 | break; | 470 | break; |
| 471 | case output_format_index: { | ||
| 472 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 473 | if (!parser.parsing_success) { | ||
| 474 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 475 | printf("Invalid output format: %s\n", optarg); | ||
| 476 | exit(STATE_UNKNOWN); | ||
| 477 | } | ||
| 478 | |||
| 479 | result.config.output_format_is_set = true; | ||
| 480 | result.config.output_format = parser.output_format; | ||
| 481 | break; | ||
| 482 | } | ||
| 400 | default: | 483 | default: |
| 401 | usage5 (); | 484 | usage5(); |
| 402 | } | 485 | } |
| 403 | } | 486 | } |
| 404 | 487 | ||
| 405 | c = optind; | 488 | int index = optind; |
| 406 | if (ld_host == NULL && is_host(argv[c])) | 489 | if ((result.config.ld_host == NULL) && is_host(argv[index])) { |
| 407 | ld_host = strdup (argv[c++]); | 490 | result.config.ld_host = strdup(argv[index++]); |
| 491 | } | ||
| 408 | 492 | ||
| 409 | if (ld_base == NULL && argv[c]) | 493 | if ((result.config.ld_base == NULL) && argv[index]) { |
| 410 | ld_base = strdup (argv[c++]); | 494 | result.config.ld_base = strdup(argv[index++]); |
| 495 | } | ||
| 411 | 496 | ||
| 412 | if (ld_port == -1) | 497 | if (result.config.ld_port == -1) { |
| 413 | ld_port = DEFAULT_PORT; | 498 | result.config.ld_port = DEFAULT_PORT; |
| 499 | } | ||
| 414 | 500 | ||
| 415 | return validate_arguments (); | 501 | if (strstr(argv[0], "check_ldaps") && !result.config.starttls && |
| 416 | } | 502 | !result.config.ssl_on_connect) { |
| 503 | result.config.starttls = true; | ||
| 504 | } | ||
| 417 | 505 | ||
| 506 | return validate_arguments(result); | ||
| 507 | } | ||
| 418 | 508 | ||
| 419 | int | 509 | check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper config_wrapper) { |
| 420 | validate_arguments () | 510 | if (config_wrapper.config.ld_host == NULL || strlen(config_wrapper.config.ld_host) == 0) { |
| 421 | { | 511 | usage4(_("Please specify the host name\n")); |
| 422 | if (ld_host==NULL || strlen(ld_host)==0) | 512 | } |
| 423 | usage4 (_("Please specify the host name\n")); | ||
| 424 | 513 | ||
| 425 | if (ld_base==NULL) | 514 | if (config_wrapper.config.ld_base == NULL) { |
| 426 | usage4 (_("Please specify the LDAP base\n")); | 515 | usage4(_("Please specify the LDAP base\n")); |
| 516 | } | ||
| 427 | 517 | ||
| 428 | if (crit_entries!=NULL || warn_entries!=NULL) { | 518 | if (config_wrapper.config.ld_passwd == NULL) { |
| 429 | set_thresholds(&entries_thresholds, | 519 | config_wrapper.config.ld_passwd = getenv("LDAP_PASSWORD"); |
| 430 | warn_entries, crit_entries); | ||
| 431 | } | 520 | } |
| 432 | if (ld_passwd==NULL) | ||
| 433 | ld_passwd = getenv("LDAP_PASSWORD"); | ||
| 434 | 521 | ||
| 435 | return OK; | 522 | return config_wrapper; |
| 436 | } | 523 | } |
| 437 | 524 | ||
| 438 | 525 | void print_help(void) { | |
| 439 | void | ||
| 440 | print_help (void) | ||
| 441 | { | ||
| 442 | char *myport; | 526 | char *myport; |
| 443 | xasprintf (&myport, "%d", DEFAULT_PORT); | 527 | xasprintf(&myport, "%d", DEFAULT_PORT); |
| 444 | 528 | ||
| 445 | print_revision (progname, NP_VERSION); | 529 | print_revision(progname, NP_VERSION); |
| 446 | 530 | ||
| 447 | printf ("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n"); | 531 | printf("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n"); |
| 448 | printf (COPYRIGHT, copyright, email); | 532 | printf(COPYRIGHT, copyright, email); |
| 449 | 533 | ||
| 450 | printf ("\n\n"); | 534 | printf("\n\n"); |
| 451 | 535 | ||
| 452 | print_usage (); | 536 | print_usage(); |
| 453 | 537 | ||
| 454 | printf (UT_HELP_VRSN); | 538 | printf(UT_HELP_VRSN); |
| 455 | printf (UT_EXTRA_OPTS); | 539 | printf(UT_EXTRA_OPTS); |
| 456 | 540 | ||
| 457 | printf (UT_HOST_PORT, 'p', myport); | 541 | printf(UT_HOST_PORT, 'p', myport); |
| 458 | 542 | ||
| 459 | printf (UT_IPv46); | 543 | printf(UT_IPv46); |
| 460 | 544 | ||
| 461 | printf (" %s\n", "-a [--attr]"); | 545 | printf(" %s\n", "-a [--attr]"); |
| 462 | printf (" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\"")); | 546 | printf(" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\"")); |
| 463 | printf (" %s\n", "-b [--base]"); | 547 | printf(" %s\n", "-b [--base]"); |
| 464 | printf (" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at")); | 548 | printf(" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at")); |
| 465 | printf (" %s\n", "-D [--bind]"); | 549 | printf(" %s\n", "-D [--bind]"); |
| 466 | printf (" %s\n", _("ldap bind DN (if required)")); | 550 | printf(" %s\n", _("ldap bind DN (if required)")); |
| 467 | printf (" %s\n", "-P [--pass]"); | 551 | printf(" %s\n", "-P [--pass]"); |
| 468 | printf (" %s\n", _("ldap password (if required, or set the password through environment variable 'LDAP_PASSWORD')")); | 552 | printf(" %s\n", _("ldap password (if required, or set the password through environment " |
| 469 | printf (" %s\n", "-T [--starttls]"); | 553 | "variable 'LDAP_PASSWORD')")); |
| 470 | printf (" %s\n", _("use starttls mechanism introduced in protocol version 3")); | 554 | printf(" %s\n", "-T [--starttls]"); |
| 471 | printf (" %s\n", "-S [--ssl]"); | 555 | printf(" %s\n", _("use starttls mechanism introduced in protocol version 3")); |
| 472 | printf (" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"), LDAPS_PORT); | 556 | printf(" %s\n", "-S [--ssl]"); |
| 557 | printf(" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"), | ||
| 558 | LDAPS_PORT); | ||
| 473 | 559 | ||
| 474 | #ifdef HAVE_LDAP_SET_OPTION | 560 | #ifdef HAVE_LDAP_SET_OPTION |
| 475 | printf (" %s\n", "-2 [--ver2]"); | 561 | printf(" %s\n", "-2 [--ver2]"); |
| 476 | printf (" %s\n", _("use ldap protocol version 2")); | 562 | printf(" %s\n", _("use ldap protocol version 2")); |
| 477 | printf (" %s\n", "-3 [--ver3]"); | 563 | printf(" %s\n", "-3 [--ver3]"); |
| 478 | printf (" %s\n", _("use ldap protocol version 3")); | 564 | printf(" %s\n", _("use ldap protocol version 3")); |
| 479 | printf (" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL); | 565 | printf(" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL); |
| 480 | #endif | 566 | #endif |
| 481 | 567 | ||
| 482 | printf (UT_WARN_CRIT); | 568 | printf(UT_WARN_CRIT); |
| 483 | 569 | ||
| 484 | printf (" %s\n", "-W [--warn-entries]"); | 570 | printf(" %s\n", "-W [--warn-entries]"); |
| 485 | printf (" %s\n", _("Number of found entries to result in warning status")); | 571 | printf(" %s\n", _("Number of found entries to result in warning status")); |
| 486 | printf (" %s\n", "-C [--crit-entries]"); | 572 | printf(" %s\n", "-C [--crit-entries]"); |
| 487 | printf (" %s\n", _("Number of found entries to result in critical status")); | 573 | printf(" %s\n", _("Number of found entries to result in critical status")); |
| 488 | 574 | ||
| 489 | printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 575 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 490 | 576 | ||
| 491 | printf (UT_VERBOSE); | 577 | printf(UT_VERBOSE); |
| 578 | printf(UT_OUTPUT_FORMAT); | ||
| 492 | 579 | ||
| 493 | printf ("\n"); | 580 | printf("\n"); |
| 494 | printf ("%s\n", _("Notes:")); | 581 | printf("%s\n", _("Notes:")); |
| 495 | printf (" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be")); | 582 | printf(" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be")); |
| 496 | printf (_(" implied (using default port %i) unless --port=636 is specified. In that case\n"), DEFAULT_PORT); | 583 | printf(_(" implied (using default port %i) unless --port=636 is specified. In that case\n"), |
| 497 | printf (" %s\n", _("'SSL on connect' will be used no matter how the plugin was called.")); | 584 | DEFAULT_PORT); |
| 498 | printf (" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' or '--ssl' flags")); | 585 | printf(" %s\n", _("'SSL on connect' will be used no matter how the plugin was called.")); |
| 499 | printf (" %s\n", _("to define the behaviour explicitly instead.")); | 586 | printf(" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' " |
| 500 | printf (" %s\n", _("The parameters --warn-entries and --crit-entries are optional.")); | 587 | "or '--ssl' flags")); |
| 588 | printf(" %s\n", _("to define the behaviour explicitly instead.")); | ||
| 589 | printf(" %s\n", _("The parameters --warn-entries and --crit-entries are optional.")); | ||
| 501 | 590 | ||
| 502 | printf (UT_SUPPORT); | 591 | printf(UT_SUPPORT); |
| 503 | } | 592 | } |
| 504 | 593 | ||
| 505 | void | 594 | void print_usage(void) { |
| 506 | print_usage (void) | 595 | printf("%s\n", _("Usage:")); |
| 507 | { | 596 | printf(" %s -H <host> -b <base_dn> [-p <port>] [-a <attr>] [-D <binddn>]", progname); |
| 508 | printf ("%s\n", _("Usage:")); | 597 | printf("\n [-P <password>] [-w <warn_time>] [-c <crit_time>] [-t timeout]%s\n", |
| 509 | printf (" %s -H <host> -b <base_dn> [-p <port>] [-a <attr>] [-D <binddn>]",progname); | ||
| 510 | printf ("\n [-P <password>] [-w <warn_time>] [-c <crit_time>] [-t timeout]%s\n", | ||
| 511 | #ifdef HAVE_LDAP_SET_OPTION | 598 | #ifdef HAVE_LDAP_SET_OPTION |
| 512 | "\n [-2|-3] [-4|-6]" | 599 | "\n [-2|-3] [-4|-6]" |
| 513 | #else | 600 | #else |
| 514 | "" | 601 | "" |
| 515 | #endif | 602 | #endif |
| 516 | ); | 603 | ); |
| 517 | } | 604 | } |
diff --git a/plugins/check_ldap.d/config.h b/plugins/check_ldap.d/config.h new file mode 100644 index 00000000..50191725 --- /dev/null +++ b/plugins/check_ldap.d/config.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | |||
| 8 | static char ld_defattr[] = "(objectclass=*)"; | ||
| 9 | |||
| 10 | enum { | ||
| 11 | #ifdef HAVE_LDAP_SET_OPTION | ||
| 12 | DEFAULT_PROTOCOL = 2, | ||
| 13 | #endif | ||
| 14 | }; | ||
| 15 | |||
| 16 | typedef struct { | ||
| 17 | char *ld_host; | ||
| 18 | char *ld_base; | ||
| 19 | char *ld_passwd; | ||
| 20 | char *ld_binddn; | ||
| 21 | char *ld_attr; | ||
| 22 | int ld_port; | ||
| 23 | bool starttls; | ||
| 24 | bool ssl_on_connect; | ||
| 25 | #ifdef HAVE_LDAP_SET_OPTION | ||
| 26 | int ld_protocol; | ||
| 27 | #endif | ||
| 28 | |||
| 29 | mp_thresholds entries_thresholds; | ||
| 30 | mp_thresholds connection_time_threshold; | ||
| 31 | |||
| 32 | bool output_format_is_set; | ||
| 33 | mp_output_format output_format; | ||
| 34 | } check_ldap_config; | ||
| 35 | |||
| 36 | check_ldap_config check_ldap_config_init() { | ||
| 37 | check_ldap_config tmp = { | ||
| 38 | .ld_host = NULL, | ||
| 39 | .ld_base = NULL, | ||
| 40 | .ld_passwd = NULL, | ||
| 41 | .ld_binddn = NULL, | ||
| 42 | .ld_attr = ld_defattr, | ||
| 43 | .ld_port = -1, | ||
| 44 | .starttls = false, | ||
| 45 | .ssl_on_connect = false, | ||
| 46 | #ifdef HAVE_LDAP_SET_OPTION | ||
| 47 | .ld_protocol = DEFAULT_PROTOCOL, | ||
| 48 | #endif | ||
| 49 | |||
| 50 | .entries_thresholds = mp_thresholds_init(), | ||
| 51 | .connection_time_threshold = mp_thresholds_init(), | ||
| 52 | |||
| 53 | .output_format_is_set = false, | ||
| 54 | }; | ||
| 55 | return tmp; | ||
| 56 | } | ||
diff --git a/plugins/check_load.c b/plugins/check_load.c index 1431d130..644cd604 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c | |||
| @@ -1,350 +1,430 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_load plugin | 3 | * Monitoring check_load plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 1999-2007 Monitoring Plugins Development Team | 6 | * Copyright (c) 1999-2007 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the check_load plugin | 10 | * This file contains the check_load plugin |
| 11 | * | 11 | * |
| 12 | * This plugin tests the current system load average. | 12 | * This plugin tests the current system load average. |
| 13 | * | 13 | * |
| 14 | * | 14 | * |
| 15 | * This program is free software: you can redistribute it and/or modify | 15 | * This program is free software: you can redistribute it and/or modify |
| 16 | * it under the terms of the GNU General Public License as published by | 16 | * it under the terms of the GNU General Public License as published by |
| 17 | * the Free Software Foundation, either version 3 of the License, or | 17 | * the Free Software Foundation, either version 3 of the License, or |
| 18 | * (at your option) any later version. | 18 | * (at your option) any later version. |
| 19 | * | 19 | * |
| 20 | * This program is distributed in the hope that it will be useful, | 20 | * This program is distributed in the hope that it will be useful, |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | * GNU General Public License for more details. | 23 | * GNU General Public License for more details. |
| 24 | * | 24 | * |
| 25 | * You should have received a copy of the GNU General Public License | 25 | * You should have received a copy of the GNU General Public License |
| 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | * | 27 | * |
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | const char *progname = "check_load"; | 31 | const char *progname = "check_load"; |
| 32 | const char *copyright = "1999-2022"; | 32 | const char *copyright = "1999-2022"; |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 33 | const 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 | ||
| 56 | typedef struct { | ||
| 57 | int errorcode; | ||
| 58 | check_load_config config; | ||
| 59 | } check_load_config_wrapper; | ||
| 60 | static check_load_config_wrapper process_arguments(int argc, char **argv); | ||
| 61 | |||
| 62 | void print_help(void); | ||
| 63 | void print_usage(void); | ||
| 64 | typedef struct { | ||
| 65 | int errorcode; | ||
| 66 | char **top_processes; | ||
| 67 | } top_processes_result; | ||
| 68 | static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show); | ||
| 69 | |||
| 70 | typedef struct { | ||
| 71 | mp_range load[3]; | ||
| 72 | } parsed_thresholds; | ||
| 73 | static 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 | ||
| 54 | static int process_arguments (int argc, char **argv); | 88 | size_t arg_length = strlen(arg); |
| 55 | static int validate_arguments (void); | 89 | for (index = 0; index < 3; index++) { |
| 56 | void print_help (void); | 90 | double tmp = strtod(str, &tmp_pointer); |
| 57 | void print_usage (void); | 91 | if (tmp_pointer == str) { |
| 58 | static int print_top_consuming_processes(); | 92 | break; |
| 59 | 93 | } | |
| 60 | static int n_procs_to_show = 0; | ||
| 61 | |||
| 62 | /* strictly for pretty-print usage in loops */ | ||
| 63 | static const int nums[3] = { 1, 5, 15 }; | ||
| 64 | |||
| 65 | /* provide some fairly sane defaults */ | ||
| 66 | double wload[3] = { 0.0, 0.0, 0.0 }; | ||
| 67 | double cload[3] = { 0.0, 0.0, 0.0 }; | ||
| 68 | #define la1 la[0] | ||
| 69 | #define la5 la[1] | ||
| 70 | #define la15 la[2] | ||
| 71 | |||
| 72 | char *status_line; | ||
| 73 | bool take_into_account_cpus = false; | ||
| 74 | |||
| 75 | static void | ||
| 76 | get_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 | 119 | int main(int argc, char **argv) { | |
| 103 | int | 120 | setlocale(LC_ALL, ""); |
| 104 | main (int argc, char **argv) | 121 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 105 | { | 122 | textdomain(PACKAGE); |
| 106 | int result = -1; | ||
| 107 | int i; | ||
| 108 | long numcpus; | ||
| 109 | |||
| 110 | double la[3] = { 0.0, 0.0, 0.0 }; /* NetBSD complains about uninitialized arrays */ | ||
| 111 | #ifndef HAVE_GETLOADAVG | ||
| 112 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 113 | #endif | ||
| 114 | |||
| 115 | setlocale (LC_ALL, ""); | ||
| 116 | bindtextdomain (PACKAGE, LOCALEDIR); | ||
| 117 | textdomain (PACKAGE); | ||
| 118 | setlocale(LC_NUMERIC, "POSIX"); | 123 | setlocale(LC_NUMERIC, "POSIX"); |
| 119 | 124 | ||
| 120 | /* Parse extra opts if any */ | 125 | /* Parse extra opts if any */ |
| 121 | argv = np_extra_opts (&argc, argv, progname); | 126 | argv = np_extra_opts(&argc, argv, progname); |
| 122 | 127 | ||
| 123 | if (process_arguments (argc, argv) == ERROR) | 128 | check_load_config_wrapper tmp_config = process_arguments(argc, argv); |
| 124 | usage4 (_("Could not parse arguments")); | 129 | if (tmp_config.errorcode == ERROR) { |
| 125 | 130 | usage4(_("Could not parse arguments")); | |
| 126 | #ifdef HAVE_GETLOADAVG | ||
| 127 | result = getloadavg (la, 3); | ||
| 128 | if (result != 3) | ||
| 129 | return STATE_UNKNOWN; | ||
| 130 | #else | ||
| 131 | child_process = spopen (PATH_TO_UPTIME); | ||
| 132 | if (child_process == NULL) { | ||
| 133 | printf (_("Error opening %s\n"), PATH_TO_UPTIME); | ||
| 134 | return STATE_UNKNOWN; | ||
| 135 | } | ||
| 136 | child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); | ||
| 137 | if (child_stderr == NULL) { | ||
| 138 | printf (_("Could not open stderr for %s\n"), PATH_TO_UPTIME); | ||
| 139 | } | ||
| 140 | fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process); | ||
| 141 | if(strstr(input_buffer, "load average:")) { | ||
| 142 | sscanf (input_buffer, "%*[^l]load average: %lf, %lf, %lf", &la1, &la5, &la15); | ||
| 143 | } | ||
| 144 | else if(strstr(input_buffer, "load averages:")) { | ||
| 145 | sscanf (input_buffer, "%*[^l]load averages: %lf, %lf, %lf", &la1, &la5, &la15); | ||
| 146 | } | ||
| 147 | else { | ||
| 148 | printf (_("could not parse load from uptime %s: %d\n"), PATH_TO_UPTIME, result); | ||
| 149 | return STATE_UNKNOWN; | ||
| 150 | } | ||
| 151 | |||
| 152 | result = spclose (child_process); | ||
| 153 | if (result) { | ||
| 154 | printf (_("Error code %d returned in %s\n"), result, PATH_TO_UPTIME); | ||
| 155 | return STATE_UNKNOWN; | ||
| 156 | } | ||
| 157 | #endif | ||
| 158 | |||
| 159 | if ((la[0] < 0.0) || (la[1] < 0.0) || (la[2] < 0.0)) { | ||
| 160 | #ifdef HAVE_GETLOADAVG | ||
| 161 | printf (_("Error in getloadavg()\n")); | ||
| 162 | #else | ||
| 163 | printf (_("Error processing %s\n"), PATH_TO_UPTIME); | ||
| 164 | #endif | ||
| 165 | return STATE_UNKNOWN; | ||
| 166 | } | 131 | } |
| 167 | 132 | ||
| 168 | /* we got this far, so assume OK until we've measured */ | 133 | const check_load_config config = tmp_config.config; |
| 169 | result = STATE_OK; | 134 | |
| 135 | double load_values[3] = {0, 0, 0}; | ||
| 170 | 136 | ||
| 171 | xasprintf(&status_line, _("load average: %.2f, %.2f, %.2f"), la1, la5, la15); | 137 | // this should be getloadavg from gnulib, should work everywhereâ„¢ |
| 172 | xasprintf(&status_line, ("total %s"), status_line); | 138 | int error = getloadavg(load_values, 3); |
| 139 | if (error != 3) { | ||
| 140 | die(STATE_UNKNOWN, _("Failed to retrieve load values")); | ||
| 141 | } | ||
| 173 | 142 | ||
| 143 | mp_check overall = mp_check_init(); | ||
| 144 | if (config.output_format_set) { | ||
| 145 | mp_set_format(config.output_format); | ||
| 146 | } | ||
| 174 | 147 | ||
| 175 | double scaled_la[3] = { 0.0, 0.0, 0.0 }; | ||
| 176 | bool is_using_scaled_load_values = false; | 148 | bool is_using_scaled_load_values = false; |
| 177 | 149 | long numcpus; | |
| 178 | if (take_into_account_cpus == true && (numcpus = GET_NUMBER_OF_CPUS()) > 0) { | 150 | if (config.take_into_account_cpus && ((numcpus = GET_NUMBER_OF_CPUS()) > 0)) { |
| 179 | is_using_scaled_load_values = true; | 151 | is_using_scaled_load_values = true; |
| 180 | 152 | ||
| 181 | scaled_la[0] = la[0] / numcpus; | 153 | double scaled_la[3] = { |
| 182 | scaled_la[1] = la[1] / numcpus; | 154 | load_values[0] / numcpus, |
| 183 | scaled_la[2] = la[2] / numcpus; | 155 | load_values[1] / numcpus, |
| 156 | load_values[2] / numcpus, | ||
| 157 | }; | ||
| 158 | |||
| 159 | mp_subcheck scaled_load_sc = mp_subcheck_init(); | ||
| 160 | scaled_load_sc = mp_set_subcheck_default_state(scaled_load_sc, STATE_OK); | ||
| 161 | scaled_load_sc.output = "Scaled Load (divided by number of CPUs"; | ||
| 162 | |||
| 163 | mp_perfdata pd_scaled_load1 = perfdata_init(); | ||
| 164 | pd_scaled_load1.label = "scaled_load1"; | ||
| 165 | pd_scaled_load1 = mp_set_pd_value(pd_scaled_load1, scaled_la[0]); | ||
| 166 | pd_scaled_load1 = mp_pd_set_thresholds(pd_scaled_load1, config.th_load[0]); | ||
| 167 | |||
| 168 | mp_subcheck scaled_load_sc1 = mp_subcheck_init(); | ||
| 169 | scaled_load_sc1 = mp_set_subcheck_state(scaled_load_sc1, mp_get_pd_status(pd_scaled_load1)); | ||
| 170 | mp_add_perfdata_to_subcheck(&scaled_load_sc1, pd_scaled_load1); | ||
| 171 | xasprintf(&scaled_load_sc1.output, "1 Minute: %s", | ||
| 172 | pd_value_to_string(pd_scaled_load1.value)); | ||
| 173 | mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc1); | ||
| 174 | |||
| 175 | mp_perfdata pd_scaled_load5 = perfdata_init(); | ||
| 176 | pd_scaled_load5.label = "scaled_load5"; | ||
| 177 | pd_scaled_load5 = mp_set_pd_value(pd_scaled_load5, scaled_la[1]); | ||
| 178 | pd_scaled_load5 = mp_pd_set_thresholds(pd_scaled_load5, config.th_load[1]); | ||
| 179 | |||
| 180 | mp_subcheck scaled_load_sc5 = mp_subcheck_init(); | ||
| 181 | scaled_load_sc5 = mp_set_subcheck_state(scaled_load_sc5, mp_get_pd_status(pd_scaled_load5)); | ||
| 182 | mp_add_perfdata_to_subcheck(&scaled_load_sc5, pd_scaled_load5); | ||
| 183 | xasprintf(&scaled_load_sc5.output, "5 Minutes: %s", | ||
| 184 | pd_value_to_string(pd_scaled_load5.value)); | ||
| 185 | mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc5); | ||
| 186 | |||
| 187 | mp_perfdata pd_scaled_load15 = perfdata_init(); | ||
| 188 | pd_scaled_load15.label = "scaled_load15"; | ||
| 189 | pd_scaled_load15 = mp_set_pd_value(pd_scaled_load15, scaled_la[2]); | ||
| 190 | pd_scaled_load15 = mp_pd_set_thresholds(pd_scaled_load15, config.th_load[2]); | ||
| 191 | |||
| 192 | mp_subcheck scaled_load_sc15 = mp_subcheck_init(); | ||
| 193 | scaled_load_sc15 = | ||
| 194 | mp_set_subcheck_state(scaled_load_sc15, mp_get_pd_status(pd_scaled_load15)); | ||
| 195 | mp_add_perfdata_to_subcheck(&scaled_load_sc15, pd_scaled_load15); | ||
| 196 | xasprintf(&scaled_load_sc15.output, "15 Minutes: %s", | ||
| 197 | pd_value_to_string(pd_scaled_load15.value)); | ||
| 198 | mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc15); | ||
| 199 | |||
| 200 | mp_add_subcheck_to_check(&overall, scaled_load_sc); | ||
| 201 | } | ||
| 202 | |||
| 203 | mp_subcheck load_sc = mp_subcheck_init(); | ||
| 204 | load_sc = mp_set_subcheck_default_state(load_sc, STATE_OK); | ||
| 205 | load_sc.output = "Total Load"; | ||
| 184 | 206 | ||
| 185 | char *tmp = NULL; | 207 | mp_perfdata pd_load1 = perfdata_init(); |
| 186 | xasprintf(&tmp, _("load average: %.2f, %.2f, %.2f"), scaled_la[0], scaled_la[1], scaled_la[2]); | 208 | pd_load1.label = "load1"; |
| 187 | xasprintf(&status_line, "scaled %s - %s", tmp, status_line); | 209 | pd_load1 = mp_set_pd_value(pd_load1, load_values[0]); |
| 210 | if (!is_using_scaled_load_values) { | ||
| 211 | pd_load1 = mp_pd_set_thresholds(pd_load1, config.th_load[0]); | ||
| 188 | } | 212 | } |
| 189 | 213 | ||
| 190 | for(i = 0; i < 3; i++) { | 214 | mp_subcheck load_sc1 = mp_subcheck_init(); |
| 191 | if (is_using_scaled_load_values) { | 215 | load_sc1 = mp_set_subcheck_state(load_sc1, mp_get_pd_status(pd_load1)); |
| 192 | if(scaled_la[i] > cload[i]) { | 216 | mp_add_perfdata_to_subcheck(&load_sc1, pd_load1); |
| 193 | result = STATE_CRITICAL; | 217 | xasprintf(&load_sc1.output, "1 Minute: %s", pd_value_to_string(pd_load1.value)); |
| 194 | break; | 218 | mp_add_subcheck_to_subcheck(&load_sc, load_sc1); |
| 195 | } | 219 | |
| 196 | else if(scaled_la[i] > wload[i]) result = STATE_WARNING; | 220 | mp_perfdata pd_load5 = perfdata_init(); |
| 197 | } else { | 221 | pd_load5.label = "load5"; |
| 198 | if(la[i] > cload[i]) { | 222 | pd_load5 = mp_set_pd_value(pd_load5, load_values[1]); |
| 199 | result = STATE_CRITICAL; | 223 | if (!is_using_scaled_load_values) { |
| 200 | break; | 224 | pd_load5 = mp_pd_set_thresholds(pd_load5, config.th_load[1]); |
| 201 | } | ||
| 202 | else if(la[i] > wload[i]) result = STATE_WARNING; | ||
| 203 | } | ||
| 204 | } | 225 | } |
| 205 | 226 | ||
| 206 | printf("LOAD %s - %s|", state_text(result), status_line); | 227 | mp_subcheck load_sc5 = mp_subcheck_init(); |
| 207 | for(i = 0; i < 3; i++) { | 228 | load_sc5 = mp_set_subcheck_state(load_sc5, mp_get_pd_status(pd_load5)); |
| 208 | if (is_using_scaled_load_values) { | 229 | mp_add_perfdata_to_subcheck(&load_sc5, pd_load5); |
| 209 | printf("load%d=%.3f;;;0; ", nums[i], la[i]); | 230 | xasprintf(&load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_load5.value)); |
| 210 | printf("scaled_load%d=%.3f;%.3f;%.3f;0; ", nums[i], scaled_la[i], wload[i], cload[i]); | 231 | mp_add_subcheck_to_subcheck(&load_sc, load_sc5); |
| 211 | } else { | 232 | |
| 212 | printf("load%d=%.3f;%.3f;%.3f;0; ", nums[i], la[i], wload[i], cload[i]); | 233 | mp_perfdata pd_load15 = perfdata_init(); |
| 213 | } | 234 | pd_load15.label = "load15"; |
| 235 | pd_load15 = mp_set_pd_value(pd_load15, load_values[2]); | ||
| 236 | if (!is_using_scaled_load_values) { | ||
| 237 | pd_load15 = mp_pd_set_thresholds(pd_load15, config.th_load[2]); | ||
| 214 | } | 238 | } |
| 215 | 239 | ||
| 216 | putchar('\n'); | 240 | mp_subcheck load_sc15 = mp_subcheck_init(); |
| 217 | if (n_procs_to_show > 0) { | 241 | load_sc15 = mp_set_subcheck_state(load_sc15, mp_get_pd_status(pd_load15)); |
| 218 | print_top_consuming_processes(); | 242 | mp_add_perfdata_to_subcheck(&load_sc15, pd_load15); |
| 243 | xasprintf(&load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_load15.value)); | ||
| 244 | mp_add_subcheck_to_subcheck(&load_sc, load_sc15); | ||
| 245 | |||
| 246 | mp_add_subcheck_to_check(&overall, load_sc); | ||
| 247 | |||
| 248 | if (config.n_procs_to_show > 0) { | ||
| 249 | mp_subcheck top_proc_sc = mp_subcheck_init(); | ||
| 250 | top_proc_sc = mp_set_subcheck_state(top_proc_sc, STATE_OK); | ||
| 251 | top_processes_result top_proc = print_top_consuming_processes(config.n_procs_to_show); | ||
| 252 | xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes", | ||
| 253 | config.n_procs_to_show); | ||
| 254 | |||
| 255 | if (top_proc.errorcode == OK) { | ||
| 256 | for (unsigned long i = 0; i < config.n_procs_to_show; i++) { | ||
| 257 | xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output, | ||
| 258 | top_proc.top_processes[i]); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | mp_add_subcheck_to_check(&overall, top_proc_sc); | ||
| 219 | } | 263 | } |
| 220 | return result; | ||
| 221 | } | ||
| 222 | 264 | ||
| 265 | mp_exit(overall); | ||
| 266 | } | ||
| 223 | 267 | ||
| 224 | /* process command-line arguments */ | 268 | /* process command-line arguments */ |
| 225 | static int | 269 | static check_load_config_wrapper process_arguments(int argc, char **argv) { |
| 226 | process_arguments (int argc, char **argv) | 270 | |
| 227 | { | 271 | enum { |
| 228 | int c = 0; | 272 | output_format_index = CHAR_MAX + 1, |
| 229 | |||
| 230 | int option = 0; | ||
| 231 | static struct option longopts[] = { | ||
| 232 | {"warning", required_argument, 0, 'w'}, | ||
| 233 | {"critical", required_argument, 0, 'c'}, | ||
| 234 | {"percpu", no_argument, 0, 'r'}, | ||
| 235 | {"version", no_argument, 0, 'V'}, | ||
| 236 | {"help", no_argument, 0, 'h'}, | ||
| 237 | {"procs-to-show", required_argument, 0, 'n'}, | ||
| 238 | {0, 0, 0, 0} | ||
| 239 | }; | 273 | }; |
| 240 | 274 | ||
| 241 | if (argc < 2) | 275 | static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, |
| 242 | return ERROR; | 276 | {"critical", required_argument, 0, 'c'}, |
| 277 | {"percpu", no_argument, 0, 'r'}, | ||
| 278 | {"version", no_argument, 0, 'V'}, | ||
| 279 | {"help", no_argument, 0, 'h'}, | ||
| 280 | {"procs-to-show", required_argument, 0, 'n'}, | ||
| 281 | {"output-format", required_argument, 0, output_format_index}, | ||
| 282 | {0, 0, 0, 0}}; | ||
| 283 | |||
| 284 | check_load_config_wrapper result = { | ||
| 285 | .errorcode = OK, | ||
| 286 | .config = check_load_config_init(), | ||
| 287 | }; | ||
| 243 | 288 | ||
| 244 | while (1) { | 289 | if (argc < 2) { |
| 245 | c = getopt_long (argc, argv, "Vhrc:w:n:", longopts, &option); | 290 | result.errorcode = ERROR; |
| 291 | return result; | ||
| 292 | } | ||
| 246 | 293 | ||
| 247 | if (c == -1 || c == EOF) | 294 | while (true) { |
| 248 | break; | 295 | int option = 0; |
| 296 | int option_index = getopt_long(argc, argv, "Vhrc:w:n:", longopts, &option); | ||
| 249 | 297 | ||
| 250 | switch (c) { | 298 | if (option_index == -1 || option_index == EOF) { |
| 251 | case 'w': /* warning time threshold */ | ||
| 252 | get_threshold(optarg, wload); | ||
| 253 | break; | 299 | break; |
| 254 | case 'c': /* critical time threshold */ | 300 | } |
| 255 | get_threshold(optarg, cload); | 301 | |
| 302 | switch (option_index) { | ||
| 303 | case output_format_index: { | ||
| 304 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 305 | if (!parser.parsing_success) { | ||
| 306 | printf("Invalid output format: %s\n", optarg); | ||
| 307 | exit(STATE_UNKNOWN); | ||
| 308 | } | ||
| 309 | |||
| 310 | result.config.output_format_set = true; | ||
| 311 | result.config.output_format = parser.output_format; | ||
| 256 | break; | 312 | break; |
| 313 | } | ||
| 314 | case 'w': /* warning time threshold */ { | ||
| 315 | parsed_thresholds warning_range = get_threshold(optarg); | ||
| 316 | result.config.th_load[0].warning = warning_range.load[0]; | ||
| 317 | result.config.th_load[0].warning_is_set = true; | ||
| 318 | |||
| 319 | result.config.th_load[1].warning = warning_range.load[1]; | ||
| 320 | result.config.th_load[1].warning_is_set = true; | ||
| 321 | |||
| 322 | result.config.th_load[2].warning = warning_range.load[2]; | ||
| 323 | result.config.th_load[2].warning_is_set = true; | ||
| 324 | } break; | ||
| 325 | case 'c': /* critical time threshold */ { | ||
| 326 | parsed_thresholds critical_range = get_threshold(optarg); | ||
| 327 | result.config.th_load[0].critical = critical_range.load[0]; | ||
| 328 | result.config.th_load[0].critical_is_set = true; | ||
| 329 | |||
| 330 | result.config.th_load[1].critical = critical_range.load[1]; | ||
| 331 | result.config.th_load[1].critical_is_set = true; | ||
| 332 | |||
| 333 | result.config.th_load[2].critical = critical_range.load[2]; | ||
| 334 | result.config.th_load[2].critical_is_set = true; | ||
| 335 | } break; | ||
| 257 | case 'r': /* Divide load average by number of CPUs */ | 336 | case 'r': /* Divide load average by number of CPUs */ |
| 258 | take_into_account_cpus = true; | 337 | result.config.take_into_account_cpus = true; |
| 259 | break; | 338 | break; |
| 260 | case 'V': /* version */ | 339 | case 'V': /* version */ |
| 261 | print_revision (progname, NP_VERSION); | 340 | print_revision(progname, NP_VERSION); |
| 262 | exit (STATE_UNKNOWN); | 341 | exit(STATE_UNKNOWN); |
| 263 | case 'h': /* help */ | 342 | case 'h': /* help */ |
| 264 | print_help (); | 343 | print_help(); |
| 265 | exit (STATE_UNKNOWN); | 344 | exit(STATE_UNKNOWN); |
| 266 | case 'n': | 345 | case 'n': |
| 267 | n_procs_to_show = atoi(optarg); | 346 | result.config.n_procs_to_show = (unsigned long)atol(optarg); |
| 268 | break; | 347 | break; |
| 269 | case '?': /* help */ | 348 | case '?': /* help */ |
| 270 | usage5 (); | 349 | usage5(); |
| 271 | } | 350 | } |
| 272 | } | 351 | } |
| 273 | 352 | ||
| 274 | c = optind; | 353 | int index = optind; |
| 275 | if (c == argc) | 354 | if (index == argc) { |
| 276 | return validate_arguments (); | 355 | return result; |
| 356 | } | ||
| 277 | 357 | ||
| 278 | /* handle the case if both arguments are missing, | 358 | /* handle the case if both arguments are missing, |
| 279 | * but not if only one is given without -c or -w flag */ | 359 | * but not if only one is given without -c or -w flag */ |
| 280 | if(c - argc == 2) { | 360 | if (index - argc == 2) { |
| 281 | get_threshold(argv[c++], wload); | 361 | parsed_thresholds warning_range = get_threshold(argv[index++]); |
| 282 | get_threshold(argv[c++], cload); | 362 | result.config.th_load[0].warning = warning_range.load[0]; |
| 283 | } | 363 | result.config.th_load[0].warning_is_set = true; |
| 284 | else if(c - argc == 1) { | 364 | |
| 285 | get_threshold(argv[c++], cload); | 365 | result.config.th_load[1].warning = warning_range.load[1]; |
| 286 | } | 366 | result.config.th_load[1].warning_is_set = true; |
| 287 | 367 | ||
| 288 | return validate_arguments (); | 368 | result.config.th_load[2].warning = warning_range.load[2]; |
| 289 | } | 369 | result.config.th_load[2].warning_is_set = true; |
| 290 | 370 | parsed_thresholds critical_range = get_threshold(argv[index++]); | |
| 291 | 371 | result.config.th_load[0].critical = critical_range.load[0]; | |
| 292 | static int | 372 | result.config.th_load[0].critical_is_set = true; |
| 293 | validate_arguments (void) | 373 | |
| 294 | { | 374 | result.config.th_load[1].critical = critical_range.load[1]; |
| 295 | int i = 0; | 375 | result.config.th_load[1].critical_is_set = true; |
| 296 | 376 | ||
| 297 | /* match cload first, as it will give the most friendly error message | 377 | result.config.th_load[2].critical = critical_range.load[2]; |
| 298 | * if user hasn't given the -c switch properly */ | 378 | result.config.th_load[2].critical_is_set = true; |
| 299 | for(i = 0; i < 3; i++) { | 379 | } else if (index - argc == 1) { |
| 300 | if(cload[i] < 0) | 380 | parsed_thresholds critical_range = get_threshold(argv[index++]); |
| 301 | die (STATE_UNKNOWN, _("Critical threshold for %d-minute load average is not specified\n"), nums[i]); | 381 | result.config.th_load[0].critical = critical_range.load[0]; |
| 302 | if(wload[i] < 0) | 382 | result.config.th_load[0].critical_is_set = true; |
| 303 | die (STATE_UNKNOWN, _("Warning threshold for %d-minute load average is not specified\n"), nums[i]); | 383 | |
| 304 | if(wload[i] > cload[i]) | 384 | result.config.th_load[1].critical = critical_range.load[1]; |
| 305 | die (STATE_UNKNOWN, _("Parameter inconsistency: %d-minute \"warning load\" is greater than \"critical load\"\n"), nums[i]); | 385 | result.config.th_load[1].critical_is_set = true; |
| 386 | |||
| 387 | result.config.th_load[2].critical = critical_range.load[2]; | ||
| 388 | result.config.th_load[2].critical_is_set = true; | ||
| 306 | } | 389 | } |
| 307 | 390 | ||
| 308 | return OK; | 391 | return result; |
| 309 | } | 392 | } |
| 310 | 393 | ||
| 394 | void print_help(void) { | ||
| 395 | print_revision(progname, NP_VERSION); | ||
| 311 | 396 | ||
| 312 | void | 397 | printf("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n"); |
| 313 | print_help (void) | 398 | printf(COPYRIGHT, copyright, email); |
| 314 | { | ||
| 315 | print_revision (progname, NP_VERSION); | ||
| 316 | 399 | ||
| 317 | printf ("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n"); | 400 | printf(_("This plugin tests the current system load average.")); |
| 318 | printf (COPYRIGHT, copyright, email); | ||
| 319 | 401 | ||
| 320 | printf (_("This plugin tests the current system load average.")); | 402 | printf("\n\n"); |
| 321 | 403 | ||
| 322 | printf ("\n\n"); | 404 | print_usage(); |
| 323 | 405 | ||
| 324 | print_usage (); | 406 | printf(UT_HELP_VRSN); |
| 407 | printf(UT_EXTRA_OPTS); | ||
| 325 | 408 | ||
| 326 | printf (UT_HELP_VRSN); | 409 | printf(" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15"); |
| 327 | printf (UT_EXTRA_OPTS); | 410 | printf(" %s\n", _("Exit with WARNING status if load average exceeds WLOADn")); |
| 411 | printf(" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15"); | ||
| 412 | printf(" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn")); | ||
| 413 | printf(" %s\n", _("the load average format is the same used by \"uptime\" and \"w\"")); | ||
| 414 | printf(" %s\n", "-r, --percpu"); | ||
| 415 | printf(" %s\n", _("Divide the load averages by the number of CPUs (when possible)")); | ||
| 416 | printf(" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS"); | ||
| 417 | printf(" %s\n", _("Number of processes to show when printing the top consuming processes.")); | ||
| 418 | printf(" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0")); | ||
| 328 | 419 | ||
| 329 | printf (" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15"); | 420 | printf(UT_OUTPUT_FORMAT); |
| 330 | printf (" %s\n", _("Exit with WARNING status if load average exceeds WLOADn")); | 421 | printf(UT_SUPPORT); |
| 331 | printf (" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15"); | ||
| 332 | printf (" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn")); | ||
| 333 | printf (" %s\n", _("the load average format is the same used by \"uptime\" and \"w\"")); | ||
| 334 | printf (" %s\n", "-r, --percpu"); | ||
| 335 | printf (" %s\n", _("Divide the load averages by the number of CPUs (when possible)")); | ||
| 336 | printf (" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS"); | ||
| 337 | printf (" %s\n", _("Number of processes to show when printing the top consuming processes.")); | ||
| 338 | printf (" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0")); | ||
| 339 | |||
| 340 | printf (UT_SUPPORT); | ||
| 341 | } | 422 | } |
| 342 | 423 | ||
| 343 | void | 424 | void print_usage(void) { |
| 344 | print_usage (void) | 425 | printf("%s\n", _("Usage:")); |
| 345 | { | 426 | printf("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", |
| 346 | printf ("%s\n", _("Usage:")); | 427 | progname); |
| 347 | printf ("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname); | ||
| 348 | } | 428 | } |
| 349 | 429 | ||
| 350 | #ifdef PS_USES_PROCPCPU | 430 | #ifdef PS_USES_PROCPCPU |
| @@ -356,36 +436,52 @@ int cmpstringp(const void *p1, const void *p2) { | |||
| 356 | int procrss = 0; | 436 | int procrss = 0; |
| 357 | float procpcpu = 0; | 437 | float procpcpu = 0; |
| 358 | char procstat[8]; | 438 | char procstat[8]; |
| 359 | #ifdef PS_USES_PROCETIME | 439 | # ifdef PS_USES_PROCETIME |
| 360 | char procetime[MAX_INPUT_BUFFER]; | 440 | char procetime[MAX_INPUT_BUFFER]; |
| 361 | #endif /* PS_USES_PROCETIME */ | 441 | # endif /* PS_USES_PROCETIME */ |
| 362 | char procprog[MAX_INPUT_BUFFER]; | 442 | char procprog[MAX_INPUT_BUFFER]; |
| 363 | int pos; | 443 | int pos; |
| 364 | sscanf (* (char * const *) p1, PS_FORMAT, PS_VARLIST); | 444 | sscanf(*(char *const *)p1, PS_FORMAT, PS_VARLIST); |
| 365 | float procpcpu1 = procpcpu; | 445 | float procpcpu1 = procpcpu; |
| 366 | sscanf (* (char * const *) p2, PS_FORMAT, PS_VARLIST); | 446 | sscanf(*(char *const *)p2, PS_FORMAT, PS_VARLIST); |
| 367 | return procpcpu1 < procpcpu; | 447 | return procpcpu1 < procpcpu; |
| 368 | } | 448 | } |
| 369 | #endif /* PS_USES_PROCPCPU */ | 449 | #endif /* PS_USES_PROCPCPU */ |
| 370 | 450 | ||
| 371 | static int print_top_consuming_processes() { | 451 | static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show) { |
| 372 | int i = 0; | 452 | top_processes_result result = { |
| 373 | struct output chld_out, chld_err; | 453 | .errorcode = OK, |
| 374 | if(np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0){ | 454 | }; |
| 455 | output chld_out; | ||
| 456 | output chld_err; | ||
| 457 | if (np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0) { | ||
| 375 | fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND); | 458 | fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND); |
| 376 | return STATE_UNKNOWN; | 459 | result.errorcode = ERROR; |
| 460 | return result; | ||
| 377 | } | 461 | } |
| 462 | |||
| 378 | if (chld_out.lines < 2) { | 463 | if (chld_out.lines < 2) { |
| 379 | fprintf(stderr, _("some error occurred getting procs list.\n")); | 464 | fprintf(stderr, _("some error occurred getting procs list.\n")); |
| 380 | return STATE_UNKNOWN; | 465 | result.errorcode = ERROR; |
| 466 | return result; | ||
| 381 | } | 467 | } |
| 468 | |||
| 382 | #ifdef PS_USES_PROCPCPU | 469 | #ifdef PS_USES_PROCPCPU |
| 383 | qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char*), cmpstringp); | 470 | qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp); |
| 384 | #endif /* PS_USES_PROCPCPU */ | 471 | #endif /* PS_USES_PROCPCPU */ |
| 385 | int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) | 472 | unsigned long lines_to_show = |
| 386 | ? (int)chld_out.lines : n_procs_to_show + 1; | 473 | chld_out.lines < (size_t)(n_procs_to_show + 1) ? chld_out.lines : n_procs_to_show + 1; |
| 387 | for (i = 0; i < lines_to_show; i += 1) { | 474 | |
| 388 | printf("%s\n", chld_out.line[i]); | 475 | result.top_processes = calloc(lines_to_show, sizeof(char *)); |
| 476 | if (result.top_processes == NULL) { | ||
| 477 | // Failed allocation | ||
| 478 | result.errorcode = ERROR; | ||
| 479 | return result; | ||
| 389 | } | 480 | } |
| 390 | return OK; | 481 | |
| 482 | for (unsigned long i = 0; i < lines_to_show; i += 1) { | ||
| 483 | xasprintf(&result.top_processes[i], "%s", chld_out.line[i]); | ||
| 484 | } | ||
| 485 | |||
| 486 | return result; | ||
| 391 | } | 487 | } |
diff --git a/plugins/check_load.d/config.h b/plugins/check_load.d/config.h new file mode 100644 index 00000000..fd735455 --- /dev/null +++ b/plugins/check_load.d/config.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "output.h" | ||
| 4 | #include "thresholds.h" | ||
| 5 | typedef 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 | |||
| 15 | check_load_config check_load_config_init() { | ||
| 16 | check_load_config tmp = { | ||
| 17 | .th_load = | ||
| 18 | { | ||
| 19 | mp_thresholds_init(), | ||
| 20 | mp_thresholds_init(), | ||
| 21 | mp_thresholds_init(), | ||
| 22 | }, | ||
| 23 | |||
| 24 | .take_into_account_cpus = false, | ||
| 25 | .n_procs_to_show = 0, | ||
| 26 | |||
| 27 | .output_format_set = false, | ||
| 28 | }; | ||
| 29 | return tmp; | ||
| 30 | } | ||
diff --git a/plugins/check_mrtg.c b/plugins/check_mrtg.c index 632e66fb..cdc2a035 100644 --- a/plugins/check_mrtg.c +++ b/plugins/check_mrtg.c | |||
| @@ -29,27 +29,28 @@ | |||
| 29 | * | 29 | * |
| 30 | *****************************************************************************/ | 30 | *****************************************************************************/ |
| 31 | 31 | ||
| 32 | #include "common.h" | ||
| 33 | #include "output.h" | ||
| 34 | #include "perfdata.h" | ||
| 35 | #include "states.h" | ||
| 36 | #include "thresholds.h" | ||
| 37 | #include "utils.h" | ||
| 38 | #include "check_mrtg.d/config.h" | ||
| 39 | |||
| 32 | const char *progname = "check_mrtg"; | 40 | const char *progname = "check_mrtg"; |
| 33 | const char *copyright = "1999-2024"; | 41 | const char *copyright = "1999-2024"; |
| 34 | const char *email = "devel@monitoring-plugins.org"; | 42 | const char *email = "devel@monitoring-plugins.org"; |
| 35 | 43 | ||
| 36 | #include "common.h" | 44 | typedef struct { |
| 37 | #include "utils.h" | 45 | int errorcode; |
| 46 | check_mrtg_config config; | ||
| 47 | } check_mrtg_config_wrapper; | ||
| 48 | static check_mrtg_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 49 | static check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper /*config_wrapper*/); | ||
| 38 | 50 | ||
| 39 | static int process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 40 | static int validate_arguments(void); | ||
| 41 | static void print_help(void); | 51 | static void print_help(void); |
| 42 | void print_usage(void); | 52 | void print_usage(void); |
| 43 | 53 | ||
| 44 | static char *log_file = NULL; | ||
| 45 | static int expire_minutes = 0; | ||
| 46 | static bool use_average = true; | ||
| 47 | static int variable_number = -1; | ||
| 48 | static unsigned long value_warning_threshold = 0L; | ||
| 49 | static unsigned long value_critical_threshold = 0L; | ||
| 50 | static char *label; | ||
| 51 | static char *units; | ||
| 52 | |||
| 53 | int main(int argc, char **argv) { | 54 | int main(int argc, char **argv) { |
| 54 | setlocale(LC_ALL, ""); | 55 | setlocale(LC_ALL, ""); |
| 55 | bindtextdomain(PACKAGE, LOCALEDIR); | 56 | bindtextdomain(PACKAGE, LOCALEDIR); |
| @@ -58,32 +59,50 @@ int main(int argc, char **argv) { | |||
| 58 | /* Parse extra opts if any */ | 59 | /* Parse extra opts if any */ |
| 59 | argv = np_extra_opts(&argc, argv, progname); | 60 | argv = np_extra_opts(&argc, argv, progname); |
| 60 | 61 | ||
| 61 | if (process_arguments(argc, argv) == ERROR) | 62 | check_mrtg_config_wrapper tmp_config = process_arguments(argc, argv); |
| 63 | if (tmp_config.errorcode == ERROR) { | ||
| 62 | usage4(_("Could not parse arguments\n")); | 64 | usage4(_("Could not parse arguments\n")); |
| 65 | } | ||
| 66 | |||
| 67 | const check_mrtg_config config = tmp_config.config; | ||
| 68 | |||
| 69 | if (config.output_format_is_set) { | ||
| 70 | mp_set_format(config.output_format); | ||
| 71 | } | ||
| 72 | |||
| 73 | mp_check overall = mp_check_init(); | ||
| 63 | 74 | ||
| 64 | /* open the MRTG log file for reading */ | 75 | /* open the MRTG log file for reading */ |
| 65 | FILE *mtrg_log_file = fopen(log_file, "r"); | 76 | mp_subcheck sc_open_mrtg_log_file = mp_subcheck_init(); |
| 77 | FILE *mtrg_log_file = fopen(config.log_file, "r"); | ||
| 66 | if (mtrg_log_file == NULL) { | 78 | if (mtrg_log_file == NULL) { |
| 67 | printf(_("Unable to open MRTG log file\n")); | 79 | xasprintf(&sc_open_mrtg_log_file.output, "unable to open MRTG log file"); |
| 68 | return STATE_UNKNOWN; | 80 | sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_UNKNOWN); |
| 81 | mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file); | ||
| 82 | mp_exit(overall); | ||
| 83 | } else { | ||
| 84 | xasprintf(&sc_open_mrtg_log_file.output, "opened MRTG log file"); | ||
| 85 | sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_OK); | ||
| 86 | mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file); | ||
| 69 | } | 87 | } |
| 70 | 88 | ||
| 71 | time_t timestamp = 0L; | 89 | time_t timestamp = 0; |
| 72 | unsigned long average_value_rate = 0L; | 90 | unsigned long average_value_rate = 0; |
| 73 | unsigned long maximum_value_rate = 0L; | 91 | unsigned long maximum_value_rate = 0; |
| 74 | char input_buffer[MAX_INPUT_BUFFER]; | 92 | char input_buffer[MAX_INPUT_BUFFER]; |
| 75 | int line = 0; | 93 | int line = 0; |
| 76 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) { | 94 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) { |
| 77 | |||
| 78 | line++; | 95 | line++; |
| 79 | 96 | ||
| 80 | /* skip the first line of the log file */ | 97 | /* skip the first line of the log file */ |
| 81 | if (line == 1) | 98 | if (line == 1) { |
| 82 | continue; | 99 | continue; |
| 100 | } | ||
| 83 | 101 | ||
| 84 | /* break out of read loop if we've passed the number of entries we want to read */ | 102 | /* break out of read loop if we've passed the number of entries we want to read */ |
| 85 | if (line > 2) | 103 | if (line > 2) { |
| 86 | break; | 104 | break; |
| 105 | } | ||
| 87 | 106 | ||
| 88 | /* grab the timestamp */ | 107 | /* grab the timestamp */ |
| 89 | char *temp_buffer = strtok(input_buffer, " "); | 108 | char *temp_buffer = strtok(input_buffer, " "); |
| @@ -91,80 +110,122 @@ int main(int argc, char **argv) { | |||
| 91 | 110 | ||
| 92 | /* grab the average value 1 rate */ | 111 | /* grab the average value 1 rate */ |
| 93 | temp_buffer = strtok(NULL, " "); | 112 | temp_buffer = strtok(NULL, " "); |
| 94 | if (variable_number == 1) | 113 | if (config.variable_number == 1) { |
| 95 | average_value_rate = strtoul(temp_buffer, NULL, 10); | 114 | average_value_rate = strtoul(temp_buffer, NULL, 10); |
| 115 | } | ||
| 96 | 116 | ||
| 97 | /* grab the average value 2 rate */ | 117 | /* grab the average value 2 rate */ |
| 98 | temp_buffer = strtok(NULL, " "); | 118 | temp_buffer = strtok(NULL, " "); |
| 99 | if (variable_number == 2) | 119 | if (config.variable_number == 2) { |
| 100 | average_value_rate = strtoul(temp_buffer, NULL, 10); | 120 | average_value_rate = strtoul(temp_buffer, NULL, 10); |
| 121 | } | ||
| 101 | 122 | ||
| 102 | /* grab the maximum value 1 rate */ | 123 | /* grab the maximum value 1 rate */ |
| 103 | temp_buffer = strtok(NULL, " "); | 124 | temp_buffer = strtok(NULL, " "); |
| 104 | if (variable_number == 1) | 125 | if (config.variable_number == 1) { |
| 105 | maximum_value_rate = strtoul(temp_buffer, NULL, 10); | 126 | maximum_value_rate = strtoul(temp_buffer, NULL, 10); |
| 127 | } | ||
| 106 | 128 | ||
| 107 | /* grab the maximum value 2 rate */ | 129 | /* grab the maximum value 2 rate */ |
| 108 | temp_buffer = strtok(NULL, " "); | 130 | temp_buffer = strtok(NULL, " "); |
| 109 | if (variable_number == 2) | 131 | if (config.variable_number == 2) { |
| 110 | maximum_value_rate = strtoul(temp_buffer, NULL, 10); | 132 | maximum_value_rate = strtoul(temp_buffer, NULL, 10); |
| 133 | } | ||
| 111 | } | 134 | } |
| 112 | 135 | ||
| 113 | /* close the log file */ | 136 | /* close the log file */ |
| 114 | fclose(mtrg_log_file); | 137 | fclose(mtrg_log_file); |
| 115 | 138 | ||
| 116 | /* if we couldn't read enough data, return an unknown error */ | 139 | /* if we couldn't read enough data, return an unknown error */ |
| 140 | mp_subcheck sc_process_mrtg_log_file = mp_subcheck_init(); | ||
| 117 | if (line <= 2) { | 141 | if (line <= 2) { |
| 118 | printf(_("Unable to process MRTG log file\n")); | 142 | xasprintf(&sc_process_mrtg_log_file.output, "unable to process MRTG log file"); |
| 119 | return STATE_UNKNOWN; | 143 | sc_process_mrtg_log_file = mp_set_subcheck_state(sc_process_mrtg_log_file, STATE_UNKNOWN); |
| 144 | mp_exit(overall); | ||
| 145 | } else { | ||
| 146 | xasprintf(&sc_process_mrtg_log_file.output, "processed MRTG log file"); | ||
| 147 | sc_process_mrtg_log_file = mp_set_subcheck_state(sc_process_mrtg_log_file, STATE_OK); | ||
| 148 | mp_add_subcheck_to_check(&overall, sc_process_mrtg_log_file); | ||
| 120 | } | 149 | } |
| 121 | 150 | ||
| 122 | /* make sure the MRTG data isn't too old */ | 151 | /* make sure the MRTG data isn't too old */ |
| 123 | time_t current_time; | 152 | time_t current_time; |
| 124 | time(¤t_time); | 153 | time(¤t_time); |
| 125 | if (expire_minutes > 0 && (current_time - timestamp) > (expire_minutes * 60)) { | 154 | mp_subcheck sc_data_expired = mp_subcheck_init(); |
| 126 | printf(_("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); | 155 | if (config.expire_minutes > 0 && (current_time - timestamp) > (config.expire_minutes * 60)) { |
| 127 | return STATE_WARNING; | 156 | xasprintf(&sc_data_expired.output, "MRTG data has expired (%d minutes old)", |
| 157 | (int)((current_time - timestamp) / 60)); | ||
| 158 | sc_data_expired = mp_set_subcheck_state(sc_data_expired, STATE_WARNING); | ||
| 159 | mp_add_subcheck_to_check(&overall, sc_data_expired); | ||
| 160 | mp_exit(overall); | ||
| 161 | } else { | ||
| 162 | xasprintf(&sc_data_expired.output, "MRTG data should be valid (%d minutes old)", | ||
| 163 | (int)((current_time - timestamp) / 60)); | ||
| 164 | sc_data_expired = mp_set_subcheck_state(sc_data_expired, STATE_OK); | ||
| 165 | mp_add_subcheck_to_check(&overall, sc_data_expired); | ||
| 128 | } | 166 | } |
| 129 | 167 | ||
| 130 | unsigned long rate = 0L; | 168 | unsigned long rate = 0L; |
| 131 | /* else check the incoming/outgoing rates */ | 169 | /* else check the incoming/outgoing rates */ |
| 132 | if (use_average) | 170 | if (config.use_average) { |
| 133 | rate = average_value_rate; | 171 | rate = average_value_rate; |
| 134 | else | 172 | } else { |
| 135 | rate = maximum_value_rate; | 173 | rate = maximum_value_rate; |
| 174 | } | ||
| 175 | |||
| 176 | mp_subcheck sc_values = mp_subcheck_init(); | ||
| 177 | mp_perfdata pd_value = perfdata_init(); | ||
| 178 | pd_value = mp_set_pd_value(pd_value, rate); | ||
| 179 | pd_value.label = config.label; | ||
| 180 | pd_value = mp_pd_set_thresholds(pd_value, config.values_threshold); | ||
| 136 | 181 | ||
| 137 | int result = STATE_OK; | 182 | sc_values = mp_set_subcheck_state(sc_values, mp_get_pd_status(pd_value)); |
| 138 | if (rate > value_critical_threshold) | 183 | xasprintf(&sc_values.output, "%s. %s = %lu %s", (config.use_average) ? _("Avg") : _("Max"), |
| 139 | result = STATE_CRITICAL; | 184 | config.label, rate, config.units); |
| 140 | else if (rate > value_warning_threshold) | ||
| 141 | result = STATE_WARNING; | ||
| 142 | 185 | ||
| 143 | printf("%s. %s = %lu %s|%s\n", (use_average) ? _("Avg") : _("Max"), label, rate, units, | 186 | mp_add_subcheck_to_check(&overall, sc_values); |
| 144 | perfdata(label, (long)rate, units, (int)value_warning_threshold, (long)value_warning_threshold, (int)value_critical_threshold, | ||
| 145 | (long)value_critical_threshold, 0, 0, 0, 0)); | ||
| 146 | 187 | ||
| 147 | return result; | 188 | mp_exit(overall); |
| 148 | } | 189 | } |
| 149 | 190 | ||
| 150 | /* process command-line arguments */ | 191 | /* process command-line arguments */ |
| 151 | int process_arguments(int argc, char **argv) { | 192 | check_mrtg_config_wrapper process_arguments(int argc, char **argv) { |
| 152 | static struct option longopts[] = { | 193 | enum { |
| 153 | {"logfile", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, {"aggregation", required_argument, 0, 'a'}, | 194 | output_format_index, |
| 154 | {"variable", required_argument, 0, 'v'}, {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, | 195 | }; |
| 155 | {"label", required_argument, 0, 'l'}, {"units", required_argument, 0, 'u'}, {"variable", required_argument, 0, 'v'}, | 196 | |
| 156 | {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; | 197 | static struct option longopts[] = {{"logfile", required_argument, 0, 'F'}, |
| 157 | 198 | {"expires", required_argument, 0, 'e'}, | |
| 158 | if (argc < 2) | 199 | {"aggregation", required_argument, 0, 'a'}, |
| 159 | return ERROR; | 200 | {"variable", required_argument, 0, 'v'}, |
| 201 | {"critical", required_argument, 0, 'c'}, | ||
| 202 | {"warning", required_argument, 0, 'w'}, | ||
| 203 | {"label", required_argument, 0, 'l'}, | ||
| 204 | {"units", required_argument, 0, 'u'}, | ||
| 205 | {"variable", required_argument, 0, 'v'}, | ||
| 206 | {"version", no_argument, 0, 'V'}, | ||
| 207 | {"help", no_argument, 0, 'h'}, | ||
| 208 | {"output-format", required_argument, 0, output_format_index}, | ||
| 209 | {0, 0, 0, 0}}; | ||
| 210 | |||
| 211 | check_mrtg_config_wrapper result = { | ||
| 212 | .errorcode = OK, | ||
| 213 | .config = check_mrtg_config_init(), | ||
| 214 | }; | ||
| 215 | |||
| 216 | if (argc < 2) { | ||
| 217 | result.errorcode = ERROR; | ||
| 218 | return result; | ||
| 219 | } | ||
| 160 | 220 | ||
| 161 | for (int i = 1; i < argc; i++) { | 221 | for (int i = 1; i < argc; i++) { |
| 162 | if (strcmp("-to", argv[i]) == 0) | 222 | if (strcmp("-to", argv[i]) == 0) { |
| 163 | strcpy(argv[i], "-t"); | 223 | strcpy(argv[i], "-t"); |
| 164 | else if (strcmp("-wt", argv[i]) == 0) | 224 | } else if (strcmp("-wt", argv[i]) == 0) { |
| 165 | strcpy(argv[i], "-w"); | 225 | strcpy(argv[i], "-w"); |
| 166 | else if (strcmp("-ct", argv[i]) == 0) | 226 | } else if (strcmp("-ct", argv[i]) == 0) { |
| 167 | strcpy(argv[i], "-c"); | 227 | strcpy(argv[i], "-c"); |
| 228 | } | ||
| 168 | } | 229 | } |
| 169 | 230 | ||
| 170 | int option_char; | 231 | int option_char; |
| @@ -172,38 +233,47 @@ int process_arguments(int argc, char **argv) { | |||
| 172 | while (1) { | 233 | while (1) { |
| 173 | option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option); | 234 | option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option); |
| 174 | 235 | ||
| 175 | if (option_char == -1 || option_char == EOF) | 236 | if (option_char == -1 || option_char == EOF) { |
| 176 | break; | 237 | break; |
| 238 | } | ||
| 177 | 239 | ||
| 178 | switch (option_char) { | 240 | switch (option_char) { |
| 179 | case 'F': /* input file */ | 241 | case 'F': /* input file */ |
| 180 | log_file = optarg; | 242 | result.config.log_file = optarg; |
| 181 | break; | 243 | break; |
| 182 | case 'e': /* ups name */ | 244 | case 'e': /* ups name */ |
| 183 | expire_minutes = atoi(optarg); | 245 | result.config.expire_minutes = atoi(optarg); |
| 184 | break; | 246 | break; |
| 185 | case 'a': /* port */ | 247 | case 'a': /* port */ |
| 186 | if (!strcmp(optarg, "MAX")) | 248 | result.config.use_average = (bool)(strcmp(optarg, "MAX")); |
| 187 | use_average = false; | ||
| 188 | else | ||
| 189 | use_average = true; | ||
| 190 | break; | 249 | break; |
| 191 | case 'v': | 250 | case 'v': |
| 192 | variable_number = atoi(optarg); | 251 | result.config.variable_number = atoi(optarg); |
| 193 | if (variable_number < 1 || variable_number > 2) | 252 | if (result.config.variable_number < 1 || result.config.variable_number > 2) { |
| 194 | usage4(_("Invalid variable number")); | 253 | usage4(_("Invalid variable number")); |
| 254 | } | ||
| 195 | break; | 255 | break; |
| 196 | case 'w': /* critical time threshold */ | 256 | case 'w': /* critical time threshold */ { |
| 197 | value_warning_threshold = strtoul(optarg, NULL, 10); | 257 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 198 | break; | 258 | if (tmp.error != MP_PARSING_SUCCES) { |
| 199 | case 'c': /* warning time threshold */ | 259 | die(STATE_UNKNOWN, "failed to parse warning threshold"); |
| 200 | value_critical_threshold = strtoul(optarg, NULL, 10); | 260 | } |
| 201 | break; | 261 | result.config.values_threshold = |
| 262 | mp_thresholds_set_warn(result.config.values_threshold, tmp.range); | ||
| 263 | } break; | ||
| 264 | case 'c': /* warning time threshold */ { | ||
| 265 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 266 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 267 | die(STATE_UNKNOWN, "failed to parse critical threshold"); | ||
| 268 | } | ||
| 269 | result.config.values_threshold = | ||
| 270 | mp_thresholds_set_crit(result.config.values_threshold, tmp.range); | ||
| 271 | } break; | ||
| 202 | case 'l': /* label */ | 272 | case 'l': /* label */ |
| 203 | label = optarg; | 273 | result.config.label = optarg; |
| 204 | break; | 274 | break; |
| 205 | case 'u': /* timeout */ | 275 | case 'u': /* timeout */ |
| 206 | units = optarg; | 276 | result.config.units = optarg; |
| 207 | break; | 277 | break; |
| 208 | case 'V': /* version */ | 278 | case 'V': /* version */ |
| 209 | print_revision(progname, NP_VERSION); | 279 | print_revision(progname, NP_VERSION); |
| @@ -213,67 +283,94 @@ int process_arguments(int argc, char **argv) { | |||
| 213 | exit(STATE_UNKNOWN); | 283 | exit(STATE_UNKNOWN); |
| 214 | case '?': /* help */ | 284 | case '?': /* help */ |
| 215 | usage5(); | 285 | usage5(); |
| 286 | case output_format_index: { | ||
| 287 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 288 | if (!parser.parsing_success) { | ||
| 289 | printf("Invalid output format: %s\n", optarg); | ||
| 290 | exit(STATE_UNKNOWN); | ||
| 291 | } | ||
| 292 | |||
| 293 | result.config.output_format_is_set = true; | ||
| 294 | result.config.output_format = parser.output_format; | ||
| 295 | break; | ||
| 296 | } | ||
| 216 | } | 297 | } |
| 217 | } | 298 | } |
| 218 | 299 | ||
| 219 | option_char = optind; | 300 | option_char = optind; |
| 220 | if (log_file == NULL && argc > option_char) { | 301 | if (result.config.log_file == NULL && argc > option_char) { |
| 221 | log_file = argv[option_char++]; | 302 | result.config.log_file = argv[option_char++]; |
| 222 | } | 303 | } |
| 223 | 304 | ||
| 224 | if (expire_minutes <= 0 && argc > option_char) { | 305 | if (result.config.expire_minutes <= 0 && argc > option_char) { |
| 225 | if (is_intpos(argv[option_char])) | 306 | if (is_intpos(argv[option_char])) { |
| 226 | expire_minutes = atoi(argv[option_char++]); | 307 | result.config.expire_minutes = atoi(argv[option_char++]); |
| 227 | else | 308 | } else { |
| 228 | die(STATE_UNKNOWN, _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), argv[option_char], progname); | 309 | die(STATE_UNKNOWN, |
| 310 | _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), | ||
| 311 | argv[option_char], progname); | ||
| 312 | } | ||
| 229 | } | 313 | } |
| 230 | 314 | ||
| 231 | if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { | 315 | if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { |
| 232 | use_average = false; | 316 | result.config.use_average = false; |
| 233 | option_char++; | 317 | option_char++; |
| 234 | } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { | 318 | } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { |
| 235 | use_average = true; | 319 | result.config.use_average = true; |
| 236 | option_char++; | 320 | option_char++; |
| 237 | } | 321 | } |
| 238 | 322 | ||
| 239 | if (argc > option_char && variable_number == -1) { | 323 | if (argc > option_char && result.config.variable_number == -1) { |
| 240 | variable_number = atoi(argv[option_char++]); | 324 | result.config.variable_number = atoi(argv[option_char++]); |
| 241 | if (variable_number < 1 || variable_number > 2) { | 325 | if (result.config.variable_number < 1 || result.config.variable_number > 2) { |
| 242 | printf("%s :", argv[option_char]); | 326 | printf("%s :", argv[option_char]); |
| 243 | usage(_("Invalid variable number\n")); | 327 | usage(_("Invalid variable number\n")); |
| 244 | } | 328 | } |
| 245 | } | 329 | } |
| 246 | 330 | ||
| 247 | if (argc > option_char && value_warning_threshold == 0) { | 331 | if (argc > option_char && !result.config.values_threshold.warning_is_set) { |
| 248 | value_warning_threshold = strtoul(argv[option_char++], NULL, 10); | 332 | mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]); |
| 333 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 334 | die(STATE_UNKNOWN, "failed to parse warning threshold"); | ||
| 335 | } | ||
| 336 | result.config.values_threshold = | ||
| 337 | mp_thresholds_set_warn(result.config.values_threshold, tmp.range); | ||
| 249 | } | 338 | } |
| 250 | 339 | ||
| 251 | if (argc > option_char && value_critical_threshold == 0) { | 340 | if (argc > option_char && !result.config.values_threshold.critical_is_set) { |
| 252 | value_critical_threshold = strtoul(argv[option_char++], NULL, 10); | 341 | mp_range_parsed tmp = mp_parse_range_string(argv[option_char++]); |
| 342 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 343 | die(STATE_UNKNOWN, "failed to parse critical threshold"); | ||
| 344 | } | ||
| 345 | result.config.values_threshold = | ||
| 346 | mp_thresholds_set_crit(result.config.values_threshold, tmp.range); | ||
| 253 | } | 347 | } |
| 254 | 348 | ||
| 255 | if (argc > option_char && strlen(label) == 0) { | 349 | if (argc > option_char && strlen(result.config.label) == 0) { |
| 256 | label = argv[option_char++]; | 350 | result.config.label = argv[option_char++]; |
| 257 | } | 351 | } |
| 258 | 352 | ||
| 259 | if (argc > option_char && strlen(units) == 0) { | 353 | if (argc > option_char && strlen(result.config.units) == 0) { |
| 260 | units = argv[option_char++]; | 354 | result.config.units = argv[option_char++]; |
| 261 | } | 355 | } |
| 262 | 356 | ||
| 263 | return validate_arguments(); | 357 | return validate_arguments(result); |
| 264 | } | 358 | } |
| 265 | 359 | ||
| 266 | int validate_arguments(void) { | 360 | check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper config_wrapper) { |
| 267 | if (variable_number == -1) | 361 | if (config_wrapper.config.variable_number == -1) { |
| 268 | usage4(_("You must supply the variable number")); | 362 | usage4(_("You must supply the variable number")); |
| 363 | } | ||
| 269 | 364 | ||
| 270 | if (label == NULL) | 365 | if (config_wrapper.config.label == NULL) { |
| 271 | label = strdup("value"); | 366 | config_wrapper.config.label = strdup("value"); |
| 367 | } | ||
| 272 | 368 | ||
| 273 | if (units == NULL) | 369 | if (config_wrapper.config.units == NULL) { |
| 274 | units = strdup(""); | 370 | config_wrapper.config.units = strdup(""); |
| 371 | } | ||
| 275 | 372 | ||
| 276 | return OK; | 373 | return config_wrapper; |
| 277 | } | 374 | } |
| 278 | 375 | ||
| 279 | void print_help(void) { | 376 | void print_help(void) { |
| @@ -310,26 +407,35 @@ void print_help(void) { | |||
| 310 | printf(" %s\n", _("Option units label for data (Example: Packets/Sec, Errors/Sec,")); | 407 | printf(" %s\n", _("Option units label for data (Example: Packets/Sec, Errors/Sec,")); |
| 311 | printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")")); | 408 | printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")")); |
| 312 | 409 | ||
| 410 | printf(UT_OUTPUT_FORMAT); | ||
| 411 | |||
| 313 | printf("\n"); | 412 | printf("\n"); |
| 314 | printf(" %s\n", _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If")); | 413 | printf(" %s\n", |
| 414 | _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If")); | ||
| 315 | printf(" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If")); | 415 | printf(" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If")); |
| 316 | printf(" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING")); | 416 | printf(" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING")); |
| 317 | printf(" %s\n", _("status is returned and a warning message is printed.")); | 417 | printf(" %s\n", _("status is returned and a warning message is printed.")); |
| 318 | 418 | ||
| 319 | printf("\n"); | 419 | printf("\n"); |
| 320 | printf(" %s\n", _("This plugin is useful for monitoring MRTG data that does not correspond to")); | 420 | printf(" %s\n", |
| 321 | printf(" %s\n", _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); | 421 | _("This plugin is useful for monitoring MRTG data that does not correspond to")); |
| 322 | printf(" %s\n", _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); | 422 | printf(" %s\n", |
| 323 | printf(" %s\n", _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); | 423 | _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); |
| 424 | printf(" %s\n", | ||
| 425 | _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); | ||
| 426 | printf(" %s\n", | ||
| 427 | _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); | ||
| 324 | printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and")); | 428 | printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and")); |
| 325 | printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well.")); | 429 | printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well.")); |
| 326 | 430 | ||
| 327 | printf("%s\n", _("Notes:")); | 431 | printf("%s\n", _("Notes:")); |
| 328 | printf(" %s\n", _("- This plugin only monitors one of the two variables stored in the MRTG log")); | 432 | printf(" %s\n", |
| 433 | _("- This plugin only monitors one of the two variables stored in the MRTG log")); | ||
| 329 | printf(" %s\n", _("file. If you want to monitor both values you will have to define two")); | 434 | printf(" %s\n", _("file. If you want to monitor both values you will have to define two")); |
| 330 | printf(" %s\n", _("commands with different values for the <variable> argument. Of course,")); | 435 | printf(" %s\n", _("commands with different values for the <variable> argument. Of course,")); |
| 331 | printf(" %s\n", _("you can always hack the code to make this plugin work for you...")); | 436 | printf(" %s\n", _("you can always hack the code to make this plugin work for you...")); |
| 332 | printf(" %s\n", _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); | 437 | printf(" %s\n", |
| 438 | _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); | ||
| 333 | printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); | 439 | printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); |
| 334 | 440 | ||
| 335 | printf(UT_SUPPORT); | 441 | printf(UT_SUPPORT); |
diff --git a/plugins/check_mrtg.d/config.h b/plugins/check_mrtg.d/config.h new file mode 100644 index 00000000..4a5b5595 --- /dev/null +++ b/plugins/check_mrtg.d/config.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <stdlib.h> | ||
| 8 | |||
| 9 | typedef struct { | ||
| 10 | bool use_average; | ||
| 11 | int variable_number; | ||
| 12 | int expire_minutes; | ||
| 13 | char *label; | ||
| 14 | char *units; | ||
| 15 | char *log_file; | ||
| 16 | |||
| 17 | mp_thresholds values_threshold; | ||
| 18 | |||
| 19 | bool output_format_is_set; | ||
| 20 | mp_output_format output_format; | ||
| 21 | } check_mrtg_config; | ||
| 22 | |||
| 23 | check_mrtg_config check_mrtg_config_init() { | ||
| 24 | check_mrtg_config tmp = { | ||
| 25 | .use_average = true, | ||
| 26 | .variable_number = -1, | ||
| 27 | .expire_minutes = 0, | ||
| 28 | .label = NULL, | ||
| 29 | .units = NULL, | ||
| 30 | .log_file = NULL, | ||
| 31 | |||
| 32 | .values_threshold = mp_thresholds_init(), | ||
| 33 | |||
| 34 | .output_format_is_set = false, | ||
| 35 | }; | ||
| 36 | return tmp; | ||
| 37 | } | ||
diff --git a/plugins/check_mrtgtraf.c b/plugins/check_mrtgtraf.c index e5a2e2ad..46b94f57 100644 --- a/plugins/check_mrtgtraf.c +++ b/plugins/check_mrtgtraf.c | |||
| @@ -29,25 +29,27 @@ | |||
| 29 | * | 29 | * |
| 30 | *****************************************************************************/ | 30 | *****************************************************************************/ |
| 31 | 31 | ||
| 32 | #include "check_mrtgtraf.d/config.h" | ||
| 32 | #include "common.h" | 33 | #include "common.h" |
| 34 | #include "output.h" | ||
| 35 | #include "perfdata.h" | ||
| 36 | #include "states.h" | ||
| 37 | #include "thresholds.h" | ||
| 33 | #include "utils.h" | 38 | #include "utils.h" |
| 34 | 39 | ||
| 35 | const char *progname = "check_mrtgtraf"; | 40 | const char *progname = "check_mrtgtraf"; |
| 36 | const char *copyright = "1999-2024"; | 41 | const char *copyright = "1999-2024"; |
| 37 | const char *email = "devel@monitoring-plugins.org"; | 42 | const char *email = "devel@monitoring-plugins.org"; |
| 38 | 43 | ||
| 39 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 44 | typedef struct { |
| 45 | int errorcode; | ||
| 46 | check_mrtgtraf_config config; | ||
| 47 | } check_mrtgtraf_config_wrapper; | ||
| 48 | |||
| 49 | static check_mrtgtraf_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 40 | static void print_help(void); | 50 | static void print_help(void); |
| 41 | void print_usage(void); | 51 | void print_usage(void); |
| 42 | 52 | ||
| 43 | static char *log_file = NULL; | ||
| 44 | static int expire_minutes = -1; | ||
| 45 | static bool use_average = true; | ||
| 46 | static unsigned long incoming_warning_threshold = 0L; | ||
| 47 | static unsigned long incoming_critical_threshold = 0L; | ||
| 48 | static unsigned long outgoing_warning_threshold = 0L; | ||
| 49 | static unsigned long outgoing_critical_threshold = 0L; | ||
| 50 | |||
| 51 | int main(int argc, char **argv) { | 53 | int main(int argc, char **argv) { |
| 52 | setlocale(LC_ALL, ""); | 54 | setlocale(LC_ALL, ""); |
| 53 | bindtextdomain(PACKAGE, LOCALEDIR); | 55 | bindtextdomain(PACKAGE, LOCALEDIR); |
| @@ -56,13 +58,32 @@ int main(int argc, char **argv) { | |||
| 56 | /* Parse extra opts if any */ | 58 | /* Parse extra opts if any */ |
| 57 | argv = np_extra_opts(&argc, argv, progname); | 59 | argv = np_extra_opts(&argc, argv, progname); |
| 58 | 60 | ||
| 59 | if (process_arguments(argc, argv) == ERROR) | 61 | check_mrtgtraf_config_wrapper tmp_config = process_arguments(argc, argv); |
| 62 | if (tmp_config.errorcode == ERROR) { | ||
| 60 | usage4(_("Could not parse arguments")); | 63 | usage4(_("Could not parse arguments")); |
| 64 | } | ||
| 65 | |||
| 66 | const check_mrtgtraf_config config = tmp_config.config; | ||
| 67 | |||
| 68 | if (config.output_format_is_set) { | ||
| 69 | mp_set_format(config.output_format); | ||
| 70 | } | ||
| 71 | |||
| 72 | mp_check overall = mp_check_init(); | ||
| 73 | mp_subcheck sc_open_mrtg_log_file = mp_subcheck_init(); | ||
| 61 | 74 | ||
| 62 | /* open the MRTG log file for reading */ | 75 | /* open the MRTG log file for reading */ |
| 63 | FILE *mrtg_log_file_ptr = fopen(log_file, "r"); | 76 | FILE *mrtg_log_file_ptr = fopen(config.log_file, "r"); |
| 64 | if (mrtg_log_file_ptr == NULL) | 77 | if (mrtg_log_file_ptr == NULL) { |
| 65 | usage4(_("Unable to open MRTG log file")); | 78 | sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_UNKNOWN); |
| 79 | xasprintf(&sc_open_mrtg_log_file.output, "unable to open MRTG log file"); | ||
| 80 | mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file); | ||
| 81 | mp_exit(overall); | ||
| 82 | } else { | ||
| 83 | sc_open_mrtg_log_file = mp_set_subcheck_state(sc_open_mrtg_log_file, STATE_OK); | ||
| 84 | xasprintf(&sc_open_mrtg_log_file.output, "opened MRTG log file"); | ||
| 85 | mp_add_subcheck_to_check(&overall, sc_open_mrtg_log_file); | ||
| 86 | } | ||
| 66 | 87 | ||
| 67 | time_t timestamp = 0L; | 88 | time_t timestamp = 0L; |
| 68 | char input_buffer[MAX_INPUT_BUFFER]; | 89 | char input_buffer[MAX_INPUT_BUFFER]; |
| @@ -72,17 +93,18 @@ int main(int argc, char **argv) { | |||
| 72 | unsigned long maximum_outgoing_rate = 0L; | 93 | unsigned long maximum_outgoing_rate = 0L; |
| 73 | int line = 0; | 94 | int line = 0; |
| 74 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mrtg_log_file_ptr)) { | 95 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mrtg_log_file_ptr)) { |
| 75 | |||
| 76 | line++; | 96 | line++; |
| 77 | 97 | ||
| 78 | /* skip the first line of the log file */ | 98 | /* skip the first line of the log file */ |
| 79 | if (line == 1) | 99 | if (line == 1) { |
| 80 | continue; | 100 | continue; |
| 101 | } | ||
| 81 | 102 | ||
| 82 | /* break out of read loop */ | 103 | /* break out of read loop */ |
| 83 | /* if we've passed the number of entries we want to read */ | 104 | /* if we've passed the number of entries we want to read */ |
| 84 | if (line > 2) | 105 | if (line > 2) { |
| 85 | break; | 106 | break; |
| 107 | } | ||
| 86 | 108 | ||
| 87 | /* grab the timestamp */ | 109 | /* grab the timestamp */ |
| 88 | char *temp_buffer = strtok(input_buffer, " "); | 110 | char *temp_buffer = strtok(input_buffer, " "); |
| @@ -109,19 +131,31 @@ int main(int argc, char **argv) { | |||
| 109 | fclose(mrtg_log_file_ptr); | 131 | fclose(mrtg_log_file_ptr); |
| 110 | 132 | ||
| 111 | /* if we couldn't read enough data, return an unknown error */ | 133 | /* if we couldn't read enough data, return an unknown error */ |
| 112 | if (line <= 2) | 134 | if (line <= 2) { |
| 113 | usage4(_("Unable to process MRTG log file")); | 135 | usage4(_("Unable to process MRTG log file")); |
| 136 | } | ||
| 114 | 137 | ||
| 115 | /* make sure the MRTG data isn't too old */ | 138 | /* make sure the MRTG data isn't too old */ |
| 116 | time_t current_time; | 139 | time_t current_time; |
| 117 | time(¤t_time); | 140 | time(¤t_time); |
| 118 | if ((expire_minutes > 0) && (current_time - timestamp) > (expire_minutes * 60)) | 141 | mp_subcheck sc_expired = mp_subcheck_init(); |
| 119 | die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); | 142 | if ((config.expire_minutes > 0) && (current_time - timestamp) > (config.expire_minutes * 60)) { |
| 143 | xasprintf(&sc_expired.output, "MRTG data has expired (%d minutes old)", | ||
| 144 | (int)((current_time - timestamp) / 60)); | ||
| 145 | sc_expired = mp_set_subcheck_state(sc_expired, STATE_WARNING); | ||
| 146 | mp_add_subcheck_to_check(&overall, sc_expired); | ||
| 147 | mp_exit(overall); | ||
| 148 | } | ||
| 149 | |||
| 150 | xasprintf(&sc_expired.output, "MRTG data should be valid (%d minutes old)", | ||
| 151 | (int)((current_time - timestamp) / 60)); | ||
| 152 | sc_expired = mp_set_subcheck_state(sc_expired, STATE_WARNING); | ||
| 153 | mp_add_subcheck_to_check(&overall, sc_expired); | ||
| 120 | 154 | ||
| 121 | unsigned long incoming_rate = 0L; | 155 | unsigned long incoming_rate = 0L; |
| 122 | unsigned long outgoing_rate = 0L; | 156 | unsigned long outgoing_rate = 0L; |
| 123 | /* else check the incoming/outgoing rates */ | 157 | /* else check the incoming/outgoing rates */ |
| 124 | if (use_average) { | 158 | if (config.use_average) { |
| 125 | incoming_rate = average_incoming_rate; | 159 | incoming_rate = average_incoming_rate; |
| 126 | outgoing_rate = average_outgoing_rate; | 160 | outgoing_rate = average_outgoing_rate; |
| 127 | } else { | 161 | } else { |
| @@ -140,58 +174,72 @@ int main(int argc, char **argv) { | |||
| 140 | /* report incoming traffic in KBytes/sec */ | 174 | /* report incoming traffic in KBytes/sec */ |
| 141 | else if (incoming_rate < (1024 * 1024)) { | 175 | else if (incoming_rate < (1024 * 1024)) { |
| 142 | strcpy(incoming_speed_rating, "KB"); | 176 | strcpy(incoming_speed_rating, "KB"); |
| 143 | adjusted_incoming_rate = (double)(incoming_rate / 1024.0); | 177 | adjusted_incoming_rate = ((double)incoming_rate / 1024.0); |
| 144 | } | 178 | } |
| 145 | 179 | ||
| 146 | /* report incoming traffic in MBytes/sec */ | 180 | /* report incoming traffic in MBytes/sec */ |
| 147 | else { | 181 | else { |
| 148 | strcpy(incoming_speed_rating, "MB"); | 182 | strcpy(incoming_speed_rating, "MB"); |
| 149 | adjusted_incoming_rate = (double)(incoming_rate / 1024.0 / 1024.0); | 183 | adjusted_incoming_rate = ((double)incoming_rate / 1024.0 / 1024.0); |
| 150 | } | 184 | } |
| 151 | 185 | ||
| 152 | double adjusted_outgoing_rate = 0.0; | 186 | double adjusted_outgoing_rate = 0.0; |
| 153 | char outgoing_speed_rating[8]; | 187 | char outgoing_speed_rating[8]; |
| 154 | /* report outgoing traffic in Bytes/sec */ | ||
| 155 | if (outgoing_rate < 1024) { | 188 | if (outgoing_rate < 1024) { |
| 189 | /* report outgoing traffic in Bytes/sec */ | ||
| 156 | strcpy(outgoing_speed_rating, "B"); | 190 | strcpy(outgoing_speed_rating, "B"); |
| 157 | adjusted_outgoing_rate = (double)outgoing_rate; | 191 | adjusted_outgoing_rate = (double)outgoing_rate; |
| 158 | } | 192 | } else if (outgoing_rate < (1024 * 1024)) { |
| 159 | 193 | /* report outgoing traffic in KBytes/sec */ | |
| 160 | /* report outgoing traffic in KBytes/sec */ | ||
| 161 | else if (outgoing_rate < (1024 * 1024)) { | ||
| 162 | strcpy(outgoing_speed_rating, "KB"); | 194 | strcpy(outgoing_speed_rating, "KB"); |
| 163 | adjusted_outgoing_rate = (double)(outgoing_rate / 1024.0); | 195 | adjusted_outgoing_rate = ((double)outgoing_rate / 1024.0); |
| 164 | } | 196 | } else { |
| 165 | 197 | /* report outgoing traffic in MBytes/sec */ | |
| 166 | /* report outgoing traffic in MBytes/sec */ | ||
| 167 | else { | ||
| 168 | strcpy(outgoing_speed_rating, "MB"); | 198 | strcpy(outgoing_speed_rating, "MB"); |
| 169 | adjusted_outgoing_rate = (double)(outgoing_rate / 1024.0 / 1024.0); | 199 | adjusted_outgoing_rate = ((double)outgoing_rate / 1024.0 / 1024.0); |
| 170 | } | ||
| 171 | |||
| 172 | int result = STATE_OK; | ||
| 173 | if (incoming_rate > incoming_critical_threshold || outgoing_rate > outgoing_critical_threshold) { | ||
| 174 | result = STATE_CRITICAL; | ||
| 175 | } else if (incoming_rate > incoming_warning_threshold || outgoing_rate > outgoing_warning_threshold) { | ||
| 176 | result = STATE_WARNING; | ||
| 177 | } | 200 | } |
| 178 | 201 | ||
| 179 | char *error_message; | 202 | mp_perfdata pd_rate_in = perfdata_init(); |
| 180 | xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"), (use_average) ? _("Avg") : _("Max"), | 203 | pd_rate_in.label = "in"; |
| 181 | adjusted_incoming_rate, incoming_speed_rating, (use_average) ? _("Avg") : _("Max"), adjusted_outgoing_rate, | 204 | pd_rate_in = mp_set_pd_value(pd_rate_in, incoming_rate); |
| 182 | outgoing_speed_rating, | 205 | pd_rate_in.uom = "B"; |
| 183 | fperfdata("in", adjusted_incoming_rate, incoming_speed_rating, (int)incoming_warning_threshold, incoming_warning_threshold, | 206 | pd_rate_in = mp_pd_set_thresholds(pd_rate_in, config.incoming_thresholds); |
| 184 | (int)incoming_critical_threshold, incoming_critical_threshold, true, 0, false, 0), | 207 | |
| 185 | fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating, (int)outgoing_warning_threshold, outgoing_warning_threshold, | 208 | mp_perfdata pd_rate_out = perfdata_init(); |
| 186 | (int)outgoing_critical_threshold, outgoing_critical_threshold, true, 0, false, 0)); | 209 | pd_rate_out.label = "out"; |
| 187 | 210 | pd_rate_out = mp_set_pd_value(pd_rate_out, outgoing_rate); | |
| 188 | printf(_("Traffic %s - %s\n"), state_text(result), error_message); | 211 | pd_rate_out.uom = "B"; |
| 189 | 212 | pd_rate_out = mp_pd_set_thresholds(pd_rate_out, config.outgoing_thresholds); | |
| 190 | return result; | 213 | |
| 214 | mp_subcheck sc_rate_in = mp_subcheck_init(); | ||
| 215 | sc_rate_in = mp_set_subcheck_state(sc_rate_in, mp_get_pd_status(pd_rate_in)); | ||
| 216 | mp_add_perfdata_to_subcheck(&sc_rate_in, pd_rate_in); | ||
| 217 | xasprintf(&sc_rate_in.output, "%s. In = %0.1f %s/s", (config.use_average) ? _("Avg") : _("Max"), | ||
| 218 | adjusted_incoming_rate, incoming_speed_rating); | ||
| 219 | |||
| 220 | mp_subcheck sc_rate_out = mp_subcheck_init(); | ||
| 221 | sc_rate_out = mp_set_subcheck_state(sc_rate_out, mp_get_pd_status(pd_rate_out)); | ||
| 222 | mp_add_perfdata_to_subcheck(&sc_rate_out, pd_rate_out); | ||
| 223 | xasprintf(&sc_rate_out.output, "%s. Out = %0.1f %s/s", | ||
| 224 | (config.use_average) ? _("Avg") : _("Max"), adjusted_outgoing_rate, | ||
| 225 | outgoing_speed_rating); | ||
| 226 | |||
| 227 | mp_subcheck sc_rate = mp_subcheck_init(); | ||
| 228 | xasprintf(&sc_rate.output, "Traffic"); | ||
| 229 | mp_add_subcheck_to_subcheck(&sc_rate, sc_rate_in); | ||
| 230 | mp_add_subcheck_to_subcheck(&sc_rate, sc_rate_out); | ||
| 231 | |||
| 232 | mp_add_subcheck_to_check(&overall, sc_rate); | ||
| 233 | |||
| 234 | mp_exit(overall); | ||
| 191 | } | 235 | } |
| 192 | 236 | ||
| 193 | /* process command-line arguments */ | 237 | /* process command-line arguments */ |
| 194 | int process_arguments(int argc, char **argv) { | 238 | check_mrtgtraf_config_wrapper process_arguments(int argc, char **argv) { |
| 239 | enum { | ||
| 240 | output_format_index = CHAR_MAX + 1, | ||
| 241 | }; | ||
| 242 | |||
| 195 | static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, | 243 | static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, |
| 196 | {"expires", required_argument, 0, 'e'}, | 244 | {"expires", required_argument, 0, 'e'}, |
| 197 | {"aggregation", required_argument, 0, 'a'}, | 245 | {"aggregation", required_argument, 0, 'a'}, |
| @@ -199,46 +247,64 @@ int process_arguments(int argc, char **argv) { | |||
| 199 | {"warning", required_argument, 0, 'w'}, | 247 | {"warning", required_argument, 0, 'w'}, |
| 200 | {"version", no_argument, 0, 'V'}, | 248 | {"version", no_argument, 0, 'V'}, |
| 201 | {"help", no_argument, 0, 'h'}, | 249 | {"help", no_argument, 0, 'h'}, |
| 250 | {"output-format", required_argument, 0, output_format_index}, | ||
| 202 | {0, 0, 0, 0}}; | 251 | {0, 0, 0, 0}}; |
| 203 | 252 | ||
| 204 | if (argc < 2) | 253 | check_mrtgtraf_config_wrapper result = { |
| 205 | return ERROR; | 254 | .errorcode = OK, |
| 255 | .config = check_mrtgtraf_config_init(), | ||
| 256 | }; | ||
| 257 | if (argc < 2) { | ||
| 258 | result.errorcode = ERROR; | ||
| 259 | return result; | ||
| 260 | } | ||
| 206 | 261 | ||
| 207 | for (int i = 1; i < argc; i++) { | 262 | for (int i = 1; i < argc; i++) { |
| 208 | if (strcmp("-to", argv[i]) == 0) | 263 | if (strcmp("-to", argv[i]) == 0) { |
| 209 | strcpy(argv[i], "-t"); | 264 | strcpy(argv[i], "-t"); |
| 210 | else if (strcmp("-wt", argv[i]) == 0) | 265 | } else if (strcmp("-wt", argv[i]) == 0) { |
| 211 | strcpy(argv[i], "-w"); | 266 | strcpy(argv[i], "-w"); |
| 212 | else if (strcmp("-ct", argv[i]) == 0) | 267 | } else if (strcmp("-ct", argv[i]) == 0) { |
| 213 | strcpy(argv[i], "-c"); | 268 | strcpy(argv[i], "-c"); |
| 269 | } | ||
| 214 | } | 270 | } |
| 215 | 271 | ||
| 216 | int option_char; | 272 | int option_char; |
| 217 | int option = 0; | 273 | int option = 0; |
| 218 | while (1) { | 274 | unsigned long incoming_warning_threshold = 0; |
| 275 | unsigned long incoming_critical_threshold = 0; | ||
| 276 | unsigned long outgoing_warning_threshold = 0; | ||
| 277 | unsigned long outgoing_critical_threshold = 0; | ||
| 278 | bool incoming_warning_set = false; | ||
| 279 | bool incoming_critical_set = false; | ||
| 280 | bool outgoing_warning_set = false; | ||
| 281 | bool outgoing_critical_set = false; | ||
| 282 | while (true) { | ||
| 219 | option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option); | 283 | option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option); |
| 220 | 284 | ||
| 221 | if (option_char == -1 || option_char == EOF) | 285 | if (option_char == -1 || option_char == EOF) { |
| 222 | break; | 286 | break; |
| 287 | } | ||
| 223 | 288 | ||
| 224 | switch (option_char) { | 289 | switch (option_char) { |
| 225 | case 'F': /* input file */ | 290 | case 'F': /* input file */ |
| 226 | log_file = optarg; | 291 | result.config.log_file = optarg; |
| 227 | break; | 292 | break; |
| 228 | case 'e': /* expiration time */ | 293 | case 'e': /* expiration time */ |
| 229 | expire_minutes = atoi(optarg); | 294 | result.config.expire_minutes = atoi(optarg); |
| 230 | break; | 295 | break; |
| 231 | case 'a': /* aggregation (AVE or MAX) */ | 296 | case 'a': /* aggregation (AVE or MAX) */ |
| 232 | if (!strcmp(optarg, "MAX")) | 297 | result.config.use_average = (bool)(strcmp(optarg, "MAX")); |
| 233 | use_average = false; | ||
| 234 | else | ||
| 235 | use_average = true; | ||
| 236 | break; | 298 | break; |
| 237 | case 'c': /* warning threshold */ | 299 | case 'c': /* critical threshold */ |
| 238 | sscanf(optarg, "%lu,%lu", &incoming_critical_threshold, &outgoing_critical_threshold); | 300 | sscanf(optarg, "%lu,%lu", &incoming_critical_threshold, &outgoing_critical_threshold); |
| 301 | incoming_critical_set = true; | ||
| 302 | outgoing_critical_set = true; | ||
| 239 | break; | 303 | break; |
| 240 | case 'w': /* critical threshold */ | 304 | case 'w': /* warning threshold */ |
| 241 | sscanf(optarg, "%lu,%lu", &incoming_warning_threshold, &outgoing_warning_threshold); | 305 | sscanf(optarg, "%lu,%lu", &incoming_warning_threshold, &outgoing_warning_threshold); |
| 306 | incoming_warning_set = true; | ||
| 307 | incoming_critical_set = true; | ||
| 242 | break; | 308 | break; |
| 243 | case 'V': /* version */ | 309 | case 'V': /* version */ |
| 244 | print_revision(progname, NP_VERSION); | 310 | print_revision(progname, NP_VERSION); |
| @@ -248,43 +314,94 @@ int process_arguments(int argc, char **argv) { | |||
| 248 | exit(STATE_UNKNOWN); | 314 | exit(STATE_UNKNOWN); |
| 249 | case '?': /* help */ | 315 | case '?': /* help */ |
| 250 | usage5(); | 316 | usage5(); |
| 317 | case output_format_index: { | ||
| 318 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 319 | if (!parser.parsing_success) { | ||
| 320 | printf("Invalid output format: %s\n", optarg); | ||
| 321 | exit(STATE_UNKNOWN); | ||
| 322 | } | ||
| 323 | |||
| 324 | result.config.output_format_is_set = true; | ||
| 325 | result.config.output_format = parser.output_format; | ||
| 326 | break; | ||
| 327 | } | ||
| 251 | } | 328 | } |
| 252 | } | 329 | } |
| 253 | 330 | ||
| 254 | option_char = optind; | 331 | option_char = optind; |
| 255 | if (argc > option_char && log_file == NULL) { | 332 | if (argc > option_char && result.config.log_file == NULL) { |
| 256 | log_file = argv[option_char++]; | 333 | result.config.log_file = argv[option_char++]; |
| 257 | } | 334 | } |
| 258 | 335 | ||
| 259 | if (argc > option_char && expire_minutes == -1) { | 336 | if (argc > option_char && result.config.expire_minutes == -1) { |
| 260 | expire_minutes = atoi(argv[option_char++]); | 337 | result.config.expire_minutes = atoi(argv[option_char++]); |
| 261 | } | 338 | } |
| 262 | 339 | ||
| 263 | if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { | 340 | if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { |
| 264 | use_average = false; | 341 | result.config.use_average = false; |
| 265 | option_char++; | 342 | option_char++; |
| 266 | } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { | 343 | } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { |
| 267 | use_average = true; | 344 | result.config.use_average = true; |
| 268 | option_char++; | 345 | option_char++; |
| 269 | } | 346 | } |
| 270 | 347 | ||
| 271 | if (argc > option_char && incoming_warning_threshold == 0) { | 348 | if (argc > option_char && incoming_warning_threshold == 0) { |
| 272 | incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10); | 349 | incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10); |
| 350 | incoming_warning_set = true; | ||
| 273 | } | 351 | } |
| 274 | 352 | ||
| 275 | if (argc > option_char && incoming_critical_threshold == 0) { | 353 | if (argc > option_char && incoming_critical_threshold == 0) { |
| 276 | incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10); | 354 | incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10); |
| 355 | incoming_critical_set = true; | ||
| 277 | } | 356 | } |
| 278 | 357 | ||
| 279 | if (argc > option_char && outgoing_warning_threshold == 0) { | 358 | if (argc > option_char && outgoing_warning_threshold == 0) { |
| 280 | outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10); | 359 | outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10); |
| 360 | outgoing_warning_set = true; | ||
| 281 | } | 361 | } |
| 282 | 362 | ||
| 283 | if (argc > option_char && outgoing_critical_threshold == 0) { | 363 | if (argc > option_char && outgoing_critical_threshold == 0) { |
| 284 | outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10); | 364 | outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10); |
| 365 | outgoing_critical_set = true; | ||
| 285 | } | 366 | } |
| 286 | 367 | ||
| 287 | return OK; | 368 | mp_range incoming_warning = mp_range_init(); |
| 369 | if (incoming_warning_set) { | ||
| 370 | incoming_warning = | ||
| 371 | mp_range_set_end(incoming_warning, mp_create_pd_value(incoming_warning_threshold)); | ||
| 372 | } | ||
| 373 | |||
| 374 | result.config.incoming_thresholds = | ||
| 375 | mp_thresholds_set_warn(result.config.incoming_thresholds, incoming_warning); | ||
| 376 | |||
| 377 | mp_range incoming_critical = mp_range_init(); | ||
| 378 | if (incoming_critical_set) { | ||
| 379 | incoming_critical = | ||
| 380 | mp_range_set_end(incoming_critical, mp_create_pd_value(incoming_critical_threshold)); | ||
| 381 | } | ||
| 382 | |||
| 383 | result.config.incoming_thresholds = | ||
| 384 | mp_thresholds_set_crit(result.config.incoming_thresholds, incoming_critical); | ||
| 385 | |||
| 386 | mp_range outgoing_warning = mp_range_init(); | ||
| 387 | if (outgoing_warning_set) { | ||
| 388 | outgoing_warning = | ||
| 389 | mp_range_set_end(outgoing_warning, mp_create_pd_value(outgoing_warning_threshold)); | ||
| 390 | } | ||
| 391 | |||
| 392 | result.config.outgoing_thresholds = | ||
| 393 | mp_thresholds_set_warn(result.config.outgoing_thresholds, outgoing_warning); | ||
| 394 | |||
| 395 | mp_range outgoing_critical = mp_range_init(); | ||
| 396 | if (outgoing_critical_set) { | ||
| 397 | outgoing_critical = | ||
| 398 | mp_range_set_end(outgoing_critical, mp_create_pd_value(outgoing_critical_threshold)); | ||
| 399 | } | ||
| 400 | |||
| 401 | result.config.outgoing_thresholds = | ||
| 402 | mp_thresholds_set_crit(result.config.outgoing_thresholds, outgoing_critical); | ||
| 403 | |||
| 404 | return result; | ||
| 288 | } | 405 | } |
| 289 | 406 | ||
| 290 | void print_help(void) { | 407 | void print_help(void) { |
| @@ -318,6 +435,8 @@ void print_help(void) { | |||
| 318 | printf(" %s\n", "-c, --critical"); | 435 | printf(" %s\n", "-c, --critical"); |
| 319 | printf(" %s\n", _("Critical threshold pair <incoming>,<outgoing>")); | 436 | printf(" %s\n", _("Critical threshold pair <incoming>,<outgoing>")); |
| 320 | 437 | ||
| 438 | printf(UT_OUTPUT_FORMAT); | ||
| 439 | |||
| 321 | printf("\n"); | 440 | printf("\n"); |
| 322 | printf("%s\n", _("Notes:")); | 441 | printf("%s\n", _("Notes:")); |
| 323 | printf(" %s\n", _("- MRTG stands for Multi Router Traffic Grapher. It can be downloaded from")); | 442 | printf(" %s\n", _("- MRTG stands for Multi Router Traffic Grapher. It can be downloaded from")); |
diff --git a/plugins/check_mrtgtraf.d/config.h b/plugins/check_mrtgtraf.d/config.h new file mode 100644 index 00000000..d9737243 --- /dev/null +++ b/plugins/check_mrtgtraf.d/config.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <stdlib.h> | ||
| 8 | |||
| 9 | typedef struct { | ||
| 10 | char *log_file; | ||
| 11 | int expire_minutes; | ||
| 12 | bool use_average; | ||
| 13 | |||
| 14 | mp_thresholds incoming_thresholds; | ||
| 15 | mp_thresholds outgoing_thresholds; | ||
| 16 | |||
| 17 | bool output_format_is_set; | ||
| 18 | mp_output_format output_format; | ||
| 19 | } check_mrtgtraf_config; | ||
| 20 | |||
| 21 | check_mrtgtraf_config check_mrtgtraf_config_init() { | ||
| 22 | check_mrtgtraf_config tmp = { | ||
| 23 | .log_file = NULL, | ||
| 24 | .expire_minutes = -1, | ||
| 25 | .use_average = true, | ||
| 26 | |||
| 27 | .incoming_thresholds = mp_thresholds_init(), | ||
| 28 | .outgoing_thresholds = mp_thresholds_init(), | ||
| 29 | |||
| 30 | .output_format_is_set = false, | ||
| 31 | }; | ||
| 32 | return tmp; | ||
| 33 | } | ||
diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index 8a73772d..26730d4c 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c | |||
| @@ -1,444 +1,552 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_mysql plugin | 3 | * Monitoring check_mysql plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at) | 6 | * Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at) |
| 7 | * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) | 7 | * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) |
| 8 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team | 8 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team |
| 9 | * | 9 | * |
| 10 | * Description: | 10 | * Description: |
| 11 | * | 11 | * |
| 12 | * This file contains the check_mysql plugin | 12 | * This file contains the check_mysql plugin |
| 13 | * | 13 | * |
| 14 | * This program tests connections to a mysql server | 14 | * This program tests connections to a mysql server |
| 15 | * | 15 | * |
| 16 | * | 16 | * |
| 17 | * This program is free software: you can redistribute it and/or modify | 17 | * This program is free software: you can redistribute it and/or modify |
| 18 | * it under the terms of the GNU General Public License as published by | 18 | * it under the terms of the GNU General Public License as published by |
| 19 | * the Free Software Foundation, either version 3 of the License, or | 19 | * the Free Software Foundation, either version 3 of the License, or |
| 20 | * (at your option) any later version. | 20 | * (at your option) any later version. |
| 21 | * | 21 | * |
| 22 | * This program is distributed in the hope that it will be useful, | 22 | * This program is distributed in the hope that it will be useful, |
| 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 25 | * GNU General Public License for more details. | 25 | * GNU General Public License for more details. |
| 26 | * | 26 | * |
| 27 | * You should have received a copy of the GNU General Public License | 27 | * You should have received a copy of the GNU General Public License |
| 28 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 28 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 29 | * | 29 | * |
| 30 | * | 30 | * |
| 31 | *****************************************************************************/ | 31 | *****************************************************************************/ |
| 32 | |||
| 33 | const char *progname = "check_mysql"; | ||
| 34 | const char *copyright = "1999-2024"; | ||
| 35 | const char *email = "devel@monitoring-plugins.org"; | ||
| 36 | |||
| 37 | #define SLAVERESULTSIZE 96 | ||
| 38 | 32 | ||
| 39 | #include "common.h" | 33 | #include "common.h" |
| 34 | #include "output.h" | ||
| 35 | #include "perfdata.h" | ||
| 36 | #include "states.h" | ||
| 37 | #include "thresholds.h" | ||
| 40 | #include "utils.h" | 38 | #include "utils.h" |
| 41 | #include "utils_base.h" | 39 | #include "utils_base.h" |
| 42 | #include "netutils.h" | 40 | #include "netutils.h" |
| 41 | #include "check_mysql.d/config.h" | ||
| 43 | 42 | ||
| 44 | #include <mysql.h> | 43 | #include <mysql.h> |
| 45 | #include <mysqld_error.h> | 44 | #include <mysqld_error.h> |
| 46 | #include <errmsg.h> | 45 | #include <errmsg.h> |
| 47 | 46 | ||
| 48 | static char *db_user = NULL; | 47 | const char *progname = "check_mysql"; |
| 49 | static char *db_host = NULL; | 48 | const char *copyright = "1999-2024"; |
| 50 | static char *db_socket = NULL; | 49 | const char *email = "devel@monitoring-plugins.org"; |
| 51 | static char *db_pass = NULL; | 50 | |
| 52 | static char *db = NULL; | ||
| 53 | static char *ca_cert = NULL; | ||
| 54 | static char *ca_dir = NULL; | ||
| 55 | static char *cert = NULL; | ||
| 56 | static char *key = NULL; | ||
| 57 | static char *ciphers = NULL; | ||
| 58 | static bool ssl = false; | ||
| 59 | static char *opt_file = NULL; | ||
| 60 | static char *opt_group = NULL; | ||
| 61 | static unsigned int db_port = MYSQL_PORT; | ||
| 62 | static bool check_slave = false; | ||
| 63 | static bool ignore_auth = false; | ||
| 64 | static int verbose = 0; | 51 | static int verbose = 0; |
| 65 | 52 | ||
| 66 | static double warning_time = 0; | 53 | #define REPLICA_RESULTSIZE 96 |
| 67 | static double critical_time = 0; | ||
| 68 | 54 | ||
| 69 | #define LENGTH_METRIC_UNIT 6 | 55 | #define LENGTH_METRIC_UNIT 6 |
| 70 | static const char *metric_unit[LENGTH_METRIC_UNIT] = { | 56 | static const char *metric_unit[LENGTH_METRIC_UNIT] = { |
| 71 | "Open_files", | 57 | "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", |
| 72 | "Open_tables", | 58 | "Threads_connected", "Threads_running"}; |
| 73 | "Qcache_free_memory", | ||
| 74 | "Qcache_queries_in_cache", | ||
| 75 | "Threads_connected", | ||
| 76 | "Threads_running" | ||
| 77 | }; | ||
| 78 | 59 | ||
| 79 | #define LENGTH_METRIC_COUNTER 9 | 60 | #define LENGTH_METRIC_COUNTER 9 |
| 80 | static const char *metric_counter[LENGTH_METRIC_COUNTER] = { | 61 | static const char *metric_counter[LENGTH_METRIC_COUNTER] = {"Connections", |
| 81 | "Connections", | 62 | "Qcache_hits", |
| 82 | "Qcache_hits", | 63 | "Qcache_inserts", |
| 83 | "Qcache_inserts", | 64 | "Qcache_lowmem_prunes", |
| 84 | "Qcache_lowmem_prunes", | 65 | "Qcache_not_cached", |
| 85 | "Qcache_not_cached", | 66 | "Queries", |
| 86 | "Queries", | 67 | "Questions", |
| 87 | "Questions", | 68 | "Table_locks_waited", |
| 88 | "Table_locks_waited", | 69 | "Uptime"}; |
| 89 | "Uptime" | 70 | |
| 90 | }; | 71 | #define MYSQLDUMP_THREADS_QUERY \ |
| 91 | 72 | "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE " \ | |
| 92 | #define MYSQLDUMP_THREADS_QUERY "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE 'SELECT /*!40001 SQL_NO_CACHE */%'" | 73 | "'SELECT /*!40001 SQL_NO_CACHE */%'" |
| 93 | 74 | ||
| 94 | static thresholds *my_threshold = NULL; | 75 | typedef struct { |
| 95 | 76 | int errorcode; | |
| 96 | static int process_arguments (int, char **); | 77 | check_mysql_config config; |
| 97 | static int validate_arguments (void); | 78 | } check_mysql_config_wrapper; |
| 98 | static void print_help (void); | 79 | static check_mysql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 99 | void print_usage (void); | 80 | static check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper /*config_wrapper*/); |
| 100 | 81 | static void print_help(void); | |
| 101 | int | 82 | void print_usage(void); |
| 102 | main (int argc, char **argv) | 83 | |
| 103 | { | 84 | int main(int argc, char **argv) { |
| 85 | setlocale(LC_ALL, ""); | ||
| 86 | bindtextdomain(PACKAGE, LOCALEDIR); | ||
| 87 | textdomain(PACKAGE); | ||
| 104 | 88 | ||
| 105 | MYSQL mysql; | 89 | /* Parse extra opts if any */ |
| 106 | MYSQL_RES *res; | 90 | argv = np_extra_opts(&argc, argv, progname); |
| 107 | MYSQL_ROW row; | ||
| 108 | 91 | ||
| 109 | /* should be status */ | 92 | check_mysql_config_wrapper tmp_config = process_arguments(argc, argv); |
| 93 | if (tmp_config.errorcode == ERROR) { | ||
| 94 | usage4(_("Could not parse arguments")); | ||
| 95 | } | ||
| 110 | 96 | ||
| 111 | char *result = NULL; | 97 | const check_mysql_config config = tmp_config.config; |
| 112 | char *error = NULL; | ||
| 113 | char slaveresult[SLAVERESULTSIZE] = { 0 }; | ||
| 114 | char* perf; | ||
| 115 | 98 | ||
| 116 | perf = strdup (""); | 99 | if (config.output_format_is_set) { |
| 100 | mp_set_format(config.output_format); | ||
| 101 | } | ||
| 117 | 102 | ||
| 118 | setlocale (LC_ALL, ""); | 103 | MYSQL mysql; |
| 119 | bindtextdomain (PACKAGE, LOCALEDIR); | 104 | /* initialize mysql */ |
| 120 | textdomain (PACKAGE); | 105 | mysql_init(&mysql); |
| 121 | 106 | ||
| 122 | /* Parse extra opts if any */ | 107 | if (config.opt_file != NULL) { |
| 123 | argv=np_extra_opts (&argc, argv, progname); | 108 | mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file); |
| 109 | } | ||
| 110 | |||
| 111 | if (config.opt_group != NULL) { | ||
| 112 | mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group); | ||
| 113 | } else { | ||
| 114 | mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); | ||
| 115 | } | ||
| 124 | 116 | ||
| 125 | if (process_arguments (argc, argv) == ERROR) | 117 | if (config.ssl) { |
| 126 | usage4 (_("Could not parse arguments")); | 118 | mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir, |
| 119 | config.ciphers); | ||
| 120 | } | ||
| 127 | 121 | ||
| 128 | /* initialize mysql */ | 122 | mp_check overall = mp_check_init(); |
| 129 | mysql_init (&mysql); | 123 | |
| 130 | 124 | mp_subcheck sc_connection = mp_subcheck_init(); | |
| 131 | if (opt_file != NULL) | 125 | /* establish a connection to the server and check for errors */ |
| 132 | mysql_options(&mysql,MYSQL_READ_DEFAULT_FILE,opt_file); | 126 | if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, |
| 133 | 127 | config.db_port, config.db_socket, 0)) { | |
| 134 | if (opt_group != NULL) | ||
| 135 | mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,opt_group); | ||
| 136 | else | ||
| 137 | mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"client"); | ||
| 138 | |||
| 139 | if (ssl) | ||
| 140 | mysql_ssl_set(&mysql,key,cert,ca_cert,ca_dir,ciphers); | ||
| 141 | /* establish a connection to the server and error checking */ | ||
| 142 | if (!mysql_real_connect(&mysql,db_host,db_user,db_pass,db,db_port,db_socket,0)) { | ||
| 143 | /* Depending on internally-selected auth plugin MySQL might return */ | 128 | /* Depending on internally-selected auth plugin MySQL might return */ |
| 144 | /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */ | 129 | /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */ |
| 145 | /* Semantically these errors are the same. */ | 130 | /* Semantically these errors are the same. */ |
| 146 | if (ignore_auth && (mysql_errno (&mysql) == ER_ACCESS_DENIED_ERROR || mysql_errno (&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) | 131 | if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR || |
| 147 | { | 132 | mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) { |
| 148 | printf("MySQL OK - Version: %s (protocol %d)\n", | 133 | xasprintf(&sc_connection.output, "Version: %s (protocol %d)", |
| 149 | mysql_get_server_info(&mysql), | 134 | mysql_get_server_info(&mysql), mysql_get_proto_info(&mysql)); |
| 150 | mysql_get_proto_info(&mysql) | 135 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK); |
| 151 | ); | 136 | |
| 152 | mysql_close (&mysql); | 137 | mysql_close(&mysql); |
| 153 | return STATE_OK; | 138 | } else { |
| 139 | if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { | ||
| 140 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); | ||
| 141 | xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); | ||
| 142 | } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { | ||
| 143 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); | ||
| 144 | xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); | ||
| 145 | } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) { | ||
| 146 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); | ||
| 147 | xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); | ||
| 148 | } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) { | ||
| 149 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); | ||
| 150 | xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); | ||
| 151 | } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) { | ||
| 152 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING); | ||
| 153 | xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); | ||
| 154 | } else { | ||
| 155 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL); | ||
| 156 | xasprintf(&sc_connection.output, "%s", mysql_error(&mysql)); | ||
| 157 | } | ||
| 154 | } | 158 | } |
| 155 | else if (mysql_errno (&mysql) == CR_UNKNOWN_HOST) | 159 | |
| 156 | die (STATE_WARNING, "%s\n", mysql_error (&mysql)); | 160 | mp_add_subcheck_to_check(&overall, sc_connection); |
| 157 | else if (mysql_errno (&mysql) == CR_VERSION_ERROR) | 161 | mp_exit(overall); |
| 158 | die (STATE_WARNING, "%s\n", mysql_error (&mysql)); | 162 | } else { |
| 159 | else if (mysql_errno (&mysql) == CR_OUT_OF_MEMORY) | 163 | // successful connection |
| 160 | die (STATE_WARNING, "%s\n", mysql_error (&mysql)); | 164 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK); |
| 161 | else if (mysql_errno (&mysql) == CR_IPSOCK_ERROR) | 165 | xasprintf(&sc_connection.output, "Version: %s (protocol %d)", mysql_get_server_info(&mysql), |
| 162 | die (STATE_WARNING, "%s\n", mysql_error (&mysql)); | 166 | mysql_get_proto_info(&mysql)); |
| 163 | else if (mysql_errno (&mysql) == CR_SOCKET_CREATE_ERROR) | 167 | mp_add_subcheck_to_check(&overall, sc_connection); |
| 164 | die (STATE_WARNING, "%s\n", mysql_error (&mysql)); | ||
| 165 | else | ||
| 166 | die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); | ||
| 167 | } | 168 | } |
| 168 | 169 | ||
| 169 | /* get the server stats */ | 170 | /* get the server stats */ |
| 170 | result = strdup (mysql_stat (&mysql)); | 171 | char *mysql_stats = strdup(mysql_stat(&mysql)); |
| 172 | |||
| 173 | mp_subcheck sc_stats = mp_subcheck_init(); | ||
| 174 | sc_stats = mp_set_subcheck_default_state(sc_stats, STATE_OK); | ||
| 171 | 175 | ||
| 172 | /* error checking once more */ | 176 | /* error checking once more */ |
| 173 | if (mysql_error (&mysql)) { | 177 | if (mysql_errno(&mysql) != 0) { |
| 174 | if (mysql_errno (&mysql) == CR_SERVER_GONE_ERROR) | 178 | if ((mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) || |
| 175 | die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); | 179 | (mysql_errno(&mysql) == CR_SERVER_LOST) || (mysql_errno(&mysql) == CR_UNKNOWN_ERROR)) { |
| 176 | else if (mysql_errno (&mysql) == CR_SERVER_LOST) | 180 | sc_stats = mp_set_subcheck_state(sc_stats, STATE_CRITICAL); |
| 177 | die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); | 181 | xasprintf(&sc_stats.output, "Retrieving stats failed: %s", mysql_error(&mysql)); |
| 178 | else if (mysql_errno (&mysql) == CR_UNKNOWN_ERROR) | 182 | } else { |
| 179 | die (STATE_CRITICAL, "%s\n", mysql_error (&mysql)); | 183 | // not sure which error modes occur here, but mysql_error indicates an error |
| 184 | sc_stats = mp_set_subcheck_state(sc_stats, STATE_WARNING); | ||
| 185 | xasprintf(&sc_stats.output, "retrieving stats caused an error: %s", | ||
| 186 | mysql_error(&mysql)); | ||
| 187 | } | ||
| 188 | |||
| 189 | mp_add_subcheck_to_check(&overall, sc_stats); | ||
| 190 | mp_exit(overall); | ||
| 191 | } else { | ||
| 192 | xasprintf(&sc_stats.output, "retrieved stats: %s", mysql_stats); | ||
| 193 | sc_stats = mp_set_subcheck_state(sc_stats, STATE_OK); | ||
| 194 | mp_add_subcheck_to_check(&overall, sc_stats); | ||
| 180 | } | 195 | } |
| 181 | 196 | ||
| 197 | MYSQL_RES *res; | ||
| 198 | MYSQL_ROW row; | ||
| 199 | mp_subcheck sc_query = mp_subcheck_init(); | ||
| 182 | /* try to fetch some perf data */ | 200 | /* try to fetch some perf data */ |
| 183 | if (mysql_query (&mysql, "show global status") == 0) { | 201 | if (mysql_query(&mysql, "show global status") == 0) { |
| 184 | if ( (res = mysql_store_result (&mysql)) == NULL) { | 202 | if ((res = mysql_store_result(&mysql)) == NULL) { |
| 185 | error = strdup(mysql_error(&mysql)); | 203 | xasprintf(&sc_connection.output, "query failed - status store_result error: %s", |
| 186 | mysql_close (&mysql); | 204 | mysql_error(&mysql)); |
| 187 | die (STATE_CRITICAL, _("status store_result error: %s\n"), error); | 205 | mysql_close(&mysql); |
| 188 | } | ||
| 189 | 206 | ||
| 190 | while ( (row = mysql_fetch_row (res)) != NULL) { | 207 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); |
| 191 | int i; | 208 | mp_add_subcheck_to_check(&overall, sc_query); |
| 209 | mp_exit(overall); | ||
| 210 | } | ||
| 192 | 211 | ||
| 193 | for(i = 0; i < LENGTH_METRIC_UNIT; i++) { | 212 | while ((row = mysql_fetch_row(res)) != NULL) { |
| 213 | for (int i = 0; i < LENGTH_METRIC_UNIT; i++) { | ||
| 194 | if (strcmp(row[0], metric_unit[i]) == 0) { | 214 | if (strcmp(row[0], metric_unit[i]) == 0) { |
| 195 | xasprintf(&perf, "%s%s ", perf, perfdata(metric_unit[i], | 215 | mp_perfdata pd_mysql_stat = perfdata_init(); |
| 196 | atol(row[1]), "", false, 0, false, 0, false, 0, false, 0)); | 216 | pd_mysql_stat.label = (char *)metric_unit[i]; |
| 217 | pd_mysql_stat.value = mp_create_pd_value(atol(row[1])); | ||
| 218 | mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat); | ||
| 197 | continue; | 219 | continue; |
| 198 | } | 220 | } |
| 199 | } | 221 | } |
| 200 | for(i = 0; i < LENGTH_METRIC_COUNTER; i++) { | 222 | |
| 223 | for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) { | ||
| 201 | if (strcmp(row[0], metric_counter[i]) == 0) { | 224 | if (strcmp(row[0], metric_counter[i]) == 0) { |
| 202 | xasprintf(&perf, "%s%s ", perf, perfdata(metric_counter[i], | 225 | mp_perfdata pd_mysql_stat = perfdata_init(); |
| 203 | atol(row[1]), "c", false, 0, false, 0, false, 0, false, 0)); | 226 | pd_mysql_stat.label = (char *)metric_counter[i]; |
| 227 | pd_mysql_stat.value = mp_create_pd_value(atol(row[1])); | ||
| 228 | pd_mysql_stat.uom = "c"; | ||
| 229 | mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat); | ||
| 204 | continue; | 230 | continue; |
| 205 | } | 231 | } |
| 206 | } | 232 | } |
| 207 | } | 233 | } |
| 208 | /* remove trailing space */ | 234 | } else { |
| 209 | if (strlen(perf) > 0) | 235 | // Query failed! |
| 210 | perf[strlen(perf) - 1] = '\0'; | 236 | xasprintf(&sc_connection.output, "query failed"); |
| 237 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 238 | mp_add_subcheck_to_check(&overall, sc_query); | ||
| 239 | mp_exit(overall); | ||
| 211 | } | 240 | } |
| 212 | 241 | ||
| 213 | if(check_slave) { | 242 | if (config.check_replica) { |
| 214 | /* check the slave status */ | 243 | // Detect which version we are, on older version |
| 215 | if (mysql_query (&mysql, "show slave status") != 0) { | 244 | // "show slave status" should work, on newer ones |
| 216 | error = strdup(mysql_error(&mysql)); | 245 | // "show replica status" |
| 217 | mysql_close (&mysql); | 246 | // But first we have to find out whether this is |
| 218 | die (STATE_CRITICAL, _("slave query error: %s\n"), error); | 247 | // MySQL or MariaDB since the version numbering scheme |
| 248 | // is different | ||
| 249 | bool use_deprecated_slave_status = false; | ||
| 250 | const char *server_version = mysql_get_server_info(&mysql); | ||
| 251 | unsigned long server_verion_int = mysql_get_server_version(&mysql); | ||
| 252 | unsigned long major_version = server_verion_int / 10000; | ||
| 253 | unsigned long minor_version = (server_verion_int % 10000) / 100; | ||
| 254 | unsigned long patch_version = (server_verion_int % 100); | ||
| 255 | |||
| 256 | if (verbose) { | ||
| 257 | printf("Found MariaDB/MySQL: %s, main version: %lu, minor version: %lu, patch version: " | ||
| 258 | "%lu\n", | ||
| 259 | server_version, major_version, minor_version, patch_version); | ||
| 260 | } | ||
| 261 | |||
| 262 | if (strstr(server_version, "MariaDB") != NULL) { | ||
| 263 | // Looks like MariaDB, new commands should be available after 10.5.1 | ||
| 264 | if (major_version < 10) { | ||
| 265 | use_deprecated_slave_status = true; | ||
| 266 | } else if (major_version == 10) { | ||
| 267 | if (minor_version < 5) { | ||
| 268 | use_deprecated_slave_status = true; | ||
| 269 | } else if (minor_version == 5 && patch_version < 1) { | ||
| 270 | use_deprecated_slave_status = true; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } else { | ||
| 274 | // Looks like MySQL or at least not like MariaDB | ||
| 275 | if (major_version < 8) { | ||
| 276 | use_deprecated_slave_status = true; | ||
| 277 | } else if (major_version == 10 && minor_version < 4) { | ||
| 278 | use_deprecated_slave_status = true; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | char *replica_query = NULL; | ||
| 283 | if (use_deprecated_slave_status) { | ||
| 284 | replica_query = "show slave status"; | ||
| 285 | } else { | ||
| 286 | replica_query = "show replica status"; | ||
| 287 | } | ||
| 288 | |||
| 289 | mp_subcheck sc_replica = mp_subcheck_init(); | ||
| 290 | |||
| 291 | /* check the replica status */ | ||
| 292 | if (mysql_query(&mysql, replica_query) != 0) { | ||
| 293 | xasprintf(&sc_replica.output, "replica query error: %s", mysql_error(&mysql)); | ||
| 294 | mysql_close(&mysql); | ||
| 295 | |||
| 296 | sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); | ||
| 297 | mp_add_subcheck_to_check(&overall, sc_replica); | ||
| 298 | mp_exit(overall); | ||
| 219 | } | 299 | } |
| 220 | 300 | ||
| 221 | /* store the result */ | 301 | /* store the result */ |
| 222 | if ( (res = mysql_store_result (&mysql)) == NULL) { | 302 | if ((res = mysql_store_result(&mysql)) == NULL) { |
| 223 | error = strdup(mysql_error(&mysql)); | 303 | xasprintf(&sc_replica.output, "replica store_result error: %s", mysql_error(&mysql)); |
| 224 | mysql_close (&mysql); | 304 | mysql_close(&mysql); |
| 225 | die (STATE_CRITICAL, _("slave store_result error: %s\n"), error); | 305 | |
| 306 | sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); | ||
| 307 | mp_add_subcheck_to_check(&overall, sc_replica); | ||
| 308 | mp_exit(overall); | ||
| 226 | } | 309 | } |
| 227 | 310 | ||
| 228 | /* Check there is some data */ | 311 | /* Check there is some data */ |
| 229 | if (mysql_num_rows(res) == 0) { | 312 | if (mysql_num_rows(res) == 0) { |
| 230 | mysql_close(&mysql); | 313 | mysql_close(&mysql); |
| 231 | die (STATE_WARNING, "%s\n", _("No slaves defined")); | 314 | |
| 315 | xasprintf(&sc_replica.output, "no replicas defined"); | ||
| 316 | sc_replica = mp_set_subcheck_state(sc_replica, STATE_WARNING); | ||
| 317 | mp_add_subcheck_to_check(&overall, sc_replica); | ||
| 318 | mp_exit(overall); | ||
| 232 | } | 319 | } |
| 233 | 320 | ||
| 234 | /* fetch the first row */ | 321 | /* fetch the first row */ |
| 235 | if ( (row = mysql_fetch_row (res)) == NULL) { | 322 | if ((row = mysql_fetch_row(res)) == NULL) { |
| 236 | error = strdup(mysql_error(&mysql)); | 323 | xasprintf(&sc_replica.output, "replica fetch row error: %s", mysql_error(&mysql)); |
| 237 | mysql_free_result (res); | 324 | mysql_free_result(res); |
| 238 | mysql_close (&mysql); | 325 | mysql_close(&mysql); |
| 239 | die (STATE_CRITICAL, _("slave fetch row error: %s\n"), error); | 326 | |
| 327 | sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); | ||
| 328 | mp_add_subcheck_to_check(&overall, sc_replica); | ||
| 329 | mp_exit(overall); | ||
| 240 | } | 330 | } |
| 241 | 331 | ||
| 242 | if (mysql_field_count (&mysql) == 12) { | 332 | if (mysql_field_count(&mysql) == 12) { |
| 243 | /* mysql 3.23.x */ | 333 | /* mysql 3.23.x */ |
| 244 | snprintf (slaveresult, SLAVERESULTSIZE, _("Slave running: %s"), row[6]); | 334 | xasprintf(&sc_replica.output, "Replica running: %s", row[6]); |
| 245 | if (strcmp (row[6], "Yes") != 0) { | 335 | if (strcmp(row[6], "Yes") != 0) { |
| 246 | mysql_free_result (res); | 336 | mysql_free_result(res); |
| 247 | mysql_close (&mysql); | 337 | mysql_close(&mysql); |
| 248 | die (STATE_CRITICAL, "%s\n", slaveresult); | 338 | |
| 339 | sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); | ||
| 340 | mp_add_subcheck_to_check(&overall, sc_replica); | ||
| 341 | mp_exit(overall); | ||
| 249 | } | 342 | } |
| 250 | |||
| 251 | } else { | 343 | } else { |
| 252 | /* mysql 4.x.x and mysql 5.x.x */ | 344 | /* mysql 4.x.x and mysql 5.x.x */ |
| 253 | int slave_io_field = -1 , slave_sql_field = -1, seconds_behind_field = -1, i, num_fields; | 345 | int replica_io_field = -1; |
| 254 | MYSQL_FIELD* fields; | 346 | int replica_sql_field = -1; |
| 255 | 347 | int seconds_behind_field = -1; | |
| 256 | num_fields = mysql_num_fields(res); | 348 | unsigned int num_fields = mysql_num_fields(res); |
| 257 | fields = mysql_fetch_fields(res); | 349 | MYSQL_FIELD *fields = mysql_fetch_fields(res); |
| 258 | for(i = 0; i < num_fields; i++) { | 350 | for (int i = 0; i < (int)num_fields; i++) { |
| 259 | if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { | 351 | if ((strcasecmp(fields[i].name, "Slave_IO_Running") == 0) || |
| 260 | slave_io_field = i; | 352 | (strcasecmp(fields[i].name, "Replica_IO_Running") == 0)) { |
| 353 | replica_io_field = i; | ||
| 261 | continue; | 354 | continue; |
| 262 | } | 355 | } |
| 263 | if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { | 356 | if ((strcasecmp(fields[i].name, "Slave_SQL_Running") == 0) || |
| 264 | slave_sql_field = i; | 357 | (strcasecmp(fields[i].name, "Replica_SQL_Running") == 0)) { |
| 358 | replica_sql_field = i; | ||
| 265 | continue; | 359 | continue; |
| 266 | } | 360 | } |
| 267 | if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) { | 361 | if ((strcasecmp(fields[i].name, "Seconds_Behind_Master") == 0) || |
| 362 | (strcasecmp(fields[i].name, "Seconds_Behind_Source") == 0)) { | ||
| 268 | seconds_behind_field = i; | 363 | seconds_behind_field = i; |
| 269 | continue; | 364 | continue; |
| 270 | } | 365 | } |
| 271 | } | 366 | } |
| 272 | 367 | ||
| 273 | /* Check if slave status is available */ | 368 | /* Check if replica status is available */ |
| 274 | if ((slave_io_field < 0) || (slave_sql_field < 0) || (num_fields == 0)) { | 369 | if ((replica_io_field < 0) || (replica_sql_field < 0) || (num_fields == 0)) { |
| 275 | mysql_free_result (res); | 370 | mysql_free_result(res); |
| 276 | mysql_close (&mysql); | 371 | mysql_close(&mysql); |
| 277 | die (STATE_CRITICAL, "Slave status unavailable\n"); | 372 | |
| 373 | xasprintf(&sc_replica.output, "Replica status unavailable"); | ||
| 374 | sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); | ||
| 375 | mp_add_subcheck_to_check(&overall, sc_replica); | ||
| 376 | mp_exit(overall); | ||
| 278 | } | 377 | } |
| 279 | 378 | ||
| 280 | /* Save slave status in slaveresult */ | 379 | /* Save replica status in replica_result */ |
| 281 | snprintf (slaveresult, SLAVERESULTSIZE, "Slave IO: %s Slave SQL: %s Seconds Behind Master: %s", row[slave_io_field], row[slave_sql_field], seconds_behind_field!=-1?row[seconds_behind_field]:"Unknown"); | 380 | xasprintf(&sc_replica.output, |
| 381 | "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s", | ||
| 382 | row[replica_io_field], row[replica_sql_field], | ||
| 383 | seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown"); | ||
| 282 | 384 | ||
| 283 | /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no mysqldump threads running */ | 385 | /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no |
| 284 | if (strcmp (row[slave_io_field], "Yes") != 0 || strcmp (row[slave_sql_field], "Yes") != 0) { | 386 | * mysqldump threads running */ |
| 387 | if (strcmp(row[replica_io_field], "Yes") != 0 || | ||
| 388 | strcmp(row[replica_sql_field], "Yes") != 0) { | ||
| 285 | MYSQL_RES *res_mysqldump; | 389 | MYSQL_RES *res_mysqldump; |
| 286 | MYSQL_ROW row_mysqldump; | 390 | MYSQL_ROW row_mysqldump; |
| 287 | unsigned int mysqldump_threads = 0; | 391 | unsigned int mysqldump_threads = 0; |
| 288 | 392 | ||
| 289 | if (mysql_query (&mysql, MYSQLDUMP_THREADS_QUERY) == 0) { | 393 | if (mysql_query(&mysql, MYSQLDUMP_THREADS_QUERY) == 0) { |
| 290 | /* store the result */ | 394 | /* store the result */ |
| 291 | if ( (res_mysqldump = mysql_store_result (&mysql)) != NULL) { | 395 | if ((res_mysqldump = mysql_store_result(&mysql)) != NULL) { |
| 292 | if (mysql_num_rows(res_mysqldump) == 1) { | 396 | if (mysql_num_rows(res_mysqldump) == 1) { |
| 293 | if ( (row_mysqldump = mysql_fetch_row (res_mysqldump)) != NULL) { | 397 | if ((row_mysqldump = mysql_fetch_row(res_mysqldump)) != NULL) { |
| 294 | mysqldump_threads = atoi(row_mysqldump[0]); | 398 | mysqldump_threads = atoi(row_mysqldump[0]); |
| 295 | } | 399 | } |
| 296 | } | 400 | } |
| 297 | /* free the result */ | 401 | /* free the result */ |
| 298 | mysql_free_result (res_mysqldump); | 402 | mysql_free_result(res_mysqldump); |
| 299 | } | 403 | } |
| 300 | mysql_close (&mysql); | 404 | mysql_close(&mysql); |
| 301 | } | 405 | } |
| 406 | |||
| 302 | if (mysqldump_threads == 0) { | 407 | if (mysqldump_threads == 0) { |
| 303 | die (STATE_CRITICAL, "%s\n", slaveresult); | 408 | sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL); |
| 409 | mp_add_subcheck_to_check(&overall, sc_replica); | ||
| 410 | mp_exit(overall); | ||
| 304 | } else { | 411 | } else { |
| 305 | strncat(slaveresult, " Mysqldump: in progress", SLAVERESULTSIZE-1); | 412 | xasprintf(&sc_replica.output, "%s %s", sc_replica.output, |
| 413 | " Mysqldump: in progress"); | ||
| 306 | } | 414 | } |
| 307 | } | 415 | } |
| 308 | 416 | ||
| 309 | if (verbose >=3) { | 417 | if (verbose >= 3) { |
| 310 | if (seconds_behind_field == -1) { | 418 | if (seconds_behind_field == -1) { |
| 311 | printf("seconds_behind_field not found\n"); | 419 | printf("seconds_behind_field not found\n"); |
| 312 | } else { | 420 | } else { |
| 313 | printf ("seconds_behind_field(index %d)=%s\n", seconds_behind_field, row[seconds_behind_field]); | 421 | printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field, |
| 422 | row[seconds_behind_field]); | ||
| 314 | } | 423 | } |
| 315 | } | 424 | } |
| 316 | 425 | ||
| 317 | /* Check Seconds Behind against threshold */ | 426 | /* Check Seconds Behind against threshold */ |
| 318 | if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && strcmp (row[seconds_behind_field], "NULL") != 0)) { | 427 | if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && |
| 319 | double value = atof(row[seconds_behind_field]); | 428 | strcmp(row[seconds_behind_field], "NULL") != 0)) { |
| 320 | int status; | 429 | mp_perfdata pd_seconds_behind = perfdata_init(); |
| 321 | 430 | pd_seconds_behind.label = "seconds behind master"; | |
| 322 | status = get_status(value, my_threshold); | 431 | pd_seconds_behind.value = mp_create_pd_value(atof(row[seconds_behind_field])); |
| 323 | 432 | pd_seconds_behind = | |
| 324 | xasprintf (&perf, "%s %s", perf, fperfdata ("seconds behind master", value, "s", | 433 | mp_pd_set_thresholds(pd_seconds_behind, config.replica_thresholds); |
| 325 | true, (double) warning_time, | 434 | pd_seconds_behind.uom = "s"; |
| 326 | true, (double) critical_time, | 435 | mp_add_perfdata_to_subcheck(&sc_replica, pd_seconds_behind); |
| 327 | false, 0, | 436 | |
| 328 | false, 0)); | 437 | mp_state_enum status = mp_get_pd_status(pd_seconds_behind); |
| 329 | 438 | ||
| 330 | if (status == STATE_WARNING) { | 439 | sc_replica = mp_set_subcheck_state(sc_replica, status); |
| 331 | printf("SLOW_SLAVE %s: %s|%s\n", _("WARNING"), slaveresult, perf); | 440 | |
| 332 | exit(STATE_WARNING); | 441 | if (status != STATE_OK) { |
| 333 | } else if (status == STATE_CRITICAL) { | 442 | xasprintf(&sc_replica.output, "slow replica - %s", sc_replica.output); |
| 334 | printf("SLOW_SLAVE %s: %s|%s\n", _("CRITICAL"), slaveresult, perf); | 443 | mp_add_subcheck_to_check(&overall, sc_replica); |
| 335 | exit(STATE_CRITICAL); | 444 | mp_exit(overall); |
| 336 | } | 445 | } |
| 337 | } | 446 | } |
| 338 | } | 447 | } |
| 339 | 448 | ||
| 340 | /* free the result */ | 449 | /* free the result */ |
| 341 | mysql_free_result (res); | 450 | mysql_free_result(res); |
| 342 | } | 451 | } |
| 343 | 452 | ||
| 344 | /* close the connection */ | 453 | /* close the connection */ |
| 345 | mysql_close (&mysql); | 454 | mysql_close(&mysql); |
| 346 | |||
| 347 | /* print out the result of stats */ | ||
| 348 | if (check_slave) { | ||
| 349 | printf ("%s %s|%s\n", result, slaveresult, perf); | ||
| 350 | } else { | ||
| 351 | printf ("%s|%s\n", result, perf); | ||
| 352 | } | ||
| 353 | 455 | ||
| 354 | return STATE_OK; | 456 | mp_exit(overall); |
| 355 | } | 457 | } |
| 356 | 458 | ||
| 357 | |||
| 358 | /* process command-line arguments */ | 459 | /* process command-line arguments */ |
| 359 | int | 460 | check_mysql_config_wrapper process_arguments(int argc, char **argv) { |
| 360 | process_arguments (int argc, char **argv) | ||
| 361 | { | ||
| 362 | int c; | ||
| 363 | char *warning = NULL; | ||
| 364 | char *critical = NULL; | ||
| 365 | 461 | ||
| 366 | int option = 0; | 462 | enum { |
| 367 | static struct option longopts[] = { | 463 | CHECK_REPLICA_OPT = CHAR_MAX + 1, |
| 368 | {"hostname", required_argument, 0, 'H'}, | 464 | output_format_index, |
| 369 | {"socket", required_argument, 0, 's'}, | 465 | }; |
| 370 | {"database", required_argument, 0, 'd'}, | 466 | |
| 371 | {"username", required_argument, 0, 'u'}, | 467 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 372 | {"password", required_argument, 0, 'p'}, | 468 | {"socket", required_argument, 0, 's'}, |
| 373 | {"file", required_argument, 0, 'f'}, | 469 | {"database", required_argument, 0, 'd'}, |
| 374 | {"group", required_argument, 0, 'g'}, | 470 | {"username", required_argument, 0, 'u'}, |
| 375 | {"port", required_argument, 0, 'P'}, | 471 | {"password", required_argument, 0, 'p'}, |
| 376 | {"critical", required_argument, 0, 'c'}, | 472 | {"file", required_argument, 0, 'f'}, |
| 377 | {"warning", required_argument, 0, 'w'}, | 473 | {"group", required_argument, 0, 'g'}, |
| 378 | {"check-slave", no_argument, 0, 'S'}, | 474 | {"port", required_argument, 0, 'P'}, |
| 379 | {"ignore-auth", no_argument, 0, 'n'}, | 475 | {"critical", required_argument, 0, 'c'}, |
| 380 | {"verbose", no_argument, 0, 'v'}, | 476 | {"warning", required_argument, 0, 'w'}, |
| 381 | {"version", no_argument, 0, 'V'}, | 477 | {"check-slave", no_argument, 0, 'S'}, |
| 382 | {"help", no_argument, 0, 'h'}, | 478 | {"check-replica", no_argument, 0, CHECK_REPLICA_OPT}, |
| 383 | {"ssl", no_argument, 0, 'l'}, | 479 | {"ignore-auth", no_argument, 0, 'n'}, |
| 384 | {"ca-cert", optional_argument, 0, 'C'}, | 480 | {"verbose", no_argument, 0, 'v'}, |
| 385 | {"key", required_argument,0,'k'}, | 481 | {"version", no_argument, 0, 'V'}, |
| 386 | {"cert", required_argument,0,'a'}, | 482 | {"help", no_argument, 0, 'h'}, |
| 387 | {"ca-dir", required_argument, 0, 'D'}, | 483 | {"ssl", no_argument, 0, 'l'}, |
| 388 | {"ciphers", required_argument, 0, 'L'}, | 484 | {"ca-cert", optional_argument, 0, 'C'}, |
| 389 | {0, 0, 0, 0} | 485 | {"key", required_argument, 0, 'k'}, |
| 486 | {"cert", required_argument, 0, 'a'}, | ||
| 487 | {"ca-dir", required_argument, 0, 'D'}, | ||
| 488 | {"ciphers", required_argument, 0, 'L'}, | ||
| 489 | {"output-format", required_argument, 0, output_format_index}, | ||
| 490 | {0, 0, 0, 0}}; | ||
| 491 | |||
| 492 | check_mysql_config_wrapper result = { | ||
| 493 | .errorcode = OK, | ||
| 494 | .config = check_mysql_config_init(), | ||
| 390 | }; | 495 | }; |
| 391 | 496 | ||
| 392 | if (argc < 1) | 497 | if (argc < 1) { |
| 393 | return ERROR; | 498 | result.errorcode = ERROR; |
| 499 | return result; | ||
| 500 | } | ||
| 394 | 501 | ||
| 395 | while (1) { | 502 | int option = 0; |
| 396 | c = getopt_long (argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); | 503 | while (true) { |
| 504 | int option_index = | ||
| 505 | getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); | ||
| 397 | 506 | ||
| 398 | if (c == -1 || c == EOF) | 507 | if (option_index == -1 || option_index == EOF) { |
| 399 | break; | 508 | break; |
| 509 | } | ||
| 400 | 510 | ||
| 401 | switch (c) { | 511 | switch (option_index) { |
| 402 | case 'H': /* hostname */ | 512 | case 'H': /* hostname */ |
| 403 | if (is_host (optarg)) { | 513 | if (is_host(optarg)) { |
| 404 | db_host = optarg; | 514 | result.config.db_host = optarg; |
| 405 | } | 515 | } else if (*optarg == '/') { |
| 406 | else if (*optarg == '/') { | 516 | result.config.db_socket = optarg; |
| 407 | db_socket = optarg; | 517 | } else { |
| 408 | } | 518 | usage2(_("Invalid hostname/address"), optarg); |
| 409 | else { | ||
| 410 | usage2 (_("Invalid hostname/address"), optarg); | ||
| 411 | } | 519 | } |
| 412 | break; | 520 | break; |
| 413 | case 's': /* socket */ | 521 | case 's': /* socket */ |
| 414 | db_socket = optarg; | 522 | result.config.db_socket = optarg; |
| 415 | break; | 523 | break; |
| 416 | case 'd': /* database */ | 524 | case 'd': /* database */ |
| 417 | db = optarg; | 525 | result.config.db = optarg; |
| 418 | break; | 526 | break; |
| 419 | case 'l': | 527 | case 'l': |
| 420 | ssl = true; | 528 | result.config.ssl = true; |
| 421 | break; | 529 | break; |
| 422 | case 'C': | 530 | case 'C': |
| 423 | ca_cert = optarg; | 531 | result.config.ca_cert = optarg; |
| 424 | break; | 532 | break; |
| 425 | case 'a': | 533 | case 'a': |
| 426 | cert = optarg; | 534 | result.config.cert = optarg; |
| 427 | break; | 535 | break; |
| 428 | case 'k': | 536 | case 'k': |
| 429 | key = optarg; | 537 | result.config.key = optarg; |
| 430 | break; | 538 | break; |
| 431 | case 'D': | 539 | case 'D': |
| 432 | ca_dir = optarg; | 540 | result.config.ca_dir = optarg; |
| 433 | break; | 541 | break; |
| 434 | case 'L': | 542 | case 'L': |
| 435 | ciphers = optarg; | 543 | result.config.ciphers = optarg; |
| 436 | break; | 544 | break; |
| 437 | case 'u': /* username */ | 545 | case 'u': /* username */ |
| 438 | db_user = optarg; | 546 | result.config.db_user = optarg; |
| 439 | break; | 547 | break; |
| 440 | case 'p': /* authentication information: password */ | 548 | case 'p': /* authentication information: password */ |
| 441 | db_pass = strdup(optarg); | 549 | result.config.db_pass = strdup(optarg); |
| 442 | 550 | ||
| 443 | /* Delete the password from process list */ | 551 | /* Delete the password from process list */ |
| 444 | while (*optarg != '\0') { | 552 | while (*optarg != '\0') { |
| @@ -446,167 +554,185 @@ process_arguments (int argc, char **argv) | |||
| 446 | optarg++; | 554 | optarg++; |
| 447 | } | 555 | } |
| 448 | break; | 556 | break; |
| 449 | case 'f': /* client options file */ | 557 | case 'f': /* client options file */ |
| 450 | opt_file = optarg; | 558 | result.config.opt_file = optarg; |
| 451 | break; | 559 | break; |
| 452 | case 'g': /* client options group */ | 560 | case 'g': /* client options group */ |
| 453 | opt_group = optarg; | 561 | result.config.opt_group = optarg; |
| 454 | break; | 562 | break; |
| 455 | case 'P': /* critical time threshold */ | 563 | case 'P': /* critical time threshold */ |
| 456 | db_port = atoi (optarg); | 564 | result.config.db_port = atoi(optarg); |
| 457 | break; | 565 | break; |
| 458 | case 'S': | 566 | case 'S': |
| 459 | check_slave = true; /* check-slave */ | 567 | case CHECK_REPLICA_OPT: |
| 568 | result.config.check_replica = true; /* check-slave */ | ||
| 460 | break; | 569 | break; |
| 461 | case 'n': | 570 | case 'n': |
| 462 | ignore_auth = true; /* ignore-auth */ | 571 | result.config.ignore_auth = true; /* ignore-auth */ |
| 463 | break; | ||
| 464 | case 'w': | ||
| 465 | warning = optarg; | ||
| 466 | warning_time = strtod (warning, NULL); | ||
| 467 | break; | 572 | break; |
| 468 | case 'c': | 573 | case 'w': { |
| 469 | critical = optarg; | 574 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 470 | critical_time = strtod (critical, NULL); | 575 | if (tmp.error != MP_PARSING_SUCCES) { |
| 471 | break; | 576 | die(STATE_UNKNOWN, "failed to parse warning time threshold"); |
| 472 | case 'V': /* version */ | 577 | } |
| 473 | print_revision (progname, NP_VERSION); | 578 | result.config.replica_thresholds = |
| 474 | exit (STATE_UNKNOWN); | 579 | mp_thresholds_set_warn(result.config.replica_thresholds, tmp.range); |
| 475 | case 'h': /* help */ | 580 | } break; |
| 476 | print_help (); | 581 | case 'c': { |
| 477 | exit (STATE_UNKNOWN); | 582 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 583 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 584 | die(STATE_UNKNOWN, "failed to parse critical time threshold"); | ||
| 585 | } | ||
| 586 | result.config.replica_thresholds = | ||
| 587 | mp_thresholds_set_crit(result.config.replica_thresholds, tmp.range); | ||
| 588 | } break; | ||
| 589 | case 'V': /* version */ | ||
| 590 | print_revision(progname, NP_VERSION); | ||
| 591 | exit(STATE_UNKNOWN); | ||
| 592 | case 'h': /* help */ | ||
| 593 | print_help(); | ||
| 594 | exit(STATE_UNKNOWN); | ||
| 478 | case 'v': | 595 | case 'v': |
| 479 | verbose++; | 596 | verbose++; |
| 480 | break; | 597 | break; |
| 481 | case '?': /* help */ | 598 | case '?': /* help */ |
| 482 | usage5 (); | 599 | usage5(); |
| 600 | case output_format_index: { | ||
| 601 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 602 | if (!parser.parsing_success) { | ||
| 603 | printf("Invalid output format: %s\n", optarg); | ||
| 604 | exit(STATE_UNKNOWN); | ||
| 605 | } | ||
| 606 | |||
| 607 | result.config.output_format_is_set = true; | ||
| 608 | result.config.output_format = parser.output_format; | ||
| 609 | break; | ||
| 610 | } | ||
| 483 | } | 611 | } |
| 484 | } | 612 | } |
| 485 | 613 | ||
| 486 | c = optind; | 614 | int index = optind; |
| 487 | |||
| 488 | set_thresholds(&my_threshold, warning, critical); | ||
| 489 | |||
| 490 | while ( argc > c ) { | ||
| 491 | 615 | ||
| 492 | if (db_host == NULL) | 616 | while (argc > index) { |
| 493 | if (is_host (argv[c])) { | 617 | if (result.config.db_host == NULL) { |
| 494 | db_host = argv[c++]; | 618 | if (is_host(argv[index])) { |
| 619 | result.config.db_host = argv[index++]; | ||
| 620 | } else { | ||
| 621 | usage2(_("Invalid hostname/address"), argv[index]); | ||
| 495 | } | 622 | } |
| 496 | else { | 623 | } else if (result.config.db_user == NULL) { |
| 497 | usage2 (_("Invalid hostname/address"), argv[c]); | 624 | result.config.db_user = argv[index++]; |
| 498 | } | 625 | } else if (result.config.db_pass == NULL) { |
| 499 | else if (db_user == NULL) | 626 | result.config.db_pass = argv[index++]; |
| 500 | db_user = argv[c++]; | 627 | } else if (result.config.db == NULL) { |
| 501 | else if (db_pass == NULL) | 628 | result.config.db = argv[index++]; |
| 502 | db_pass = argv[c++]; | 629 | } else if (is_intnonneg(argv[index])) { |
| 503 | else if (db == NULL) | 630 | result.config.db_port = atoi(argv[index++]); |
| 504 | db = argv[c++]; | 631 | } else { |
| 505 | else if (is_intnonneg (argv[c])) | ||
| 506 | db_port = atoi (argv[c++]); | ||
| 507 | else | ||
| 508 | break; | 632 | break; |
| 633 | } | ||
| 509 | } | 634 | } |
| 510 | 635 | ||
| 511 | return validate_arguments (); | 636 | return validate_arguments(result); |
| 512 | } | 637 | } |
| 513 | 638 | ||
| 639 | check_mysql_config_wrapper validate_arguments(check_mysql_config_wrapper config_wrapper) { | ||
| 640 | if (config_wrapper.config.db_user == NULL) { | ||
| 641 | config_wrapper.config.db_user = strdup(""); | ||
| 642 | } | ||
| 514 | 643 | ||
| 515 | int | 644 | if (config_wrapper.config.db_host == NULL) { |
| 516 | validate_arguments (void) | 645 | config_wrapper.config.db_host = strdup(""); |
| 517 | { | 646 | } |
| 518 | if (db_user == NULL) | ||
| 519 | db_user = strdup(""); | ||
| 520 | |||
| 521 | if (db_host == NULL) | ||
| 522 | db_host = strdup(""); | ||
| 523 | 647 | ||
| 524 | if (db == NULL) | 648 | if (config_wrapper.config.db == NULL) { |
| 525 | db = strdup(""); | 649 | config_wrapper.config.db = strdup(""); |
| 650 | } | ||
| 526 | 651 | ||
| 527 | return OK; | 652 | return config_wrapper; |
| 528 | } | 653 | } |
| 529 | 654 | ||
| 530 | 655 | void print_help(void) { | |
| 531 | void | ||
| 532 | print_help (void) | ||
| 533 | { | ||
| 534 | char *myport; | 656 | char *myport; |
| 535 | xasprintf (&myport, "%d", MYSQL_PORT); | 657 | xasprintf(&myport, "%d", MYSQL_PORT); |
| 536 | 658 | ||
| 537 | print_revision (progname, NP_VERSION); | 659 | print_revision(progname, NP_VERSION); |
| 538 | 660 | ||
| 539 | printf (_(COPYRIGHT), copyright, email); | 661 | printf(_(COPYRIGHT), copyright, email); |
| 540 | 662 | ||
| 541 | printf ("%s\n", _("This program tests connections to a MySQL server")); | 663 | printf("%s\n", _("This program tests connections to a MySQL server")); |
| 542 | 664 | ||
| 543 | printf ("\n\n"); | 665 | printf("\n\n"); |
| 544 | 666 | ||
| 545 | print_usage (); | 667 | print_usage(); |
| 546 | 668 | ||
| 547 | printf (UT_HELP_VRSN); | 669 | printf(UT_HELP_VRSN); |
| 548 | printf (UT_EXTRA_OPTS); | 670 | printf(UT_EXTRA_OPTS); |
| 549 | 671 | ||
| 550 | printf (UT_HOST_PORT, 'P', myport); | 672 | printf(UT_HOST_PORT, 'P', myport); |
| 551 | printf (" %s\n", "-n, --ignore-auth"); | 673 | printf(" %s\n", "-n, --ignore-auth"); |
| 552 | printf (" %s\n", _("Ignore authentication failure and check for mysql connectivity only")); | 674 | printf(" %s\n", _("Ignore authentication failure and check for mysql connectivity only")); |
| 553 | 675 | ||
| 554 | printf (" %s\n", "-s, --socket=STRING"); | 676 | printf(" %s\n", "-s, --socket=STRING"); |
| 555 | printf (" %s\n", _("Use the specified socket (has no effect if -H is used)")); | 677 | printf(" %s\n", _("Use the specified socket (has no effect if -H is used)")); |
| 556 | 678 | ||
| 557 | printf (" %s\n", "-d, --database=STRING"); | 679 | printf(" %s\n", "-d, --database=STRING"); |
| 558 | printf (" %s\n", _("Check database with indicated name")); | 680 | printf(" %s\n", _("Check database with indicated name")); |
| 559 | printf (" %s\n", "-f, --file=STRING"); | 681 | printf(" %s\n", "-f, --file=STRING"); |
| 560 | printf (" %s\n", _("Read from the specified client options file")); | 682 | printf(" %s\n", _("Read from the specified client options file")); |
| 561 | printf (" %s\n", "-g, --group=STRING"); | 683 | printf(" %s\n", "-g, --group=STRING"); |
| 562 | printf (" %s\n", _("Use a client options group")); | 684 | printf(" %s\n", _("Use a client options group")); |
| 563 | printf (" %s\n", "-u, --username=STRING"); | 685 | printf(" %s\n", "-u, --username=STRING"); |
| 564 | printf (" %s\n", _("Connect using the indicated username")); | 686 | printf(" %s\n", _("Connect using the indicated username")); |
| 565 | printf (" %s\n", "-p, --password=STRING"); | 687 | printf(" %s\n", "-p, --password=STRING"); |
| 566 | printf (" %s\n", _("Use the indicated password to authenticate the connection")); | 688 | printf(" %s\n", _("Use the indicated password to authenticate the connection")); |
| 567 | printf (" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); | 689 | printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); |
| 568 | printf (" %s\n", _("Your clear-text password could be visible as a process table entry")); | 690 | printf(" %s\n", _("Your clear-text password could be visible as a process table entry")); |
| 569 | printf (" %s\n", "-S, --check-slave"); | 691 | printf(" %s\n", "-S, --check-slave"); |
| 570 | printf (" %s\n", _("Check if the slave thread is running properly.")); | 692 | printf(" %s\n", _("Check if the slave thread is running properly. This option is deprecated " |
| 571 | printf (" %s\n", "-w, --warning"); | 693 | "in favour of check-replica, which does the same")); |
| 572 | printf (" %s\n", _("Exit with WARNING status if slave server is more than INTEGER seconds")); | 694 | printf(" %s\n", "--check-replica"); |
| 573 | printf (" %s\n", _("behind master")); | 695 | printf(" %s\n", _("Check if the replica thread is running properly.")); |
| 574 | printf (" %s\n", "-c, --critical"); | 696 | printf(" %s\n", "-w, --warning"); |
| 575 | printf (" %s\n", _("Exit with CRITICAL status if slave server is more then INTEGER seconds")); | 697 | printf(" %s\n", |
| 576 | printf (" %s\n", _("behind master")); | 698 | _("Exit with WARNING status if replica server is more than INTEGER seconds")); |
| 577 | printf (" %s\n", "-l, --ssl"); | 699 | printf(" %s\n", _("behind master")); |
| 578 | printf (" %s\n", _("Use ssl encryption")); | 700 | printf(" %s\n", "-c, --critical"); |
| 579 | printf (" %s\n", "-C, --ca-cert=STRING"); | 701 | printf(" %s\n", |
| 580 | printf (" %s\n", _("Path to CA signing the cert")); | 702 | _("Exit with CRITICAL status if replica server is more then INTEGER seconds")); |
| 581 | printf (" %s\n", "-a, --cert=STRING"); | 703 | printf(" %s\n", _("behind master")); |
| 582 | printf (" %s\n", _("Path to SSL certificate")); | 704 | printf(" %s\n", "-l, --ssl"); |
| 583 | printf (" %s\n", "-k, --key=STRING"); | 705 | printf(" %s\n", _("Use ssl encryption")); |
| 584 | printf (" %s\n", _("Path to private SSL key")); | 706 | printf(" %s\n", "-C, --ca-cert=STRING"); |
| 585 | printf (" %s\n", "-D, --ca-dir=STRING"); | 707 | printf(" %s\n", _("Path to CA signing the cert")); |
| 586 | printf (" %s\n", _("Path to CA directory")); | 708 | printf(" %s\n", "-a, --cert=STRING"); |
| 587 | printf (" %s\n", "-L, --ciphers=STRING"); | 709 | printf(" %s\n", _("Path to SSL certificate")); |
| 588 | printf (" %s\n", _("List of valid SSL ciphers")); | 710 | printf(" %s\n", "-k, --key=STRING"); |
| 589 | 711 | printf(" %s\n", _("Path to private SSL key")); | |
| 590 | 712 | printf(" %s\n", "-D, --ca-dir=STRING"); | |
| 591 | printf ("\n"); | 713 | printf(" %s\n", _("Path to CA directory")); |
| 592 | printf (" %s\n", _("There are no required arguments. By default, the local database is checked")); | 714 | printf(" %s\n", "-L, --ciphers=STRING"); |
| 593 | printf (" %s\n", _("using the default unix socket. You can force TCP on localhost by using an")); | 715 | printf(" %s\n", _("List of valid SSL ciphers")); |
| 594 | printf (" %s\n", _("IP address or FQDN ('localhost' will use the socket as well).")); | 716 | |
| 595 | 717 | printf(UT_OUTPUT_FORMAT); | |
| 596 | printf ("\n"); | 718 | |
| 597 | printf ("%s\n", _("Notes:")); | 719 | printf("\n"); |
| 598 | printf (" %s\n", _("You must specify -p with an empty string to force an empty password,")); | 720 | printf(" %s\n", |
| 599 | printf (" %s\n", _("overriding any my.cnf settings.")); | 721 | _("There are no required arguments. By default, the local database is checked")); |
| 600 | 722 | printf(" %s\n", _("using the default unix socket. You can force TCP on localhost by using an")); | |
| 601 | printf (UT_SUPPORT); | 723 | printf(" %s\n", _("IP address or FQDN ('localhost' will use the socket as well).")); |
| 724 | |||
| 725 | printf("\n"); | ||
| 726 | printf("%s\n", _("Notes:")); | ||
| 727 | printf(" %s\n", _("You must specify -p with an empty string to force an empty password,")); | ||
| 728 | printf(" %s\n", _("overriding any my.cnf settings.")); | ||
| 729 | |||
| 730 | printf(UT_SUPPORT); | ||
| 602 | } | 731 | } |
| 603 | 732 | ||
| 604 | 733 | void print_usage(void) { | |
| 605 | void | 734 | printf("%s\n", _("Usage:")); |
| 606 | print_usage (void) | 735 | printf(" %s [-d database] [-H host] [-P port] [-s socket]\n", progname); |
| 607 | { | 736 | printf(" [-u user] [-p password] [-S] [-l] [-a cert] [-k key]\n"); |
| 608 | printf ("%s\n", _("Usage:")); | 737 | printf(" [-C ca-cert] [-D ca-dir] [-L ciphers] [-f optfile] [-g group]\n"); |
| 609 | printf (" %s [-d database] [-H host] [-P port] [-s socket]\n",progname); | ||
| 610 | printf (" [-u user] [-p password] [-S] [-l] [-a cert] [-k key]\n"); | ||
| 611 | printf (" [-C ca-cert] [-D ca-dir] [-L ciphers] [-f optfile] [-g group]\n"); | ||
| 612 | } | 738 | } |
diff --git a/plugins/check_mysql.d/config.h b/plugins/check_mysql.d/config.h new file mode 100644 index 00000000..1d8c82bb --- /dev/null +++ b/plugins/check_mysql.d/config.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <mysql.h> | ||
| 8 | |||
| 9 | typedef struct { | ||
| 10 | char *db_host; | ||
| 11 | unsigned int db_port; | ||
| 12 | char *db_user; | ||
| 13 | char *db_socket; | ||
| 14 | char *db_pass; | ||
| 15 | char *db; | ||
| 16 | char *ca_cert; | ||
| 17 | char *ca_dir; | ||
| 18 | char *cert; | ||
| 19 | char *key; | ||
| 20 | char *ciphers; | ||
| 21 | bool ssl; | ||
| 22 | char *opt_file; | ||
| 23 | char *opt_group; | ||
| 24 | |||
| 25 | bool check_replica; | ||
| 26 | bool ignore_auth; | ||
| 27 | |||
| 28 | mp_thresholds replica_thresholds; | ||
| 29 | |||
| 30 | bool output_format_is_set; | ||
| 31 | mp_output_format output_format; | ||
| 32 | } check_mysql_config; | ||
| 33 | |||
| 34 | check_mysql_config check_mysql_config_init() { | ||
| 35 | check_mysql_config tmp = { | ||
| 36 | .db_host = NULL, | ||
| 37 | .db_port = MYSQL_PORT, | ||
| 38 | .db = NULL, | ||
| 39 | .db_pass = NULL, | ||
| 40 | .db_socket = NULL, | ||
| 41 | .db_user = NULL, | ||
| 42 | .ca_cert = NULL, | ||
| 43 | .ca_dir = NULL, | ||
| 44 | .cert = NULL, | ||
| 45 | .key = NULL, | ||
| 46 | .ciphers = NULL, | ||
| 47 | .ssl = false, | ||
| 48 | .opt_file = NULL, | ||
| 49 | .opt_group = NULL, | ||
| 50 | |||
| 51 | .check_replica = false, | ||
| 52 | .ignore_auth = false, | ||
| 53 | |||
| 54 | .replica_thresholds = mp_thresholds_init(), | ||
| 55 | |||
| 56 | .output_format_is_set = false, | ||
| 57 | }; | ||
| 58 | return tmp; | ||
| 59 | } | ||
diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c index 79b6e2f4..ae6cc15d 100644 --- a/plugins/check_mysql_query.c +++ b/plugins/check_mysql_query.c | |||
| @@ -29,35 +29,34 @@ | |||
| 29 | * | 29 | * |
| 30 | *****************************************************************************/ | 30 | *****************************************************************************/ |
| 31 | 31 | ||
| 32 | const char *progname = "check_mysql_query"; | ||
| 33 | const char *copyright = "1999-2024"; | ||
| 34 | const char *email = "devel@monitoring-plugins.org"; | ||
| 35 | |||
| 36 | #include "common.h" | 32 | #include "common.h" |
| 33 | #include "output.h" | ||
| 34 | #include "perfdata.h" | ||
| 35 | #include "states.h" | ||
| 36 | #include "thresholds.h" | ||
| 37 | #include "utils.h" | 37 | #include "utils.h" |
| 38 | #include "utils_base.h" | 38 | #include "utils_base.h" |
| 39 | #include "netutils.h" | 39 | #include "netutils.h" |
| 40 | #include "check_mysql_query.d/config.h" | ||
| 40 | 41 | ||
| 41 | #include <mysql.h> | 42 | #include <mysql.h> |
| 42 | #include <errmsg.h> | 43 | #include <errmsg.h> |
| 43 | 44 | ||
| 44 | static char *db_user = NULL; | 45 | const char *progname = "check_mysql_query"; |
| 45 | static char *db_host = NULL; | 46 | const char *copyright = "1999-2024"; |
| 46 | static char *db_socket = NULL; | 47 | const char *email = "devel@monitoring-plugins.org"; |
| 47 | static char *db_pass = NULL; | 48 | |
| 48 | static char *db = NULL; | 49 | typedef struct { |
| 49 | static char *opt_file = NULL; | 50 | int errorcode; |
| 50 | static char *opt_group = NULL; | 51 | check_mysql_query_config config; |
| 51 | static unsigned int db_port = MYSQL_PORT; | 52 | } check_mysql_query_config_wrapper; |
| 52 | 53 | static check_mysql_query_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | |
| 53 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 54 | static check_mysql_query_config_wrapper |
| 54 | static int validate_arguments(void); | 55 | validate_arguments(check_mysql_query_config_wrapper /*config_wrapper*/); |
| 55 | static void print_help(void); | 56 | static void print_help(void); |
| 56 | void print_usage(void); | 57 | void print_usage(void); |
| 57 | 58 | ||
| 58 | static char *sql_query = NULL; | ||
| 59 | static int verbose = 0; | 59 | static int verbose = 0; |
| 60 | static thresholds *my_thresholds = NULL; | ||
| 61 | 60 | ||
| 62 | int main(int argc, char **argv) { | 61 | int main(int argc, char **argv) { |
| 63 | setlocale(LC_ALL, ""); | 62 | setlocale(LC_ALL, ""); |
| @@ -67,40 +66,63 @@ int main(int argc, char **argv) { | |||
| 67 | /* Parse extra opts if any */ | 66 | /* Parse extra opts if any */ |
| 68 | argv = np_extra_opts(&argc, argv, progname); | 67 | argv = np_extra_opts(&argc, argv, progname); |
| 69 | 68 | ||
| 70 | if (process_arguments(argc, argv) == ERROR) | 69 | check_mysql_query_config_wrapper tmp_config = process_arguments(argc, argv); |
| 70 | if (tmp_config.errorcode == ERROR) { | ||
| 71 | usage4(_("Could not parse arguments")); | 71 | usage4(_("Could not parse arguments")); |
| 72 | } | ||
| 73 | |||
| 74 | const check_mysql_query_config config = tmp_config.config; | ||
| 75 | |||
| 76 | if (config.output_format_is_set) { | ||
| 77 | mp_set_format(config.output_format); | ||
| 78 | } | ||
| 72 | 79 | ||
| 73 | MYSQL mysql; | 80 | MYSQL mysql; |
| 74 | /* initialize mysql */ | 81 | /* initialize mysql */ |
| 75 | mysql_init(&mysql); | 82 | mysql_init(&mysql); |
| 76 | 83 | ||
| 77 | if (opt_file != NULL) | 84 | if (config.opt_file != NULL) { |
| 78 | mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, opt_file); | 85 | mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file); |
| 86 | } | ||
| 79 | 87 | ||
| 80 | if (opt_group != NULL) | 88 | if (config.opt_group != NULL) { |
| 81 | mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, opt_group); | 89 | mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group); |
| 82 | else | 90 | } else { |
| 83 | mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); | 91 | mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); |
| 92 | } | ||
| 93 | |||
| 94 | mp_check overall = mp_check_init(); | ||
| 95 | mp_subcheck sc_connect = mp_subcheck_init(); | ||
| 84 | 96 | ||
| 85 | /* establish a connection to the server and error checking */ | 97 | /* establish a connection to the server and error checking */ |
| 86 | if (!mysql_real_connect(&mysql, db_host, db_user, db_pass, db, db_port, db_socket, 0)) { | 98 | if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, |
| 87 | if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) | 99 | config.db_port, config.db_socket, 0)) { |
| 88 | die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); | 100 | xasprintf(&sc_connect.output, "query failed: %s", mysql_error(&mysql)); |
| 89 | else if (mysql_errno(&mysql) == CR_VERSION_ERROR) | 101 | |
| 90 | die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); | 102 | if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { |
| 91 | else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) | 103 | sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); |
| 92 | die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); | 104 | } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { |
| 93 | else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) | 105 | sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); |
| 94 | die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); | 106 | } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) { |
| 95 | else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) | 107 | sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); |
| 96 | die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); | 108 | } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) { |
| 97 | else | 109 | sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); |
| 98 | die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql)); | 110 | } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) { |
| 111 | sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING); | ||
| 112 | } else { | ||
| 113 | sc_connect = mp_set_subcheck_state(sc_connect, STATE_CRITICAL); | ||
| 114 | } | ||
| 115 | |||
| 116 | mp_add_subcheck_to_check(&overall, sc_connect); | ||
| 117 | mp_exit(overall); | ||
| 99 | } | 118 | } |
| 100 | 119 | ||
| 101 | char *error = NULL; | 120 | sc_connect = mp_set_subcheck_state(sc_connect, STATE_OK); |
| 102 | if (mysql_query(&mysql, sql_query) != 0) { | 121 | xasprintf(&sc_connect.output, "query succeeded"); |
| 103 | error = strdup(mysql_error(&mysql)); | 122 | mp_add_subcheck_to_check(&overall, sc_connect); |
| 123 | |||
| 124 | if (mysql_query(&mysql, config.sql_query) != 0) { | ||
| 125 | char *error = strdup(mysql_error(&mysql)); | ||
| 104 | mysql_close(&mysql); | 126 | mysql_close(&mysql); |
| 105 | die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); | 127 | die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); |
| 106 | } | 128 | } |
| @@ -108,7 +130,7 @@ int main(int argc, char **argv) { | |||
| 108 | MYSQL_RES *res; | 130 | MYSQL_RES *res; |
| 109 | /* store the result */ | 131 | /* store the result */ |
| 110 | if ((res = mysql_store_result(&mysql)) == NULL) { | 132 | if ((res = mysql_store_result(&mysql)) == NULL) { |
| 111 | error = strdup(mysql_error(&mysql)); | 133 | char *error = strdup(mysql_error(&mysql)); |
| 112 | mysql_close(&mysql); | 134 | mysql_close(&mysql); |
| 113 | die(STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error); | 135 | die(STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error); |
| 114 | } | 136 | } |
| @@ -119,17 +141,24 @@ int main(int argc, char **argv) { | |||
| 119 | die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned")); | 141 | die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned")); |
| 120 | } | 142 | } |
| 121 | 143 | ||
| 144 | mp_subcheck sc_value = mp_subcheck_init(); | ||
| 122 | MYSQL_ROW row; | 145 | MYSQL_ROW row; |
| 123 | /* fetch the first row */ | 146 | /* fetch the first row */ |
| 124 | if ((row = mysql_fetch_row(res)) == NULL) { | 147 | if ((row = mysql_fetch_row(res)) == NULL) { |
| 125 | error = strdup(mysql_error(&mysql)); | 148 | xasprintf(&sc_value.output, "fetch row error - %s", mysql_error(&mysql)); |
| 126 | mysql_free_result(res); | 149 | mysql_free_result(res); |
| 127 | mysql_close(&mysql); | 150 | mysql_close(&mysql); |
| 128 | die(STATE_CRITICAL, "QUERY %s: Fetch row error - %s\n", _("CRITICAL"), error); | 151 | |
| 152 | sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL); | ||
| 153 | mp_add_subcheck_to_check(&overall, sc_value); | ||
| 154 | mp_exit(overall); | ||
| 129 | } | 155 | } |
| 130 | 156 | ||
| 131 | if (!is_numeric(row[0])) { | 157 | if (!is_numeric(row[0])) { |
| 132 | die(STATE_CRITICAL, "QUERY %s: %s - '%s'\n", _("CRITICAL"), _("Is not a numeric"), row[0]); | 158 | xasprintf(&sc_value.output, "query result is not numeric"); |
| 159 | sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL); | ||
| 160 | mp_add_subcheck_to_check(&overall, sc_value); | ||
| 161 | mp_exit(overall); | ||
| 133 | } | 162 | } |
| 134 | 163 | ||
| 135 | double value = strtod(row[0], NULL); | 164 | double value = strtod(row[0], NULL); |
| @@ -140,68 +169,84 @@ int main(int argc, char **argv) { | |||
| 140 | /* close the connection */ | 169 | /* close the connection */ |
| 141 | mysql_close(&mysql); | 170 | mysql_close(&mysql); |
| 142 | 171 | ||
| 143 | if (verbose >= 3) | 172 | if (verbose >= 3) { |
| 144 | printf("mysql result: %f\n", value); | 173 | printf("mysql result: %f\n", value); |
| 174 | } | ||
| 145 | 175 | ||
| 146 | int status = get_status(value, my_thresholds); | 176 | mp_perfdata pd_query_result = perfdata_init(); |
| 177 | pd_query_result = mp_set_pd_value(pd_query_result, value); | ||
| 178 | pd_query_result = mp_pd_set_thresholds(pd_query_result, config.thresholds); | ||
| 179 | pd_query_result.label = "result"; | ||
| 180 | mp_add_perfdata_to_subcheck(&sc_value, pd_query_result); | ||
| 147 | 181 | ||
| 148 | if (status == STATE_OK) { | 182 | sc_value = mp_set_subcheck_state(sc_value, mp_get_pd_status(pd_query_result)); |
| 149 | printf("QUERY %s: ", _("OK")); | 183 | xasprintf(&sc_value.output, "'%s' returned '%f'", config.sql_query, value); |
| 150 | } else if (status == STATE_WARNING) { | 184 | |
| 151 | printf("QUERY %s: ", _("WARNING")); | 185 | mp_add_subcheck_to_check(&overall, sc_value); |
| 152 | } else if (status == STATE_CRITICAL) { | ||
| 153 | printf("QUERY %s: ", _("CRITICAL")); | ||
| 154 | } | ||
| 155 | printf(_("'%s' returned %f | %s"), sql_query, value, | ||
| 156 | fperfdata("result", value, "", my_thresholds->warning ? true : false, my_thresholds->warning ? my_thresholds->warning->end : 0, | ||
| 157 | my_thresholds->critical ? true : false, my_thresholds->critical ? my_thresholds->critical->end : 0, false, 0, false, | ||
| 158 | 0)); | ||
| 159 | printf("\n"); | ||
| 160 | 186 | ||
| 161 | return status; | 187 | mp_exit(overall); |
| 162 | } | 188 | } |
| 163 | 189 | ||
| 164 | /* process command-line arguments */ | 190 | /* process command-line arguments */ |
| 165 | int process_arguments(int argc, char **argv) { | 191 | check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { |
| 166 | static struct option longopts[] = { | 192 | enum { |
| 167 | {"hostname", required_argument, 0, 'H'}, {"socket", required_argument, 0, 's'}, {"database", required_argument, 0, 'd'}, | 193 | output_format_index = CHAR_MAX + 1, |
| 168 | {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'}, {"file", required_argument, 0, 'f'}, | 194 | }; |
| 169 | {"group", required_argument, 0, 'g'}, {"port", required_argument, 0, 'P'}, {"verbose", no_argument, 0, 'v'}, | 195 | |
| 170 | {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"query", required_argument, 0, 'q'}, | 196 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 171 | {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {0, 0, 0, 0}}; | 197 | {"socket", required_argument, 0, 's'}, |
| 172 | 198 | {"database", required_argument, 0, 'd'}, | |
| 173 | if (argc < 1) | 199 | {"username", required_argument, 0, 'u'}, |
| 174 | return ERROR; | 200 | {"password", required_argument, 0, 'p'}, |
| 175 | 201 | {"file", required_argument, 0, 'f'}, | |
| 176 | char *warning = NULL; | 202 | {"group", required_argument, 0, 'g'}, |
| 177 | char *critical = NULL; | 203 | {"port", required_argument, 0, 'P'}, |
| 204 | {"verbose", no_argument, 0, 'v'}, | ||
| 205 | {"version", no_argument, 0, 'V'}, | ||
| 206 | {"help", no_argument, 0, 'h'}, | ||
| 207 | {"query", required_argument, 0, 'q'}, | ||
| 208 | {"warning", required_argument, 0, 'w'}, | ||
| 209 | {"critical", required_argument, 0, 'c'}, | ||
| 210 | {"output-format", required_argument, 0, output_format_index}, | ||
| 211 | {0, 0, 0, 0}}; | ||
| 212 | |||
| 213 | check_mysql_query_config_wrapper result = { | ||
| 214 | .errorcode = OK, | ||
| 215 | .config = check_mysql_query_config_init(), | ||
| 216 | }; | ||
| 217 | |||
| 218 | if (argc < 1) { | ||
| 219 | result.errorcode = ERROR; | ||
| 220 | return result; | ||
| 221 | } | ||
| 178 | 222 | ||
| 179 | while (true) { | 223 | while (true) { |
| 180 | int option = 0; | 224 | int option = 0; |
| 181 | int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); | 225 | int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); |
| 182 | 226 | ||
| 183 | if (option_char == -1 || option_char == EOF) | 227 | if (option_char == -1 || option_char == EOF) { |
| 184 | break; | 228 | break; |
| 229 | } | ||
| 185 | 230 | ||
| 186 | switch (option_char) { | 231 | switch (option_char) { |
| 187 | case 'H': /* hostname */ | 232 | case 'H': /* hostname */ |
| 188 | if (is_host(optarg)) { | 233 | if (is_host(optarg)) { |
| 189 | db_host = optarg; | 234 | result.config.db_host = optarg; |
| 190 | } else { | 235 | } else { |
| 191 | usage2(_("Invalid hostname/address"), optarg); | 236 | usage2(_("Invalid hostname/address"), optarg); |
| 192 | } | 237 | } |
| 193 | break; | 238 | break; |
| 194 | case 's': /* socket */ | 239 | case 's': /* socket */ |
| 195 | db_socket = optarg; | 240 | result.config.db_socket = optarg; |
| 196 | break; | 241 | break; |
| 197 | case 'd': /* database */ | 242 | case 'd': /* database */ |
| 198 | db = optarg; | 243 | result.config.db = optarg; |
| 199 | break; | 244 | break; |
| 200 | case 'u': /* username */ | 245 | case 'u': /* username */ |
| 201 | db_user = optarg; | 246 | result.config.db_user = optarg; |
| 202 | break; | 247 | break; |
| 203 | case 'p': /* authentication information: password */ | 248 | case 'p': /* authentication information: password */ |
| 204 | db_pass = strdup(optarg); | 249 | result.config.db_pass = strdup(optarg); |
| 205 | 250 | ||
| 206 | /* Delete the password from process list */ | 251 | /* Delete the password from process list */ |
| 207 | while (*optarg != '\0') { | 252 | while (*optarg != '\0') { |
| @@ -210,13 +255,13 @@ int process_arguments(int argc, char **argv) { | |||
| 210 | } | 255 | } |
| 211 | break; | 256 | break; |
| 212 | case 'f': /* client options file */ | 257 | case 'f': /* client options file */ |
| 213 | opt_file = optarg; | 258 | result.config.opt_file = optarg; |
| 214 | break; | 259 | break; |
| 215 | case 'g': /* client options group */ | 260 | case 'g': /* client options group */ |
| 216 | opt_group = optarg; | 261 | result.config.opt_group = optarg; |
| 217 | break; | 262 | break; |
| 218 | case 'P': /* critical time threshold */ | 263 | case 'P': /* critical time threshold */ |
| 219 | db_port = atoi(optarg); | 264 | result.config.db_port = atoi(optarg); |
| 220 | break; | 265 | break; |
| 221 | case 'v': | 266 | case 'v': |
| 222 | verbose++; | 267 | verbose++; |
| @@ -228,38 +273,60 @@ int process_arguments(int argc, char **argv) { | |||
| 228 | print_help(); | 273 | print_help(); |
| 229 | exit(STATE_UNKNOWN); | 274 | exit(STATE_UNKNOWN); |
| 230 | case 'q': | 275 | case 'q': |
| 231 | xasprintf(&sql_query, "%s", optarg); | 276 | xasprintf(&result.config.sql_query, "%s", optarg); |
| 232 | break; | ||
| 233 | case 'w': | ||
| 234 | warning = optarg; | ||
| 235 | break; | ||
| 236 | case 'c': | ||
| 237 | critical = optarg; | ||
| 238 | break; | 277 | break; |
| 278 | case 'w': { | ||
| 279 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 280 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 281 | die(STATE_UNKNOWN, "failed to parse warning threshold"); | ||
| 282 | } | ||
| 283 | result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); | ||
| 284 | } break; | ||
| 285 | case 'c': { | ||
| 286 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 287 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 288 | die(STATE_UNKNOWN, "failed to parse critical threshold"); | ||
| 289 | } | ||
| 290 | result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range); | ||
| 291 | } break; | ||
| 239 | case '?': /* help */ | 292 | case '?': /* help */ |
| 240 | usage5(); | 293 | usage5(); |
| 294 | case output_format_index: { | ||
| 295 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 296 | if (!parser.parsing_success) { | ||
| 297 | printf("Invalid output format: %s\n", optarg); | ||
| 298 | exit(STATE_UNKNOWN); | ||
| 299 | } | ||
| 300 | |||
| 301 | result.config.output_format_is_set = true; | ||
| 302 | result.config.output_format = parser.output_format; | ||
| 303 | break; | ||
| 304 | } | ||
| 241 | } | 305 | } |
| 242 | } | 306 | } |
| 243 | 307 | ||
| 244 | set_thresholds(&my_thresholds, warning, critical); | 308 | return validate_arguments(result); |
| 245 | |||
| 246 | return validate_arguments(); | ||
| 247 | } | 309 | } |
| 248 | 310 | ||
| 249 | int validate_arguments(void) { | 311 | check_mysql_query_config_wrapper |
| 250 | if (sql_query == NULL) | 312 | validate_arguments(check_mysql_query_config_wrapper config_wrapper) { |
| 313 | if (config_wrapper.config.sql_query == NULL) { | ||
| 251 | usage("Must specify a SQL query to run"); | 314 | usage("Must specify a SQL query to run"); |
| 315 | } | ||
| 252 | 316 | ||
| 253 | if (db_user == NULL) | 317 | if (config_wrapper.config.db_user == NULL) { |
| 254 | db_user = strdup(""); | 318 | config_wrapper.config.db_user = strdup(""); |
| 319 | } | ||
| 255 | 320 | ||
| 256 | if (db_host == NULL) | 321 | if (config_wrapper.config.db_host == NULL) { |
| 257 | db_host = strdup(""); | 322 | config_wrapper.config.db_host = strdup(""); |
| 323 | } | ||
| 258 | 324 | ||
| 259 | if (db == NULL) | 325 | if (config_wrapper.config.db == NULL) { |
| 260 | db = strdup(""); | 326 | config_wrapper.config.db = strdup(""); |
| 327 | } | ||
| 261 | 328 | ||
| 262 | return OK; | 329 | return config_wrapper; |
| 263 | } | 330 | } |
| 264 | 331 | ||
| 265 | void print_help(void) { | 332 | void print_help(void) { |
| @@ -297,6 +364,8 @@ void print_help(void) { | |||
| 297 | printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); | 364 | printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); |
| 298 | printf(" %s\n", _("Your clear-text password could be visible as a process table entry")); | 365 | printf(" %s\n", _("Your clear-text password could be visible as a process table entry")); |
| 299 | 366 | ||
| 367 | printf(UT_OUTPUT_FORMAT); | ||
| 368 | |||
| 300 | printf("\n"); | 369 | printf("\n"); |
| 301 | printf(" %s\n", _("A query is required. The result from the query should be numeric.")); | 370 | printf(" %s\n", _("A query is required. The result from the query should be numeric.")); |
| 302 | printf(" %s\n", _("For extra security, create a user with minimal access.")); | 371 | printf(" %s\n", _("For extra security, create a user with minimal access.")); |
diff --git a/plugins/check_mysql_query.d/config.h b/plugins/check_mysql_query.d/config.h new file mode 100644 index 00000000..32ab455a --- /dev/null +++ b/plugins/check_mysql_query.d/config.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <mysql.h> | ||
| 7 | |||
| 8 | typedef struct { | ||
| 9 | char *db_host; | ||
| 10 | char *db_socket; | ||
| 11 | char *db; | ||
| 12 | char *db_user; | ||
| 13 | char *db_pass; | ||
| 14 | char *opt_file; | ||
| 15 | char *opt_group; | ||
| 16 | unsigned int db_port; | ||
| 17 | |||
| 18 | char *sql_query; | ||
| 19 | mp_thresholds thresholds; | ||
| 20 | |||
| 21 | bool output_format_is_set; | ||
| 22 | mp_output_format output_format; | ||
| 23 | } check_mysql_query_config; | ||
| 24 | |||
| 25 | check_mysql_query_config check_mysql_query_config_init() { | ||
| 26 | check_mysql_query_config tmp = { | ||
| 27 | .db_host = NULL, | ||
| 28 | .db_socket = NULL, | ||
| 29 | .db = NULL, | ||
| 30 | .db_user = NULL, | ||
| 31 | .db_pass = NULL, | ||
| 32 | .opt_file = NULL, | ||
| 33 | .opt_group = NULL, | ||
| 34 | .db_port = MYSQL_PORT, | ||
| 35 | |||
| 36 | .sql_query = NULL, | ||
| 37 | .thresholds = mp_thresholds_init(), | ||
| 38 | |||
| 39 | .output_format_is_set = false, | ||
| 40 | }; | ||
| 41 | return tmp; | ||
| 42 | } | ||
diff --git a/plugins/check_nagios.c b/plugins/check_nagios.c index 48629be3..a46dc1ed 100644 --- a/plugins/check_nagios.c +++ b/plugins/check_nagios.c | |||
| @@ -39,47 +39,20 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 39 | #include "common.h" | 39 | #include "common.h" |
| 40 | #include "runcmd.h" | 40 | #include "runcmd.h" |
| 41 | #include "utils.h" | 41 | #include "utils.h" |
| 42 | 42 | #include "states.h" | |
| 43 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 43 | #include "check_nagios.d/config.h" |
| 44 | |||
| 45 | typedef struct { | ||
| 46 | int errorcode; | ||
| 47 | check_nagios_config config; | ||
| 48 | } check_nagios_config_wrapper; | ||
| 49 | static check_nagios_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 44 | static void print_help(void); | 50 | static void print_help(void); |
| 45 | void print_usage(void); | 51 | void print_usage(void); |
| 46 | 52 | ||
| 47 | static char *status_log = NULL; | ||
| 48 | static char *process_string = NULL; | ||
| 49 | static int expire_minutes = 0; | ||
| 50 | |||
| 51 | static int verbose = 0; | 53 | static int verbose = 0; |
| 52 | 54 | ||
| 53 | int main(int argc, char **argv) { | 55 | int main(int argc, char **argv) { |
| 54 | int result = STATE_UNKNOWN; | ||
| 55 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 56 | unsigned long latest_entry_time = 0L; | ||
| 57 | unsigned long temp_entry_time = 0L; | ||
| 58 | int proc_entries = 0; | ||
| 59 | time_t current_time; | ||
| 60 | char *temp_ptr; | ||
| 61 | FILE *fp; | ||
| 62 | int procuid = 0; | ||
| 63 | int procpid = 0; | ||
| 64 | int procppid = 0; | ||
| 65 | int procvsz = 0; | ||
| 66 | int procrss = 0; | ||
| 67 | float procpcpu = 0; | ||
| 68 | char procstat[8]; | ||
| 69 | #ifdef PS_USES_PROCETIME | ||
| 70 | char procetime[MAX_INPUT_BUFFER]; | ||
| 71 | #endif /* PS_USES_PROCETIME */ | ||
| 72 | char procprog[MAX_INPUT_BUFFER]; | ||
| 73 | char *procargs; | ||
| 74 | int pos; | ||
| 75 | int cols; | ||
| 76 | int expected_cols = PS_COLS - 1; | ||
| 77 | const char *zombie = "Z"; | ||
| 78 | char *temp_string; | ||
| 79 | output chld_out; | ||
| 80 | output chld_err; | ||
| 81 | size_t i; | ||
| 82 | |||
| 83 | setlocale(LC_ALL, ""); | 56 | setlocale(LC_ALL, ""); |
| 84 | bindtextdomain(PACKAGE, LOCALEDIR); | 57 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 85 | textdomain(PACKAGE); | 58 | textdomain(PACKAGE); |
| @@ -87,8 +60,13 @@ int main(int argc, char **argv) { | |||
| 87 | /* Parse extra opts if any */ | 60 | /* Parse extra opts if any */ |
| 88 | argv = np_extra_opts(&argc, argv, progname); | 61 | argv = np_extra_opts(&argc, argv, progname); |
| 89 | 62 | ||
| 90 | if (process_arguments(argc, argv) == ERROR) | 63 | check_nagios_config_wrapper tmp_config = process_arguments(argc, argv); |
| 64 | |||
| 65 | if (tmp_config.errorcode == ERROR) { | ||
| 91 | usage_va(_("Could not parse arguments")); | 66 | usage_va(_("Could not parse arguments")); |
| 67 | } | ||
| 68 | |||
| 69 | const check_nagios_config config = tmp_config.config; | ||
| 92 | 70 | ||
| 93 | /* Set signal handling and alarm timeout */ | 71 | /* Set signal handling and alarm timeout */ |
| 94 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 72 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| @@ -99,13 +77,17 @@ int main(int argc, char **argv) { | |||
| 99 | alarm(timeout_interval); | 77 | alarm(timeout_interval); |
| 100 | 78 | ||
| 101 | /* open the status log */ | 79 | /* open the status log */ |
| 102 | fp = fopen(status_log, "r"); | 80 | FILE *log_file = fopen(config.status_log, "r"); |
| 103 | if (fp == NULL) { | 81 | if (log_file == NULL) { |
| 104 | die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!")); | 82 | die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!")); |
| 105 | } | 83 | } |
| 106 | 84 | ||
| 85 | unsigned long latest_entry_time = 0L; | ||
| 86 | unsigned long temp_entry_time = 0L; | ||
| 87 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 88 | char *temp_ptr; | ||
| 107 | /* get the date/time of the last item updated in the log */ | 89 | /* get the date/time of the last item updated in the log */ |
| 108 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, fp)) { | 90 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, log_file)) { |
| 109 | if ((temp_ptr = strstr(input_buffer, "created=")) != NULL) { | 91 | if ((temp_ptr = strstr(input_buffer, "created=")) != NULL) { |
| 110 | temp_entry_time = strtoul(temp_ptr + 8, NULL, 10); | 92 | temp_entry_time = strtoul(temp_ptr + 8, NULL, 10); |
| 111 | latest_entry_time = temp_entry_time; | 93 | latest_entry_time = temp_entry_time; |
| @@ -113,22 +95,44 @@ int main(int argc, char **argv) { | |||
| 113 | } | 95 | } |
| 114 | if ((temp_ptr = strtok(input_buffer, "]")) != NULL) { | 96 | if ((temp_ptr = strtok(input_buffer, "]")) != NULL) { |
| 115 | temp_entry_time = strtoul(temp_ptr + 1, NULL, 10); | 97 | temp_entry_time = strtoul(temp_ptr + 1, NULL, 10); |
| 116 | if (temp_entry_time > latest_entry_time) | 98 | if (temp_entry_time > latest_entry_time) { |
| 117 | latest_entry_time = temp_entry_time; | 99 | latest_entry_time = temp_entry_time; |
| 100 | } | ||
| 118 | } | 101 | } |
| 119 | } | 102 | } |
| 120 | fclose(fp); | 103 | fclose(log_file); |
| 121 | 104 | ||
| 122 | if (verbose >= 2) | 105 | if (verbose >= 2) { |
| 123 | printf("command: %s\n", PS_COMMAND); | 106 | printf("command: %s\n", PS_COMMAND); |
| 107 | } | ||
| 124 | 108 | ||
| 125 | /* run the command to check for the Nagios process.. */ | 109 | /* run the command to check for the Nagios process.. */ |
| 126 | if ((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) | 110 | mp_state_enum result = STATE_UNKNOWN; |
| 111 | output chld_out; | ||
| 112 | output chld_err; | ||
| 113 | if ((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) { | ||
| 127 | result = STATE_WARNING; | 114 | result = STATE_WARNING; |
| 115 | } | ||
| 128 | 116 | ||
| 117 | int procuid = 0; | ||
| 118 | int procpid = 0; | ||
| 119 | int procppid = 0; | ||
| 120 | int procvsz = 0; | ||
| 121 | int procrss = 0; | ||
| 122 | int proc_entries = 0; | ||
| 123 | float procpcpu = 0; | ||
| 124 | char procstat[8]; | ||
| 125 | char procprog[MAX_INPUT_BUFFER]; | ||
| 126 | char *procargs; | ||
| 127 | #ifdef PS_USES_PROCETIME | ||
| 128 | char procetime[MAX_INPUT_BUFFER]; | ||
| 129 | #endif /* PS_USES_PROCETIME */ | ||
| 130 | int pos; | ||
| 131 | int expected_cols = PS_COLS - 1; | ||
| 132 | const char *zombie = "Z"; | ||
| 129 | /* count the number of matching Nagios processes... */ | 133 | /* count the number of matching Nagios processes... */ |
| 130 | for (i = 0; i < chld_out.lines; i++) { | 134 | for (size_t i = 0; i < chld_out.lines; i++) { |
| 131 | cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST); | 135 | int cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST); |
| 132 | /* Zombie processes do not give a procprog command */ | 136 | /* Zombie processes do not give a procprog command */ |
| 133 | if (cols == (expected_cols - 1) && strstr(procstat, zombie)) { | 137 | if (cols == (expected_cols - 1) && strstr(procstat, zombie)) { |
| 134 | cols = expected_cols; | 138 | cols = expected_cols; |
| @@ -142,14 +146,14 @@ int main(int argc, char **argv) { | |||
| 142 | strip(procargs); | 146 | strip(procargs); |
| 143 | 147 | ||
| 144 | /* Some ps return full pathname for command. This removes path */ | 148 | /* Some ps return full pathname for command. This removes path */ |
| 145 | temp_string = strtok((char *)procprog, "/"); | 149 | char *temp_string = strtok((char *)procprog, "/"); |
| 146 | while (temp_string) { | 150 | while (temp_string) { |
| 147 | strcpy(procprog, temp_string); | 151 | strcpy(procprog, temp_string); |
| 148 | temp_string = strtok(NULL, "/"); | 152 | temp_string = strtok(NULL, "/"); |
| 149 | } | 153 | } |
| 150 | 154 | ||
| 151 | /* May get empty procargs */ | 155 | /* May get empty procargs */ |
| 152 | if (!strstr(procargs, argv[0]) && strstr(procargs, process_string) && strcmp(procargs, "")) { | 156 | if (!strstr(procargs, argv[0]) && strstr(procargs, config.process_string) && strcmp(procargs, "")) { |
| 153 | proc_entries++; | 157 | proc_entries++; |
| 154 | if (verbose >= 2) { | 158 | if (verbose >= 2) { |
| 155 | printf(_("Found process: %s %s\n"), procprog, procargs); | 159 | printf(_("Found process: %s %s\n"), procprog, procargs); |
| @@ -159,8 +163,9 @@ int main(int argc, char **argv) { | |||
| 159 | } | 163 | } |
| 160 | 164 | ||
| 161 | /* If we get anything on stderr, at least set warning */ | 165 | /* If we get anything on stderr, at least set warning */ |
| 162 | if (chld_err.buflen) | 166 | if (chld_err.buflen) { |
| 163 | result = max_state(result, STATE_WARNING); | 167 | result = max_state(result, STATE_WARNING); |
| 168 | } | ||
| 164 | 169 | ||
| 165 | /* reset the alarm handler */ | 170 | /* reset the alarm handler */ |
| 166 | alarm(0); | 171 | alarm(0); |
| @@ -173,8 +178,9 @@ int main(int argc, char **argv) { | |||
| 173 | die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time")); | 178 | die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time")); |
| 174 | } | 179 | } |
| 175 | 180 | ||
| 181 | time_t current_time; | ||
| 176 | time(¤t_time); | 182 | time(¤t_time); |
| 177 | if ((int)(current_time - latest_entry_time) > (expire_minutes * 60)) { | 183 | if ((int)(current_time - latest_entry_time) > (config.expire_minutes * 60)) { |
| 178 | result = STATE_WARNING; | 184 | result = STATE_WARNING; |
| 179 | } else { | 185 | } else { |
| 180 | result = STATE_OK; | 186 | result = STATE_OK; |
| @@ -187,39 +193,45 @@ int main(int argc, char **argv) { | |||
| 187 | (int)(current_time - latest_entry_time)); | 193 | (int)(current_time - latest_entry_time)); |
| 188 | printf("\n"); | 194 | printf("\n"); |
| 189 | 195 | ||
| 190 | return result; | 196 | exit(result); |
| 191 | } | 197 | } |
| 192 | 198 | ||
| 193 | /* process command-line arguments */ | 199 | /* process command-line arguments */ |
| 194 | int process_arguments(int argc, char **argv) { | 200 | check_nagios_config_wrapper process_arguments(int argc, char **argv) { |
| 195 | int c; | ||
| 196 | |||
| 197 | int option = 0; | ||
| 198 | static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, | 201 | static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, |
| 199 | {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'}, | 202 | {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'}, |
| 200 | {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, | 203 | {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, |
| 201 | {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}}; | 204 | {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}}; |
| 202 | 205 | ||
| 203 | if (argc < 2) | 206 | check_nagios_config_wrapper result = { |
| 204 | return ERROR; | 207 | .errorcode = OK, |
| 208 | .config = check_nagios_config_init(), | ||
| 209 | }; | ||
| 210 | if (argc < 2) { | ||
| 211 | result.errorcode = ERROR; | ||
| 212 | return result; | ||
| 213 | } | ||
| 205 | 214 | ||
| 206 | if (!is_option(argv[1])) { | 215 | if (!is_option(argv[1])) { |
| 207 | status_log = argv[1]; | 216 | result.config.status_log = argv[1]; |
| 208 | if (is_intnonneg(argv[2])) | 217 | if (is_intnonneg(argv[2])) { |
| 209 | expire_minutes = atoi(argv[2]); | 218 | result.config.expire_minutes = atoi(argv[2]); |
| 210 | else | 219 | } else { |
| 211 | die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); | 220 | die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); |
| 212 | process_string = argv[3]; | 221 | } |
| 213 | return OK; | 222 | result.config.process_string = argv[3]; |
| 223 | return result; | ||
| 214 | } | 224 | } |
| 215 | 225 | ||
| 216 | while (1) { | 226 | int option = 0; |
| 217 | c = getopt_long(argc, argv, "+hVvF:C:e:t:", longopts, &option); | 227 | while (true) { |
| 228 | int option_index = getopt_long(argc, argv, "+hVvF:C:e:t:", longopts, &option); | ||
| 218 | 229 | ||
| 219 | if (c == -1 || c == EOF || c == 1) | 230 | if (option_index == -1 || option_index == EOF || option_index == 1) { |
| 220 | break; | 231 | break; |
| 232 | } | ||
| 221 | 233 | ||
| 222 | switch (c) { | 234 | switch (option_index) { |
| 223 | case 'h': /* help */ | 235 | case 'h': /* help */ |
| 224 | print_help(); | 236 | print_help(); |
| 225 | exit(STATE_UNKNOWN); | 237 | exit(STATE_UNKNOWN); |
| @@ -227,22 +239,24 @@ int process_arguments(int argc, char **argv) { | |||
| 227 | print_revision(progname, NP_VERSION); | 239 | print_revision(progname, NP_VERSION); |
| 228 | exit(STATE_UNKNOWN); | 240 | exit(STATE_UNKNOWN); |
| 229 | case 'F': /* status log */ | 241 | case 'F': /* status log */ |
| 230 | status_log = optarg; | 242 | result.config.status_log = optarg; |
| 231 | break; | 243 | break; |
| 232 | case 'C': /* command */ | 244 | case 'C': /* command */ |
| 233 | process_string = optarg; | 245 | result.config.process_string = optarg; |
| 234 | break; | 246 | break; |
| 235 | case 'e': /* expiry time */ | 247 | case 'e': /* expiry time */ |
| 236 | if (is_intnonneg(optarg)) | 248 | if (is_intnonneg(optarg)) { |
| 237 | expire_minutes = atoi(optarg); | 249 | result.config.expire_minutes = atoi(optarg); |
| 238 | else | 250 | } else { |
| 239 | die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); | 251 | die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); |
| 252 | } | ||
| 240 | break; | 253 | break; |
| 241 | case 't': /* timeout */ | 254 | case 't': /* timeout */ |
| 242 | if (is_intnonneg(optarg)) | 255 | if (is_intnonneg(optarg)) { |
| 243 | timeout_interval = atoi(optarg); | 256 | timeout_interval = atoi(optarg); |
| 244 | else | 257 | } else { |
| 245 | die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n")); | 258 | die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n")); |
| 259 | } | ||
| 246 | break; | 260 | break; |
| 247 | case 'v': | 261 | case 'v': |
| 248 | verbose++; | 262 | verbose++; |
| @@ -252,13 +266,15 @@ int process_arguments(int argc, char **argv) { | |||
| 252 | } | 266 | } |
| 253 | } | 267 | } |
| 254 | 268 | ||
| 255 | if (status_log == NULL) | 269 | if (result.config.status_log == NULL) { |
| 256 | die(STATE_UNKNOWN, _("You must provide the status_log\n")); | 270 | die(STATE_UNKNOWN, _("You must provide the status_log\n")); |
| 271 | } | ||
| 257 | 272 | ||
| 258 | if (process_string == NULL) | 273 | if (result.config.process_string == NULL) { |
| 259 | die(STATE_UNKNOWN, _("You must provide a process string\n")); | 274 | die(STATE_UNKNOWN, _("You must provide a process string\n")); |
| 275 | } | ||
| 260 | 276 | ||
| 261 | return OK; | 277 | return result; |
| 262 | } | 278 | } |
| 263 | 279 | ||
| 264 | void print_help(void) { | 280 | void print_help(void) { |
diff --git a/plugins/check_nagios.d/config.h b/plugins/check_nagios.d/config.h new file mode 100644 index 00000000..efe139f9 --- /dev/null +++ b/plugins/check_nagios.d/config.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | typedef struct { | ||
| 7 | char *status_log; | ||
| 8 | char *process_string; | ||
| 9 | int expire_minutes; | ||
| 10 | } check_nagios_config; | ||
| 11 | |||
| 12 | check_nagios_config check_nagios_config_init() { | ||
| 13 | check_nagios_config tmp = { | ||
| 14 | .status_log = NULL, | ||
| 15 | .process_string = NULL, | ||
| 16 | .expire_minutes = 0, | ||
| 17 | }; | ||
| 18 | return tmp; | ||
| 19 | } | ||
diff --git a/plugins/check_nt.c b/plugins/check_nt.c deleted file mode 100644 index dec0b668..00000000 --- a/plugins/check_nt.c +++ /dev/null | |||
| @@ -1,767 +0,0 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * Monitoring check_nt plugin | ||
| 4 | * | ||
| 5 | * License: GPL | ||
| 6 | * Copyright (c) 2000-2002 Yves Rubin (rubiyz@yahoo.com) | ||
| 7 | * Copyright (c) 2003-2024 Monitoring Plugins Development Team | ||
| 8 | * | ||
| 9 | * Description: | ||
| 10 | * | ||
| 11 | * This file contains the check_nt plugin | ||
| 12 | * | ||
| 13 | * This plugin collects data from the NSClient service running on a | ||
| 14 | * Windows NT/2000/XP/2003 server. | ||
| 15 | * This plugin requires NSClient software to run on NT | ||
| 16 | * (http://nsclient.ready2run.nl/) | ||
| 17 | * | ||
| 18 | * | ||
| 19 | * This program is free software: you can redistribute it and/or modify | ||
| 20 | * it under the terms of the GNU General Public License as published by | ||
| 21 | * the Free Software Foundation, either version 3 of the License, or | ||
| 22 | * (at your option) any later version. | ||
| 23 | * | ||
| 24 | * This program is distributed in the hope that it will be useful, | ||
| 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 27 | * GNU General Public License for more details. | ||
| 28 | * | ||
| 29 | * You should have received a copy of the GNU General Public License | ||
| 30 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | *****************************************************************************/ | ||
| 34 | |||
| 35 | const char *progname = "check_nt"; | ||
| 36 | const char *copyright = "2000-2024"; | ||
| 37 | const char *email = "devel@monitoring-plugins.org"; | ||
| 38 | |||
| 39 | #include "common.h" | ||
| 40 | #include "netutils.h" | ||
| 41 | #include "utils.h" | ||
| 42 | |||
| 43 | enum checkvars { | ||
| 44 | CHECK_NONE, | ||
| 45 | CHECK_CLIENTVERSION, | ||
| 46 | CHECK_CPULOAD, | ||
| 47 | CHECK_UPTIME, | ||
| 48 | CHECK_USEDDISKSPACE, | ||
| 49 | CHECK_SERVICESTATE, | ||
| 50 | CHECK_PROCSTATE, | ||
| 51 | CHECK_MEMUSE, | ||
| 52 | CHECK_COUNTER, | ||
| 53 | CHECK_FILEAGE, | ||
| 54 | CHECK_INSTANCES | ||
| 55 | }; | ||
| 56 | |||
| 57 | enum { | ||
| 58 | MAX_VALUE_LIST = 30, | ||
| 59 | PORT = 1248 | ||
| 60 | }; | ||
| 61 | |||
| 62 | static char *server_address = NULL; | ||
| 63 | static int server_port = PORT; | ||
| 64 | static char *value_list = NULL; | ||
| 65 | static char *req_password = NULL; | ||
| 66 | static unsigned long lvalue_list[MAX_VALUE_LIST]; | ||
| 67 | static unsigned long warning_value = 0L; | ||
| 68 | static unsigned long critical_value = 0L; | ||
| 69 | static bool check_warning_value = false; | ||
| 70 | static bool check_critical_value = false; | ||
| 71 | static enum checkvars vars_to_check = CHECK_NONE; | ||
| 72 | static bool show_all = false; | ||
| 73 | |||
| 74 | static char recv_buffer[MAX_INPUT_BUFFER]; | ||
| 75 | |||
| 76 | static void fetch_data(const char *address, int port, const char *sendb); | ||
| 77 | static int process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 78 | static void preparelist(char *string); | ||
| 79 | static bool strtoularray(unsigned long *array, char *string, const char *delim); | ||
| 80 | static void print_help(void); | ||
| 81 | void print_usage(void); | ||
| 82 | |||
| 83 | int main(int argc, char **argv) { | ||
| 84 | |||
| 85 | /* should be int result = STATE_UNKNOWN; */ | ||
| 86 | |||
| 87 | int return_code = STATE_UNKNOWN; | ||
| 88 | char *send_buffer = NULL; | ||
| 89 | char *output_message = NULL; | ||
| 90 | char *perfdata = NULL; | ||
| 91 | char *temp_string = NULL; | ||
| 92 | char *temp_string_perf = NULL; | ||
| 93 | char *description = NULL, *counter_unit = NULL; | ||
| 94 | char *minval = NULL, *maxval = NULL, *errcvt = NULL; | ||
| 95 | char *fds = NULL, *tds = NULL; | ||
| 96 | char *numstr; | ||
| 97 | |||
| 98 | double total_disk_space = 0; | ||
| 99 | double free_disk_space = 0; | ||
| 100 | double percent_used_space = 0; | ||
| 101 | double warning_used_space = 0; | ||
| 102 | double critical_used_space = 0; | ||
| 103 | double mem_commitLimit = 0; | ||
| 104 | double mem_commitByte = 0; | ||
| 105 | double fminval = 0, fmaxval = 0; | ||
| 106 | unsigned long utilization; | ||
| 107 | unsigned long uptime; | ||
| 108 | unsigned long age_in_minutes; | ||
| 109 | double counter_value = 0.0; | ||
| 110 | int offset = 0; | ||
| 111 | int updays = 0; | ||
| 112 | int uphours = 0; | ||
| 113 | int upminutes = 0; | ||
| 114 | |||
| 115 | bool isPercent = false; | ||
| 116 | bool allRight = false; | ||
| 117 | |||
| 118 | setlocale(LC_ALL, ""); | ||
| 119 | bindtextdomain(PACKAGE, LOCALEDIR); | ||
| 120 | textdomain(PACKAGE); | ||
| 121 | |||
| 122 | /* Parse extra opts if any */ | ||
| 123 | argv = np_extra_opts(&argc, argv, progname); | ||
| 124 | |||
| 125 | if (process_arguments(argc, argv) == ERROR) | ||
| 126 | usage4(_("Could not parse arguments")); | ||
| 127 | |||
| 128 | /* initialize alarm signal handling */ | ||
| 129 | signal(SIGALRM, socket_timeout_alarm_handler); | ||
| 130 | |||
| 131 | /* set socket timeout */ | ||
| 132 | alarm(socket_timeout); | ||
| 133 | |||
| 134 | switch (vars_to_check) { | ||
| 135 | |||
| 136 | case CHECK_CLIENTVERSION: | ||
| 137 | |||
| 138 | xasprintf(&send_buffer, "%s&1", req_password); | ||
| 139 | fetch_data(server_address, server_port, send_buffer); | ||
| 140 | if (value_list != NULL && strcmp(recv_buffer, value_list) != 0) { | ||
| 141 | xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"), recv_buffer, value_list); | ||
| 142 | return_code = STATE_WARNING; | ||
| 143 | } else { | ||
| 144 | xasprintf(&output_message, "%s", recv_buffer); | ||
| 145 | return_code = STATE_OK; | ||
| 146 | } | ||
| 147 | break; | ||
| 148 | |||
| 149 | case CHECK_CPULOAD: | ||
| 150 | |||
| 151 | if (value_list == NULL) | ||
| 152 | output_message = strdup(_("missing -l parameters")); | ||
| 153 | else if (!strtoularray(lvalue_list, value_list, ",")) | ||
| 154 | output_message = strdup(_("wrong -l parameter.")); | ||
| 155 | else { | ||
| 156 | /* -l parameters is present with only integers */ | ||
| 157 | return_code = STATE_OK; | ||
| 158 | temp_string = strdup(_("CPU Load")); | ||
| 159 | temp_string_perf = strdup(" "); | ||
| 160 | |||
| 161 | /* loop until one of the parameters is wrong or not present */ | ||
| 162 | while (lvalue_list[0 + offset] > (unsigned long)0 && lvalue_list[0 + offset] <= (unsigned long)17280 && | ||
| 163 | lvalue_list[1 + offset] > (unsigned long)0 && lvalue_list[1 + offset] <= (unsigned long)100 && | ||
| 164 | lvalue_list[2 + offset] > (unsigned long)0 && lvalue_list[2 + offset] <= (unsigned long)100) { | ||
| 165 | |||
| 166 | /* Send request and retrieve data */ | ||
| 167 | xasprintf(&send_buffer, "%s&2&%lu", req_password, lvalue_list[0 + offset]); | ||
| 168 | fetch_data(server_address, server_port, send_buffer); | ||
| 169 | |||
| 170 | utilization = strtoul(recv_buffer, NULL, 10); | ||
| 171 | |||
| 172 | /* Check if any of the request is in a warning or critical state */ | ||
| 173 | if (utilization >= lvalue_list[2 + offset]) | ||
| 174 | return_code = STATE_CRITICAL; | ||
| 175 | else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) | ||
| 176 | return_code = STATE_WARNING; | ||
| 177 | |||
| 178 | xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization, lvalue_list[0 + offset]); | ||
| 179 | xasprintf(&temp_string, "%s%s", temp_string, output_message); | ||
| 180 | xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"), lvalue_list[0 + offset], utilization, | ||
| 181 | lvalue_list[1 + offset], lvalue_list[2 + offset]); | ||
| 182 | xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata); | ||
| 183 | offset += 3; /* move across the array */ | ||
| 184 | } | ||
| 185 | |||
| 186 | if (strlen(temp_string) > 10) { /* we had at least one loop */ | ||
| 187 | output_message = strdup(temp_string); | ||
| 188 | perfdata = temp_string_perf; | ||
| 189 | } else | ||
| 190 | output_message = strdup(_("not enough values for -l parameters")); | ||
| 191 | } | ||
| 192 | break; | ||
| 193 | |||
| 194 | case CHECK_UPTIME: | ||
| 195 | |||
| 196 | if (value_list == NULL) { | ||
| 197 | value_list = "minutes"; | ||
| 198 | } | ||
| 199 | if (strncmp(value_list, "seconds", strlen("seconds") + 1) && strncmp(value_list, "minutes", strlen("minutes") + 1) && | ||
| 200 | strncmp(value_list, "hours", strlen("hours") + 1) && strncmp(value_list, "days", strlen("days") + 1)) { | ||
| 201 | |||
| 202 | output_message = strdup(_("wrong -l argument")); | ||
| 203 | } else { | ||
| 204 | xasprintf(&send_buffer, "%s&3", req_password); | ||
| 205 | fetch_data(server_address, server_port, send_buffer); | ||
| 206 | uptime = strtoul(recv_buffer, NULL, 10); | ||
| 207 | updays = uptime / 86400; | ||
| 208 | uphours = (uptime % 86400) / 3600; | ||
| 209 | upminutes = ((uptime % 86400) % 3600) / 60; | ||
| 210 | |||
| 211 | if (!strncmp(value_list, "minutes", strlen("minutes"))) | ||
| 212 | uptime = uptime / 60; | ||
| 213 | else if (!strncmp(value_list, "hours", strlen("hours"))) | ||
| 214 | uptime = uptime / 3600; | ||
| 215 | else if (!strncmp(value_list, "days", strlen("days"))) | ||
| 216 | uptime = uptime / 86400; | ||
| 217 | /* else uptime in seconds, nothing to do */ | ||
| 218 | |||
| 219 | xasprintf(&output_message, _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays, uphours, upminutes, | ||
| 220 | uptime); | ||
| 221 | |||
| 222 | if (check_critical_value && uptime <= critical_value) | ||
| 223 | return_code = STATE_CRITICAL; | ||
| 224 | else if (check_warning_value && uptime <= warning_value) | ||
| 225 | return_code = STATE_WARNING; | ||
| 226 | else | ||
| 227 | return_code = STATE_OK; | ||
| 228 | } | ||
| 229 | break; | ||
| 230 | |||
| 231 | case CHECK_USEDDISKSPACE: | ||
| 232 | |||
| 233 | if (value_list == NULL) | ||
| 234 | output_message = strdup(_("missing -l parameters")); | ||
| 235 | else if (strlen(value_list) != 1) | ||
| 236 | output_message = strdup(_("wrong -l argument")); | ||
| 237 | else { | ||
| 238 | xasprintf(&send_buffer, "%s&4&%s", req_password, value_list); | ||
| 239 | fetch_data(server_address, server_port, send_buffer); | ||
| 240 | fds = strtok(recv_buffer, "&"); | ||
| 241 | tds = strtok(NULL, "&"); | ||
| 242 | if (fds != NULL) | ||
| 243 | free_disk_space = atof(fds); | ||
| 244 | if (tds != NULL) | ||
| 245 | total_disk_space = atof(tds); | ||
| 246 | |||
| 247 | if (total_disk_space > 0 && free_disk_space >= 0) { | ||
| 248 | percent_used_space = ((total_disk_space - free_disk_space) / total_disk_space) * 100; | ||
| 249 | warning_used_space = ((float)warning_value / 100) * total_disk_space; | ||
| 250 | critical_used_space = ((float)critical_value / 100) * total_disk_space; | ||
| 251 | |||
| 252 | xasprintf(&temp_string, _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"), value_list, | ||
| 253 | total_disk_space / 1073741824, (total_disk_space - free_disk_space) / 1073741824, percent_used_space, | ||
| 254 | free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100); | ||
| 255 | xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"), value_list, | ||
| 256 | (total_disk_space - free_disk_space) / 1073741824, warning_used_space / 1073741824, | ||
| 257 | critical_used_space / 1073741824, total_disk_space / 1073741824); | ||
| 258 | |||
| 259 | if (check_critical_value && percent_used_space >= critical_value) | ||
| 260 | return_code = STATE_CRITICAL; | ||
| 261 | else if (check_warning_value && percent_used_space >= warning_value) | ||
| 262 | return_code = STATE_WARNING; | ||
| 263 | else | ||
| 264 | return_code = STATE_OK; | ||
| 265 | |||
| 266 | output_message = strdup(temp_string); | ||
| 267 | perfdata = temp_string_perf; | ||
| 268 | } else { | ||
| 269 | output_message = strdup(_("Free disk space : Invalid drive")); | ||
| 270 | return_code = STATE_UNKNOWN; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | break; | ||
| 274 | |||
| 275 | case CHECK_SERVICESTATE: | ||
| 276 | case CHECK_PROCSTATE: | ||
| 277 | |||
| 278 | if (value_list == NULL) | ||
| 279 | output_message = strdup(_("No service/process specified")); | ||
| 280 | else { | ||
| 281 | preparelist(value_list); /* replace , between services with & to send the request */ | ||
| 282 | xasprintf(&send_buffer, "%s&%u&%s&%s", req_password, (vars_to_check == CHECK_SERVICESTATE) ? 5 : 6, | ||
| 283 | (show_all) ? "ShowAll" : "ShowFail", value_list); | ||
| 284 | fetch_data(server_address, server_port, send_buffer); | ||
| 285 | numstr = strtok(recv_buffer, "&"); | ||
| 286 | if (numstr == NULL) | ||
| 287 | die(STATE_UNKNOWN, _("could not fetch information from server\n")); | ||
| 288 | return_code = atoi(numstr); | ||
| 289 | temp_string = strtok(NULL, "&"); | ||
| 290 | output_message = strdup(temp_string); | ||
| 291 | } | ||
| 292 | break; | ||
| 293 | |||
| 294 | case CHECK_MEMUSE: | ||
| 295 | |||
| 296 | xasprintf(&send_buffer, "%s&7", req_password); | ||
| 297 | fetch_data(server_address, server_port, send_buffer); | ||
| 298 | numstr = strtok(recv_buffer, "&"); | ||
| 299 | if (numstr == NULL) | ||
| 300 | die(STATE_UNKNOWN, _("could not fetch information from server\n")); | ||
| 301 | mem_commitLimit = atof(numstr); | ||
| 302 | numstr = strtok(NULL, "&"); | ||
| 303 | if (numstr == NULL) | ||
| 304 | die(STATE_UNKNOWN, _("could not fetch information from server\n")); | ||
| 305 | mem_commitByte = atof(numstr); | ||
| 306 | percent_used_space = (mem_commitByte / mem_commitLimit) * 100; | ||
| 307 | warning_used_space = ((float)warning_value / 100) * mem_commitLimit; | ||
| 308 | critical_used_space = ((float)critical_value / 100) * mem_commitLimit; | ||
| 309 | |||
| 310 | /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here, | ||
| 311 | which equals RAM + Pagefiles. */ | ||
| 312 | xasprintf(&output_message, _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"), | ||
| 313 | mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space, (mem_commitLimit - mem_commitByte) / 1048567, | ||
| 314 | (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100); | ||
| 315 | xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"), mem_commitByte / 1048567, warning_used_space / 1048567, | ||
| 316 | critical_used_space / 1048567, mem_commitLimit / 1048567); | ||
| 317 | |||
| 318 | return_code = STATE_OK; | ||
| 319 | if (check_critical_value && percent_used_space >= critical_value) | ||
| 320 | return_code = STATE_CRITICAL; | ||
| 321 | else if (check_warning_value && percent_used_space >= warning_value) | ||
| 322 | return_code = STATE_WARNING; | ||
| 323 | |||
| 324 | break; | ||
| 325 | |||
| 326 | case CHECK_COUNTER: | ||
| 327 | |||
| 328 | /* | ||
| 329 | CHECK_COUNTER has been modified to provide extensive perfdata information. | ||
| 330 | In order to do this, some modifications have been done to the code | ||
| 331 | and some constraints have been introduced. | ||
| 332 | |||
| 333 | 1) For the sake of simplicity of the code, perfdata information will only be | ||
| 334 | provided when the "description" field is added. | ||
| 335 | |||
| 336 | 2) If the counter you're going to measure is percent-based, the code will detect | ||
| 337 | the percent sign in its name and will attribute minimum (0%) and maximum (100%) | ||
| 338 | values automagically, as well the "%" sign to graph units. | ||
| 339 | |||
| 340 | 3) OTOH, if the counter is "absolute", you'll have to provide the following | ||
| 341 | the counter unit - that is, the dimensions of the counter you're getting. Examples: | ||
| 342 | pages/s, packets transferred, etc. | ||
| 343 | |||
| 344 | 4) If you want, you may provide the minimum and maximum values to expect. They aren't mandatory, | ||
| 345 | but once specified they MUST have the same order of magnitude and units of -w and -c; otherwise. | ||
| 346 | strange things will happen when you make graphs of your data. | ||
| 347 | */ | ||
| 348 | |||
| 349 | if (value_list == NULL) | ||
| 350 | output_message = strdup(_("No counter specified")); | ||
| 351 | else { | ||
| 352 | preparelist(value_list); /* replace , between services with & to send the request */ | ||
| 353 | isPercent = (strchr(value_list, '%') != NULL); | ||
| 354 | |||
| 355 | strtok(value_list, "&"); /* burn the first parameters */ | ||
| 356 | description = strtok(NULL, "&"); | ||
| 357 | counter_unit = strtok(NULL, "&"); | ||
| 358 | xasprintf(&send_buffer, "%s&8&%s", req_password, value_list); | ||
| 359 | fetch_data(server_address, server_port, send_buffer); | ||
| 360 | counter_value = atof(recv_buffer); | ||
| 361 | |||
| 362 | if (description == NULL) | ||
| 363 | xasprintf(&output_message, "%.f", counter_value); | ||
| 364 | else if (isPercent) { | ||
| 365 | counter_unit = strdup("%"); | ||
| 366 | allRight = true; | ||
| 367 | } | ||
| 368 | |||
| 369 | if ((counter_unit != NULL) && (!allRight)) { | ||
| 370 | minval = strtok(NULL, "&"); | ||
| 371 | maxval = strtok(NULL, "&"); | ||
| 372 | |||
| 373 | /* All parameters specified. Let's check the numbers */ | ||
| 374 | |||
| 375 | fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1; | ||
| 376 | fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1; | ||
| 377 | |||
| 378 | if ((fminval == 0) && (minval == errcvt)) | ||
| 379 | output_message = strdup(_("Minimum value contains non-numbers")); | ||
| 380 | else { | ||
| 381 | if ((fmaxval == 0) && (maxval == errcvt)) | ||
| 382 | output_message = strdup(_("Maximum value contains non-numbers")); | ||
| 383 | else | ||
| 384 | allRight = true; /* Everything is OK. */ | ||
| 385 | } | ||
| 386 | } else if ((counter_unit == NULL) && (description != NULL)) | ||
| 387 | output_message = strdup(_("No unit counter specified")); | ||
| 388 | |||
| 389 | if (allRight) { | ||
| 390 | /* Let's format the output string, finally... */ | ||
| 391 | if (strstr(description, "%") == NULL) { | ||
| 392 | xasprintf(&output_message, "%s = %.2f %s", description, counter_value, counter_unit); | ||
| 393 | } else { | ||
| 394 | /* has formatting, will segv if wrong */ | ||
| 395 | xasprintf(&output_message, description, counter_value); | ||
| 396 | } | ||
| 397 | xasprintf(&output_message, "%s |", output_message); | ||
| 398 | xasprintf(&output_message, "%s %s", output_message, | ||
| 399 | fperfdata(description, counter_value, counter_unit, 1, warning_value, 1, critical_value, | ||
| 400 | (!(isPercent) && (minval != NULL)), fminval, (!(isPercent) && (minval != NULL)), fmaxval)); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | if (critical_value > warning_value) { /* Normal thresholds */ | ||
| 405 | if (check_critical_value && counter_value >= critical_value) | ||
| 406 | return_code = STATE_CRITICAL; | ||
| 407 | else if (check_warning_value && counter_value >= warning_value) | ||
| 408 | return_code = STATE_WARNING; | ||
| 409 | else | ||
| 410 | return_code = STATE_OK; | ||
| 411 | } else { /* inverse thresholds */ | ||
| 412 | return_code = STATE_OK; | ||
| 413 | if (check_critical_value && counter_value <= critical_value) | ||
| 414 | return_code = STATE_CRITICAL; | ||
| 415 | else if (check_warning_value && counter_value <= warning_value) | ||
| 416 | return_code = STATE_WARNING; | ||
| 417 | } | ||
| 418 | break; | ||
| 419 | |||
| 420 | case CHECK_FILEAGE: | ||
| 421 | |||
| 422 | if (value_list == NULL) | ||
| 423 | output_message = strdup(_("No counter specified")); | ||
| 424 | else { | ||
| 425 | preparelist(value_list); /* replace , between services with & to send the request */ | ||
| 426 | xasprintf(&send_buffer, "%s&9&%s", req_password, value_list); | ||
| 427 | fetch_data(server_address, server_port, send_buffer); | ||
| 428 | age_in_minutes = atoi(strtok(recv_buffer, "&")); | ||
| 429 | description = strtok(NULL, "&"); | ||
| 430 | output_message = strdup(description); | ||
| 431 | |||
| 432 | if (critical_value > warning_value) { /* Normal thresholds */ | ||
| 433 | if (check_critical_value && age_in_minutes >= critical_value) | ||
| 434 | return_code = STATE_CRITICAL; | ||
| 435 | else if (check_warning_value && age_in_minutes >= warning_value) | ||
| 436 | return_code = STATE_WARNING; | ||
| 437 | else | ||
| 438 | return_code = STATE_OK; | ||
| 439 | } else { /* inverse thresholds */ | ||
| 440 | if (check_critical_value && age_in_minutes <= critical_value) | ||
| 441 | return_code = STATE_CRITICAL; | ||
| 442 | else if (check_warning_value && age_in_minutes <= warning_value) | ||
| 443 | return_code = STATE_WARNING; | ||
| 444 | else | ||
| 445 | return_code = STATE_OK; | ||
| 446 | } | ||
| 447 | } | ||
| 448 | break; | ||
| 449 | |||
| 450 | case CHECK_INSTANCES: | ||
| 451 | if (value_list == NULL) | ||
| 452 | output_message = strdup(_("No counter specified")); | ||
| 453 | else { | ||
| 454 | xasprintf(&send_buffer, "%s&10&%s", req_password, value_list); | ||
| 455 | fetch_data(server_address, server_port, send_buffer); | ||
| 456 | if (!strncmp(recv_buffer, "ERROR", 5)) { | ||
| 457 | printf("NSClient - %s\n", recv_buffer); | ||
| 458 | exit(STATE_UNKNOWN); | ||
| 459 | } | ||
| 460 | xasprintf(&output_message, "%s", recv_buffer); | ||
| 461 | return_code = STATE_OK; | ||
| 462 | } | ||
| 463 | break; | ||
| 464 | |||
| 465 | case CHECK_NONE: | ||
| 466 | default: | ||
| 467 | usage4(_("Please specify a variable to check")); | ||
| 468 | break; | ||
| 469 | } | ||
| 470 | |||
| 471 | /* reset timeout */ | ||
| 472 | alarm(0); | ||
| 473 | |||
| 474 | if (perfdata == NULL) | ||
| 475 | printf("%s\n", output_message); | ||
| 476 | else | ||
| 477 | printf("%s | %s\n", output_message, perfdata); | ||
| 478 | return return_code; | ||
| 479 | } | ||
| 480 | |||
| 481 | /* process command-line arguments */ | ||
| 482 | int process_arguments(int argc, char **argv) { | ||
| 483 | int c; | ||
| 484 | |||
| 485 | int option = 0; | ||
| 486 | static struct option longopts[] = {{"port", required_argument, 0, 'p'}, | ||
| 487 | {"timeout", required_argument, 0, 't'}, | ||
| 488 | {"critical", required_argument, 0, 'c'}, | ||
| 489 | {"warning", required_argument, 0, 'w'}, | ||
| 490 | {"variable", required_argument, 0, 'v'}, | ||
| 491 | {"hostname", required_argument, 0, 'H'}, | ||
| 492 | {"params", required_argument, 0, 'l'}, | ||
| 493 | {"secret", required_argument, 0, 's'}, | ||
| 494 | {"display", required_argument, 0, 'd'}, | ||
| 495 | {"unknown-timeout", no_argument, 0, 'u'}, | ||
| 496 | {"version", no_argument, 0, 'V'}, | ||
| 497 | {"help", no_argument, 0, 'h'}, | ||
| 498 | {0, 0, 0, 0}}; | ||
| 499 | |||
| 500 | /* no options were supplied */ | ||
| 501 | if (argc < 2) | ||
| 502 | return ERROR; | ||
| 503 | |||
| 504 | /* backwards compatibility */ | ||
| 505 | if (!is_option(argv[1])) { | ||
| 506 | server_address = strdup(argv[1]); | ||
| 507 | argv[1] = argv[0]; | ||
| 508 | argv = &argv[1]; | ||
| 509 | argc--; | ||
| 510 | } | ||
| 511 | |||
| 512 | for (c = 1; c < argc; c++) { | ||
| 513 | if (strcmp("-to", argv[c]) == 0) | ||
| 514 | strcpy(argv[c], "-t"); | ||
| 515 | else if (strcmp("-wv", argv[c]) == 0) | ||
| 516 | strcpy(argv[c], "-w"); | ||
| 517 | else if (strcmp("-cv", argv[c]) == 0) | ||
| 518 | strcpy(argv[c], "-c"); | ||
| 519 | } | ||
| 520 | |||
| 521 | while (1) { | ||
| 522 | c = getopt_long(argc, argv, "+hVH:t:c:w:p:v:l:s:d:u", longopts, &option); | ||
| 523 | |||
| 524 | if (c == -1 || c == EOF || c == 1) | ||
| 525 | break; | ||
| 526 | |||
| 527 | switch (c) { | ||
| 528 | case '?': /* print short usage statement if args not parsable */ | ||
| 529 | usage5(); | ||
| 530 | case 'h': /* help */ | ||
| 531 | print_help(); | ||
| 532 | exit(STATE_UNKNOWN); | ||
| 533 | case 'V': /* version */ | ||
| 534 | print_revision(progname, NP_VERSION); | ||
| 535 | exit(STATE_UNKNOWN); | ||
| 536 | case 'H': /* hostname */ | ||
| 537 | server_address = optarg; | ||
| 538 | break; | ||
| 539 | case 's': /* password */ | ||
| 540 | req_password = optarg; | ||
| 541 | break; | ||
| 542 | case 'p': /* port */ | ||
| 543 | if (is_intnonneg(optarg)) | ||
| 544 | server_port = atoi(optarg); | ||
| 545 | else | ||
| 546 | die(STATE_UNKNOWN, _("Server port must be an integer\n")); | ||
| 547 | break; | ||
| 548 | case 'v': | ||
| 549 | if (strlen(optarg) < 4) | ||
| 550 | return ERROR; | ||
| 551 | if (!strcmp(optarg, "CLIENTVERSION")) | ||
| 552 | vars_to_check = CHECK_CLIENTVERSION; | ||
| 553 | else if (!strcmp(optarg, "CPULOAD")) | ||
| 554 | vars_to_check = CHECK_CPULOAD; | ||
| 555 | else if (!strcmp(optarg, "UPTIME")) | ||
| 556 | vars_to_check = CHECK_UPTIME; | ||
| 557 | else if (!strcmp(optarg, "USEDDISKSPACE")) | ||
| 558 | vars_to_check = CHECK_USEDDISKSPACE; | ||
| 559 | else if (!strcmp(optarg, "SERVICESTATE")) | ||
| 560 | vars_to_check = CHECK_SERVICESTATE; | ||
| 561 | else if (!strcmp(optarg, "PROCSTATE")) | ||
| 562 | vars_to_check = CHECK_PROCSTATE; | ||
| 563 | else if (!strcmp(optarg, "MEMUSE")) | ||
| 564 | vars_to_check = CHECK_MEMUSE; | ||
| 565 | else if (!strcmp(optarg, "COUNTER")) | ||
| 566 | vars_to_check = CHECK_COUNTER; | ||
| 567 | else if (!strcmp(optarg, "FILEAGE")) | ||
| 568 | vars_to_check = CHECK_FILEAGE; | ||
| 569 | else if (!strcmp(optarg, "INSTANCES")) | ||
| 570 | vars_to_check = CHECK_INSTANCES; | ||
| 571 | else | ||
| 572 | return ERROR; | ||
| 573 | break; | ||
| 574 | case 'l': /* value list */ | ||
| 575 | value_list = optarg; | ||
| 576 | break; | ||
| 577 | case 'w': /* warning threshold */ | ||
| 578 | warning_value = strtoul(optarg, NULL, 10); | ||
| 579 | check_warning_value = true; | ||
| 580 | break; | ||
| 581 | case 'c': /* critical threshold */ | ||
| 582 | critical_value = strtoul(optarg, NULL, 10); | ||
| 583 | check_critical_value = true; | ||
| 584 | break; | ||
| 585 | case 'd': /* Display select for services */ | ||
| 586 | if (!strcmp(optarg, "SHOWALL")) | ||
| 587 | show_all = true; | ||
| 588 | break; | ||
| 589 | case 'u': | ||
| 590 | socket_timeout_state = STATE_UNKNOWN; | ||
| 591 | break; | ||
| 592 | case 't': /* timeout */ | ||
| 593 | socket_timeout = atoi(optarg); | ||
| 594 | if (socket_timeout <= 0) | ||
| 595 | return ERROR; | ||
| 596 | } | ||
| 597 | } | ||
| 598 | if (server_address == NULL) | ||
| 599 | usage4(_("You must provide a server address or host name")); | ||
| 600 | |||
| 601 | if (vars_to_check == CHECK_NONE) | ||
| 602 | return ERROR; | ||
| 603 | |||
| 604 | if (req_password == NULL) | ||
| 605 | req_password = strdup(_("None")); | ||
| 606 | |||
| 607 | return OK; | ||
| 608 | } | ||
| 609 | |||
| 610 | void fetch_data(const char *address, int port, const char *sendb) { | ||
| 611 | int result; | ||
| 612 | |||
| 613 | result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer)); | ||
| 614 | |||
| 615 | if (result != STATE_OK) | ||
| 616 | die(result, _("could not fetch information from server\n")); | ||
| 617 | |||
| 618 | if (!strncmp(recv_buffer, "ERROR", 5)) | ||
| 619 | die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer); | ||
| 620 | } | ||
| 621 | |||
| 622 | bool strtoularray(unsigned long *array, char *string, const char *delim) { | ||
| 623 | /* split a <delim> delimited string into a long array */ | ||
| 624 | int idx = 0; | ||
| 625 | char *t1; | ||
| 626 | |||
| 627 | for (idx = 0; idx < MAX_VALUE_LIST; idx++) | ||
| 628 | array[idx] = 0; | ||
| 629 | |||
| 630 | idx = 0; | ||
| 631 | for (t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) { | ||
| 632 | if (is_numeric(t1) && idx < MAX_VALUE_LIST) { | ||
| 633 | array[idx] = strtoul(t1, NULL, 10); | ||
| 634 | idx++; | ||
| 635 | } else | ||
| 636 | return false; | ||
| 637 | } | ||
| 638 | return true; | ||
| 639 | } | ||
| 640 | |||
| 641 | void preparelist(char *string) { | ||
| 642 | /* Replace all , with & which is the delimiter for the request */ | ||
| 643 | int i; | ||
| 644 | |||
| 645 | for (i = 0; (size_t)i < strlen(string); i++) | ||
| 646 | if (string[i] == ',') { | ||
| 647 | string[i] = '&'; | ||
| 648 | } | ||
| 649 | } | ||
| 650 | |||
| 651 | void print_help(void) { | ||
| 652 | print_revision(progname, NP_VERSION); | ||
| 653 | |||
| 654 | printf("Copyright (c) 2000 Yves Rubin (rubiyz@yahoo.com)\n"); | ||
| 655 | printf(COPYRIGHT, copyright, email); | ||
| 656 | |||
| 657 | printf("%s\n", _("This plugin collects data from the NSClient service running on a")); | ||
| 658 | printf("%s\n", _("Windows NT/2000/XP/2003 server.")); | ||
| 659 | |||
| 660 | printf("\n\n"); | ||
| 661 | |||
| 662 | print_usage(); | ||
| 663 | |||
| 664 | printf(UT_HELP_VRSN); | ||
| 665 | printf(UT_EXTRA_OPTS); | ||
| 666 | |||
| 667 | printf("%s\n", _("Options:")); | ||
| 668 | printf(" %s\n", "-H, --hostname=HOST"); | ||
| 669 | printf(" %s\n", _("Name of the host to check")); | ||
| 670 | printf(" %s\n", "-p, --port=INTEGER"); | ||
| 671 | printf(" %s", _("Optional port number (default: ")); | ||
| 672 | printf("%d)\n", PORT); | ||
| 673 | printf(" %s\n", "-s, --secret=<password>"); | ||
| 674 | printf(" %s\n", _("Password needed for the request")); | ||
| 675 | printf(" %s\n", "-w, --warning=INTEGER"); | ||
| 676 | printf(" %s\n", _("Threshold which will result in a warning status")); | ||
| 677 | printf(" %s\n", "-c, --critical=INTEGER"); | ||
| 678 | printf(" %s\n", _("Threshold which will result in a critical status")); | ||
| 679 | printf(" %s\n", "-t, --timeout=INTEGER"); | ||
| 680 | printf(" %s", _("Seconds before connection attempt times out (default: ")); | ||
| 681 | printf(" %s\n", "-l, --params=<parameters>"); | ||
| 682 | printf(" %s", _("Parameters passed to specified check (see below)")); | ||
| 683 | printf(" %s\n", "-d, --display={SHOWALL}"); | ||
| 684 | printf(" %s", _("Display options (currently only SHOWALL works)")); | ||
| 685 | printf(" %s\n", "-u, --unknown-timeout"); | ||
| 686 | printf(" %s", _("Return UNKNOWN on timeouts")); | ||
| 687 | printf("%d)\n", DEFAULT_SOCKET_TIMEOUT); | ||
| 688 | printf(" %s\n", "-h, --help"); | ||
| 689 | printf(" %s\n", _("Print this help screen")); | ||
| 690 | printf(" %s\n", "-V, --version"); | ||
| 691 | printf(" %s\n", _("Print version information")); | ||
| 692 | printf(" %s\n", "-v, --variable=STRING"); | ||
| 693 | printf(" %s\n\n", _("Variable to check")); | ||
| 694 | printf("%s\n", _("Valid variables are:")); | ||
| 695 | printf(" %s", "CLIENTVERSION ="); | ||
| 696 | printf(" %s\n", _("Get the NSClient version")); | ||
| 697 | printf(" %s\n", _("If -l <version> is specified, will return warning if versions differ.")); | ||
| 698 | printf(" %s\n", "CPULOAD ="); | ||
| 699 | printf(" %s\n", _("Average CPU load on last x minutes.")); | ||
| 700 | printf(" %s\n", _("Request a -l parameter with the following syntax:")); | ||
| 701 | printf(" %s\n", _("-l <minutes range>,<warning threshold>,<critical threshold>.")); | ||
| 702 | printf(" %s\n", _("<minute range> should be less than 24*60.")); | ||
| 703 | printf(" %s\n", _("Thresholds are percentage and up to 10 requests can be done in one shot.")); | ||
| 704 | printf(" %s\n", "ie: -l 60,90,95,120,90,95"); | ||
| 705 | printf(" %s\n", "UPTIME ="); | ||
| 706 | printf(" %s\n", _("Get the uptime of the machine.")); | ||
| 707 | printf(" %s\n", _("-l <unit> ")); | ||
| 708 | printf(" %s\n", _("<unit> = seconds, minutes, hours, or days. (default: minutes)")); | ||
| 709 | printf(" %s\n", _("Thresholds will use the unit specified above.")); | ||
| 710 | printf(" %s\n", "USEDDISKSPACE ="); | ||
| 711 | printf(" %s\n", _("Size and percentage of disk use.")); | ||
| 712 | printf(" %s\n", _("Request a -l parameter containing the drive letter only.")); | ||
| 713 | printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c.")); | ||
| 714 | printf(" %s\n", "MEMUSE ="); | ||
| 715 | printf(" %s\n", _("Memory use.")); | ||
| 716 | printf(" %s\n", _("Warning and critical thresholds can be specified with -w and -c.")); | ||
| 717 | printf(" %s\n", "SERVICESTATE ="); | ||
| 718 | printf(" %s\n", _("Check the state of one or several services.")); | ||
| 719 | printf(" %s\n", _("Request a -l parameters with the following syntax:")); | ||
| 720 | printf(" %s\n", _("-l <service1>,<service2>,<service3>,...")); | ||
| 721 | printf(" %s\n", _("You can specify -d SHOWALL in case you want to see working services")); | ||
| 722 | printf(" %s\n", _("in the returned string.")); | ||
| 723 | printf(" %s\n", "PROCSTATE ="); | ||
| 724 | printf(" %s\n", _("Check if one or several process are running.")); | ||
| 725 | printf(" %s\n", _("Same syntax as SERVICESTATE.")); | ||
| 726 | printf(" %s\n", "COUNTER ="); | ||
| 727 | printf(" %s\n", _("Check any performance counter of Windows NT/2000.")); | ||
| 728 | printf(" %s\n", _("Request a -l parameters with the following syntax:")); | ||
| 729 | printf(" %s\n", _("-l \"\\\\<performance object>\\\\counter\",\"<description>")); | ||
| 730 | printf(" %s\n", _("The <description> parameter is optional and is given to a printf ")); | ||
| 731 | printf(" %s\n", _("output command which requires a float parameter.")); | ||
| 732 | printf(" %s\n", _("If <description> does not include \"%%\", it is used as a label.")); | ||
| 733 | printf(" %s\n", _("Some examples:")); | ||
| 734 | printf(" %s\n", "\"Paging file usage is %%.2f %%%%\""); | ||
| 735 | printf(" %s\n", "\"%%.f %%%% paging file used.\""); | ||
| 736 | printf(" %s\n", "INSTANCES ="); | ||
| 737 | printf(" %s\n", _("Check any performance counter object of Windows NT/2000.")); | ||
| 738 | printf(" %s\n", _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>")); | ||
| 739 | printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),")); | ||
| 740 | printf(" %s\n", _("if it is two words, it should be enclosed in quotes")); | ||
| 741 | printf(" %s\n", _("The returned results will be a comma-separated list of instances on ")); | ||
| 742 | printf(" %s\n", _(" the selected computer for that object.")); | ||
| 743 | printf(" %s\n", _("The purpose of this is to be run from command line to determine what instances")); | ||
| 744 | printf(" %s\n", _(" are available for monitoring without having to log onto the Windows server")); | ||
| 745 | printf(" %s\n", _(" to run Perfmon directly.")); | ||
| 746 | printf(" %s\n", _("It can also be used in scripts that automatically create the monitoring service")); | ||
| 747 | printf(" %s\n", _(" configuration files.")); | ||
| 748 | printf(" %s\n", _("Some examples:")); | ||
| 749 | printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process")); | ||
| 750 | |||
| 751 | printf("%s\n", _("Notes:")); | ||
| 752 | printf(" %s\n", _("- The NSClient service should be running on the server to get any information")); | ||
| 753 | printf(" %s\n", "(http://nsclient.ready2run.nl)."); | ||
| 754 | printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds")); | ||
| 755 | printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error")); | ||
| 756 | printf(" %s\n", _("output when this happens contains \"Cannot map xxxxx to protocol number\".")); | ||
| 757 | printf(" %s\n", _("One fix for this is to change the port to something else on check_nt ")); | ||
| 758 | printf(" %s\n", _("and on the client service it\'s connecting to.")); | ||
| 759 | |||
| 760 | printf(UT_SUPPORT); | ||
| 761 | } | ||
| 762 | |||
| 763 | void print_usage(void) { | ||
| 764 | printf("%s\n", _("Usage:")); | ||
| 765 | printf("%s -H host -v variable [-p port] [-w warning] [-c critical]\n", progname); | ||
| 766 | printf("[-l params] [-d SHOWALL] [-u] [-t timeout]\n"); | ||
| 767 | } | ||
diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c index d33f8786..b22cc3c1 100644 --- a/plugins/check_ntp.c +++ b/plugins/check_ntp.c | |||
| @@ -1,34 +1,34 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_ntp plugin | 3 | * Monitoring check_ntp plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2006 Sean Finney <seanius@seanius.net> | 6 | * Copyright (c) 2006 Sean Finney <seanius@seanius.net> |
| 7 | * Copyright (c) 2006-2024 Monitoring Plugins Development Team | 7 | * Copyright (c) 2006-2024 Monitoring Plugins Development Team |
| 8 | * | 8 | * |
| 9 | * Description: | 9 | * Description: |
| 10 | * | 10 | * |
| 11 | * This file contains the check_ntp plugin | 11 | * This file contains the check_ntp plugin |
| 12 | * | 12 | * |
| 13 | * This plugin to check ntp servers independent of any commandline | 13 | * This plugin to check ntp servers independent of any commandline |
| 14 | * programs or external libraries. | 14 | * programs or external libraries. |
| 15 | * | 15 | * |
| 16 | * | 16 | * |
| 17 | * This program is free software: you can redistribute it and/or modify | 17 | * This program is free software: you can redistribute it and/or modify |
| 18 | * it under the terms of the GNU General Public License as published by | 18 | * it under the terms of the GNU General Public License as published by |
| 19 | * the Free Software Foundation, either version 3 of the License, or | 19 | * the Free Software Foundation, either version 3 of the License, or |
| 20 | * (at your option) any later version. | 20 | * (at your option) any later version. |
| 21 | * | 21 | * |
| 22 | * This program is distributed in the hope that it will be useful, | 22 | * This program is distributed in the hope that it will be useful, |
| 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 25 | * GNU General Public License for more details. | 25 | * GNU General Public License for more details. |
| 26 | * | 26 | * |
| 27 | * You should have received a copy of the GNU General Public License | 27 | * You should have received a copy of the GNU General Public License |
| 28 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 28 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 29 | * | 29 | * |
| 30 | * | 30 | * |
| 31 | *****************************************************************************/ | 31 | *****************************************************************************/ |
| 32 | 32 | ||
| 33 | const char *progname = "check_ntp"; | 33 | const char *progname = "check_ntp"; |
| 34 | const char *copyright = "2006-2024"; | 34 | const char *copyright = "2006-2024"; |
| @@ -38,24 +38,24 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 38 | #include "netutils.h" | 38 | #include "netutils.h" |
| 39 | #include "utils.h" | 39 | #include "utils.h" |
| 40 | 40 | ||
| 41 | static char *server_address=NULL; | 41 | static char *server_address = NULL; |
| 42 | static int verbose=0; | 42 | static int verbose = 0; |
| 43 | static bool do_offset = false; | 43 | static bool do_offset = false; |
| 44 | static char *owarn="60"; | 44 | static char *owarn = "60"; |
| 45 | static char *ocrit="120"; | 45 | static char *ocrit = "120"; |
| 46 | static bool do_jitter = false; | 46 | static bool do_jitter = false; |
| 47 | static char *jwarn="5000"; | 47 | static char *jwarn = "5000"; |
| 48 | static char *jcrit="10000"; | 48 | static char *jcrit = "10000"; |
| 49 | 49 | ||
| 50 | static int process_arguments (int /*argc*/, char ** /*argv*/); | 50 | static int process_arguments(int /*argc*/, char ** /*argv*/); |
| 51 | static thresholds *offset_thresholds = NULL; | 51 | static thresholds *offset_thresholds = NULL; |
| 52 | static thresholds *jitter_thresholds = NULL; | 52 | static thresholds *jitter_thresholds = NULL; |
| 53 | static void print_help (void); | 53 | static void print_help(void); |
| 54 | void print_usage (void); | 54 | void print_usage(void); |
| 55 | 55 | ||
| 56 | /* number of times to perform each request to get a good average. */ | 56 | /* number of times to perform each request to get a good average. */ |
| 57 | #ifndef AVG_NUM | 57 | #ifndef AVG_NUM |
| 58 | #define AVG_NUM 4 | 58 | # define AVG_NUM 4 |
| 59 | #endif | 59 | #endif |
| 60 | 60 | ||
| 61 | /* max size of control message data */ | 61 | /* max size of control message data */ |
| @@ -63,17 +63,17 @@ void print_usage (void); | |||
| 63 | 63 | ||
| 64 | /* this structure holds everything in an ntp request/response as per rfc1305 */ | 64 | /* this structure holds everything in an ntp request/response as per rfc1305 */ |
| 65 | typedef struct { | 65 | typedef struct { |
| 66 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ | 66 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ |
| 67 | uint8_t stratum; /* clock stratum */ | 67 | uint8_t stratum; /* clock stratum */ |
| 68 | int8_t poll; /* polling interval */ | 68 | int8_t poll; /* polling interval */ |
| 69 | int8_t precision; /* precision of the local clock */ | 69 | int8_t precision; /* precision of the local clock */ |
| 70 | int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ | 70 | int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ |
| 71 | uint32_t rtdisp; /* like above, but for max err to primary src */ | 71 | uint32_t rtdisp; /* like above, but for max err to primary src */ |
| 72 | uint32_t refid; /* ref clock identifier */ | 72 | uint32_t refid; /* ref clock identifier */ |
| 73 | uint64_t refts; /* reference timestamp. local time local clock */ | 73 | uint64_t refts; /* reference timestamp. local time local clock */ |
| 74 | uint64_t origts; /* time at which request departed client */ | 74 | uint64_t origts; /* time at which request departed client */ |
| 75 | uint64_t rxts; /* time at which request arrived at server */ | 75 | uint64_t rxts; /* time at which request arrived at server */ |
| 76 | uint64_t txts; /* time at which request departed server */ | 76 | uint64_t txts; /* time at which request departed server */ |
| 77 | } ntp_message; | 77 | } ntp_message; |
| 78 | 78 | ||
| 79 | /* this structure holds data about results from querying offset from a peer */ | 79 | /* this structure holds data about results from querying offset from a peer */ |
| @@ -84,20 +84,20 @@ typedef struct { | |||
| 84 | double rtdelay; /* converted from the ntp_message */ | 84 | double rtdelay; /* converted from the ntp_message */ |
| 85 | double rtdisp; /* converted from the ntp_message */ | 85 | double rtdisp; /* converted from the ntp_message */ |
| 86 | double offset[AVG_NUM]; /* offsets from each response */ | 86 | double offset[AVG_NUM]; /* offsets from each response */ |
| 87 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ | 87 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ |
| 88 | } ntp_server_results; | 88 | } ntp_server_results; |
| 89 | 89 | ||
| 90 | /* this structure holds everything in an ntp control message as per rfc1305 */ | 90 | /* this structure holds everything in an ntp control message as per rfc1305 */ |
| 91 | typedef struct { | 91 | typedef struct { |
| 92 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ | 92 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ |
| 93 | uint8_t op; /* R,E,M bits and Opcode */ | 93 | uint8_t op; /* R,E,M bits and Opcode */ |
| 94 | uint16_t seq; /* Packet sequence */ | 94 | uint16_t seq; /* Packet sequence */ |
| 95 | uint16_t status; /* Clock status */ | 95 | uint16_t status; /* Clock status */ |
| 96 | uint16_t assoc; /* Association */ | 96 | uint16_t assoc; /* Association */ |
| 97 | uint16_t offset; /* Similar to TCP sequence # */ | 97 | uint16_t offset; /* Similar to TCP sequence # */ |
| 98 | uint16_t count; /* # bytes of data */ | 98 | uint16_t count; /* # bytes of data */ |
| 99 | char data[MAX_CM_SIZE]; /* ASCII data of the request */ | 99 | char data[MAX_CM_SIZE]; /* ASCII data of the request */ |
| 100 | /* NB: not necessarily NULL terminated! */ | 100 | /* NB: not necessarily NULL terminated! */ |
| 101 | } ntp_control_message; | 101 | } ntp_control_message; |
| 102 | 102 | ||
| 103 | /* this is an association/status-word pair found in control packet responses */ | 103 | /* this is an association/status-word pair found in control packet responses */ |
| @@ -108,38 +108,50 @@ typedef struct { | |||
| 108 | 108 | ||
| 109 | /* bits 1,2 are the leap indicator */ | 109 | /* bits 1,2 are the leap indicator */ |
| 110 | #define LI_MASK 0xc0 | 110 | #define LI_MASK 0xc0 |
| 111 | #define LI(x) ((x&LI_MASK)>>6) | 111 | #define LI(x) ((x & LI_MASK) >> 6) |
| 112 | #define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0) | 112 | #define LI_SET(x, y) \ |
| 113 | do { \ | ||
| 114 | x |= ((y << 6) & LI_MASK); \ | ||
| 115 | } while (0) | ||
| 113 | /* and these are the values of the leap indicator */ | 116 | /* and these are the values of the leap indicator */ |
| 114 | #define LI_NOWARNING 0x00 | 117 | #define LI_NOWARNING 0x00 |
| 115 | #define LI_EXTRASEC 0x01 | 118 | #define LI_EXTRASEC 0x01 |
| 116 | #define LI_MISSINGSEC 0x02 | 119 | #define LI_MISSINGSEC 0x02 |
| 117 | #define LI_ALARM 0x03 | 120 | #define LI_ALARM 0x03 |
| 118 | /* bits 3,4,5 are the ntp version */ | 121 | /* bits 3,4,5 are the ntp version */ |
| 119 | #define VN_MASK 0x38 | 122 | #define VN_MASK 0x38 |
| 120 | #define VN(x) ((x&VN_MASK)>>3) | 123 | #define VN(x) ((x & VN_MASK) >> 3) |
| 121 | #define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) | 124 | #define VN_SET(x, y) \ |
| 125 | do { \ | ||
| 126 | x |= ((y << 3) & VN_MASK); \ | ||
| 127 | } while (0) | ||
| 122 | #define VN_RESERVED 0x02 | 128 | #define VN_RESERVED 0x02 |
| 123 | /* bits 6,7,8 are the ntp mode */ | 129 | /* bits 6,7,8 are the ntp mode */ |
| 124 | #define MODE_MASK 0x07 | 130 | #define MODE_MASK 0x07 |
| 125 | #define MODE(x) (x&MODE_MASK) | 131 | #define MODE(x) (x & MODE_MASK) |
| 126 | #define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) | 132 | #define MODE_SET(x, y) \ |
| 133 | do { \ | ||
| 134 | x |= (y & MODE_MASK); \ | ||
| 135 | } while (0) | ||
| 127 | /* here are some values */ | 136 | /* here are some values */ |
| 128 | #define MODE_CLIENT 0x03 | 137 | #define MODE_CLIENT 0x03 |
| 129 | #define MODE_CONTROLMSG 0x06 | 138 | #define MODE_CONTROLMSG 0x06 |
| 130 | /* In control message, bits 8-10 are R,E,M bits */ | 139 | /* In control message, bits 8-10 are R,E,M bits */ |
| 131 | #define REM_MASK 0xe0 | 140 | #define REM_MASK 0xe0 |
| 132 | #define REM_RESP 0x80 | 141 | #define REM_RESP 0x80 |
| 133 | #define REM_ERROR 0x40 | 142 | #define REM_ERROR 0x40 |
| 134 | #define REM_MORE 0x20 | 143 | #define REM_MORE 0x20 |
| 135 | /* In control message, bits 11 - 15 are opcode */ | 144 | /* In control message, bits 11 - 15 are opcode */ |
| 136 | #define OP_MASK 0x1f | 145 | #define OP_MASK 0x1f |
| 137 | #define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) | 146 | #define OP_SET(x, y) \ |
| 147 | do { \ | ||
| 148 | x |= (y & OP_MASK); \ | ||
| 149 | } while (0) | ||
| 138 | #define OP_READSTAT 0x01 | 150 | #define OP_READSTAT 0x01 |
| 139 | #define OP_READVAR 0x02 | 151 | #define OP_READVAR 0x02 |
| 140 | /* In peer status bytes, bits 6,7,8 determine clock selection status */ | 152 | /* In peer status bytes, bits 6,7,8 determine clock selection status */ |
| 141 | #define PEER_SEL(x) ((ntohs(x)>>8)&0x07) | 153 | #define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07) |
| 142 | #define PEER_INCLUDED 0x04 | 154 | #define PEER_INCLUDED 0x04 |
| 143 | #define PEER_SYNCSOURCE 0x06 | 155 | #define PEER_SYNCSOURCE 0x06 |
| 144 | 156 | ||
| 145 | /** | 157 | /** |
| @@ -153,82 +165,92 @@ typedef struct { | |||
| 153 | 165 | ||
| 154 | /* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" | 166 | /* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" |
| 155 | number. note that these can be used as lvalues too */ | 167 | number. note that these can be used as lvalues too */ |
| 156 | #define L16(x) (((uint16_t*)&x)[0]) | 168 | #define L16(x) (((uint16_t *)&x)[0]) |
| 157 | #define R16(x) (((uint16_t*)&x)[1]) | 169 | #define R16(x) (((uint16_t *)&x)[1]) |
| 158 | /* macros to access the left/right 32 bits of a 64-bit ntp "fixed point" | 170 | /* macros to access the left/right 32 bits of a 64-bit ntp "fixed point" |
| 159 | number. these too can be used as lvalues */ | 171 | number. these too can be used as lvalues */ |
| 160 | #define L32(x) (((uint32_t*)&x)[0]) | 172 | #define L32(x) (((uint32_t *)&x)[0]) |
| 161 | #define R32(x) (((uint32_t*)&x)[1]) | 173 | #define R32(x) (((uint32_t *)&x)[1]) |
| 162 | 174 | ||
| 163 | /* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */ | 175 | /* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */ |
| 164 | #define EPOCHDIFF 0x83aa7e80UL | 176 | #define EPOCHDIFF 0x83aa7e80UL |
| 165 | 177 | ||
| 166 | /* extract a 32-bit ntp fixed point number into a double */ | 178 | /* extract a 32-bit ntp fixed point number into a double */ |
| 167 | #define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0) | 179 | #define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0) |
| 168 | 180 | ||
| 169 | /* likewise for a 64-bit ntp fp number */ | 181 | /* likewise for a 64-bit ntp fp number */ |
| 170 | #define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\ | 182 | #define NTP64asDOUBLE(n) \ |
| 171 | (ntohl(L32(n))-EPOCHDIFF) + \ | 183 | (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \ |
| 172 | (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\ | 184 | (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \ |
| 173 | 0) | 185 | : 0) |
| 174 | 186 | ||
| 175 | /* convert a struct timeval to a double */ | 187 | /* convert a struct timeval to a double */ |
| 176 | #define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec)) | 188 | #define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) |
| 177 | 189 | ||
| 178 | /* convert an ntp 64-bit fp number to a struct timeval */ | 190 | /* convert an ntp 64-bit fp number to a struct timeval */ |
| 179 | #define NTP64toTV(n,t) \ | 191 | #define NTP64toTV(n, t) \ |
| 180 | do{ if(!n) t.tv_sec = t.tv_usec = 0; \ | 192 | do { \ |
| 181 | else { \ | 193 | if (!n) \ |
| 182 | t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ | 194 | t.tv_sec = t.tv_usec = 0; \ |
| 183 | t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ | 195 | else { \ |
| 184 | } \ | 196 | t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ |
| 185 | }while(0) | 197 | t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ |
| 198 | } \ | ||
| 199 | } while (0) | ||
| 186 | 200 | ||
| 187 | /* convert a struct timeval to an ntp 64-bit fp number */ | 201 | /* convert a struct timeval to an ntp 64-bit fp number */ |
| 188 | #define TVtoNTP64(t,n) \ | 202 | #define TVtoNTP64(t, n) \ |
| 189 | do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \ | 203 | do { \ |
| 190 | else { \ | 204 | if (!t.tv_usec && !t.tv_sec) \ |
| 191 | L32(n)=htonl(t.tv_sec + EPOCHDIFF); \ | 205 | n = 0x0UL; \ |
| 192 | R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \ | 206 | else { \ |
| 193 | } \ | 207 | L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ |
| 194 | } while(0) | 208 | R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ |
| 209 | } \ | ||
| 210 | } while (0) | ||
| 195 | 211 | ||
| 196 | /* NTP control message header is 12 bytes, plus any data in the data | 212 | /* NTP control message header is 12 bytes, plus any data in the data |
| 197 | * field, plus null padding to the nearest 32-bit boundary per rfc. | 213 | * field, plus null padding to the nearest 32-bit boundary per rfc. |
| 198 | */ | 214 | */ |
| 199 | #define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0)) | 215 | #define SIZEOF_NTPCM(m) \ |
| 216 | (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) | ||
| 200 | 217 | ||
| 201 | /* finally, a little helper or two for debugging: */ | 218 | /* finally, a little helper or two for debugging: */ |
| 202 | #define DBG(x) do{if(verbose>1){ x; }}while(0); | 219 | #define DBG(x) \ |
| 203 | #define PRINTSOCKADDR(x) \ | 220 | do { \ |
| 204 | do{ \ | 221 | if (verbose > 1) { \ |
| 205 | printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ | 222 | x; \ |
| 206 | }while(0); | 223 | } \ |
| 224 | } while (0); | ||
| 225 | #define PRINTSOCKADDR(x) \ | ||
| 226 | do { \ | ||
| 227 | printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ | ||
| 228 | } while (0); | ||
| 207 | 229 | ||
| 208 | /* calculate the offset of the local clock */ | 230 | /* calculate the offset of the local clock */ |
| 209 | static inline double calc_offset(const ntp_message *m, const struct timeval *t){ | 231 | static inline double calc_offset(const ntp_message *m, const struct timeval *t) { |
| 210 | double client_tx, peer_rx, peer_tx, client_rx; | 232 | double client_tx, peer_rx, peer_tx, client_rx; |
| 211 | client_tx = NTP64asDOUBLE(m->origts); | 233 | client_tx = NTP64asDOUBLE(m->origts); |
| 212 | peer_rx = NTP64asDOUBLE(m->rxts); | 234 | peer_rx = NTP64asDOUBLE(m->rxts); |
| 213 | peer_tx = NTP64asDOUBLE(m->txts); | 235 | peer_tx = NTP64asDOUBLE(m->txts); |
| 214 | client_rx=TVasDOUBLE((*t)); | 236 | client_rx = TVasDOUBLE((*t)); |
| 215 | return (.5*((peer_tx-client_rx)+(peer_rx-client_tx))); | 237 | return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx))); |
| 216 | } | 238 | } |
| 217 | 239 | ||
| 218 | /* print out a ntp packet in human readable/debuggable format */ | 240 | /* print out a ntp packet in human readable/debuggable format */ |
| 219 | void print_ntp_message(const ntp_message *p){ | 241 | void print_ntp_message(const ntp_message *p) { |
| 220 | struct timeval ref, orig, rx, tx; | 242 | struct timeval ref, orig, rx, tx; |
| 221 | 243 | ||
| 222 | NTP64toTV(p->refts,ref); | 244 | NTP64toTV(p->refts, ref); |
| 223 | NTP64toTV(p->origts,orig); | 245 | NTP64toTV(p->origts, orig); |
| 224 | NTP64toTV(p->rxts,rx); | 246 | NTP64toTV(p->rxts, rx); |
| 225 | NTP64toTV(p->txts,tx); | 247 | NTP64toTV(p->txts, tx); |
| 226 | 248 | ||
| 227 | printf("packet contents:\n"); | 249 | printf("packet contents:\n"); |
| 228 | printf("\tflags: 0x%.2x\n", p->flags); | 250 | printf("\tflags: 0x%.2x\n", p->flags); |
| 229 | printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); | 251 | printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); |
| 230 | printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); | 252 | printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); |
| 231 | printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); | 253 | printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); |
| 232 | printf("\tstratum = %d\n", p->stratum); | 254 | printf("\tstratum = %d\n", p->stratum); |
| 233 | printf("\tpoll = %g\n", pow(2, p->poll)); | 255 | printf("\tpoll = %g\n", pow(2, p->poll)); |
| 234 | printf("\tprecision = %g\n", pow(2, p->precision)); | 256 | printf("\tprecision = %g\n", pow(2, p->precision)); |
| @@ -241,32 +263,31 @@ void print_ntp_message(const ntp_message *p){ | |||
| 241 | printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); | 263 | printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); |
| 242 | } | 264 | } |
| 243 | 265 | ||
| 244 | void print_ntp_control_message(const ntp_control_message *p){ | 266 | void print_ntp_control_message(const ntp_control_message *p) { |
| 245 | int i=0, numpeers=0; | 267 | int i = 0, numpeers = 0; |
| 246 | const ntp_assoc_status_pair *peer=NULL; | 268 | const ntp_assoc_status_pair *peer = NULL; |
| 247 | 269 | ||
| 248 | printf("control packet contents:\n"); | 270 | printf("control packet contents:\n"); |
| 249 | printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); | 271 | printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); |
| 250 | printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); | 272 | printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); |
| 251 | printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); | 273 | printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); |
| 252 | printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); | 274 | printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); |
| 253 | printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP); | 275 | printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP); |
| 254 | printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE); | 276 | printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE); |
| 255 | printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR); | 277 | printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR); |
| 256 | printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK); | 278 | printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK); |
| 257 | printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); | 279 | printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); |
| 258 | printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); | 280 | printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); |
| 259 | printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); | 281 | printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); |
| 260 | printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); | 282 | printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); |
| 261 | printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); | 283 | printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); |
| 262 | numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair)); | 284 | numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair)); |
| 263 | if(p->op&REM_RESP && p->op&OP_READSTAT){ | 285 | if (p->op & REM_RESP && p->op & OP_READSTAT) { |
| 264 | peer=(ntp_assoc_status_pair*)p->data; | 286 | peer = (ntp_assoc_status_pair *)p->data; |
| 265 | for(i=0;i<numpeers;i++){ | 287 | for (i = 0; i < numpeers; i++) { |
| 266 | printf("\tpeer id %.2x status %.2x", | 288 | printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status)); |
| 267 | ntohs(peer[i].assoc), ntohs(peer[i].status)); | 289 | if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) { |
| 268 | if (PEER_SEL(peer[i].status) >= PEER_INCLUDED){ | 290 | if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) { |
| 269 | if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){ | ||
| 270 | printf(" <-- current sync source"); | 291 | printf(" <-- current sync source"); |
| 271 | } else { | 292 | } else { |
| 272 | printf(" <-- current sync candidate"); | 293 | printf(" <-- current sync candidate"); |
| @@ -277,41 +298,45 @@ void print_ntp_control_message(const ntp_control_message *p){ | |||
| 277 | } | 298 | } |
| 278 | } | 299 | } |
| 279 | 300 | ||
| 280 | void setup_request(ntp_message *p){ | 301 | void setup_request(ntp_message *p) { |
| 281 | struct timeval t; | 302 | struct timeval t; |
| 282 | 303 | ||
| 283 | memset(p, 0, sizeof(ntp_message)); | 304 | memset(p, 0, sizeof(ntp_message)); |
| 284 | LI_SET(p->flags, LI_ALARM); | 305 | LI_SET(p->flags, LI_ALARM); |
| 285 | VN_SET(p->flags, 4); | 306 | VN_SET(p->flags, 4); |
| 286 | MODE_SET(p->flags, MODE_CLIENT); | 307 | MODE_SET(p->flags, MODE_CLIENT); |
| 287 | p->poll=4; | 308 | p->poll = 4; |
| 288 | p->precision=(int8_t)0xfa; | 309 | p->precision = (int8_t)0xfa; |
| 289 | L16(p->rtdelay)=htons(1); | 310 | L16(p->rtdelay) = htons(1); |
| 290 | L16(p->rtdisp)=htons(1); | 311 | L16(p->rtdisp) = htons(1); |
| 291 | 312 | ||
| 292 | gettimeofday(&t, NULL); | 313 | gettimeofday(&t, NULL); |
| 293 | TVtoNTP64(t,p->txts); | 314 | TVtoNTP64(t, p->txts); |
| 294 | } | 315 | } |
| 295 | 316 | ||
| 296 | /* select the "best" server from a list of servers, and return its index. | 317 | /* select the "best" server from a list of servers, and return its index. |
| 297 | * this is done by filtering servers based on stratum, dispersion, and | 318 | * this is done by filtering servers based on stratum, dispersion, and |
| 298 | * finally round-trip delay. */ | 319 | * finally round-trip delay. */ |
| 299 | int best_offset_server(const ntp_server_results *slist, int nservers){ | 320 | int best_offset_server(const ntp_server_results *slist, int nservers) { |
| 300 | int cserver=0, best_server=-1; | 321 | int cserver = 0, best_server = -1; |
| 301 | 322 | ||
| 302 | /* for each server */ | 323 | /* for each server */ |
| 303 | for(cserver=0; cserver<nservers; cserver++){ | 324 | for (cserver = 0; cserver < nservers; cserver++) { |
| 304 | /* We don't want any servers that fails these tests */ | 325 | /* We don't want any servers that fails these tests */ |
| 305 | /* Sort out servers that didn't respond or responede with a 0 stratum; | 326 | /* Sort out servers that didn't respond or responede with a 0 stratum; |
| 306 | * stratum 0 is for reference clocks so no NTP server should ever report | 327 | * stratum 0 is for reference clocks so no NTP server should ever report |
| 307 | * a stratum 0 */ | 328 | * a stratum 0 */ |
| 308 | if ( slist[cserver].stratum == 0){ | 329 | if (slist[cserver].stratum == 0) { |
| 309 | if (verbose) printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); | 330 | if (verbose) { |
| 331 | printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); | ||
| 332 | } | ||
| 310 | continue; | 333 | continue; |
| 311 | } | 334 | } |
| 312 | /* Sort out servers with error flags */ | 335 | /* Sort out servers with error flags */ |
| 313 | if ( LI(slist[cserver].flags) == LI_ALARM ){ | 336 | if (LI(slist[cserver].flags) == LI_ALARM) { |
| 314 | if (verbose) printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); | 337 | if (verbose) { |
| 338 | printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); | ||
| 339 | } | ||
| 315 | continue; | 340 | continue; |
| 316 | } | 341 | } |
| 317 | 342 | ||
| @@ -325,13 +350,13 @@ int best_offset_server(const ntp_server_results *slist, int nservers){ | |||
| 325 | /* compare the server to the best one we've seen so far */ | 350 | /* compare the server to the best one we've seen so far */ |
| 326 | /* does it have an equal or better stratum? */ | 351 | /* does it have an equal or better stratum? */ |
| 327 | DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); | 352 | DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); |
| 328 | if(slist[cserver].stratum <= slist[best_server].stratum){ | 353 | if (slist[cserver].stratum <= slist[best_server].stratum) { |
| 329 | DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); | 354 | DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); |
| 330 | /* does it have an equal or better dispersion? */ | 355 | /* does it have an equal or better dispersion? */ |
| 331 | if(slist[cserver].rtdisp <= slist[best_server].rtdisp){ | 356 | if (slist[cserver].rtdisp <= slist[best_server].rtdisp) { |
| 332 | DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); | 357 | DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); |
| 333 | /* does it have a better rtdelay? */ | 358 | /* does it have a better rtdelay? */ |
| 334 | if(slist[cserver].rtdelay < slist[best_server].rtdelay){ | 359 | if (slist[cserver].rtdelay < slist[best_server].rtdelay) { |
| 335 | DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); | 360 | DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); |
| 336 | best_server = cserver; | 361 | best_server = cserver; |
| 337 | DBG(printf("peer %d is now our best candidate\n", best_server)); | 362 | DBG(printf("peer %d is now our best candidate\n", best_server)); |
| @@ -340,7 +365,7 @@ int best_offset_server(const ntp_server_results *slist, int nservers){ | |||
| 340 | } | 365 | } |
| 341 | } | 366 | } |
| 342 | 367 | ||
| 343 | if(best_server >= 0) { | 368 | if (best_server >= 0) { |
| 344 | DBG(printf("best server selected: peer %d\n", best_server)); | 369 | DBG(printf("best server selected: peer %d\n", best_server)); |
| 345 | return best_server; | 370 | return best_server; |
| 346 | } else { | 371 | } else { |
| @@ -354,16 +379,16 @@ int best_offset_server(const ntp_server_results *slist, int nservers){ | |||
| 354 | * we don't waste time sitting around waiting for single packets. | 379 | * we don't waste time sitting around waiting for single packets. |
| 355 | * - we also "manually" handle resolving host names and connecting, because | 380 | * - we also "manually" handle resolving host names and connecting, because |
| 356 | * we have to do it in a way that our lazy macros don't handle currently :( */ | 381 | * we have to do it in a way that our lazy macros don't handle currently :( */ |
| 357 | double offset_request(const char *host, int *status){ | 382 | double offset_request(const char *host, int *status) { |
| 358 | int i=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0; | 383 | int i = 0, ga_result = 0, num_hosts = 0, *socklist = NULL, respnum = 0; |
| 359 | int servers_completed=0, one_read=0, servers_readable=0, best_index=-1; | 384 | int servers_completed = 0, one_read = 0, servers_readable = 0, best_index = -1; |
| 360 | time_t now_time=0, start_ts=0; | 385 | time_t now_time = 0, start_ts = 0; |
| 361 | ntp_message *req=NULL; | 386 | ntp_message *req = NULL; |
| 362 | double avg_offset=0.; | 387 | double avg_offset = 0.; |
| 363 | struct timeval recv_time; | 388 | struct timeval recv_time; |
| 364 | struct addrinfo *ai=NULL, *ai_tmp=NULL, hints; | 389 | struct addrinfo *ai = NULL, *ai_tmp = NULL, hints; |
| 365 | struct pollfd *ufds=NULL; | 390 | struct pollfd *ufds = NULL; |
| 366 | ntp_server_results *servers=NULL; | 391 | ntp_server_results *servers = NULL; |
| 367 | 392 | ||
| 368 | /* setup hints to only return results from getaddrinfo that we'd like */ | 393 | /* setup hints to only return results from getaddrinfo that we'd like */ |
| 369 | memset(&hints, 0, sizeof(struct addrinfo)); | 394 | memset(&hints, 0, sizeof(struct addrinfo)); |
| @@ -373,97 +398,112 @@ double offset_request(const char *host, int *status){ | |||
| 373 | 398 | ||
| 374 | /* fill in ai with the list of hosts resolved by the host name */ | 399 | /* fill in ai with the list of hosts resolved by the host name */ |
| 375 | ga_result = getaddrinfo(host, "123", &hints, &ai); | 400 | ga_result = getaddrinfo(host, "123", &hints, &ai); |
| 376 | if(ga_result!=0){ | 401 | if (ga_result != 0) { |
| 377 | die(STATE_UNKNOWN, "error getting address for %s: %s\n", | 402 | die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); |
| 378 | host, gai_strerror(ga_result)); | ||
| 379 | } | 403 | } |
| 380 | 404 | ||
| 381 | /* count the number of returned hosts, and allocate stuff accordingly */ | 405 | /* count the number of returned hosts, and allocate stuff accordingly */ |
| 382 | for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; } | 406 | for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) { |
| 383 | req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts); | 407 | num_hosts++; |
| 384 | if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array"); | 408 | } |
| 385 | socklist=(int*)malloc(sizeof(int)*num_hosts); | 409 | req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts); |
| 386 | if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); | 410 | if (req == NULL) { |
| 387 | ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts); | 411 | die(STATE_UNKNOWN, "can not allocate ntp message array"); |
| 388 | if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); | 412 | } |
| 389 | servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts); | 413 | socklist = (int *)malloc(sizeof(int) * num_hosts); |
| 390 | if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array"); | 414 | if (socklist == NULL) { |
| 391 | memset(servers, 0, sizeof(ntp_server_results)*num_hosts); | 415 | die(STATE_UNKNOWN, "can not allocate socket array"); |
| 416 | } | ||
| 417 | ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts); | ||
| 418 | if (ufds == NULL) { | ||
| 419 | die(STATE_UNKNOWN, "can not allocate socket array"); | ||
| 420 | } | ||
| 421 | servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); | ||
| 422 | if (servers == NULL) { | ||
| 423 | die(STATE_UNKNOWN, "can not allocate server array"); | ||
| 424 | } | ||
| 425 | memset(servers, 0, sizeof(ntp_server_results) * num_hosts); | ||
| 392 | DBG(printf("Found %d peers to check\n", num_hosts)); | 426 | DBG(printf("Found %d peers to check\n", num_hosts)); |
| 393 | 427 | ||
| 394 | /* setup each socket for writing, and the corresponding struct pollfd */ | 428 | /* setup each socket for writing, and the corresponding struct pollfd */ |
| 395 | ai_tmp=ai; | 429 | ai_tmp = ai; |
| 396 | for(i=0;ai_tmp;i++){ | 430 | for (i = 0; ai_tmp; i++) { |
| 397 | socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); | 431 | socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); |
| 398 | if(socklist[i] == -1) { | 432 | if (socklist[i] == -1) { |
| 399 | perror(NULL); | 433 | perror(NULL); |
| 400 | die(STATE_UNKNOWN, "can not create new socket"); | 434 | die(STATE_UNKNOWN, "can not create new socket"); |
| 401 | } | 435 | } |
| 402 | if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){ | 436 | if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) { |
| 403 | /* don't die here, because it is enough if there is one server | 437 | /* don't die here, because it is enough if there is one server |
| 404 | answering in time. This also would break for dual ipv4/6 stacked | 438 | answering in time. This also would break for dual ipv4/6 stacked |
| 405 | ntp servers when the client only supports on of them. | 439 | ntp servers when the client only supports on of them. |
| 406 | */ | 440 | */ |
| 407 | DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); | 441 | DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); |
| 408 | } else { | 442 | } else { |
| 409 | ufds[i].fd=socklist[i]; | 443 | ufds[i].fd = socklist[i]; |
| 410 | ufds[i].events=POLLIN; | 444 | ufds[i].events = POLLIN; |
| 411 | ufds[i].revents=0; | 445 | ufds[i].revents = 0; |
| 412 | } | 446 | } |
| 413 | ai_tmp = ai_tmp->ai_next; | 447 | ai_tmp = ai_tmp->ai_next; |
| 414 | } | 448 | } |
| 415 | 449 | ||
| 416 | /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds | 450 | /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds |
| 417 | * have passed in order to ensure post-processing and jitter time. */ | 451 | * have passed in order to ensure post-processing and jitter time. */ |
| 418 | now_time=start_ts=time(NULL); | 452 | now_time = start_ts = time(NULL); |
| 419 | while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){ | 453 | while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) { |
| 420 | /* loop through each server and find each one which hasn't | 454 | /* loop through each server and find each one which hasn't |
| 421 | * been touched in the past second or so and is still lacking | 455 | * been touched in the past second or so and is still lacking |
| 422 | * some responses. for each of these servers, send a new request, | 456 | * some responses. for each of these servers, send a new request, |
| 423 | * and update the "waiting" timestamp with the current time. */ | 457 | * and update the "waiting" timestamp with the current time. */ |
| 424 | now_time=time(NULL); | 458 | now_time = time(NULL); |
| 425 | 459 | ||
| 426 | for(i=0; i<num_hosts; i++){ | 460 | for (i = 0; i < num_hosts; i++) { |
| 427 | if(servers[i].waiting<now_time && servers[i].num_responses<AVG_NUM){ | 461 | if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) { |
| 428 | if(verbose && servers[i].waiting != 0) printf("re-"); | 462 | if (verbose && servers[i].waiting != 0) { |
| 429 | if(verbose) printf("sending request to peer %d\n", i); | 463 | printf("re-"); |
| 464 | } | ||
| 465 | if (verbose) { | ||
| 466 | printf("sending request to peer %d\n", i); | ||
| 467 | } | ||
| 430 | setup_request(&req[i]); | 468 | setup_request(&req[i]); |
| 431 | write(socklist[i], &req[i], sizeof(ntp_message)); | 469 | write(socklist[i], &req[i], sizeof(ntp_message)); |
| 432 | servers[i].waiting=now_time; | 470 | servers[i].waiting = now_time; |
| 433 | break; | 471 | break; |
| 434 | } | 472 | } |
| 435 | } | 473 | } |
| 436 | 474 | ||
| 437 | /* quickly poll for any sockets with pending data */ | 475 | /* quickly poll for any sockets with pending data */ |
| 438 | servers_readable=poll(ufds, num_hosts, 100); | 476 | servers_readable = poll(ufds, num_hosts, 100); |
| 439 | if(servers_readable==-1){ | 477 | if (servers_readable == -1) { |
| 440 | perror("polling ntp sockets"); | 478 | perror("polling ntp sockets"); |
| 441 | die(STATE_UNKNOWN, "communication errors"); | 479 | die(STATE_UNKNOWN, "communication errors"); |
| 442 | } | 480 | } |
| 443 | 481 | ||
| 444 | /* read from any sockets with pending data */ | 482 | /* read from any sockets with pending data */ |
| 445 | for(i=0; servers_readable && i<num_hosts; i++){ | 483 | for (i = 0; servers_readable && i < num_hosts; i++) { |
| 446 | if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){ | 484 | if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) { |
| 447 | if(verbose) { | 485 | if (verbose) { |
| 448 | printf("response from peer %d: ", i); | 486 | printf("response from peer %d: ", i); |
| 449 | } | 487 | } |
| 450 | 488 | ||
| 451 | read(ufds[i].fd, &req[i], sizeof(ntp_message)); | 489 | read(ufds[i].fd, &req[i], sizeof(ntp_message)); |
| 452 | gettimeofday(&recv_time, NULL); | 490 | gettimeofday(&recv_time, NULL); |
| 453 | DBG(print_ntp_message(&req[i])); | 491 | DBG(print_ntp_message(&req[i])); |
| 454 | respnum=servers[i].num_responses++; | 492 | respnum = servers[i].num_responses++; |
| 455 | servers[i].offset[respnum]=calc_offset(&req[i], &recv_time); | 493 | servers[i].offset[respnum] = calc_offset(&req[i], &recv_time); |
| 456 | if(verbose) { | 494 | if (verbose) { |
| 457 | printf("offset %.10g\n", servers[i].offset[respnum]); | 495 | printf("offset %.10g\n", servers[i].offset[respnum]); |
| 458 | } | 496 | } |
| 459 | servers[i].stratum=req[i].stratum; | 497 | servers[i].stratum = req[i].stratum; |
| 460 | servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp); | 498 | servers[i].rtdisp = NTP32asDOUBLE(req[i].rtdisp); |
| 461 | servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay); | 499 | servers[i].rtdelay = NTP32asDOUBLE(req[i].rtdelay); |
| 462 | servers[i].waiting=0; | 500 | servers[i].waiting = 0; |
| 463 | servers[i].flags=req[i].flags; | 501 | servers[i].flags = req[i].flags; |
| 464 | servers_readable--; | 502 | servers_readable--; |
| 465 | one_read = 1; | 503 | one_read = 1; |
| 466 | if(servers[i].num_responses==AVG_NUM) servers_completed++; | 504 | if (servers[i].num_responses == AVG_NUM) { |
| 505 | servers_completed++; | ||
| 506 | } | ||
| 467 | } | 507 | } |
| 468 | } | 508 | } |
| 469 | /* lather, rinse, repeat. */ | 509 | /* lather, rinse, repeat. */ |
| @@ -474,15 +514,15 @@ double offset_request(const char *host, int *status){ | |||
| 474 | } | 514 | } |
| 475 | 515 | ||
| 476 | /* now, pick the best server from the list */ | 516 | /* now, pick the best server from the list */ |
| 477 | best_index=best_offset_server(servers, num_hosts); | 517 | best_index = best_offset_server(servers, num_hosts); |
| 478 | if(best_index < 0){ | 518 | if (best_index < 0) { |
| 479 | *status=STATE_UNKNOWN; | 519 | *status = STATE_UNKNOWN; |
| 480 | } else { | 520 | } else { |
| 481 | /* finally, calculate the average offset */ | 521 | /* finally, calculate the average offset */ |
| 482 | for(i=0; i<servers[best_index].num_responses;i++){ | 522 | for (i = 0; i < servers[best_index].num_responses; i++) { |
| 483 | avg_offset+=servers[best_index].offset[i]; | 523 | avg_offset += servers[best_index].offset[i]; |
| 484 | } | 524 | } |
| 485 | avg_offset/=servers[best_index].num_responses; | 525 | avg_offset /= servers[best_index].num_responses; |
| 486 | } | 526 | } |
| 487 | 527 | ||
| 488 | /* cleanup */ | 528 | /* cleanup */ |
| @@ -496,12 +536,13 @@ double offset_request(const char *host, int *status){ | |||
| 496 | free(req); | 536 | free(req); |
| 497 | freeaddrinfo(ai); | 537 | freeaddrinfo(ai); |
| 498 | 538 | ||
| 499 | if(verbose) printf("overall average offset: %.10g\n", avg_offset); | 539 | if (verbose) { |
| 540 | printf("overall average offset: %.10g\n", avg_offset); | ||
| 541 | } | ||
| 500 | return avg_offset; | 542 | return avg_offset; |
| 501 | } | 543 | } |
| 502 | 544 | ||
| 503 | void | 545 | void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) { |
| 504 | setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){ | ||
| 505 | memset(p, 0, sizeof(ntp_control_message)); | 546 | memset(p, 0, sizeof(ntp_control_message)); |
| 506 | LI_SET(p->flags, LI_NOWARNING); | 547 | LI_SET(p->flags, LI_NOWARNING); |
| 507 | VN_SET(p->flags, VN_RESERVED); | 548 | VN_SET(p->flags, VN_RESERVED); |
| @@ -512,16 +553,16 @@ setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){ | |||
| 512 | } | 553 | } |
| 513 | 554 | ||
| 514 | /* XXX handle responses with the error bit set */ | 555 | /* XXX handle responses with the error bit set */ |
| 515 | double jitter_request(int *status){ | 556 | double jitter_request(int *status) { |
| 516 | int conn=-1, i, npeers=0, num_candidates=0; | 557 | int conn = -1, i, npeers = 0, num_candidates = 0; |
| 517 | bool syncsource_found = false; | 558 | bool syncsource_found = false; |
| 518 | int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0; | 559 | int run = 0, min_peer_sel = PEER_INCLUDED, num_selected = 0, num_valid = 0; |
| 519 | int peers_size=0, peer_offset=0; | 560 | int peers_size = 0, peer_offset = 0; |
| 520 | ntp_assoc_status_pair *peers=NULL; | 561 | ntp_assoc_status_pair *peers = NULL; |
| 521 | ntp_control_message req; | 562 | ntp_control_message req; |
| 522 | const char *getvar = "jitter"; | 563 | const char *getvar = "jitter"; |
| 523 | double rval = 0.0, jitter = -1.0; | 564 | double rval = 0.0, jitter = -1.0; |
| 524 | char *startofvalue=NULL, *nptr=NULL; | 565 | char *startofvalue = NULL, *nptr = NULL; |
| 525 | void *tmp; | 566 | void *tmp; |
| 526 | 567 | ||
| 527 | /* Long-winded explanation: | 568 | /* Long-winded explanation: |
| @@ -542,54 +583,62 @@ double jitter_request(int *status){ | |||
| 542 | 583 | ||
| 543 | /* keep sending requests until the server stops setting the | 584 | /* keep sending requests until the server stops setting the |
| 544 | * REM_MORE bit, though usually this is only 1 packet. */ | 585 | * REM_MORE bit, though usually this is only 1 packet. */ |
| 545 | do{ | 586 | do { |
| 546 | setup_control_request(&req, OP_READSTAT, 1); | 587 | setup_control_request(&req, OP_READSTAT, 1); |
| 547 | DBG(printf("sending READSTAT request")); | 588 | DBG(printf("sending READSTAT request")); |
| 548 | write(conn, &req, SIZEOF_NTPCM(req)); | 589 | write(conn, &req, SIZEOF_NTPCM(req)); |
| 549 | DBG(print_ntp_control_message(&req)); | 590 | DBG(print_ntp_control_message(&req)); |
| 550 | /* Attempt to read the largest size packet possible */ | 591 | /* Attempt to read the largest size packet possible */ |
| 551 | req.count=htons(MAX_CM_SIZE); | 592 | req.count = htons(MAX_CM_SIZE); |
| 552 | DBG(printf("receiving READSTAT response")) | 593 | DBG(printf("receiving READSTAT response")) |
| 553 | read(conn, &req, SIZEOF_NTPCM(req)); | 594 | read(conn, &req, SIZEOF_NTPCM(req)); |
| 554 | DBG(print_ntp_control_message(&req)); | 595 | DBG(print_ntp_control_message(&req)); |
| 555 | /* Each peer identifier is 4 bytes in the data section, which | 596 | /* Each peer identifier is 4 bytes in the data section, which |
| 556 | * we represent as a ntp_assoc_status_pair datatype. | 597 | * we represent as a ntp_assoc_status_pair datatype. |
| 557 | */ | 598 | */ |
| 558 | peers_size+=ntohs(req.count); | 599 | peers_size += ntohs(req.count); |
| 559 | if((tmp=realloc(peers, peers_size)) == NULL) | 600 | if ((tmp = realloc(peers, peers_size)) == NULL) { |
| 560 | free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); | 601 | free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); |
| 561 | peers=tmp; | 602 | } |
| 562 | memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count)); | 603 | peers = tmp; |
| 563 | npeers=peers_size/sizeof(ntp_assoc_status_pair); | 604 | memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count)); |
| 564 | peer_offset+=ntohs(req.count); | 605 | npeers = peers_size / sizeof(ntp_assoc_status_pair); |
| 565 | } while(req.op&REM_MORE); | 606 | peer_offset += ntohs(req.count); |
| 607 | } while (req.op & REM_MORE); | ||
| 566 | 608 | ||
| 567 | /* first, let's find out if we have a sync source, or if there are | 609 | /* first, let's find out if we have a sync source, or if there are |
| 568 | * at least some candidates. in the case of the latter we'll issue | 610 | * at least some candidates. in the case of the latter we'll issue |
| 569 | * a warning but go ahead with the check on them. */ | 611 | * a warning but go ahead with the check on them. */ |
| 570 | for (i = 0; i < npeers; i++){ | 612 | for (i = 0; i < npeers; i++) { |
| 571 | if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){ | 613 | if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { |
| 572 | num_candidates++; | 614 | num_candidates++; |
| 573 | if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){ | 615 | if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { |
| 574 | syncsource_found = true; | 616 | syncsource_found = true; |
| 575 | min_peer_sel=PEER_SYNCSOURCE; | 617 | min_peer_sel = PEER_SYNCSOURCE; |
| 576 | } | 618 | } |
| 577 | } | 619 | } |
| 578 | } | 620 | } |
| 579 | if(verbose) printf("%d candidate peers available\n", num_candidates); | 621 | if (verbose) { |
| 580 | if(verbose && syncsource_found) printf("synchronization source found\n"); | 622 | printf("%d candidate peers available\n", num_candidates); |
| 581 | if(! syncsource_found){ | 623 | } |
| 624 | if (verbose && syncsource_found) { | ||
| 625 | printf("synchronization source found\n"); | ||
| 626 | } | ||
| 627 | if (!syncsource_found) { | ||
| 582 | *status = STATE_UNKNOWN; | 628 | *status = STATE_UNKNOWN; |
| 583 | if(verbose) printf("warning: no synchronization source found\n"); | 629 | if (verbose) { |
| 630 | printf("warning: no synchronization source found\n"); | ||
| 631 | } | ||
| 584 | } | 632 | } |
| 585 | 633 | ||
| 586 | 634 | for (run = 0; run < AVG_NUM; run++) { | |
| 587 | for (run=0; run<AVG_NUM; run++){ | 635 | if (verbose) { |
| 588 | if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM); | 636 | printf("jitter run %d of %d\n", run + 1, AVG_NUM); |
| 589 | for (i = 0; i < npeers; i++){ | 637 | } |
| 638 | for (i = 0; i < npeers; i++) { | ||
| 590 | /* Only query this server if it is the current sync source */ | 639 | /* Only query this server if it is the current sync source */ |
| 591 | if (PEER_SEL(peers[i].status) >= min_peer_sel){ | 640 | if (PEER_SEL(peers[i].status) >= min_peer_sel) { |
| 592 | char jitter_data[MAX_CM_SIZE+1]; | 641 | char jitter_data[MAX_CM_SIZE + 1]; |
| 593 | size_t jitter_data_count; | 642 | size_t jitter_data_count; |
| 594 | 643 | ||
| 595 | num_selected++; | 644 | num_selected++; |
| @@ -602,7 +651,7 @@ double jitter_request(int *status){ | |||
| 602 | */ | 651 | */ |
| 603 | /* Older servers doesn't know what jitter is, so if we get an | 652 | /* Older servers doesn't know what jitter is, so if we get an |
| 604 | * error on the first pass we redo it with "dispersion" */ | 653 | * error on the first pass we redo it with "dispersion" */ |
| 605 | strncpy(req.data, getvar, MAX_CM_SIZE-1); | 654 | strncpy(req.data, getvar, MAX_CM_SIZE - 1); |
| 606 | req.count = htons(strlen(getvar)); | 655 | req.count = htons(strlen(getvar)); |
| 607 | DBG(printf("sending READVAR request...\n")); | 656 | DBG(printf("sending READVAR request...\n")); |
| 608 | write(conn, &req, SIZEOF_NTPCM(req)); | 657 | write(conn, &req, SIZEOF_NTPCM(req)); |
| @@ -613,8 +662,11 @@ double jitter_request(int *status){ | |||
| 613 | read(conn, &req, SIZEOF_NTPCM(req)); | 662 | read(conn, &req, SIZEOF_NTPCM(req)); |
| 614 | DBG(print_ntp_control_message(&req)); | 663 | DBG(print_ntp_control_message(&req)); |
| 615 | 664 | ||
| 616 | if(req.op&REM_ERROR && strstr(getvar, "jitter")) { | 665 | if (req.op & REM_ERROR && strstr(getvar, "jitter")) { |
| 617 | if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n"); | 666 | if (verbose) { |
| 667 | printf("The 'jitter' command failed (old ntp server?)\nRestarting with " | ||
| 668 | "'dispersion'...\n"); | ||
| 669 | } | ||
| 618 | getvar = "dispersion"; | 670 | getvar = "dispersion"; |
| 619 | num_selected--; | 671 | num_selected--; |
| 620 | i--; | 672 | i--; |
| @@ -622,32 +674,33 @@ double jitter_request(int *status){ | |||
| 622 | } | 674 | } |
| 623 | 675 | ||
| 624 | /* get to the float value */ | 676 | /* get to the float value */ |
| 625 | if(verbose) { | 677 | if (verbose) { |
| 626 | printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc)); | 678 | printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc)); |
| 627 | } | 679 | } |
| 628 | if((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)){ | 680 | if ((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)) { |
| 629 | die(STATE_UNKNOWN, | 681 | die(STATE_UNKNOWN, _("jitter response too large (%lu bytes)\n"), |
| 630 | _("jitter response too large (%lu bytes)\n"), | 682 | (unsigned long)jitter_data_count); |
| 631 | (unsigned long)jitter_data_count); | ||
| 632 | } | 683 | } |
| 633 | memcpy(jitter_data, req.data, jitter_data_count); | 684 | memcpy(jitter_data, req.data, jitter_data_count); |
| 634 | jitter_data[jitter_data_count] = '\0'; | 685 | jitter_data[jitter_data_count] = '\0'; |
| 635 | startofvalue = strchr(jitter_data, '='); | 686 | startofvalue = strchr(jitter_data, '='); |
| 636 | if(startofvalue != NULL) { | 687 | if (startofvalue != NULL) { |
| 637 | startofvalue++; | 688 | startofvalue++; |
| 638 | jitter = strtod(startofvalue, &nptr); | 689 | jitter = strtod(startofvalue, &nptr); |
| 639 | } | 690 | } |
| 640 | if(startofvalue == NULL || startofvalue==nptr){ | 691 | if (startofvalue == NULL || startofvalue == nptr) { |
| 641 | printf("warning: unable to read server jitter response.\n"); | 692 | printf("warning: unable to read server jitter response.\n"); |
| 642 | *status = STATE_UNKNOWN; | 693 | *status = STATE_UNKNOWN; |
| 643 | } else { | 694 | } else { |
| 644 | if(verbose) printf("%g\n", jitter); | 695 | if (verbose) { |
| 696 | printf("%g\n", jitter); | ||
| 697 | } | ||
| 645 | num_valid++; | 698 | num_valid++; |
| 646 | rval += jitter; | 699 | rval += jitter; |
| 647 | } | 700 | } |
| 648 | } | 701 | } |
| 649 | } | 702 | } |
| 650 | if(verbose){ | 703 | if (verbose) { |
| 651 | printf("jitter parsed from %d/%d peers\n", num_valid, num_selected); | 704 | printf("jitter parsed from %d/%d peers\n", num_valid, num_selected); |
| 652 | } | 705 | } |
| 653 | } | 706 | } |
| @@ -655,37 +708,33 @@ double jitter_request(int *status){ | |||
| 655 | rval = num_valid ? rval / num_valid : -1.0; | 708 | rval = num_valid ? rval / num_valid : -1.0; |
| 656 | 709 | ||
| 657 | close(conn); | 710 | close(conn); |
| 658 | if(peers!=NULL) free(peers); | 711 | if (peers != NULL) { |
| 712 | free(peers); | ||
| 713 | } | ||
| 659 | /* If we return -1.0, it means no synchronization source was found */ | 714 | /* If we return -1.0, it means no synchronization source was found */ |
| 660 | return rval; | 715 | return rval; |
| 661 | } | 716 | } |
| 662 | 717 | ||
| 663 | int process_arguments(int argc, char **argv){ | 718 | int process_arguments(int argc, char **argv) { |
| 664 | int c; | 719 | int c; |
| 665 | int option=0; | 720 | int option = 0; |
| 666 | static struct option longopts[] = { | 721 | static struct option longopts[] = { |
| 667 | {"version", no_argument, 0, 'V'}, | 722 | {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, |
| 668 | {"help", no_argument, 0, 'h'}, | 723 | {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'}, |
| 669 | {"verbose", no_argument, 0, 'v'}, | 724 | {"use-ipv6", no_argument, 0, '6'}, {"warning", required_argument, 0, 'w'}, |
| 670 | {"use-ipv4", no_argument, 0, '4'}, | 725 | {"critical", required_argument, 0, 'c'}, {"jwarn", required_argument, 0, 'j'}, |
| 671 | {"use-ipv6", no_argument, 0, '6'}, | 726 | {"jcrit", required_argument, 0, 'k'}, {"timeout", required_argument, 0, 't'}, |
| 672 | {"warning", required_argument, 0, 'w'}, | 727 | {"hostname", required_argument, 0, 'H'}, {0, 0, 0, 0}}; |
| 673 | {"critical", required_argument, 0, 'c'}, | 728 | |
| 674 | {"jwarn", required_argument, 0, 'j'}, | 729 | if (argc < 2) { |
| 675 | {"jcrit", required_argument, 0, 'k'}, | 730 | usage("\n"); |
| 676 | {"timeout", required_argument, 0, 't'}, | 731 | } |
| 677 | {"hostname", required_argument, 0, 'H'}, | ||
| 678 | {0, 0, 0, 0} | ||
| 679 | }; | ||
| 680 | |||
| 681 | |||
| 682 | if (argc < 2) | ||
| 683 | usage ("\n"); | ||
| 684 | 732 | ||
| 685 | while (1) { | 733 | while (1) { |
| 686 | c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); | 734 | c = getopt_long(argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); |
| 687 | if (c == -1 || c == EOF || c == 1) | 735 | if (c == -1 || c == EOF || c == 1) { |
| 688 | break; | 736 | break; |
| 737 | } | ||
| 689 | 738 | ||
| 690 | switch (c) { | 739 | switch (c) { |
| 691 | case 'h': | 740 | case 'h': |
| @@ -716,12 +765,13 @@ int process_arguments(int argc, char **argv){ | |||
| 716 | jcrit = optarg; | 765 | jcrit = optarg; |
| 717 | break; | 766 | break; |
| 718 | case 'H': | 767 | case 'H': |
| 719 | if(!is_host(optarg)) | 768 | if (!is_host(optarg)) { |
| 720 | usage2(_("Invalid hostname/address"), optarg); | 769 | usage2(_("Invalid hostname/address"), optarg); |
| 770 | } | ||
| 721 | server_address = strdup(optarg); | 771 | server_address = strdup(optarg); |
| 722 | break; | 772 | break; |
| 723 | case 't': | 773 | case 't': |
| 724 | socket_timeout=atoi(optarg); | 774 | socket_timeout = atoi(optarg); |
| 725 | break; | 775 | break; |
| 726 | case '4': | 776 | case '4': |
| 727 | address_family = AF_INET; | 777 | address_family = AF_INET; |
| @@ -730,64 +780,59 @@ int process_arguments(int argc, char **argv){ | |||
| 730 | #ifdef USE_IPV6 | 780 | #ifdef USE_IPV6 |
| 731 | address_family = AF_INET6; | 781 | address_family = AF_INET6; |
| 732 | #else | 782 | #else |
| 733 | usage4 (_("IPv6 support not available")); | 783 | usage4(_("IPv6 support not available")); |
| 734 | #endif | 784 | #endif |
| 735 | break; | 785 | break; |
| 736 | case '?': | 786 | case '?': |
| 737 | /* print short usage statement if args not parsable */ | 787 | /* print short usage statement if args not parsable */ |
| 738 | usage5 (); | 788 | usage5(); |
| 739 | break; | 789 | break; |
| 740 | } | 790 | } |
| 741 | } | 791 | } |
| 742 | 792 | ||
| 743 | if(server_address == NULL){ | 793 | if (server_address == NULL) { |
| 744 | usage4(_("Hostname was not supplied")); | 794 | usage4(_("Hostname was not supplied")); |
| 745 | } | 795 | } |
| 746 | 796 | ||
| 747 | return 0; | 797 | return 0; |
| 748 | } | 798 | } |
| 749 | 799 | ||
| 750 | char *perfd_offset (double offset) | 800 | char *perfd_offset(double offset) { |
| 751 | { | 801 | return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, |
| 752 | return fperfdata ("offset", offset, "s", | 802 | offset_thresholds->critical->end, false, 0, false, 0); |
| 753 | true, offset_thresholds->warning->end, | ||
| 754 | true, offset_thresholds->critical->end, | ||
| 755 | false, 0, false, 0); | ||
| 756 | } | 803 | } |
| 757 | 804 | ||
| 758 | char *perfd_jitter (double jitter) | 805 | char *perfd_jitter(double jitter) { |
| 759 | { | 806 | return fperfdata("jitter", jitter, "s", do_jitter, jitter_thresholds->warning->end, do_jitter, |
| 760 | return fperfdata ("jitter", jitter, "s", | 807 | jitter_thresholds->critical->end, true, 0, false, 0); |
| 761 | do_jitter, jitter_thresholds->warning->end, | ||
| 762 | do_jitter, jitter_thresholds->critical->end, | ||
| 763 | true, 0, false, 0); | ||
| 764 | } | 808 | } |
| 765 | 809 | ||
| 766 | int main(int argc, char *argv[]){ | 810 | int main(int argc, char *argv[]) { |
| 767 | int result, offset_result, jitter_result; | 811 | int result, offset_result, jitter_result; |
| 768 | double offset=0, jitter=0; | 812 | double offset = 0, jitter = 0; |
| 769 | char *result_line, *perfdata_line; | 813 | char *result_line, *perfdata_line; |
| 770 | 814 | ||
| 771 | setlocale (LC_ALL, ""); | 815 | setlocale(LC_ALL, ""); |
| 772 | bindtextdomain (PACKAGE, LOCALEDIR); | 816 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 773 | textdomain (PACKAGE); | 817 | textdomain(PACKAGE); |
| 774 | 818 | ||
| 775 | result = offset_result = jitter_result = STATE_OK; | 819 | result = offset_result = jitter_result = STATE_OK; |
| 776 | 820 | ||
| 777 | /* Parse extra opts if any */ | 821 | /* Parse extra opts if any */ |
| 778 | argv=np_extra_opts (&argc, argv, progname); | 822 | argv = np_extra_opts(&argc, argv, progname); |
| 779 | 823 | ||
| 780 | if (process_arguments (argc, argv) == ERROR) | 824 | if (process_arguments(argc, argv) == ERROR) { |
| 781 | usage4 (_("Could not parse arguments")); | 825 | usage4(_("Could not parse arguments")); |
| 826 | } | ||
| 782 | 827 | ||
| 783 | set_thresholds(&offset_thresholds, owarn, ocrit); | 828 | set_thresholds(&offset_thresholds, owarn, ocrit); |
| 784 | set_thresholds(&jitter_thresholds, jwarn, jcrit); | 829 | set_thresholds(&jitter_thresholds, jwarn, jcrit); |
| 785 | 830 | ||
| 786 | /* initialize alarm signal handling */ | 831 | /* initialize alarm signal handling */ |
| 787 | signal (SIGALRM, socket_timeout_alarm_handler); | 832 | signal(SIGALRM, socket_timeout_alarm_handler); |
| 788 | 833 | ||
| 789 | /* set socket timeout */ | 834 | /* set socket timeout */ |
| 790 | alarm (socket_timeout); | 835 | alarm(socket_timeout); |
| 791 | 836 | ||
| 792 | offset = offset_request(server_address, &offset_result); | 837 | offset = offset_request(server_address, &offset_result); |
| 793 | /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. | 838 | /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. |
| @@ -803,31 +848,32 @@ int main(int argc, char *argv[]){ | |||
| 803 | * servers recognize. Trying to check the jitter on OpenNTPD | 848 | * servers recognize. Trying to check the jitter on OpenNTPD |
| 804 | * (for example) will result in an error | 849 | * (for example) will result in an error |
| 805 | */ | 850 | */ |
| 806 | if(do_jitter){ | 851 | if (do_jitter) { |
| 807 | jitter=jitter_request(&jitter_result); | 852 | jitter = jitter_request(&jitter_result); |
| 808 | result = max_state_alt(result, get_status(jitter, jitter_thresholds)); | 853 | result = max_state_alt(result, get_status(jitter, jitter_thresholds)); |
| 809 | /* -1 indicates that we couldn't calculate the jitter | 854 | /* -1 indicates that we couldn't calculate the jitter |
| 810 | * Only overrides STATE_OK from the offset */ | 855 | * Only overrides STATE_OK from the offset */ |
| 811 | if(jitter == -1.0 && result == STATE_OK) | 856 | if (jitter == -1.0 && result == STATE_OK) { |
| 812 | result = STATE_UNKNOWN; | 857 | result = STATE_UNKNOWN; |
| 858 | } | ||
| 813 | } | 859 | } |
| 814 | result = max_state_alt(result, jitter_result); | 860 | result = max_state_alt(result, jitter_result); |
| 815 | 861 | ||
| 816 | switch (result) { | 862 | switch (result) { |
| 817 | case STATE_CRITICAL : | 863 | case STATE_CRITICAL: |
| 818 | xasprintf(&result_line, _("NTP CRITICAL:")); | 864 | xasprintf(&result_line, _("NTP CRITICAL:")); |
| 819 | break; | 865 | break; |
| 820 | case STATE_WARNING : | 866 | case STATE_WARNING: |
| 821 | xasprintf(&result_line, _("NTP WARNING:")); | 867 | xasprintf(&result_line, _("NTP WARNING:")); |
| 822 | break; | 868 | break; |
| 823 | case STATE_OK : | 869 | case STATE_OK: |
| 824 | xasprintf(&result_line, _("NTP OK:")); | 870 | xasprintf(&result_line, _("NTP OK:")); |
| 825 | break; | 871 | break; |
| 826 | default : | 872 | default: |
| 827 | xasprintf(&result_line, _("NTP UNKNOWN:")); | 873 | xasprintf(&result_line, _("NTP UNKNOWN:")); |
| 828 | break; | 874 | break; |
| 829 | } | 875 | } |
| 830 | if(offset_result == STATE_UNKNOWN){ | 876 | if (offset_result == STATE_UNKNOWN) { |
| 831 | xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); | 877 | xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); |
| 832 | xasprintf(&perfdata_line, ""); | 878 | xasprintf(&perfdata_line, ""); |
| 833 | } else { | 879 | } else { |
| @@ -836,41 +882,41 @@ int main(int argc, char *argv[]){ | |||
| 836 | } | 882 | } |
| 837 | if (do_jitter) { | 883 | if (do_jitter) { |
| 838 | xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); | 884 | xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); |
| 839 | xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); | 885 | xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); |
| 840 | } | 886 | } |
| 841 | printf("%s|%s\n", result_line, perfdata_line); | 887 | printf("%s|%s\n", result_line, perfdata_line); |
| 842 | 888 | ||
| 843 | if(server_address!=NULL) free(server_address); | 889 | if (server_address != NULL) { |
| 890 | free(server_address); | ||
| 891 | } | ||
| 844 | return result; | 892 | return result; |
| 845 | } | 893 | } |
| 846 | 894 | ||
| 847 | 895 | void print_help(void) { | |
| 848 | |||
| 849 | void print_help(void){ | ||
| 850 | print_revision(progname, NP_VERSION); | 896 | print_revision(progname, NP_VERSION); |
| 851 | 897 | ||
| 852 | printf ("Copyright (c) 2006 Sean Finney\n"); | 898 | printf("Copyright (c) 2006 Sean Finney\n"); |
| 853 | printf (COPYRIGHT, copyright, email); | 899 | printf(COPYRIGHT, copyright, email); |
| 854 | 900 | ||
| 855 | printf ("%s\n", _("This plugin checks the selected ntp server")); | 901 | printf("%s\n", _("This plugin checks the selected ntp server")); |
| 856 | 902 | ||
| 857 | printf ("\n\n"); | 903 | printf("\n\n"); |
| 858 | 904 | ||
| 859 | print_usage(); | 905 | print_usage(); |
| 860 | printf (UT_HELP_VRSN); | 906 | printf(UT_HELP_VRSN); |
| 861 | printf (UT_EXTRA_OPTS); | 907 | printf(UT_EXTRA_OPTS); |
| 862 | printf (UT_HOST_PORT, 'p', "123"); | 908 | printf(UT_HOST_PORT, 'p', "123"); |
| 863 | printf (UT_IPv46); | 909 | printf(UT_IPv46); |
| 864 | printf (" %s\n", "-w, --warning=THRESHOLD"); | 910 | printf(" %s\n", "-w, --warning=THRESHOLD"); |
| 865 | printf (" %s\n", _("Offset to result in warning status (seconds)")); | 911 | printf(" %s\n", _("Offset to result in warning status (seconds)")); |
| 866 | printf (" %s\n", "-c, --critical=THRESHOLD"); | 912 | printf(" %s\n", "-c, --critical=THRESHOLD"); |
| 867 | printf (" %s\n", _("Offset to result in critical status (seconds)")); | 913 | printf(" %s\n", _("Offset to result in critical status (seconds)")); |
| 868 | printf (" %s\n", "-j, --jwarn=THRESHOLD"); | 914 | printf(" %s\n", "-j, --jwarn=THRESHOLD"); |
| 869 | printf (" %s\n", _("Warning threshold for jitter")); | 915 | printf(" %s\n", _("Warning threshold for jitter")); |
| 870 | printf (" %s\n", "-k, --jcrit=THRESHOLD"); | 916 | printf(" %s\n", "-k, --jcrit=THRESHOLD"); |
| 871 | printf (" %s\n", _("Critical threshold for jitter")); | 917 | printf(" %s\n", _("Critical threshold for jitter")); |
| 872 | printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 918 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 873 | printf (UT_VERBOSE); | 919 | printf(UT_VERBOSE); |
| 874 | 920 | ||
| 875 | printf("\n"); | 921 | printf("\n"); |
| 876 | printf("%s\n", _("Notes:")); | 922 | printf("%s\n", _("Notes:")); |
| @@ -881,21 +927,21 @@ void print_help(void){ | |||
| 881 | printf(" %s\n", _("Normal offset check:")); | 927 | printf(" %s\n", _("Normal offset check:")); |
| 882 | printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1")); | 928 | printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1")); |
| 883 | printf("\n"); | 929 | printf("\n"); |
| 884 | printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); | 930 | printf(" %s\n", |
| 931 | _("Check jitter too, avoiding critical notifications if jitter isn't available")); | ||
| 885 | printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); | 932 | printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); |
| 886 | printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); | 933 | printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); |
| 887 | 934 | ||
| 888 | printf (UT_SUPPORT); | 935 | printf(UT_SUPPORT); |
| 889 | 936 | ||
| 890 | printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); | 937 | printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); |
| 891 | printf ("%s\n\n", _("check_ntp_time instead.")); | 938 | printf("%s\n\n", _("check_ntp_time instead.")); |
| 892 | } | 939 | } |
| 893 | 940 | ||
| 894 | void | 941 | void print_usage(void) { |
| 895 | print_usage(void) | 942 | printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); |
| 896 | { | 943 | printf("%s\n\n", _("check_ntp_time instead.")); |
| 897 | printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); | 944 | printf("%s\n", _("Usage:")); |
| 898 | printf ("%s\n\n", _("check_ntp_time instead.")); | 945 | printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n", |
| 899 | printf ("%s\n", _("Usage:")); | 946 | progname); |
| 900 | printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n", progname); | ||
| 901 | } | 947 | } |
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index f99e5032..26f74286 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c | |||
| @@ -39,33 +39,23 @@ const char *progname = "check_ntp_peer"; | |||
| 39 | const char *copyright = "2006-2024"; | 39 | const char *copyright = "2006-2024"; |
| 40 | const char *email = "devel@monitoring-plugins.org"; | 40 | const char *email = "devel@monitoring-plugins.org"; |
| 41 | 41 | ||
| 42 | #include "output.h" | ||
| 43 | #include "perfdata.h" | ||
| 44 | #include <openssl/x509.h> | ||
| 45 | #include "thresholds.h" | ||
| 42 | #include "common.h" | 46 | #include "common.h" |
| 43 | #include "netutils.h" | 47 | #include "netutils.h" |
| 44 | #include "utils.h" | 48 | #include "utils.h" |
| 49 | #include "../lib/states.h" | ||
| 50 | #include "check_ntp_peer.d/config.h" | ||
| 45 | 51 | ||
| 46 | static char *server_address = NULL; | ||
| 47 | static int port = 123; | ||
| 48 | static int verbose = 0; | 52 | static int verbose = 0; |
| 49 | static bool quiet = false; | 53 | |
| 50 | static char *owarn = "60"; | 54 | typedef struct { |
| 51 | static char *ocrit = "120"; | 55 | int errorcode; |
| 52 | static bool do_stratum = false; | 56 | check_ntp_peer_config config; |
| 53 | static char *swarn = "-1:16"; | 57 | } check_ntp_peer_config_wrapper; |
| 54 | static char *scrit = "-1:16"; | 58 | static check_ntp_peer_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 55 | static bool do_jitter = false; | ||
| 56 | static char *jwarn = "-1:5000"; | ||
| 57 | static char *jcrit = "-1:10000"; | ||
| 58 | static bool do_truechimers = false; | ||
| 59 | static char *twarn = "0:"; | ||
| 60 | static char *tcrit = "0:"; | ||
| 61 | static bool syncsource_found = false; | ||
| 62 | static bool li_alarm = false; | ||
| 63 | |||
| 64 | static int process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 65 | static thresholds *offset_thresholds = NULL; | ||
| 66 | static thresholds *jitter_thresholds = NULL; | ||
| 67 | static thresholds *stratum_thresholds = NULL; | ||
| 68 | static thresholds *truechimer_thresholds = NULL; | ||
| 69 | static void print_help(void); | 59 | static void print_help(void); |
| 70 | void print_usage(void); | 60 | void print_usage(void); |
| 71 | 61 | ||
| @@ -94,9 +84,9 @@ typedef struct { | |||
| 94 | /* bits 1,2 are the leap indicator */ | 84 | /* bits 1,2 are the leap indicator */ |
| 95 | #define LI_MASK 0xc0 | 85 | #define LI_MASK 0xc0 |
| 96 | #define LI(x) ((x & LI_MASK) >> 6) | 86 | #define LI(x) ((x & LI_MASK) >> 6) |
| 97 | #define LI_SET(x, y) \ | 87 | #define LI_SET(x, y) \ |
| 98 | do { \ | 88 | do { \ |
| 99 | x |= ((y << 6) & LI_MASK); \ | 89 | x |= ((y << 6) & LI_MASK); \ |
| 100 | } while (0) | 90 | } while (0) |
| 101 | /* and these are the values of the leap indicator */ | 91 | /* and these are the values of the leap indicator */ |
| 102 | #define LI_NOWARNING 0x00 | 92 | #define LI_NOWARNING 0x00 |
| @@ -106,17 +96,17 @@ typedef struct { | |||
| 106 | /* bits 3,4,5 are the ntp version */ | 96 | /* bits 3,4,5 are the ntp version */ |
| 107 | #define VN_MASK 0x38 | 97 | #define VN_MASK 0x38 |
| 108 | #define VN(x) ((x & VN_MASK) >> 3) | 98 | #define VN(x) ((x & VN_MASK) >> 3) |
| 109 | #define VN_SET(x, y) \ | 99 | #define VN_SET(x, y) \ |
| 110 | do { \ | 100 | do { \ |
| 111 | x |= ((y << 3) & VN_MASK); \ | 101 | x |= ((y << 3) & VN_MASK); \ |
| 112 | } while (0) | 102 | } while (0) |
| 113 | #define VN_RESERVED 0x02 | 103 | #define VN_RESERVED 0x02 |
| 114 | /* bits 6,7,8 are the ntp mode */ | 104 | /* bits 6,7,8 are the ntp mode */ |
| 115 | #define MODE_MASK 0x07 | 105 | #define MODE_MASK 0x07 |
| 116 | #define MODE(x) (x & MODE_MASK) | 106 | #define MODE(x) (x & MODE_MASK) |
| 117 | #define MODE_SET(x, y) \ | 107 | #define MODE_SET(x, y) \ |
| 118 | do { \ | 108 | do { \ |
| 119 | x |= (y & MODE_MASK); \ | 109 | x |= (y & MODE_MASK); \ |
| 120 | } while (0) | 110 | } while (0) |
| 121 | /* here are some values */ | 111 | /* here are some values */ |
| 122 | #define MODE_CLIENT 0x03 | 112 | #define MODE_CLIENT 0x03 |
| @@ -128,9 +118,9 @@ typedef struct { | |||
| 128 | #define REM_MORE 0x20 | 118 | #define REM_MORE 0x20 |
| 129 | /* In control message, bits 11 - 15 are opcode */ | 119 | /* In control message, bits 11 - 15 are opcode */ |
| 130 | #define OP_MASK 0x1f | 120 | #define OP_MASK 0x1f |
| 131 | #define OP_SET(x, y) \ | 121 | #define OP_SET(x, y) \ |
| 132 | do { \ | 122 | do { \ |
| 133 | x |= (y & OP_MASK); \ | 123 | x |= (y & OP_MASK); \ |
| 134 | } while (0) | 124 | } while (0) |
| 135 | #define OP_READSTAT 0x01 | 125 | #define OP_READSTAT 0x01 |
| 136 | #define OP_READVAR 0x02 | 126 | #define OP_READVAR 0x02 |
| @@ -143,39 +133,40 @@ typedef struct { | |||
| 143 | /* NTP control message header is 12 bytes, plus any data in the data | 133 | /* NTP control message header is 12 bytes, plus any data in the data |
| 144 | * field, plus null padding to the nearest 32-bit boundary per rfc. | 134 | * field, plus null padding to the nearest 32-bit boundary per rfc. |
| 145 | */ | 135 | */ |
| 146 | #define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) | 136 | #define SIZEOF_NTPCM(m) \ |
| 137 | (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) | ||
| 147 | 138 | ||
| 148 | /* finally, a little helper or two for debugging: */ | 139 | /* finally, a little helper or two for debugging: */ |
| 149 | #define DBG(x) \ | 140 | #define DBG(x) \ |
| 150 | do { \ | 141 | do { \ |
| 151 | if (verbose > 1) { \ | 142 | if (verbose > 1) { \ |
| 152 | x; \ | 143 | x; \ |
| 153 | } \ | 144 | } \ |
| 154 | } while (0); | 145 | } while (0); |
| 155 | #define PRINTSOCKADDR(x) \ | 146 | #define PRINTSOCKADDR(x) \ |
| 156 | do { \ | 147 | do { \ |
| 157 | printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ | 148 | printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ |
| 158 | } while (0); | 149 | } while (0); |
| 159 | 150 | ||
| 160 | void print_ntp_control_message(const ntp_control_message *p) { | 151 | void print_ntp_control_message(const ntp_control_message *message) { |
| 161 | printf("control packet contents:\n"); | 152 | printf("control packet contents:\n"); |
| 162 | printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); | 153 | printf("\tflags: 0x%.2x , 0x%.2x\n", message->flags, message->op); |
| 163 | printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); | 154 | printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK); |
| 164 | printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); | 155 | printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK); |
| 165 | printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); | 156 | printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK); |
| 166 | printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP); | 157 | printf("\t response=%d (0x%.2x)\n", (message->op & REM_RESP) > 0, message->op & REM_RESP); |
| 167 | printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE); | 158 | printf("\t more=%d (0x%.2x)\n", (message->op & REM_MORE) > 0, message->op & REM_MORE); |
| 168 | printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR); | 159 | printf("\t error=%d (0x%.2x)\n", (message->op & REM_ERROR) > 0, message->op & REM_ERROR); |
| 169 | printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK); | 160 | printf("\t op=%d (0x%.2x)\n", message->op & OP_MASK, message->op & OP_MASK); |
| 170 | printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); | 161 | printf("\tsequence: %d (0x%.2x)\n", ntohs(message->seq), ntohs(message->seq)); |
| 171 | printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); | 162 | printf("\tstatus: %d (0x%.2x)\n", ntohs(message->status), ntohs(message->status)); |
| 172 | printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); | 163 | printf("\tassoc: %d (0x%.2x)\n", ntohs(message->assoc), ntohs(message->assoc)); |
| 173 | printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); | 164 | printf("\toffset: %d (0x%.2x)\n", ntohs(message->offset), ntohs(message->offset)); |
| 174 | printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); | 165 | printf("\tcount: %d (0x%.2x)\n", ntohs(message->count), ntohs(message->count)); |
| 175 | 166 | ||
| 176 | int numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair)); | 167 | int numpeers = ntohs(message->count) / (sizeof(ntp_assoc_status_pair)); |
| 177 | if (p->op & REM_RESP && p->op & OP_READSTAT) { | 168 | if (message->op & REM_RESP && message->op & OP_READSTAT) { |
| 178 | const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)p->data; | 169 | const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)message->data; |
| 179 | for (int i = 0; i < numpeers; i++) { | 170 | for (int i = 0; i < numpeers; i++) { |
| 180 | printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status)); | 171 | printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status)); |
| 181 | if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) { | 172 | if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) { |
| @@ -190,13 +181,13 @@ void print_ntp_control_message(const ntp_control_message *p) { | |||
| 190 | } | 181 | } |
| 191 | } | 182 | } |
| 192 | 183 | ||
| 193 | void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) { | 184 | void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_t seq) { |
| 194 | memset(p, 0, sizeof(ntp_control_message)); | 185 | memset(message, 0, sizeof(ntp_control_message)); |
| 195 | LI_SET(p->flags, LI_NOWARNING); | 186 | LI_SET(message->flags, LI_NOWARNING); |
| 196 | VN_SET(p->flags, VN_RESERVED); | 187 | VN_SET(message->flags, VN_RESERVED); |
| 197 | MODE_SET(p->flags, MODE_CONTROLMSG); | 188 | MODE_SET(message->flags, MODE_CONTROLMSG); |
| 198 | OP_SET(p->op, opcode); | 189 | OP_SET(message->op, opcode); |
| 199 | p->seq = htons(seq); | 190 | message->seq = htons(seq); |
| 200 | /* Remaining fields are zero for requests */ | 191 | /* Remaining fields are zero for requests */ |
| 201 | } | 192 | } |
| 202 | 193 | ||
| @@ -208,13 +199,28 @@ void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) | |||
| 208 | * positive value means a success retrieving the value. | 199 | * positive value means a success retrieving the value. |
| 209 | * - status is set to WARNING if there's no sync.peer (otherwise OK) and is | 200 | * - status is set to WARNING if there's no sync.peer (otherwise OK) and is |
| 210 | * the return value of the function. | 201 | * the return value of the function. |
| 211 | * status is pretty much useless as syncsource_found is a global variable | 202 | */ |
| 212 | * used later in main to check is the server was synchronized. It works | 203 | typedef struct { |
| 213 | * so I left it alone */ | 204 | mp_state_enum state; |
| 214 | int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum, int *num_truechimers) { | 205 | mp_state_enum offset_result; |
| 215 | *offset_result = STATE_UNKNOWN; | 206 | double offset; |
| 216 | *jitter = *stratum = -1; | 207 | double jitter; |
| 217 | *num_truechimers = 0; | 208 | long stratum; |
| 209 | int num_truechimers; | ||
| 210 | bool syncsource_found; | ||
| 211 | bool li_alarm; | ||
| 212 | } ntp_request_result; | ||
| 213 | ntp_request_result ntp_request(const check_ntp_peer_config config) { | ||
| 214 | |||
| 215 | ntp_request_result result = { | ||
| 216 | .state = STATE_OK, | ||
| 217 | .offset_result = STATE_UNKNOWN, | ||
| 218 | .jitter = -1, | ||
| 219 | .stratum = -1, | ||
| 220 | .num_truechimers = 0, | ||
| 221 | .syncsource_found = false, | ||
| 222 | .li_alarm = false, | ||
| 223 | }; | ||
| 218 | 224 | ||
| 219 | /* Long-winded explanation: | 225 | /* Long-winded explanation: |
| 220 | * Getting the sync peer offset, jitter and stratum requires a number of | 226 | * Getting the sync peer offset, jitter and stratum requires a number of |
| @@ -232,19 +238,16 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum | |||
| 232 | * 4) Extract the offset, jitter and stratum value from the data[] | 238 | * 4) Extract the offset, jitter and stratum value from the data[] |
| 233 | * (it's ASCII) | 239 | * (it's ASCII) |
| 234 | */ | 240 | */ |
| 235 | int min_peer_sel = PEER_INCLUDED; | ||
| 236 | int num_candidates = 0; | ||
| 237 | void *tmp; | ||
| 238 | ntp_assoc_status_pair *peers = NULL; | ||
| 239 | int peer_offset = 0; | ||
| 240 | int peers_size = 0; | ||
| 241 | int npeers = 0; | ||
| 242 | int conn = -1; | 241 | int conn = -1; |
| 243 | my_udp_connect(server_address, port, &conn); | 242 | my_udp_connect(config.server_address, config.port, &conn); |
| 244 | 243 | ||
| 245 | /* keep sending requests until the server stops setting the | 244 | /* keep sending requests until the server stops setting the |
| 246 | * REM_MORE bit, though usually this is only 1 packet. */ | 245 | * REM_MORE bit, though usually this is only 1 packet. */ |
| 247 | ntp_control_message req; | 246 | ntp_control_message req; |
| 247 | ntp_assoc_status_pair *peers = NULL; | ||
| 248 | int peer_offset = 0; | ||
| 249 | size_t peers_size = 0; | ||
| 250 | size_t npeers = 0; | ||
| 248 | do { | 251 | do { |
| 249 | setup_control_request(&req, OP_READSTAT, 1); | 252 | setup_control_request(&req, OP_READSTAT, 1); |
| 250 | DBG(printf("sending READSTAT request")); | 253 | DBG(printf("sending READSTAT request")); |
| @@ -255,24 +258,29 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum | |||
| 255 | /* Attempt to read the largest size packet possible */ | 258 | /* Attempt to read the largest size packet possible */ |
| 256 | req.count = htons(MAX_CM_SIZE); | 259 | req.count = htons(MAX_CM_SIZE); |
| 257 | DBG(printf("receiving READSTAT response")) | 260 | DBG(printf("receiving READSTAT response")) |
| 258 | if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) | 261 | if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) { |
| 259 | die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); | 262 | die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); |
| 263 | } | ||
| 260 | DBG(print_ntp_control_message(&req)); | 264 | DBG(print_ntp_control_message(&req)); |
| 261 | /* discard obviously invalid packets */ | 265 | /* discard obviously invalid packets */ |
| 262 | if (ntohs(req.count) > MAX_CM_SIZE) | 266 | if (ntohs(req.count) > MAX_CM_SIZE) { |
| 263 | die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n"); | 267 | die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n"); |
| 268 | } | ||
| 264 | } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); | 269 | } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); |
| 265 | 270 | ||
| 266 | if (LI(req.flags) == LI_ALARM) | 271 | if (LI(req.flags) == LI_ALARM) { |
| 267 | li_alarm = true; | 272 | result.li_alarm = true; |
| 273 | } | ||
| 268 | /* Each peer identifier is 4 bytes in the data section, which | 274 | /* Each peer identifier is 4 bytes in the data section, which |
| 269 | * we represent as a ntp_assoc_status_pair datatype. | 275 | * we represent as a ntp_assoc_status_pair datatype. |
| 270 | */ | 276 | */ |
| 271 | peers_size += ntohs(req.count); | 277 | peers_size += ntohs(req.count); |
| 272 | if ((tmp = realloc(peers, peers_size)) == NULL) | 278 | void *tmp; |
| 279 | if ((tmp = realloc(peers, peers_size)) == NULL) { | ||
| 273 | free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); | 280 | free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); |
| 281 | } | ||
| 274 | peers = tmp; | 282 | peers = tmp; |
| 275 | memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count)); | 283 | memcpy((peers + peer_offset), (void *)req.data, ntohs(req.count)); |
| 276 | npeers = peers_size / sizeof(ntp_assoc_status_pair); | 284 | npeers = peers_size / sizeof(ntp_assoc_status_pair); |
| 277 | peer_offset += ntohs(req.count); | 285 | peer_offset += ntohs(req.count); |
| 278 | } while (req.op & REM_MORE); | 286 | } while (req.op & REM_MORE); |
| @@ -280,45 +288,51 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum | |||
| 280 | /* first, let's find out if we have a sync source, or if there are | 288 | /* first, let's find out if we have a sync source, or if there are |
| 281 | * at least some candidates. In the latter case we'll issue | 289 | * at least some candidates. In the latter case we'll issue |
| 282 | * a warning but go ahead with the check on them. */ | 290 | * a warning but go ahead with the check on them. */ |
| 283 | for (int i = 0; i < npeers; i++) { | 291 | int min_peer_sel = PEER_INCLUDED; |
| 292 | int num_candidates = 0; | ||
| 293 | for (size_t i = 0; i < npeers; i++) { | ||
| 284 | if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { | 294 | if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { |
| 285 | (*num_truechimers)++; | 295 | result.num_truechimers++; |
| 286 | if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { | 296 | if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { |
| 287 | num_candidates++; | 297 | num_candidates++; |
| 288 | if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { | 298 | if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { |
| 289 | syncsource_found = true; | 299 | result.syncsource_found = true; |
| 290 | min_peer_sel = PEER_SYNCSOURCE; | 300 | min_peer_sel = PEER_SYNCSOURCE; |
| 291 | } | 301 | } |
| 292 | } | 302 | } |
| 293 | } | 303 | } |
| 294 | } | 304 | } |
| 295 | 305 | ||
| 296 | if (verbose) | 306 | if (verbose) { |
| 297 | printf("%d candidate peers available\n", num_candidates); | 307 | printf("%d candidate peers available\n", num_candidates); |
| 298 | if (verbose && syncsource_found) | 308 | if (result.syncsource_found) { |
| 299 | printf("synchronization source found\n"); | 309 | printf("synchronization source found\n"); |
| 310 | } | ||
| 311 | } | ||
| 300 | 312 | ||
| 301 | int status = STATE_OK; | 313 | if (!result.syncsource_found) { |
| 302 | if (!syncsource_found) { | 314 | result.state = STATE_WARNING; |
| 303 | status = STATE_WARNING; | 315 | if (verbose) { |
| 304 | if (verbose) | ||
| 305 | printf("warning: no synchronization source found\n"); | 316 | printf("warning: no synchronization source found\n"); |
| 317 | } | ||
| 306 | } | 318 | } |
| 307 | if (li_alarm) { | 319 | if (result.li_alarm) { |
| 308 | status = STATE_WARNING; | 320 | result.state = STATE_WARNING; |
| 309 | if (verbose) | 321 | if (verbose) { |
| 310 | printf("warning: LI_ALARM bit is set\n"); | 322 | printf("warning: LI_ALARM bit is set\n"); |
| 323 | } | ||
| 311 | } | 324 | } |
| 312 | 325 | ||
| 313 | const char *getvar = "stratum,offset,jitter"; | 326 | const char *getvar = "stratum,offset,jitter"; |
| 314 | char *data; | 327 | char *data; |
| 315 | for (int i = 0; i < npeers; i++) { | 328 | for (size_t i = 0; i < npeers; i++) { |
| 316 | /* Only query this server if it is the current sync source */ | 329 | /* Only query this server if it is the current sync source */ |
| 317 | /* If there's no sync.peer, query all candidates and use the best one */ | 330 | /* If there's no sync.peer, query all candidates and use the best one */ |
| 318 | if (PEER_SEL(peers[i].status) >= min_peer_sel) { | 331 | if (PEER_SEL(peers[i].status) >= min_peer_sel) { |
| 319 | if (verbose) | 332 | if (verbose) { |
| 320 | printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); | 333 | printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); |
| 321 | xasprintf(&data, ""); | 334 | } |
| 335 | data = strdup(""); | ||
| 322 | do { | 336 | do { |
| 323 | setup_control_request(&req, OP_READVAR, 2); | 337 | setup_control_request(&req, OP_READVAR, 2); |
| 324 | req.assoc = peers[i].assoc; | 338 | req.assoc = peers[i].assoc; |
| @@ -342,81 +356,95 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum | |||
| 342 | DBG(print_ntp_control_message(&req)); | 356 | DBG(print_ntp_control_message(&req)); |
| 343 | } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2)); | 357 | } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2)); |
| 344 | 358 | ||
| 345 | if (!(req.op & REM_ERROR)) | 359 | if (!(req.op & REM_ERROR)) { |
| 346 | xasprintf(&data, "%s%s", data, req.data); | 360 | xasprintf(&data, "%s%s", data, req.data); |
| 361 | } | ||
| 347 | } while (req.op & REM_MORE); | 362 | } while (req.op & REM_MORE); |
| 348 | 363 | ||
| 349 | if (req.op & REM_ERROR) { | 364 | if (req.op & REM_ERROR) { |
| 350 | if (strstr(getvar, "jitter")) { | 365 | if (strstr(getvar, "jitter")) { |
| 351 | if (verbose) | 366 | if (verbose) { |
| 352 | printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with " | 367 | printf("The command failed. This is usually caused by servers refusing the " |
| 368 | "'jitter'\nvariable. Restarting with " | ||
| 353 | "'dispersion'...\n"); | 369 | "'dispersion'...\n"); |
| 370 | } | ||
| 354 | getvar = "stratum,offset,dispersion"; | 371 | getvar = "stratum,offset,dispersion"; |
| 355 | i--; | 372 | i--; |
| 356 | continue; | 373 | continue; |
| 357 | } | 374 | } |
| 358 | if (strlen(getvar)) { | 375 | if (strlen(getvar)) { |
| 359 | if (verbose) | 376 | if (verbose) { |
| 360 | printf("Server didn't like dispersion either; will retrieve everything\n"); | 377 | printf("Server didn't like dispersion either; will retrieve everything\n"); |
| 378 | } | ||
| 361 | getvar = ""; | 379 | getvar = ""; |
| 362 | i--; | 380 | i--; |
| 363 | continue; | 381 | continue; |
| 364 | } | 382 | } |
| 365 | } | 383 | } |
| 366 | 384 | ||
| 367 | if (verbose > 1) | 385 | if (verbose > 1) { |
| 368 | printf("Server responded: >>>%s<<<\n", data); | 386 | printf("Server responded: >>>%s<<<\n", data); |
| 387 | } | ||
| 369 | 388 | ||
| 370 | double tmp_offset = 0; | 389 | double tmp_offset = 0; |
| 371 | char *value; | 390 | char *value; |
| 372 | char *nptr; | 391 | char *nptr; |
| 373 | /* get the offset */ | 392 | /* get the offset */ |
| 374 | if (verbose) | 393 | if (verbose) { |
| 375 | printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc)); | 394 | printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc)); |
| 395 | } | ||
| 376 | 396 | ||
| 377 | value = np_extract_ntpvar(data, "offset"); | 397 | value = np_extract_ntpvar(data, "offset"); |
| 378 | nptr = NULL; | 398 | nptr = NULL; |
| 379 | /* Convert the value if we have one */ | 399 | /* Convert the value if we have one */ |
| 380 | if (value != NULL) | 400 | if (value != NULL) { |
| 381 | tmp_offset = strtod(value, &nptr) / 1000; | 401 | tmp_offset = strtod(value, &nptr) / 1000; |
| 402 | } | ||
| 382 | /* If value is null or no conversion was performed */ | 403 | /* If value is null or no conversion was performed */ |
| 383 | if (value == NULL || value == nptr) { | 404 | if (value == NULL || value == nptr) { |
| 384 | if (verbose) | 405 | if (verbose) { |
| 385 | printf("error: unable to read server offset response.\n"); | 406 | printf("error: unable to read server offset response.\n"); |
| 407 | } | ||
| 386 | } else { | 408 | } else { |
| 387 | if (verbose) | 409 | if (verbose) { |
| 388 | printf("%.10g\n", tmp_offset); | 410 | printf("%.10g\n", tmp_offset); |
| 389 | if (*offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(*offset)) { | 411 | } |
| 390 | *offset = tmp_offset; | 412 | if (result.offset_result == STATE_UNKNOWN || |
| 391 | *offset_result = STATE_OK; | 413 | fabs(tmp_offset) < fabs(result.offset)) { |
| 414 | result.offset = tmp_offset; | ||
| 415 | result.offset_result = STATE_OK; | ||
| 392 | } else { | 416 | } else { |
| 393 | /* Skip this one; move to the next */ | 417 | /* Skip this one; move to the next */ |
| 394 | continue; | 418 | continue; |
| 395 | } | 419 | } |
| 396 | } | 420 | } |
| 397 | 421 | ||
| 398 | if (do_jitter) { | 422 | if (config.do_jitter) { |
| 399 | /* get the jitter */ | 423 | /* get the jitter */ |
| 400 | if (verbose) { | 424 | if (verbose) { |
| 401 | printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", | 425 | printf("parsing %s from peer %.2x: ", |
| 426 | strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", | ||
| 402 | ntohs(peers[i].assoc)); | 427 | ntohs(peers[i].assoc)); |
| 403 | } | 428 | } |
| 404 | value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); | 429 | value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" |
| 430 | : "jitter"); | ||
| 405 | nptr = NULL; | 431 | nptr = NULL; |
| 406 | /* Convert the value if we have one */ | 432 | /* Convert the value if we have one */ |
| 407 | if (value != NULL) | 433 | if (value != NULL) { |
| 408 | *jitter = strtod(value, &nptr); | 434 | result.jitter = strtod(value, &nptr); |
| 435 | } | ||
| 409 | /* If value is null or no conversion was performed */ | 436 | /* If value is null or no conversion was performed */ |
| 410 | if (value == NULL || value == nptr) { | 437 | if (value == NULL || value == nptr) { |
| 411 | if (verbose) | 438 | if (verbose) { |
| 412 | printf("error: unable to read server jitter/dispersion response.\n"); | 439 | printf("error: unable to read server jitter/dispersion response.\n"); |
| 413 | *jitter = -1; | 440 | } |
| 441 | result.jitter = -1; | ||
| 414 | } else if (verbose) { | 442 | } else if (verbose) { |
| 415 | printf("%.10g\n", *jitter); | 443 | printf("%.10g\n", result.jitter); |
| 416 | } | 444 | } |
| 417 | } | 445 | } |
| 418 | 446 | ||
| 419 | if (do_stratum) { | 447 | if (config.do_stratum) { |
| 420 | /* get the stratum */ | 448 | /* get the stratum */ |
| 421 | if (verbose) { | 449 | if (verbose) { |
| 422 | printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc)); | 450 | printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc)); |
| @@ -424,46 +452,86 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum | |||
| 424 | value = np_extract_ntpvar(data, "stratum"); | 452 | value = np_extract_ntpvar(data, "stratum"); |
| 425 | nptr = NULL; | 453 | nptr = NULL; |
| 426 | /* Convert the value if we have one */ | 454 | /* Convert the value if we have one */ |
| 427 | if (value != NULL) | 455 | if (value != NULL) { |
| 428 | *stratum = strtol(value, &nptr, 10); | 456 | result.stratum = strtol(value, &nptr, 10); |
| 457 | } | ||
| 429 | if (value == NULL || value == nptr) { | 458 | if (value == NULL || value == nptr) { |
| 430 | if (verbose) | 459 | if (verbose) { |
| 431 | printf("error: unable to read server stratum response.\n"); | 460 | printf("error: unable to read server stratum response.\n"); |
| 432 | *stratum = -1; | 461 | } |
| 462 | result.stratum = -1; | ||
| 433 | } else { | 463 | } else { |
| 434 | if (verbose) | 464 | if (verbose) { |
| 435 | printf("%i\n", *stratum); | 465 | printf("%li\n", result.stratum); |
| 466 | } | ||
| 436 | } | 467 | } |
| 437 | } | 468 | } |
| 438 | } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */ | 469 | } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */ |
| 439 | } /* for (i = 0; i < npeers; i++) */ | 470 | } /* for (i = 0; i < npeers; i++) */ |
| 440 | 471 | ||
| 441 | close(conn); | 472 | close(conn); |
| 442 | if (peers != NULL) | 473 | if (peers != NULL) { |
| 443 | free(peers); | 474 | free(peers); |
| 475 | } | ||
| 444 | 476 | ||
| 445 | return status; | 477 | return result; |
| 446 | } | 478 | } |
| 447 | 479 | ||
| 448 | int process_arguments(int argc, char **argv) { | 480 | check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { |
| 449 | static struct option longopts[] = { | 481 | |
| 450 | {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, | 482 | enum { |
| 451 | {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, | 483 | output_format_index = CHAR_MAX + 1, |
| 452 | {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"swarn", required_argument, 0, 'W'}, | 484 | }; |
| 453 | {"scrit", required_argument, 0, 'C'}, {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, | 485 | |
| 454 | {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, {"timeout", required_argument, 0, 't'}, | 486 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, |
| 455 | {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; | 487 | {"help", no_argument, 0, 'h'}, |
| 456 | 488 | {"verbose", no_argument, 0, 'v'}, | |
| 457 | if (argc < 2) | 489 | {"use-ipv4", no_argument, 0, '4'}, |
| 490 | {"use-ipv6", no_argument, 0, '6'}, | ||
| 491 | {"quiet", no_argument, 0, 'q'}, | ||
| 492 | {"warning", required_argument, 0, 'w'}, | ||
| 493 | {"critical", required_argument, 0, 'c'}, | ||
| 494 | {"swarn", required_argument, 0, 'W'}, | ||
| 495 | {"scrit", required_argument, 0, 'C'}, | ||
| 496 | {"jwarn", required_argument, 0, 'j'}, | ||
| 497 | {"jcrit", required_argument, 0, 'k'}, | ||
| 498 | {"twarn", required_argument, 0, 'm'}, | ||
| 499 | {"tcrit", required_argument, 0, 'n'}, | ||
| 500 | {"timeout", required_argument, 0, 't'}, | ||
| 501 | {"hostname", required_argument, 0, 'H'}, | ||
| 502 | {"port", required_argument, 0, 'p'}, | ||
| 503 | {"output-format", required_argument, 0, output_format_index}, | ||
| 504 | {0, 0, 0, 0}}; | ||
| 505 | |||
| 506 | if (argc < 2) { | ||
| 458 | usage("\n"); | 507 | usage("\n"); |
| 508 | } | ||
| 509 | |||
| 510 | check_ntp_peer_config_wrapper result = { | ||
| 511 | .errorcode = OK, | ||
| 512 | .config = check_ntp_peer_config_init(), | ||
| 513 | }; | ||
| 459 | 514 | ||
| 460 | while (true) { | 515 | while (true) { |
| 461 | int option = 0; | 516 | int option = 0; |
| 462 | int option_char = getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); | 517 | int option_char = |
| 463 | if (option_char == -1 || option_char == EOF || option_char == 1) | 518 | getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); |
| 519 | if (option_char == -1 || option_char == EOF || option_char == 1) { | ||
| 464 | break; | 520 | break; |
| 521 | } | ||
| 465 | 522 | ||
| 466 | switch (option_char) { | 523 | switch (option_char) { |
| 524 | case output_format_index: { | ||
| 525 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 526 | if (!parser.parsing_success) { | ||
| 527 | printf("Invalid output format: %s\n", optarg); | ||
| 528 | exit(STATE_UNKNOWN); | ||
| 529 | } | ||
| 530 | |||
| 531 | result.config.output_format_is_set = true; | ||
| 532 | result.config.output_format = parser.output_format; | ||
| 533 | break; | ||
| 534 | } | ||
| 467 | case 'h': | 535 | case 'h': |
| 468 | print_help(); | 536 | print_help(); |
| 469 | exit(STATE_UNKNOWN); | 537 | exit(STATE_UNKNOWN); |
| @@ -476,45 +544,94 @@ int process_arguments(int argc, char **argv) { | |||
| 476 | verbose++; | 544 | verbose++; |
| 477 | break; | 545 | break; |
| 478 | case 'q': | 546 | case 'q': |
| 479 | quiet = true; | 547 | result.config.quiet = true; |
| 480 | break; | ||
| 481 | case 'w': | ||
| 482 | owarn = optarg; | ||
| 483 | break; | ||
| 484 | case 'c': | ||
| 485 | ocrit = optarg; | ||
| 486 | break; | ||
| 487 | case 'W': | ||
| 488 | do_stratum = true; | ||
| 489 | swarn = optarg; | ||
| 490 | break; | ||
| 491 | case 'C': | ||
| 492 | do_stratum = true; | ||
| 493 | scrit = optarg; | ||
| 494 | break; | ||
| 495 | case 'j': | ||
| 496 | do_jitter = true; | ||
| 497 | jwarn = optarg; | ||
| 498 | break; | ||
| 499 | case 'k': | ||
| 500 | do_jitter = true; | ||
| 501 | jcrit = optarg; | ||
| 502 | break; | ||
| 503 | case 'm': | ||
| 504 | do_truechimers = true; | ||
| 505 | twarn = optarg; | ||
| 506 | break; | ||
| 507 | case 'n': | ||
| 508 | do_truechimers = true; | ||
| 509 | tcrit = optarg; | ||
| 510 | break; | 548 | break; |
| 549 | case 'w': { | ||
| 550 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 551 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 552 | die(STATE_UNKNOWN, "failed to parse warning offset threshold"); | ||
| 553 | } | ||
| 554 | |||
| 555 | result.config.offset_thresholds = | ||
| 556 | mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range); | ||
| 557 | } break; | ||
| 558 | case 'c': { | ||
| 559 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 560 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 561 | die(STATE_UNKNOWN, "failed to parse critical offset threshold"); | ||
| 562 | } | ||
| 563 | |||
| 564 | result.config.offset_thresholds = | ||
| 565 | mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range); | ||
| 566 | } break; | ||
| 567 | case 'W': { | ||
| 568 | result.config.do_stratum = true; | ||
| 569 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 570 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 571 | die(STATE_UNKNOWN, "failed to parse warning stratum threshold"); | ||
| 572 | } | ||
| 573 | |||
| 574 | result.config.stratum_thresholds = | ||
| 575 | mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range); | ||
| 576 | } break; | ||
| 577 | case 'C': { | ||
| 578 | result.config.do_stratum = true; | ||
| 579 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 580 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 581 | die(STATE_UNKNOWN, "failed to parse critical stratum threshold"); | ||
| 582 | } | ||
| 583 | |||
| 584 | result.config.stratum_thresholds = | ||
| 585 | mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range); | ||
| 586 | } break; | ||
| 587 | case 'j': { | ||
| 588 | result.config.do_jitter = true; | ||
| 589 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 590 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 591 | die(STATE_UNKNOWN, "failed to parse warning jitter threshold"); | ||
| 592 | } | ||
| 593 | |||
| 594 | result.config.jitter_thresholds = | ||
| 595 | mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range); | ||
| 596 | } break; | ||
| 597 | case 'k': { | ||
| 598 | result.config.do_jitter = true; | ||
| 599 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 600 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 601 | die(STATE_UNKNOWN, "failed to parse critical jitter threshold"); | ||
| 602 | } | ||
| 603 | |||
| 604 | result.config.jitter_thresholds = | ||
| 605 | mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range); | ||
| 606 | } break; | ||
| 607 | case 'm': { | ||
| 608 | result.config.do_truechimers = true; | ||
| 609 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 610 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 611 | die(STATE_UNKNOWN, "failed to parse warning truechimer threshold"); | ||
| 612 | } | ||
| 613 | |||
| 614 | result.config.truechimer_thresholds = | ||
| 615 | mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range); | ||
| 616 | } break; | ||
| 617 | case 'n': { | ||
| 618 | result.config.do_truechimers = true; | ||
| 619 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 620 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 621 | die(STATE_UNKNOWN, "failed to parse critical truechimer threshold"); | ||
| 622 | } | ||
| 623 | |||
| 624 | result.config.truechimer_thresholds = | ||
| 625 | mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range); | ||
| 626 | } break; | ||
| 511 | case 'H': | 627 | case 'H': |
| 512 | if (!is_host(optarg)) | 628 | if (!is_host(optarg) && (optarg[0] != '/')) { |
| 513 | usage2(_("Invalid hostname/address"), optarg); | 629 | usage2(_("Invalid hostname/address"), optarg); |
| 514 | server_address = strdup(optarg); | 630 | } |
| 631 | result.config.server_address = strdup(optarg); | ||
| 515 | break; | 632 | break; |
| 516 | case 'p': | 633 | case 'p': |
| 517 | port = atoi(optarg); | 634 | result.config.port = atoi(optarg); |
| 518 | break; | 635 | break; |
| 519 | case 't': | 636 | case 't': |
| 520 | socket_timeout = atoi(optarg); | 637 | socket_timeout = atoi(optarg); |
| @@ -536,30 +653,32 @@ int process_arguments(int argc, char **argv) { | |||
| 536 | } | 653 | } |
| 537 | } | 654 | } |
| 538 | 655 | ||
| 539 | if (server_address == NULL) { | 656 | if (result.config.server_address == NULL) { |
| 540 | usage4(_("Hostname was not supplied")); | 657 | usage4(_("Hostname was not supplied")); |
| 541 | } | 658 | } |
| 542 | 659 | ||
| 543 | return 0; | 660 | return result; |
| 544 | } | 661 | } |
| 545 | 662 | ||
| 546 | char *perfd_offset(double offset) { | 663 | char *perfd_offset(double offset, thresholds *offset_thresholds) { |
| 547 | return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, | 664 | return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, |
| 548 | 0); | 665 | offset_thresholds->critical->end, false, 0, false, 0); |
| 549 | } | 666 | } |
| 550 | 667 | ||
| 551 | char *perfd_jitter(double jitter) { | 668 | char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) { |
| 552 | return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, jitter_thresholds->critical->end, true, 0, | 669 | return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, |
| 553 | false, 0); | 670 | jitter_thresholds->critical->end, true, 0, false, 0); |
| 554 | } | 671 | } |
| 555 | 672 | ||
| 556 | char *perfd_stratum(int stratum) { | 673 | char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) { |
| 557 | return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, do_stratum, | 674 | return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, |
| 558 | (int)stratum_thresholds->critical->end, true, 0, true, 16); | 675 | do_stratum, (int)stratum_thresholds->critical->end, true, 0, true, 16); |
| 559 | } | 676 | } |
| 560 | 677 | ||
| 561 | char *perfd_truechimers(int num_truechimers) { | 678 | char *perfd_truechimers(int num_truechimers, const bool do_truechimers, |
| 562 | return perfdata("truechimers", num_truechimers, "", do_truechimers, (int)truechimer_thresholds->warning->end, do_truechimers, | 679 | thresholds *truechimer_thresholds) { |
| 680 | return perfdata("truechimers", num_truechimers, "", do_truechimers, | ||
| 681 | (int)truechimer_thresholds->warning->end, do_truechimers, | ||
| 563 | (int)truechimer_thresholds->critical->end, true, 0, false, 0); | 682 | (int)truechimer_thresholds->critical->end, true, 0, false, 0); |
| 564 | } | 683 | } |
| 565 | 684 | ||
| @@ -571,13 +690,17 @@ int main(int argc, char *argv[]) { | |||
| 571 | /* Parse extra opts if any */ | 690 | /* Parse extra opts if any */ |
| 572 | argv = np_extra_opts(&argc, argv, progname); | 691 | argv = np_extra_opts(&argc, argv, progname); |
| 573 | 692 | ||
| 574 | if (process_arguments(argc, argv) == ERROR) | 693 | check_ntp_peer_config_wrapper tmp_config = process_arguments(argc, argv); |
| 694 | |||
| 695 | if (tmp_config.errorcode == ERROR) { | ||
| 575 | usage4(_("Could not parse arguments")); | 696 | usage4(_("Could not parse arguments")); |
| 697 | } | ||
| 698 | |||
| 699 | const check_ntp_peer_config config = tmp_config.config; | ||
| 576 | 700 | ||
| 577 | set_thresholds(&offset_thresholds, owarn, ocrit); | 701 | if (config.output_format_is_set) { |
| 578 | set_thresholds(&jitter_thresholds, jwarn, jcrit); | 702 | mp_set_format(config.output_format); |
| 579 | set_thresholds(&stratum_thresholds, swarn, scrit); | 703 | } |
| 580 | set_thresholds(&truechimer_thresholds, twarn, tcrit); | ||
| 581 | 704 | ||
| 582 | /* initialize alarm signal handling */ | 705 | /* initialize alarm signal handling */ |
| 583 | signal(SIGALRM, socket_timeout_alarm_handler); | 706 | signal(SIGALRM, socket_timeout_alarm_handler); |
| @@ -585,115 +708,114 @@ int main(int argc, char *argv[]) { | |||
| 585 | /* set socket timeout */ | 708 | /* set socket timeout */ |
| 586 | alarm(socket_timeout); | 709 | alarm(socket_timeout); |
| 587 | 710 | ||
| 588 | int offset_result; | ||
| 589 | int stratum; | ||
| 590 | int num_truechimers; | ||
| 591 | double offset = 0; | ||
| 592 | double jitter = 0; | ||
| 593 | /* This returns either OK or WARNING (See comment preceding ntp_request) */ | 711 | /* This returns either OK or WARNING (See comment preceding ntp_request) */ |
| 594 | int result = ntp_request(&offset, &offset_result, &jitter, &stratum, &num_truechimers); | 712 | const ntp_request_result ntp_res = ntp_request(config); |
| 713 | mp_check overall = mp_check_init(); | ||
| 595 | 714 | ||
| 596 | if (offset_result == STATE_UNKNOWN) { | 715 | mp_subcheck sc_offset = mp_subcheck_init(); |
| 716 | xasprintf(&sc_offset.output, "offset"); | ||
| 717 | if (ntp_res.offset_result == STATE_UNKNOWN) { | ||
| 597 | /* if there's no sync peer (this overrides ntp_request output): */ | 718 | /* if there's no sync peer (this overrides ntp_request output): */ |
| 598 | result = (quiet ? STATE_UNKNOWN : STATE_CRITICAL); | 719 | sc_offset = |
| 720 | mp_set_subcheck_state(sc_offset, (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL)); | ||
| 721 | xasprintf(&sc_offset.output, "%s unknown", sc_offset.output); | ||
| 599 | } else { | 722 | } else { |
| 600 | /* Be quiet if there's no candidates either */ | 723 | /* Be quiet if there's no candidates either */ |
| 601 | if (quiet && result == STATE_WARNING) | 724 | mp_state_enum tmp = STATE_OK; |
| 602 | result = STATE_UNKNOWN; | 725 | if (config.quiet && ntp_res.state == STATE_WARNING) { |
| 603 | result = max_state_alt(result, get_status(fabs(offset), offset_thresholds)); | 726 | tmp = STATE_UNKNOWN; |
| 604 | } | 727 | } |
| 605 | 728 | ||
| 606 | int oresult = result; | 729 | xasprintf(&sc_offset.output, "%s: %.6fs", sc_offset.output, ntp_res.offset); |
| 607 | 730 | ||
| 608 | int tresult = STATE_UNKNOWN; | 731 | mp_perfdata pd_offset = perfdata_init(); |
| 732 | pd_offset.value = mp_create_pd_value(fabs(ntp_res.offset)); | ||
| 733 | pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); | ||
| 734 | pd_offset.label = "offset"; | ||
| 735 | pd_offset.uom = "s"; | ||
| 736 | mp_add_perfdata_to_subcheck(&sc_offset, pd_offset); | ||
| 609 | 737 | ||
| 610 | if (do_truechimers) { | 738 | tmp = max_state_alt(tmp, mp_get_pd_status(pd_offset)); |
| 611 | tresult = get_status(num_truechimers, truechimer_thresholds); | 739 | sc_offset = mp_set_subcheck_state(sc_offset, tmp); |
| 612 | result = max_state_alt(result, tresult); | ||
| 613 | } | 740 | } |
| 614 | 741 | ||
| 615 | int sresult = STATE_UNKNOWN; | 742 | mp_add_subcheck_to_check(&overall, sc_offset); |
| 616 | 743 | ||
| 617 | if (do_stratum) { | 744 | // truechimers |
| 618 | sresult = get_status(stratum, stratum_thresholds); | 745 | if (config.do_truechimers) { |
| 619 | result = max_state_alt(result, sresult); | 746 | mp_subcheck sc_truechimers = mp_subcheck_init(); |
| 620 | } | 747 | xasprintf(&sc_truechimers.output, "truechimers: %i", ntp_res.num_truechimers); |
| 748 | |||
| 749 | mp_perfdata pd_truechimers = perfdata_init(); | ||
| 750 | pd_truechimers.value = mp_create_pd_value(ntp_res.num_truechimers); | ||
| 751 | pd_truechimers.label = "truechimers"; | ||
| 752 | pd_truechimers = mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds); | ||
| 753 | |||
| 754 | mp_add_perfdata_to_subcheck(&sc_truechimers, pd_truechimers); | ||
| 621 | 755 | ||
| 622 | int jresult = STATE_UNKNOWN; | 756 | sc_truechimers = mp_set_subcheck_state(sc_truechimers, mp_get_pd_status(pd_truechimers)); |
| 623 | 757 | ||
| 624 | if (do_jitter) { | 758 | mp_add_subcheck_to_check(&overall, sc_truechimers); |
| 625 | jresult = get_status(jitter, jitter_thresholds); | ||
| 626 | result = max_state_alt(result, jresult); | ||
| 627 | } | 759 | } |
| 628 | 760 | ||
| 629 | char *result_line; | 761 | if (config.do_stratum) { |
| 630 | switch (result) { | 762 | mp_subcheck sc_stratum = mp_subcheck_init(); |
| 631 | case STATE_CRITICAL: | 763 | xasprintf(&sc_stratum.output, "stratum: %li", ntp_res.stratum); |
| 632 | xasprintf(&result_line, _("NTP CRITICAL:")); | 764 | |
| 633 | break; | 765 | mp_perfdata pd_stratum = perfdata_init(); |
| 634 | case STATE_WARNING: | 766 | pd_stratum.value = mp_create_pd_value(ntp_res.stratum); |
| 635 | xasprintf(&result_line, _("NTP WARNING:")); | 767 | pd_stratum = mp_pd_set_thresholds(pd_stratum, config.stratum_thresholds); |
| 636 | break; | 768 | pd_stratum.label = "stratum"; |
| 637 | case STATE_OK: | 769 | |
| 638 | xasprintf(&result_line, _("NTP OK:")); | 770 | mp_add_perfdata_to_subcheck(&sc_stratum, pd_stratum); |
| 639 | break; | 771 | |
| 640 | default: | 772 | sc_stratum = mp_set_subcheck_state(sc_stratum, mp_get_pd_status(pd_stratum)); |
| 641 | xasprintf(&result_line, _("NTP UNKNOWN:")); | 773 | |
| 642 | break; | 774 | mp_add_subcheck_to_check(&overall, sc_stratum); |
| 643 | } | 775 | } |
| 644 | if (!syncsource_found) | 776 | |
| 645 | xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); | 777 | if (config.do_jitter) { |
| 646 | else if (li_alarm) | 778 | mp_subcheck sc_jitter = mp_subcheck_init(); |
| 647 | xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); | 779 | xasprintf(&sc_jitter.output, "jitter: %f", ntp_res.jitter); |
| 648 | 780 | ||
| 649 | char *perfdata_line; | 781 | mp_perfdata pd_jitter = perfdata_init(); |
| 650 | if (offset_result == STATE_UNKNOWN) { | 782 | pd_jitter.value = mp_create_pd_value(ntp_res.jitter); |
| 651 | xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); | 783 | pd_jitter = mp_pd_set_thresholds(pd_jitter, config.jitter_thresholds); |
| 652 | xasprintf(&perfdata_line, ""); | 784 | pd_jitter.label = "jitter"; |
| 653 | } else if (oresult == STATE_WARNING) { | 785 | |
| 654 | xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), offset); | 786 | mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter); |
| 655 | } else if (oresult == STATE_CRITICAL) { | 787 | |
| 656 | xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), offset); | 788 | sc_jitter = mp_set_subcheck_state(sc_jitter, mp_get_pd_status(pd_jitter)); |
| 657 | } else { | 789 | mp_add_subcheck_to_check(&overall, sc_jitter); |
| 658 | xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); | ||
| 659 | } | 790 | } |
| 660 | xasprintf(&perfdata_line, "%s", perfd_offset(offset)); | 791 | |
| 661 | 792 | mp_subcheck sc_other_info = mp_subcheck_init(); | |
| 662 | if (do_jitter) { | 793 | sc_other_info = mp_set_subcheck_default_state(sc_other_info, STATE_OK); |
| 663 | if (jresult == STATE_WARNING) { | 794 | if (!ntp_res.syncsource_found) { |
| 664 | xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, jitter); | 795 | xasprintf(&sc_other_info.output, "%s", _("Server not synchronized")); |
| 665 | } else if (jresult == STATE_CRITICAL) { | 796 | mp_add_subcheck_to_check(&overall, sc_other_info); |
| 666 | xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, jitter); | 797 | } else if (ntp_res.li_alarm) { |
| 667 | } else { | 798 | xasprintf(&sc_other_info.output, "%s", _("Server has the LI_ALARM bit set")); |
| 668 | xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); | 799 | mp_add_subcheck_to_check(&overall, sc_other_info); |
| 669 | } | ||
| 670 | xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); | ||
| 671 | } | 800 | } |
| 672 | if (do_stratum) { | 801 | |
| 673 | if (sresult == STATE_WARNING) { | 802 | { |
| 674 | xasprintf(&result_line, "%s, stratum=%i (WARNING)", result_line, stratum); | 803 | mp_subcheck sc_offset = mp_subcheck_init(); |
| 675 | } else if (sresult == STATE_CRITICAL) { | 804 | sc_offset = mp_set_subcheck_default_state(sc_offset, STATE_OK); |
| 676 | xasprintf(&result_line, "%s, stratum=%i (CRITICAL)", result_line, stratum); | 805 | xasprintf(&sc_offset.output, "offset: %.10gs", ntp_res.offset); |
| 677 | } else { | 806 | |
| 678 | xasprintf(&result_line, "%s, stratum=%i", result_line, stratum); | 807 | mp_perfdata pd_offset = perfdata_init(); |
| 679 | } | 808 | pd_offset.value = mp_create_pd_value(ntp_res.offset); |
| 680 | xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(stratum)); | 809 | pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); |
| 810 | |||
| 811 | sc_offset = mp_set_subcheck_state(sc_offset, ntp_res.offset_result); | ||
| 681 | } | 812 | } |
| 682 | if (do_truechimers) { | 813 | |
| 683 | if (tresult == STATE_WARNING) { | 814 | if (config.server_address != NULL) { |
| 684 | xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, num_truechimers); | 815 | free(config.server_address); |
| 685 | } else if (tresult == STATE_CRITICAL) { | ||
| 686 | xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, num_truechimers); | ||
| 687 | } else { | ||
| 688 | xasprintf(&result_line, "%s, truechimers=%i", result_line, num_truechimers); | ||
| 689 | } | ||
| 690 | xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_truechimers(num_truechimers)); | ||
| 691 | } | 816 | } |
| 692 | printf("%s|%s\n", result_line, perfdata_line); | ||
| 693 | 817 | ||
| 694 | if (server_address != NULL) | 818 | mp_exit(overall); |
| 695 | free(server_address); | ||
| 696 | return result; | ||
| 697 | } | 819 | } |
| 698 | 820 | ||
| 699 | void print_help(void) { | 821 | void print_help(void) { |
| @@ -712,7 +834,8 @@ void print_help(void) { | |||
| 712 | printf(UT_IPv46); | 834 | printf(UT_IPv46); |
| 713 | printf(UT_HOST_PORT, 'p', "123"); | 835 | printf(UT_HOST_PORT, 'p', "123"); |
| 714 | printf(" %s\n", "-q, --quiet"); | 836 | printf(" %s\n", "-q, --quiet"); |
| 715 | printf(" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); | 837 | printf(" %s\n", |
| 838 | _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); | ||
| 716 | printf(" %s\n", "-w, --warning=THRESHOLD"); | 839 | printf(" %s\n", "-w, --warning=THRESHOLD"); |
| 717 | printf(" %s\n", _("Offset to result in warning status (seconds)")); | 840 | printf(" %s\n", _("Offset to result in warning status (seconds)")); |
| 718 | printf(" %s\n", "-c, --critical=THRESHOLD"); | 841 | printf(" %s\n", "-c, --critical=THRESHOLD"); |
| @@ -731,6 +854,7 @@ void print_help(void) { | |||
| 731 | printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); | 854 | printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); |
| 732 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 855 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 733 | printf(UT_VERBOSE); | 856 | printf(UT_VERBOSE); |
| 857 | printf(UT_OUTPUT_FORMAT); | ||
| 734 | 858 | ||
| 735 | printf("\n"); | 859 | printf("\n"); |
| 736 | printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); | 860 | printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); |
| @@ -749,7 +873,8 @@ void print_help(void) { | |||
| 749 | printf(" %s\n", _("Simple NTP server check:")); | 873 | printf(" %s\n", _("Simple NTP server check:")); |
| 750 | printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1")); | 874 | printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1")); |
| 751 | printf("\n"); | 875 | printf("\n"); |
| 752 | printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); | 876 | printf(" %s\n", |
| 877 | _("Check jitter too, avoiding critical notifications if jitter isn't available")); | ||
| 753 | printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); | 878 | printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); |
| 754 | printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); | 879 | printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); |
| 755 | printf("\n"); | 880 | printf("\n"); |
diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h new file mode 100644 index 00000000..488d936c --- /dev/null +++ b/plugins/check_ntp_peer.d/config.h | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "perfdata.h" | ||
| 6 | #include "thresholds.h" | ||
| 7 | #include <stddef.h> | ||
| 8 | |||
| 9 | enum { | ||
| 10 | DEFAULT_NTP_PORT = 123, | ||
| 11 | }; | ||
| 12 | |||
| 13 | typedef struct { | ||
| 14 | char *server_address; | ||
| 15 | int port; | ||
| 16 | |||
| 17 | bool quiet; | ||
| 18 | |||
| 19 | // truechimer stuff | ||
| 20 | bool do_truechimers; | ||
| 21 | mp_thresholds truechimer_thresholds; | ||
| 22 | |||
| 23 | // offset thresholds | ||
| 24 | mp_thresholds offset_thresholds; | ||
| 25 | |||
| 26 | // stratum stuff | ||
| 27 | bool do_stratum; | ||
| 28 | mp_thresholds stratum_thresholds; | ||
| 29 | |||
| 30 | // jitter stuff | ||
| 31 | bool do_jitter; | ||
| 32 | mp_thresholds jitter_thresholds; | ||
| 33 | |||
| 34 | bool output_format_is_set; | ||
| 35 | mp_output_format output_format; | ||
| 36 | } check_ntp_peer_config; | ||
| 37 | |||
| 38 | check_ntp_peer_config check_ntp_peer_config_init() { | ||
| 39 | check_ntp_peer_config tmp = { | ||
| 40 | .server_address = NULL, | ||
| 41 | .port = DEFAULT_NTP_PORT, | ||
| 42 | |||
| 43 | .quiet = false, | ||
| 44 | .do_truechimers = false, | ||
| 45 | .truechimer_thresholds = mp_thresholds_init(), | ||
| 46 | |||
| 47 | .offset_thresholds = mp_thresholds_init(), | ||
| 48 | |||
| 49 | .do_stratum = false, | ||
| 50 | .stratum_thresholds = mp_thresholds_init(), | ||
| 51 | |||
| 52 | .do_jitter = false, | ||
| 53 | .jitter_thresholds = mp_thresholds_init(), | ||
| 54 | |||
| 55 | .output_format_is_set = false, | ||
| 56 | }; | ||
| 57 | |||
| 58 | mp_range stratum_default = mp_range_init(); | ||
| 59 | stratum_default = mp_range_set_start(stratum_default, mp_create_pd_value(-1)); | ||
| 60 | stratum_default = mp_range_set_end(stratum_default, mp_create_pd_value(16)); | ||
| 61 | tmp.stratum_thresholds = mp_thresholds_set_warn(tmp.stratum_thresholds, stratum_default); | ||
| 62 | tmp.stratum_thresholds = mp_thresholds_set_crit(tmp.stratum_thresholds, stratum_default); | ||
| 63 | |||
| 64 | mp_range jitter_w_default = mp_range_init(); | ||
| 65 | jitter_w_default = mp_range_set_start(jitter_w_default, mp_create_pd_value(-1)); | ||
| 66 | jitter_w_default = mp_range_set_end(jitter_w_default, mp_create_pd_value(5000)); | ||
| 67 | tmp.jitter_thresholds = mp_thresholds_set_warn(tmp.jitter_thresholds, jitter_w_default); | ||
| 68 | |||
| 69 | mp_range jitter_c_default = mp_range_init(); | ||
| 70 | jitter_c_default = mp_range_set_start(jitter_c_default, mp_create_pd_value(-1)); | ||
| 71 | jitter_c_default = mp_range_set_end(jitter_c_default, mp_create_pd_value(10000)); | ||
| 72 | tmp.jitter_thresholds = mp_thresholds_set_crit(tmp.jitter_thresholds, jitter_c_default); | ||
| 73 | |||
| 74 | mp_range offset_w_default = mp_range_init(); | ||
| 75 | offset_w_default = mp_range_set_end(offset_w_default, mp_create_pd_value(60)); | ||
| 76 | tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, offset_w_default); | ||
| 77 | |||
| 78 | mp_range offset_c_default = mp_range_init(); | ||
| 79 | offset_c_default = mp_range_set_end(offset_c_default, mp_create_pd_value(120)); | ||
| 80 | tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, offset_c_default); | ||
| 81 | return tmp; | ||
| 82 | } | ||
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index 703b69df..1300faea 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c | |||
| @@ -34,24 +34,29 @@ | |||
| 34 | * | 34 | * |
| 35 | *****************************************************************************/ | 35 | *****************************************************************************/ |
| 36 | 36 | ||
| 37 | const char *progname = "check_ntp_time"; | 37 | #include "output.h" |
| 38 | const char *copyright = "2006-2024"; | ||
| 39 | const char *email = "devel@monitoring-plugins.org"; | ||
| 40 | |||
| 41 | #include "common.h" | 38 | #include "common.h" |
| 42 | #include "netutils.h" | 39 | #include "netutils.h" |
| 40 | #include "perfdata.h" | ||
| 43 | #include "utils.h" | 41 | #include "utils.h" |
| 42 | #include "states.h" | ||
| 43 | #include "thresholds.h" | ||
| 44 | #include "check_ntp_time.d/config.h" | ||
| 45 | #include <netinet/in.h> | ||
| 46 | #include <sys/socket.h> | ||
| 44 | 47 | ||
| 45 | static char *server_address = NULL; | ||
| 46 | static char *port = "123"; | ||
| 47 | static int verbose = 0; | 48 | static int verbose = 0; |
| 48 | static bool quiet = false; | ||
| 49 | static char *owarn = "60"; | ||
| 50 | static char *ocrit = "120"; | ||
| 51 | static int time_offset = 0; | ||
| 52 | 49 | ||
| 53 | static int process_arguments(int, char **); | 50 | const char *progname = "check_ntp_time"; |
| 54 | static thresholds *offset_thresholds = NULL; | 51 | const char *copyright = "2006-2024"; |
| 52 | const char *email = "devel@monitoring-plugins.org"; | ||
| 53 | |||
| 54 | typedef struct { | ||
| 55 | int errorcode; | ||
| 56 | check_ntp_time_config config; | ||
| 57 | } check_ntp_time_config_wrapper; | ||
| 58 | static check_ntp_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 59 | |||
| 55 | static void print_help(void); | 60 | static void print_help(void); |
| 56 | void print_usage(void); | 61 | void print_usage(void); |
| 57 | 62 | ||
| @@ -60,9 +65,6 @@ void print_usage(void); | |||
| 60 | # define AVG_NUM 4 | 65 | # define AVG_NUM 4 |
| 61 | #endif | 66 | #endif |
| 62 | 67 | ||
| 63 | /* max size of control message data */ | ||
| 64 | #define MAX_CM_SIZE 468 | ||
| 65 | |||
| 66 | /* this structure holds everything in an ntp request/response as per rfc1305 */ | 68 | /* this structure holds everything in an ntp request/response as per rfc1305 */ |
| 67 | typedef struct { | 69 | typedef struct { |
| 68 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ | 70 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ |
| @@ -92,9 +94,9 @@ typedef struct { | |||
| 92 | /* bits 1,2 are the leap indicator */ | 94 | /* bits 1,2 are the leap indicator */ |
| 93 | #define LI_MASK 0xc0 | 95 | #define LI_MASK 0xc0 |
| 94 | #define LI(x) ((x & LI_MASK) >> 6) | 96 | #define LI(x) ((x & LI_MASK) >> 6) |
| 95 | #define LI_SET(x, y) \ | 97 | #define LI_SET(x, y) \ |
| 96 | do { \ | 98 | do { \ |
| 97 | x |= ((y << 6) & LI_MASK); \ | 99 | x |= ((y << 6) & LI_MASK); \ |
| 98 | } while (0) | 100 | } while (0) |
| 99 | /* and these are the values of the leap indicator */ | 101 | /* and these are the values of the leap indicator */ |
| 100 | #define LI_NOWARNING 0x00 | 102 | #define LI_NOWARNING 0x00 |
| @@ -104,17 +106,17 @@ typedef struct { | |||
| 104 | /* bits 3,4,5 are the ntp version */ | 106 | /* bits 3,4,5 are the ntp version */ |
| 105 | #define VN_MASK 0x38 | 107 | #define VN_MASK 0x38 |
| 106 | #define VN(x) ((x & VN_MASK) >> 3) | 108 | #define VN(x) ((x & VN_MASK) >> 3) |
| 107 | #define VN_SET(x, y) \ | 109 | #define VN_SET(x, y) \ |
| 108 | do { \ | 110 | do { \ |
| 109 | x |= ((y << 3) & VN_MASK); \ | 111 | x |= ((y << 3) & VN_MASK); \ |
| 110 | } while (0) | 112 | } while (0) |
| 111 | #define VN_RESERVED 0x02 | 113 | #define VN_RESERVED 0x02 |
| 112 | /* bits 6,7,8 are the ntp mode */ | 114 | /* bits 6,7,8 are the ntp mode */ |
| 113 | #define MODE_MASK 0x07 | 115 | #define MODE_MASK 0x07 |
| 114 | #define MODE(x) (x & MODE_MASK) | 116 | #define MODE(x) (x & MODE_MASK) |
| 115 | #define MODE_SET(x, y) \ | 117 | #define MODE_SET(x, y) \ |
| 116 | do { \ | 118 | do { \ |
| 117 | x |= (y & MODE_MASK); \ | 119 | x |= (y & MODE_MASK); \ |
| 118 | } while (0) | 120 | } while (0) |
| 119 | /* here are some values */ | 121 | /* here are some values */ |
| 120 | #define MODE_CLIENT 0x03 | 122 | #define MODE_CLIENT 0x03 |
| @@ -126,9 +128,9 @@ typedef struct { | |||
| 126 | #define REM_MORE 0x20 | 128 | #define REM_MORE 0x20 |
| 127 | /* In control message, bits 11 - 15 are opcode */ | 129 | /* In control message, bits 11 - 15 are opcode */ |
| 128 | #define OP_MASK 0x1f | 130 | #define OP_MASK 0x1f |
| 129 | #define OP_SET(x, y) \ | 131 | #define OP_SET(x, y) \ |
| 130 | do { \ | 132 | do { \ |
| 131 | x |= (y & OP_MASK); \ | 133 | x |= (y & OP_MASK); \ |
| 132 | } while (0) | 134 | } while (0) |
| 133 | #define OP_READSTAT 0x01 | 135 | #define OP_READSTAT 0x01 |
| 134 | #define OP_READVAR 0x02 | 136 | #define OP_READVAR 0x02 |
| @@ -159,35 +161,39 @@ typedef struct { | |||
| 159 | #define EPOCHDIFF 0x83aa7e80UL | 161 | #define EPOCHDIFF 0x83aa7e80UL |
| 160 | 162 | ||
| 161 | /* extract a 32-bit ntp fixed point number into a double */ | 163 | /* extract a 32-bit ntp fixed point number into a double */ |
| 162 | #define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0) | 164 | #define NTP32asDOUBLE(x) (ntohs(L16(x)) + ((double)ntohs(R16(x)) / 65536.0)) |
| 163 | 165 | ||
| 164 | /* likewise for a 64-bit ntp fp number */ | 166 | /* likewise for a 64-bit ntp fp number */ |
| 165 | #define NTP64asDOUBLE(n) \ | 167 | #define NTP64asDOUBLE(n) \ |
| 166 | (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) : 0) | 168 | (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \ |
| 169 | (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \ | ||
| 170 | : 0) | ||
| 167 | 171 | ||
| 168 | /* convert a struct timeval to a double */ | 172 | /* convert a struct timeval to a double */ |
| 169 | #define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) | 173 | static double TVasDOUBLE(struct timeval time) { |
| 174 | return ((double)time.tv_sec + (0.000001 * (double)time.tv_usec)); | ||
| 175 | } | ||
| 170 | 176 | ||
| 171 | /* convert an ntp 64-bit fp number to a struct timeval */ | 177 | /* convert an ntp 64-bit fp number to a struct timeval */ |
| 172 | #define NTP64toTV(n, t) \ | 178 | #define NTP64toTV(n, t) \ |
| 173 | do { \ | 179 | do { \ |
| 174 | if (!n) \ | 180 | if (!n) \ |
| 175 | t.tv_sec = t.tv_usec = 0; \ | 181 | t.tv_sec = t.tv_usec = 0; \ |
| 176 | else { \ | 182 | else { \ |
| 177 | t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ | 183 | t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ |
| 178 | t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ | 184 | t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ |
| 179 | } \ | 185 | } \ |
| 180 | } while (0) | 186 | } while (0) |
| 181 | 187 | ||
| 182 | /* convert a struct timeval to an ntp 64-bit fp number */ | 188 | /* convert a struct timeval to an ntp 64-bit fp number */ |
| 183 | #define TVtoNTP64(t, n) \ | 189 | #define TVtoNTP64(t, n) \ |
| 184 | do { \ | 190 | do { \ |
| 185 | if (!t.tv_usec && !t.tv_sec) \ | 191 | if (!t.tv_usec && !t.tv_sec) \ |
| 186 | n = 0x0UL; \ | 192 | n = 0x0UL; \ |
| 187 | else { \ | 193 | else { \ |
| 188 | L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ | 194 | L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ |
| 189 | R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ | 195 | R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ |
| 190 | } \ | 196 | } \ |
| 191 | } while (0) | 197 | } while (0) |
| 192 | 198 | ||
| 193 | /* NTP control message header is 12 bytes, plus any data in the data | 199 | /* NTP control message header is 12 bytes, plus any data in the data |
| @@ -196,75 +202,71 @@ typedef struct { | |||
| 196 | #define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0)) | 202 | #define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0)) |
| 197 | 203 | ||
| 198 | /* finally, a little helper or two for debugging: */ | 204 | /* finally, a little helper or two for debugging: */ |
| 199 | #define DBG(x) \ | 205 | #define DBG(x) \ |
| 200 | do { \ | 206 | do { \ |
| 201 | if (verbose > 1) { \ | 207 | if (verbose > 1) { \ |
| 202 | x; \ | 208 | x; \ |
| 203 | } \ | 209 | } \ |
| 204 | } while (0); | 210 | } while (0); |
| 205 | #define PRINTSOCKADDR(x) \ | 211 | #define PRINTSOCKADDR(x) \ |
| 206 | do { \ | 212 | do { \ |
| 207 | printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ | 213 | printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ |
| 208 | } while (0); | 214 | } while (0); |
| 209 | 215 | ||
| 210 | /* calculate the offset of the local clock */ | 216 | /* calculate the offset of the local clock */ |
| 211 | static inline double calc_offset(const ntp_message *m, const struct timeval *t) { | 217 | static inline double calc_offset(const ntp_message *message, const struct timeval *time_value) { |
| 212 | double client_tx = NTP64asDOUBLE(m->origts); | 218 | double client_tx = NTP64asDOUBLE(message->origts); |
| 213 | double peer_rx = NTP64asDOUBLE(m->rxts); | 219 | double peer_rx = NTP64asDOUBLE(message->rxts); |
| 214 | double peer_tx = NTP64asDOUBLE(m->txts); | 220 | double peer_tx = NTP64asDOUBLE(message->txts); |
| 215 | double client_rx = TVasDOUBLE((*t)); | 221 | double client_rx = TVasDOUBLE((*time_value)); |
| 216 | return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx))); | 222 | return (((peer_tx - client_rx) + (peer_rx - client_tx)) / 2); |
| 217 | } | 223 | } |
| 218 | 224 | ||
| 219 | /* print out a ntp packet in human readable/debuggable format */ | 225 | /* print out a ntp packet in human readable/debuggable format */ |
| 220 | void print_ntp_message(const ntp_message *p) { | 226 | void print_ntp_message(const ntp_message *message) { |
| 221 | struct timeval ref; | 227 | struct timeval ref; |
| 222 | struct timeval orig; | 228 | struct timeval orig; |
| 223 | struct timeval rx; | ||
| 224 | struct timeval tx; | ||
| 225 | 229 | ||
| 226 | NTP64toTV(p->refts, ref); | 230 | NTP64toTV(message->refts, ref); |
| 227 | NTP64toTV(p->origts, orig); | 231 | NTP64toTV(message->origts, orig); |
| 228 | NTP64toTV(p->rxts, rx); | ||
| 229 | NTP64toTV(p->txts, tx); | ||
| 230 | 232 | ||
| 231 | printf("packet contents:\n"); | 233 | printf("packet contents:\n"); |
| 232 | printf("\tflags: 0x%.2x\n", p->flags); | 234 | printf("\tflags: 0x%.2x\n", message->flags); |
| 233 | printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); | 235 | printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK); |
| 234 | printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); | 236 | printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK); |
| 235 | printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); | 237 | printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK); |
| 236 | printf("\tstratum = %d\n", p->stratum); | 238 | printf("\tstratum = %d\n", message->stratum); |
| 237 | printf("\tpoll = %g\n", pow(2, p->poll)); | 239 | printf("\tpoll = %g\n", pow(2, message->poll)); |
| 238 | printf("\tprecision = %g\n", pow(2, p->precision)); | 240 | printf("\tprecision = %g\n", pow(2, message->precision)); |
| 239 | printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay)); | 241 | printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(message->rtdelay)); |
| 240 | printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp)); | 242 | printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(message->rtdisp)); |
| 241 | printf("\trefid = %x\n", p->refid); | 243 | printf("\trefid = %x\n", message->refid); |
| 242 | printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts)); | 244 | printf("\trefts = %-.16g\n", NTP64asDOUBLE(message->refts)); |
| 243 | printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts)); | 245 | printf("\torigts = %-.16g\n", NTP64asDOUBLE(message->origts)); |
| 244 | printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts)); | 246 | printf("\trxts = %-.16g\n", NTP64asDOUBLE(message->rxts)); |
| 245 | printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); | 247 | printf("\ttxts = %-.16g\n", NTP64asDOUBLE(message->txts)); |
| 246 | } | 248 | } |
| 247 | 249 | ||
| 248 | void setup_request(ntp_message *p) { | 250 | void setup_request(ntp_message *message) { |
| 249 | memset(p, 0, sizeof(ntp_message)); | 251 | memset(message, 0, sizeof(ntp_message)); |
| 250 | LI_SET(p->flags, LI_ALARM); | 252 | LI_SET(message->flags, LI_ALARM); |
| 251 | VN_SET(p->flags, 4); | 253 | VN_SET(message->flags, 4); |
| 252 | MODE_SET(p->flags, MODE_CLIENT); | 254 | MODE_SET(message->flags, MODE_CLIENT); |
| 253 | p->poll = 4; | 255 | message->poll = 4; |
| 254 | p->precision = (int8_t)0xfa; | 256 | message->precision = (int8_t)0xfa; |
| 255 | L16(p->rtdelay) = htons(1); | 257 | L16(message->rtdelay) = htons(1); |
| 256 | L16(p->rtdisp) = htons(1); | 258 | L16(message->rtdisp) = htons(1); |
| 257 | 259 | ||
| 258 | struct timeval t; | 260 | struct timeval t; |
| 259 | gettimeofday(&t, NULL); | 261 | gettimeofday(&t, NULL); |
| 260 | TVtoNTP64(t, p->txts); | 262 | TVtoNTP64(t, message->txts); |
| 261 | } | 263 | } |
| 262 | 264 | ||
| 263 | /* select the "best" server from a list of servers, and return its index. | 265 | /* select the "best" server from a list of servers, and return its index. |
| 264 | * this is done by filtering servers based on stratum, dispersion, and | 266 | * this is done by filtering servers based on stratum, dispersion, and |
| 265 | * finally round-trip delay. */ | 267 | * finally round-trip delay. */ |
| 266 | int best_offset_server(const ntp_server_results *slist, int nservers) { | 268 | static int best_offset_server(const ntp_server_results *slist, int nservers) { |
| 267 | int best_server = -1; | 269 | int best_server_index = -1; |
| 268 | 270 | ||
| 269 | /* for each server */ | 271 | /* for each server */ |
| 270 | for (int cserver = 0; cserver < nservers; cserver++) { | 272 | for (int cserver = 0; cserver < nservers; cserver++) { |
| @@ -273,45 +275,47 @@ int best_offset_server(const ntp_server_results *slist, int nservers) { | |||
| 273 | * stratum 0 is for reference clocks so no NTP server should ever report | 275 | * stratum 0 is for reference clocks so no NTP server should ever report |
| 274 | * a stratum 0 */ | 276 | * a stratum 0 */ |
| 275 | if (slist[cserver].stratum == 0) { | 277 | if (slist[cserver].stratum == 0) { |
| 276 | if (verbose) | 278 | if (verbose) { |
| 277 | printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); | 279 | printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); |
| 280 | } | ||
| 278 | continue; | 281 | continue; |
| 279 | } | 282 | } |
| 280 | /* Sort out servers with error flags */ | 283 | /* Sort out servers with error flags */ |
| 281 | if (LI(slist[cserver].flags) == LI_ALARM) { | 284 | if (LI(slist[cserver].flags) == LI_ALARM) { |
| 282 | if (verbose) | 285 | if (verbose) { |
| 283 | printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); | 286 | printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); |
| 287 | } | ||
| 284 | continue; | 288 | continue; |
| 285 | } | 289 | } |
| 286 | 290 | ||
| 287 | /* If we don't have a server yet, use the first one */ | 291 | /* If we don't have a server yet, use the first one */ |
| 288 | if (best_server == -1) { | 292 | if (best_server_index == -1) { |
| 289 | best_server = cserver; | 293 | best_server_index = cserver; |
| 290 | DBG(printf("using peer %d as our first candidate\n", best_server)); | 294 | DBG(printf("using peer %d as our first candidate\n", best_server_index)); |
| 291 | continue; | 295 | continue; |
| 292 | } | 296 | } |
| 293 | 297 | ||
| 294 | /* compare the server to the best one we've seen so far */ | 298 | /* compare the server to the best one we've seen so far */ |
| 295 | /* does it have an equal or better stratum? */ | 299 | /* does it have an equal or better stratum? */ |
| 296 | DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); | 300 | DBG(printf("comparing peer %d with peer %d\n", cserver, best_server_index)); |
| 297 | if (slist[cserver].stratum <= slist[best_server].stratum) { | 301 | if (slist[cserver].stratum <= slist[best_server_index].stratum) { |
| 298 | DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); | 302 | DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server_index)); |
| 299 | /* does it have an equal or better dispersion? */ | 303 | /* does it have an equal or better dispersion? */ |
| 300 | if (slist[cserver].rtdisp <= slist[best_server].rtdisp) { | 304 | if (slist[cserver].rtdisp <= slist[best_server_index].rtdisp) { |
| 301 | DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); | 305 | DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server_index)); |
| 302 | /* does it have a better rtdelay? */ | 306 | /* does it have a better rtdelay? */ |
| 303 | if (slist[cserver].rtdelay < slist[best_server].rtdelay) { | 307 | if (slist[cserver].rtdelay < slist[best_server_index].rtdelay) { |
| 304 | DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); | 308 | DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server_index)); |
| 305 | best_server = cserver; | 309 | best_server_index = cserver; |
| 306 | DBG(printf("peer %d is now our best candidate\n", best_server)); | 310 | DBG(printf("peer %d is now our best candidate\n", best_server_index)); |
| 307 | } | 311 | } |
| 308 | } | 312 | } |
| 309 | } | 313 | } |
| 310 | } | 314 | } |
| 311 | 315 | ||
| 312 | if (best_server >= 0) { | 316 | if (best_server_index >= 0) { |
| 313 | DBG(printf("best server selected: peer %d\n", best_server)); | 317 | DBG(printf("best server selected: peer %d\n", best_server_index)); |
| 314 | return best_server; | 318 | return best_server_index; |
| 315 | } | 319 | } |
| 316 | DBG(printf("no peers meeting synchronization criteria :(\n")); | 320 | DBG(printf("no peers meeting synchronization criteria :(\n")); |
| 317 | return -1; | 321 | return -1; |
| @@ -322,7 +326,11 @@ int best_offset_server(const ntp_server_results *slist, int nservers) { | |||
| 322 | * we don't waste time sitting around waiting for single packets. | 326 | * we don't waste time sitting around waiting for single packets. |
| 323 | * - we also "manually" handle resolving host names and connecting, because | 327 | * - we also "manually" handle resolving host names and connecting, because |
| 324 | * we have to do it in a way that our lazy macros don't handle currently :( */ | 328 | * we have to do it in a way that our lazy macros don't handle currently :( */ |
| 325 | double offset_request(const char *host, int *status) { | 329 | typedef struct { |
| 330 | mp_state_enum offset_result; | ||
| 331 | double offset; | ||
| 332 | } offset_request_wrapper; | ||
| 333 | static offset_request_wrapper offset_request(const char *host, const char *port, int time_offset) { | ||
| 326 | /* setup hints to only return results from getaddrinfo that we'd like */ | 334 | /* setup hints to only return results from getaddrinfo that we'd like */ |
| 327 | struct addrinfo hints; | 335 | struct addrinfo hints; |
| 328 | memset(&hints, 0, sizeof(struct addrinfo)); | 336 | memset(&hints, 0, sizeof(struct addrinfo)); |
| @@ -330,58 +338,98 @@ double offset_request(const char *host, int *status) { | |||
| 330 | hints.ai_protocol = IPPROTO_UDP; | 338 | hints.ai_protocol = IPPROTO_UDP; |
| 331 | hints.ai_socktype = SOCK_DGRAM; | 339 | hints.ai_socktype = SOCK_DGRAM; |
| 332 | 340 | ||
| 333 | /* fill in ai with the list of hosts resolved by the host name */ | 341 | bool is_socket; |
| 334 | struct addrinfo *ai = NULL; | 342 | struct addrinfo *addresses = NULL; |
| 335 | int ga_result = getaddrinfo(host, port, &hints, &ai); | 343 | size_t num_hosts = 0; |
| 336 | if (ga_result != 0) { | 344 | if (host[0] == '/') { |
| 337 | die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); | 345 | num_hosts = 1; |
| 338 | } | 346 | is_socket = true; |
| 347 | } else { | ||
| 348 | is_socket = false; | ||
| 349 | |||
| 350 | /* fill in ai with the list of hosts resolved by the host name */ | ||
| 351 | struct addrinfo *addresses = NULL; | ||
| 352 | int ga_result = getaddrinfo(host, port, &hints, &addresses); | ||
| 353 | if (ga_result != 0) { | ||
| 354 | die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); | ||
| 355 | } | ||
| 339 | 356 | ||
| 340 | /* count the number of returned hosts, and allocate stuff accordingly */ | 357 | /* count the number of returned hosts, and allocate stuff accordingly */ |
| 341 | int num_hosts = 0; | 358 | for (struct addrinfo *ai_tmp = addresses; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) { |
| 342 | for (struct addrinfo *ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) { | 359 | num_hosts++; |
| 343 | num_hosts++; | 360 | } |
| 344 | } | 361 | } |
| 345 | 362 | ||
| 346 | ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts); | 363 | ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts); |
| 347 | 364 | ||
| 348 | if (req == NULL) | 365 | if (req == NULL) { |
| 349 | die(STATE_UNKNOWN, "can not allocate ntp message array"); | 366 | die(STATE_UNKNOWN, "can not allocate ntp message array"); |
| 367 | } | ||
| 350 | int *socklist = (int *)malloc(sizeof(int) * num_hosts); | 368 | int *socklist = (int *)malloc(sizeof(int) * num_hosts); |
| 351 | 369 | ||
| 352 | if (socklist == NULL) | 370 | if (socklist == NULL) { |
| 353 | die(STATE_UNKNOWN, "can not allocate socket array"); | 371 | die(STATE_UNKNOWN, "can not allocate socket array"); |
| 372 | } | ||
| 354 | 373 | ||
| 355 | struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts); | 374 | struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts); |
| 356 | if (ufds == NULL) | 375 | if (ufds == NULL) { |
| 357 | die(STATE_UNKNOWN, "can not allocate socket array"); | 376 | die(STATE_UNKNOWN, "can not allocate socket array"); |
| 377 | } | ||
| 358 | 378 | ||
| 359 | ntp_server_results *servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); | 379 | ntp_server_results *servers = |
| 360 | if (servers == NULL) | 380 | (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); |
| 381 | if (servers == NULL) { | ||
| 361 | die(STATE_UNKNOWN, "can not allocate server array"); | 382 | die(STATE_UNKNOWN, "can not allocate server array"); |
| 383 | } | ||
| 362 | memset(servers, 0, sizeof(ntp_server_results) * num_hosts); | 384 | memset(servers, 0, sizeof(ntp_server_results) * num_hosts); |
| 363 | DBG(printf("Found %d peers to check\n", num_hosts)); | 385 | DBG(printf("Found %zu peers to check\n", num_hosts)); |
| 364 | 386 | ||
| 365 | /* setup each socket for writing, and the corresponding struct pollfd */ | 387 | /* setup each socket for writing, and the corresponding struct pollfd */ |
| 366 | struct addrinfo *ai_tmp = ai; | 388 | if (is_socket) { |
| 367 | for (int i = 0; ai_tmp; i++) { | 389 | socklist[0] = socket(AF_UNIX, SOCK_STREAM, 0); |
| 368 | socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); | 390 | if (socklist[0] == -1) { |
| 369 | if (socklist[i] == -1) { | 391 | DBG(printf("can't create socket: %s\n", strerror(errno))); |
| 370 | perror(NULL); | 392 | die(STATE_UNKNOWN, "can not create new socket\n"); |
| 371 | die(STATE_UNKNOWN, "can not create new socket"); | ||
| 372 | } | 393 | } |
| 373 | if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) { | 394 | |
| 395 | struct sockaddr_un unix_socket = { | ||
| 396 | .sun_family = AF_UNIX, | ||
| 397 | }; | ||
| 398 | |||
| 399 | strncpy(unix_socket.sun_path, host, strlen(host)); | ||
| 400 | |||
| 401 | if (connect(socklist[0], &unix_socket, sizeof(unix_socket))) { | ||
| 374 | /* don't die here, because it is enough if there is one server | 402 | /* don't die here, because it is enough if there is one server |
| 375 | answering in time. This also would break for dual ipv4/6 stacked | 403 | answering in time. This also would break for dual ipv4/6 stacked |
| 376 | ntp servers when the client only supports on of them. | 404 | ntp servers when the client only supports on of them. |
| 377 | */ | 405 | */ |
| 378 | DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); | 406 | DBG(printf("can't create socket connection on peer %i: %s\n", 0, strerror(errno))); |
| 379 | } else { | 407 | } else { |
| 380 | ufds[i].fd = socklist[i]; | 408 | ufds[0].fd = socklist[0]; |
| 381 | ufds[i].events = POLLIN; | 409 | ufds[0].events = POLLIN; |
| 382 | ufds[i].revents = 0; | 410 | ufds[0].revents = 0; |
| 411 | } | ||
| 412 | } else { | ||
| 413 | struct addrinfo *ai_tmp = addresses; | ||
| 414 | for (int i = 0; ai_tmp; i++) { | ||
| 415 | socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); | ||
| 416 | if (socklist[i] == -1) { | ||
| 417 | perror(NULL); | ||
| 418 | die(STATE_UNKNOWN, "can not create new socket"); | ||
| 419 | } | ||
| 420 | if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) { | ||
| 421 | /* don't die here, because it is enough if there is one server | ||
| 422 | answering in time. This also would break for dual ipv4/6 stacked | ||
| 423 | ntp servers when the client only supports on of them. | ||
| 424 | */ | ||
| 425 | DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); | ||
| 426 | } else { | ||
| 427 | ufds[i].fd = socklist[i]; | ||
| 428 | ufds[i].events = POLLIN; | ||
| 429 | ufds[i].revents = 0; | ||
| 430 | } | ||
| 431 | ai_tmp = ai_tmp->ai_next; | ||
| 383 | } | 432 | } |
| 384 | ai_tmp = ai_tmp->ai_next; | ||
| 385 | } | 433 | } |
| 386 | 434 | ||
| 387 | /* now do AVG_NUM checks to each host. We stop before timeout/2 seconds | 435 | /* now do AVG_NUM checks to each host. We stop before timeout/2 seconds |
| @@ -389,7 +437,7 @@ double offset_request(const char *host, int *status) { | |||
| 389 | time_t start_ts = 0; | 437 | time_t start_ts = 0; |
| 390 | time_t now_time = 0; | 438 | time_t now_time = 0; |
| 391 | now_time = start_ts = time(NULL); | 439 | now_time = start_ts = time(NULL); |
| 392 | int servers_completed = 0; | 440 | size_t servers_completed = 0; |
| 393 | bool one_read = false; | 441 | bool one_read = false; |
| 394 | while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) { | 442 | while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) { |
| 395 | /* loop through each server and find each one which hasn't | 443 | /* loop through each server and find each one which hasn't |
| @@ -398,12 +446,14 @@ double offset_request(const char *host, int *status) { | |||
| 398 | * and update the "waiting" timestamp with the current time. */ | 446 | * and update the "waiting" timestamp with the current time. */ |
| 399 | now_time = time(NULL); | 447 | now_time = time(NULL); |
| 400 | 448 | ||
| 401 | for (int i = 0; i < num_hosts; i++) { | 449 | for (size_t i = 0; i < num_hosts; i++) { |
| 402 | if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) { | 450 | if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) { |
| 403 | if (verbose && servers[i].waiting != 0) | 451 | if (verbose && servers[i].waiting != 0) { |
| 404 | printf("re-"); | 452 | printf("re-"); |
| 405 | if (verbose) | 453 | } |
| 406 | printf("sending request to peer %d\n", i); | 454 | if (verbose) { |
| 455 | printf("sending request to peer %zu\n", i); | ||
| 456 | } | ||
| 407 | setup_request(&req[i]); | 457 | setup_request(&req[i]); |
| 408 | write(socklist[i], &req[i], sizeof(ntp_message)); | 458 | write(socklist[i], &req[i], sizeof(ntp_message)); |
| 409 | servers[i].waiting = now_time; | 459 | servers[i].waiting = now_time; |
| @@ -419,10 +469,10 @@ double offset_request(const char *host, int *status) { | |||
| 419 | } | 469 | } |
| 420 | 470 | ||
| 421 | /* read from any sockets with pending data */ | 471 | /* read from any sockets with pending data */ |
| 422 | for (int i = 0; servers_readable && i < num_hosts; i++) { | 472 | for (size_t i = 0; servers_readable && i < num_hosts; i++) { |
| 423 | if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) { | 473 | if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) { |
| 424 | if (verbose) { | 474 | if (verbose) { |
| 425 | printf("response from peer %d: ", i); | 475 | printf("response from peer %zu: ", i); |
| 426 | } | 476 | } |
| 427 | 477 | ||
| 428 | read(ufds[i].fd, &req[i], sizeof(ntp_message)); | 478 | read(ufds[i].fd, &req[i], sizeof(ntp_message)); |
| @@ -442,23 +492,30 @@ double offset_request(const char *host, int *status) { | |||
| 442 | servers[i].flags = req[i].flags; | 492 | servers[i].flags = req[i].flags; |
| 443 | servers_readable--; | 493 | servers_readable--; |
| 444 | one_read = true; | 494 | one_read = true; |
| 445 | if (servers[i].num_responses == AVG_NUM) | 495 | if (servers[i].num_responses == AVG_NUM) { |
| 446 | servers_completed++; | 496 | servers_completed++; |
| 497 | } | ||
| 447 | } | 498 | } |
| 448 | } | 499 | } |
| 449 | /* lather, rinse, repeat. */ | 500 | /* lather, rinse, repeat. */ |
| 450 | } | 501 | } |
| 451 | 502 | ||
| 452 | if (one_read == false) { | 503 | if (!one_read) { |
| 453 | die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); | 504 | die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); |
| 454 | } | 505 | } |
| 455 | 506 | ||
| 507 | offset_request_wrapper result = { | ||
| 508 | .offset = 0, | ||
| 509 | .offset_result = STATE_UNKNOWN, | ||
| 510 | }; | ||
| 511 | |||
| 456 | /* now, pick the best server from the list */ | 512 | /* now, pick the best server from the list */ |
| 457 | double avg_offset = 0.; | 513 | double avg_offset = 0.; |
| 458 | int best_index = best_offset_server(servers, num_hosts); | 514 | int best_index = best_offset_server(servers, num_hosts); |
| 459 | if (best_index < 0) { | 515 | if (best_index < 0) { |
| 460 | *status = STATE_UNKNOWN; | 516 | result.offset_result = STATE_UNKNOWN; |
| 461 | } else { | 517 | } else { |
| 518 | result.offset_result = STATE_OK; | ||
| 462 | /* finally, calculate the average offset */ | 519 | /* finally, calculate the average offset */ |
| 463 | for (int i = 0; i < servers[best_index].num_responses; i++) { | 520 | for (int i = 0; i < servers[best_index].num_responses; i++) { |
| 464 | avg_offset += servers[best_index].offset[i]; | 521 | avg_offset += servers[best_index].offset[i]; |
| @@ -467,45 +524,72 @@ double offset_request(const char *host, int *status) { | |||
| 467 | } | 524 | } |
| 468 | 525 | ||
| 469 | /* cleanup */ | 526 | /* cleanup */ |
| 470 | for (int j = 0; j < num_hosts; j++) { | 527 | for (size_t j = 0; j < num_hosts; j++) { |
| 471 | close(socklist[j]); | 528 | close(socklist[j]); |
| 472 | } | 529 | } |
| 473 | free(socklist); | 530 | free(socklist); |
| 474 | free(ufds); | 531 | free(ufds); |
| 475 | free(servers); | 532 | free(servers); |
| 476 | free(req); | 533 | free(req); |
| 477 | freeaddrinfo(ai); | 534 | freeaddrinfo(addresses); |
| 478 | 535 | ||
| 479 | if (verbose) | 536 | if (verbose) { |
| 480 | printf("overall average offset: %.10g\n", avg_offset); | 537 | printf("overall average offset: %.10g\n", avg_offset); |
| 481 | return avg_offset; | 538 | } |
| 539 | |||
| 540 | result.offset = avg_offset; | ||
| 541 | return result; | ||
| 482 | } | 542 | } |
| 483 | 543 | ||
| 484 | int process_arguments(int argc, char **argv) { | 544 | static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { |
| 545 | |||
| 546 | enum { | ||
| 547 | output_format_index = CHAR_MAX + 1, | ||
| 548 | }; | ||
| 549 | |||
| 485 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, | 550 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, |
| 486 | {"help", no_argument, 0, 'h'}, | 551 | {"help", no_argument, 0, 'h'}, |
| 487 | {"verbose", no_argument, 0, 'v'}, | 552 | {"verbose", no_argument, 0, 'v'}, |
| 488 | {"use-ipv4", no_argument, 0, '4'}, | 553 | {"use-ipv4", no_argument, 0, '4'}, |
| 489 | {"use-ipv6", no_argument, 0, '6'}, | 554 | {"use-ipv6", no_argument, 0, '6'}, |
| 490 | {"quiet", no_argument, 0, 'q'}, | 555 | {"quiet", no_argument, 0, 'q'}, |
| 491 | {"time-offset", optional_argument, 0, 'o'}, | 556 | {"time-offset", required_argument, 0, 'o'}, |
| 492 | {"warning", required_argument, 0, 'w'}, | 557 | {"warning", required_argument, 0, 'w'}, |
| 493 | {"critical", required_argument, 0, 'c'}, | 558 | {"critical", required_argument, 0, 'c'}, |
| 494 | {"timeout", required_argument, 0, 't'}, | 559 | {"timeout", required_argument, 0, 't'}, |
| 495 | {"hostname", required_argument, 0, 'H'}, | 560 | {"hostname", required_argument, 0, 'H'}, |
| 496 | {"port", required_argument, 0, 'p'}, | 561 | {"port", required_argument, 0, 'p'}, |
| 562 | {"output-format", required_argument, 0, output_format_index}, | ||
| 497 | {0, 0, 0, 0}}; | 563 | {0, 0, 0, 0}}; |
| 498 | 564 | ||
| 499 | if (argc < 2) | 565 | if (argc < 2) { |
| 500 | usage("\n"); | 566 | usage("\n"); |
| 567 | } | ||
| 568 | |||
| 569 | check_ntp_time_config_wrapper result = { | ||
| 570 | .errorcode = OK, | ||
| 571 | .config = check_ntp_time_config_init(), | ||
| 572 | }; | ||
| 501 | 573 | ||
| 502 | while (true) { | 574 | while (true) { |
| 503 | int option = 0; | 575 | int option = 0; |
| 504 | int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); | 576 | int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); |
| 505 | if (option_char == -1 || option_char == EOF || option_char == 1) | 577 | if (option_char == -1 || option_char == EOF || option_char == 1) { |
| 506 | break; | 578 | break; |
| 579 | } | ||
| 507 | 580 | ||
| 508 | switch (option_char) { | 581 | switch (option_char) { |
| 582 | case output_format_index: { | ||
| 583 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 584 | if (!parser.parsing_success) { | ||
| 585 | printf("Invalid output format: %s\n", optarg); | ||
| 586 | exit(STATE_UNKNOWN); | ||
| 587 | } | ||
| 588 | |||
| 589 | result.config.output_format_is_set = true; | ||
| 590 | result.config.output_format = parser.output_format; | ||
| 591 | break; | ||
| 592 | } | ||
| 509 | case 'h': | 593 | case 'h': |
| 510 | print_help(); | 594 | print_help(); |
| 511 | exit(STATE_UNKNOWN); | 595 | exit(STATE_UNKNOWN); |
| @@ -518,27 +602,40 @@ int process_arguments(int argc, char **argv) { | |||
| 518 | verbose++; | 602 | verbose++; |
| 519 | break; | 603 | break; |
| 520 | case 'q': | 604 | case 'q': |
| 521 | quiet = true; | 605 | result.config.quiet = true; |
| 522 | break; | ||
| 523 | case 'w': | ||
| 524 | owarn = optarg; | ||
| 525 | break; | ||
| 526 | case 'c': | ||
| 527 | ocrit = optarg; | ||
| 528 | break; | 606 | break; |
| 607 | case 'w': { | ||
| 608 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 609 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 610 | die(STATE_UNKNOWN, "failed to parse warning threshold"); | ||
| 611 | } | ||
| 612 | |||
| 613 | result.config.offset_thresholds = | ||
| 614 | mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range); | ||
| 615 | } break; | ||
| 616 | case 'c': { | ||
| 617 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 618 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 619 | die(STATE_UNKNOWN, "failed to parse crit threshold"); | ||
| 620 | } | ||
| 621 | |||
| 622 | result.config.offset_thresholds = | ||
| 623 | mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range); | ||
| 624 | } break; | ||
| 529 | case 'H': | 625 | case 'H': |
| 530 | if (!is_host(optarg)) | 626 | if (!is_host(optarg) && (optarg[0] != '/')) { |
| 531 | usage2(_("Invalid hostname/address"), optarg); | 627 | usage2(_("Invalid hostname/address"), optarg); |
| 532 | server_address = strdup(optarg); | 628 | } |
| 629 | result.config.server_address = strdup(optarg); | ||
| 533 | break; | 630 | break; |
| 534 | case 'p': | 631 | case 'p': |
| 535 | port = strdup(optarg); | 632 | result.config.port = strdup(optarg); |
| 536 | break; | 633 | break; |
| 537 | case 't': | 634 | case 't': |
| 538 | socket_timeout = atoi(optarg); | 635 | socket_timeout = atoi(optarg); |
| 539 | break; | 636 | break; |
| 540 | case 'o': | 637 | case 'o': |
| 541 | time_offset = atoi(optarg); | 638 | result.config.time_offset = atoi(optarg); |
| 542 | break; | 639 | break; |
| 543 | case '4': | 640 | case '4': |
| 544 | address_family = AF_INET; | 641 | address_family = AF_INET; |
| @@ -557,16 +654,11 @@ int process_arguments(int argc, char **argv) { | |||
| 557 | } | 654 | } |
| 558 | } | 655 | } |
| 559 | 656 | ||
| 560 | if (server_address == NULL) { | 657 | if (result.config.server_address == NULL) { |
| 561 | usage4(_("Hostname was not supplied")); | 658 | usage4(_("Hostname was not supplied")); |
| 562 | } | 659 | } |
| 563 | 660 | ||
| 564 | return 0; | 661 | return result; |
| 565 | } | ||
| 566 | |||
| 567 | char *perfd_offset(double offset) { | ||
| 568 | return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, | ||
| 569 | 0); | ||
| 570 | } | 662 | } |
| 571 | 663 | ||
| 572 | int main(int argc, char *argv[]) { | 664 | int main(int argc, char *argv[]) { |
| @@ -577,10 +669,17 @@ int main(int argc, char *argv[]) { | |||
| 577 | /* Parse extra opts if any */ | 669 | /* Parse extra opts if any */ |
| 578 | argv = np_extra_opts(&argc, argv, progname); | 670 | argv = np_extra_opts(&argc, argv, progname); |
| 579 | 671 | ||
| 580 | if (process_arguments(argc, argv) == ERROR) | 672 | check_ntp_time_config_wrapper tmp_config = process_arguments(argc, argv); |
| 673 | |||
| 674 | if (tmp_config.errorcode == ERROR) { | ||
| 581 | usage4(_("Could not parse arguments")); | 675 | usage4(_("Could not parse arguments")); |
| 676 | } | ||
| 677 | |||
| 678 | const check_ntp_time_config config = tmp_config.config; | ||
| 582 | 679 | ||
| 583 | set_thresholds(&offset_thresholds, owarn, ocrit); | 680 | if (config.output_format_is_set) { |
| 681 | mp_set_format(config.output_format); | ||
| 682 | } | ||
| 584 | 683 | ||
| 585 | /* initialize alarm signal handling */ | 684 | /* initialize alarm signal handling */ |
| 586 | signal(SIGALRM, socket_timeout_alarm_handler); | 685 | signal(SIGALRM, socket_timeout_alarm_handler); |
| @@ -588,44 +687,37 @@ int main(int argc, char *argv[]) { | |||
| 588 | /* set socket timeout */ | 687 | /* set socket timeout */ |
| 589 | alarm(socket_timeout); | 688 | alarm(socket_timeout); |
| 590 | 689 | ||
| 591 | int offset_result = STATE_OK; | 690 | mp_check overall = mp_check_init(); |
| 592 | int result = STATE_OK; | ||
| 593 | double offset = offset_request(server_address, &offset_result); | ||
| 594 | if (offset_result == STATE_UNKNOWN) { | ||
| 595 | result = ((!quiet) ? STATE_UNKNOWN : STATE_CRITICAL); | ||
| 596 | } else { | ||
| 597 | result = get_status(fabs(offset), offset_thresholds); | ||
| 598 | } | ||
| 599 | 691 | ||
| 600 | char *result_line; | 692 | mp_subcheck sc_offset = mp_subcheck_init(); |
| 601 | switch (result) { | 693 | offset_request_wrapper offset_result = |
| 602 | case STATE_CRITICAL: | 694 | offset_request(config.server_address, config.port, config.time_offset); |
| 603 | xasprintf(&result_line, _("NTP CRITICAL:")); | ||
| 604 | break; | ||
| 605 | case STATE_WARNING: | ||
| 606 | xasprintf(&result_line, _("NTP WARNING:")); | ||
| 607 | break; | ||
| 608 | case STATE_OK: | ||
| 609 | xasprintf(&result_line, _("NTP OK:")); | ||
| 610 | break; | ||
| 611 | default: | ||
| 612 | xasprintf(&result_line, _("NTP UNKNOWN:")); | ||
| 613 | break; | ||
| 614 | } | ||
| 615 | 695 | ||
| 616 | char *perfdata_line; | 696 | if (offset_result.offset_result == STATE_UNKNOWN) { |
| 617 | if (offset_result == STATE_UNKNOWN) { | 697 | sc_offset = |
| 618 | xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); | 698 | mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL); |
| 619 | xasprintf(&perfdata_line, ""); | 699 | xasprintf(&sc_offset.output, "Offset unknown"); |
| 620 | } else { | 700 | mp_add_subcheck_to_check(&overall, sc_offset); |
| 621 | xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); | 701 | mp_exit(overall); |
| 622 | xasprintf(&perfdata_line, "%s", perfd_offset(offset)); | ||
| 623 | } | 702 | } |
| 624 | printf("%s|%s\n", result_line, perfdata_line); | ||
| 625 | 703 | ||
| 626 | if (server_address != NULL) | 704 | xasprintf(&sc_offset.output, "Offset: %.6fs", offset_result.offset); |
| 627 | free(server_address); | 705 | |
| 628 | return result; | 706 | mp_perfdata pd_offset = perfdata_init(); |
| 707 | pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset)); | ||
| 708 | pd_offset.label = "offset"; | ||
| 709 | pd_offset.uom = "s"; | ||
| 710 | pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); | ||
| 711 | |||
| 712 | sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset)); | ||
| 713 | |||
| 714 | mp_add_perfdata_to_subcheck(&sc_offset, pd_offset); | ||
| 715 | mp_add_subcheck_to_check(&overall, sc_offset); | ||
| 716 | |||
| 717 | if (config.server_address != NULL) { | ||
| 718 | free(config.server_address); | ||
| 719 | } | ||
| 720 | mp_exit(overall); | ||
| 629 | } | 721 | } |
| 630 | 722 | ||
| 631 | void print_help(void) { | 723 | void print_help(void) { |
| @@ -649,10 +741,11 @@ void print_help(void) { | |||
| 649 | printf(" %s\n", _("Offset to result in warning status (seconds)")); | 741 | printf(" %s\n", _("Offset to result in warning status (seconds)")); |
| 650 | printf(" %s\n", "-c, --critical=THRESHOLD"); | 742 | printf(" %s\n", "-c, --critical=THRESHOLD"); |
| 651 | printf(" %s\n", _("Offset to result in critical status (seconds)")); | 743 | printf(" %s\n", _("Offset to result in critical status (seconds)")); |
| 652 | printf(" %s\n", "-o, --time_offset=INTEGER"); | 744 | printf(" %s\n", "-o, --time-offset=INTEGER"); |
| 653 | printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); | 745 | printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); |
| 654 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 746 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 655 | printf(UT_VERBOSE); | 747 | printf(UT_VERBOSE); |
| 748 | printf(UT_OUTPUT_FORMAT); | ||
| 656 | 749 | ||
| 657 | printf("\n"); | 750 | printf("\n"); |
| 658 | printf("%s\n", _("This plugin checks the clock offset between the local host and a")); | 751 | printf("%s\n", _("This plugin checks the clock offset between the local host and a")); |
| @@ -677,5 +770,6 @@ void print_help(void) { | |||
| 677 | 770 | ||
| 678 | void print_usage(void) { | 771 | void print_usage(void) { |
| 679 | printf("%s\n", _("Usage:")); | 772 | printf("%s\n", _("Usage:")); |
| 680 | printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n", progname); | 773 | printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n", |
| 774 | progname); | ||
| 681 | } | 775 | } |
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h new file mode 100644 index 00000000..9bbd82aa --- /dev/null +++ b/plugins/check_ntp_time.d/config.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | |||
| 8 | typedef struct { | ||
| 9 | char *server_address; | ||
| 10 | char *port; | ||
| 11 | |||
| 12 | bool quiet; | ||
| 13 | int time_offset; | ||
| 14 | |||
| 15 | mp_thresholds offset_thresholds; | ||
| 16 | |||
| 17 | bool output_format_is_set; | ||
| 18 | mp_output_format output_format; | ||
| 19 | } check_ntp_time_config; | ||
| 20 | |||
| 21 | check_ntp_time_config check_ntp_time_config_init() { | ||
| 22 | check_ntp_time_config tmp = { | ||
| 23 | .server_address = NULL, | ||
| 24 | .port = "123", | ||
| 25 | |||
| 26 | .quiet = false, | ||
| 27 | .time_offset = 0, | ||
| 28 | |||
| 29 | .offset_thresholds = mp_thresholds_init(), | ||
| 30 | |||
| 31 | .output_format_is_set = false, | ||
| 32 | }; | ||
| 33 | |||
| 34 | mp_range warning = mp_range_init(); | ||
| 35 | warning = mp_range_set_end(warning, mp_create_pd_value(60)); | ||
| 36 | tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, warning); | ||
| 37 | |||
| 38 | mp_range critical = mp_range_init(); | ||
| 39 | critical = mp_range_set_end(warning, mp_create_pd_value(120)); | ||
| 40 | tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, critical); | ||
| 41 | |||
| 42 | return tmp; | ||
| 43 | } | ||
diff --git a/plugins/check_nwstat.c b/plugins/check_nwstat.c deleted file mode 100644 index 176dfbc8..00000000 --- a/plugins/check_nwstat.c +++ /dev/null | |||
| @@ -1,1527 +0,0 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * Monitoring check_nwstat plugin | ||
| 4 | * | ||
| 5 | * License: GPL | ||
| 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team | ||
| 7 | * | ||
| 8 | * Description: | ||
| 9 | * | ||
| 10 | * This file contains the check_nwstat plugin | ||
| 11 | * | ||
| 12 | * This plugin attempts to contact the MRTGEXT NLM running on a | ||
| 13 | * Novell server to gather the requested system information. | ||
| 14 | * | ||
| 15 | * | ||
| 16 | * This program is free software: you can redistribute it and/or modify | ||
| 17 | * it under the terms of the GNU General Public License as published by | ||
| 18 | * the Free Software Foundation, either version 3 of the License, or | ||
| 19 | * (at your option) any later version. | ||
| 20 | * | ||
| 21 | * This program is distributed in the hope that it will be useful, | ||
| 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 24 | * GNU General Public License for more details. | ||
| 25 | * | ||
| 26 | * You should have received a copy of the GNU General Public License | ||
| 27 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 28 | * | ||
| 29 | * | ||
| 30 | *****************************************************************************/ | ||
| 31 | |||
| 32 | const char *progname = "check_nwstat"; | ||
| 33 | const char *copyright = "2000-2024"; | ||
| 34 | const char *email = "devel@monitoring-plugins.org"; | ||
| 35 | |||
| 36 | #include "common.h" | ||
| 37 | #include "netutils.h" | ||
| 38 | #include "utils.h" | ||
| 39 | |||
| 40 | enum checkvar { | ||
| 41 | NONE, | ||
| 42 | LOAD1, /* check 1 minute CPU load */ | ||
| 43 | LOAD5, /* check 5 minute CPU load */ | ||
| 44 | LOAD15, /* check 15 minute CPU load */ | ||
| 45 | CONNS, /* check number of connections */ | ||
| 46 | VPF, /* check % free space on volume */ | ||
| 47 | VMF, /* check MB free space on volume */ | ||
| 48 | VMU, /* check MB used space on volume */ | ||
| 49 | VPU, /* check % used space on volume */ | ||
| 50 | VMP, /* check MB purgeable space on volume */ | ||
| 51 | VKF, /* check KB free space on volume */ | ||
| 52 | LTCH, /* check long-term cache hit percentage */ | ||
| 53 | CBUFF, /* check total cache buffers */ | ||
| 54 | CDBUFF, /* check dirty cache buffers */ | ||
| 55 | LRUM, /* check LRU sitting time in minutes */ | ||
| 56 | DSDB, /* check to see if DS Database is open */ | ||
| 57 | LOGINS, /* check to see if logins are enabled */ | ||
| 58 | NRMH, /* check to see NRM Health Status */ | ||
| 59 | PUPRB, /* check % of used packet receive buffers */ | ||
| 60 | UPRB, /* check used packet receive buffers */ | ||
| 61 | SAPENTRIES, /* check SAP entries */ | ||
| 62 | OFILES, /* check number of open files */ | ||
| 63 | VKP, /* check KB purgeable space on volume */ | ||
| 64 | VPP, /* check % purgeable space on volume */ | ||
| 65 | VKNP, /* check KB not yet purgeable space on volume */ | ||
| 66 | VPNP, /* check % not yet purgeable space on volume */ | ||
| 67 | ABENDS, /* check abended thread count */ | ||
| 68 | CSPROCS, /* check number of current service processes */ | ||
| 69 | TSYNC, /* check timesync status 0=no 1=yes in sync to the network */ | ||
| 70 | LRUS, /* check LRU sitting time in seconds */ | ||
| 71 | DCB, /* check dirty cache buffers as a percentage of the total */ | ||
| 72 | TCB, /* check total cache buffers as a percentage of the original */ | ||
| 73 | DSVER, /* check NDS version */ | ||
| 74 | UPTIME, /* check server uptime */ | ||
| 75 | NLM, /* check NLM loaded */ | ||
| 76 | NRMP, /* check NRM Process Values */ | ||
| 77 | NRMM, /* check NRM Memory Values */ | ||
| 78 | NRMS, /* check NRM Values */ | ||
| 79 | NSS1, /* check Statistics from _Admin:Manage_NSS\GeneralStats.xml */ | ||
| 80 | NSS2, /* check Statistics from _Admin:Manage_NSS\BufferCache.xml */ | ||
| 81 | NSS3, /* check statistics from _Admin:Manage_NSS\NameCache.xml */ | ||
| 82 | NSS4, /* check statistics from _Admin:Manage_NSS\FileStats.xml */ | ||
| 83 | NSS5, /* check statistics from _Admin:Manage_NSS\ObjectCache.xml */ | ||
| 84 | NSS6, /* check statistics from _Admin:Manage_NSS\Thread.xml */ | ||
| 85 | NSS7 /* check statistics from _Admin:Manage_NSS\AuthorizationCache.xml */ | ||
| 86 | }; | ||
| 87 | |||
| 88 | enum { | ||
| 89 | PORT = 9999 | ||
| 90 | }; | ||
| 91 | |||
| 92 | static char *server_address = NULL; | ||
| 93 | static char *volume_name = NULL; | ||
| 94 | static char *nlm_name = NULL; | ||
| 95 | static char *nrmp_name = NULL; | ||
| 96 | static char *nrmm_name = NULL; | ||
| 97 | static char *nrms_name = NULL; | ||
| 98 | static char *nss1_name = NULL; | ||
| 99 | static char *nss2_name = NULL; | ||
| 100 | static char *nss3_name = NULL; | ||
| 101 | static char *nss4_name = NULL; | ||
| 102 | static char *nss5_name = NULL; | ||
| 103 | static char *nss6_name = NULL; | ||
| 104 | static char *nss7_name = NULL; | ||
| 105 | static int server_port = PORT; | ||
| 106 | static unsigned long warning_value = 0L; | ||
| 107 | static unsigned long critical_value = 0L; | ||
| 108 | static bool check_warning_value = false; | ||
| 109 | static bool check_critical_value = false; | ||
| 110 | static bool check_netware_version = false; | ||
| 111 | static enum checkvar vars_to_check = NONE; | ||
| 112 | static int sap_number = -1; | ||
| 113 | |||
| 114 | static int process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 115 | static void print_help(void); | ||
| 116 | void print_usage(void); | ||
| 117 | |||
| 118 | int main(int argc, char **argv) { | ||
| 119 | int result = STATE_UNKNOWN; | ||
| 120 | int sd; | ||
| 121 | char *send_buffer = NULL; | ||
| 122 | char recv_buffer[MAX_INPUT_BUFFER]; | ||
| 123 | char *output_message = NULL; | ||
| 124 | char *temp_buffer = NULL; | ||
| 125 | char *netware_version = NULL; | ||
| 126 | |||
| 127 | int time_sync_status = 0; | ||
| 128 | int nrm_health_status = 0; | ||
| 129 | unsigned long total_cache_buffers = 0; | ||
| 130 | unsigned long dirty_cache_buffers = 0; | ||
| 131 | unsigned long open_files = 0; | ||
| 132 | unsigned long abended_threads = 0; | ||
| 133 | unsigned long max_service_processes = 0; | ||
| 134 | unsigned long current_service_processes = 0; | ||
| 135 | unsigned long free_disk_space = 0L; | ||
| 136 | unsigned long nrmp_value = 0L; | ||
| 137 | unsigned long nrmm_value = 0L; | ||
| 138 | unsigned long nrms_value = 0L; | ||
| 139 | unsigned long nss1_value = 0L; | ||
| 140 | unsigned long nss2_value = 0L; | ||
| 141 | unsigned long nss3_value = 0L; | ||
| 142 | unsigned long nss4_value = 0L; | ||
| 143 | unsigned long nss5_value = 0L; | ||
| 144 | unsigned long nss6_value = 0L; | ||
| 145 | unsigned long nss7_value = 0L; | ||
| 146 | unsigned long total_disk_space = 0L; | ||
| 147 | unsigned long used_disk_space = 0L; | ||
| 148 | unsigned long percent_used_disk_space = 0L; | ||
| 149 | unsigned long purgeable_disk_space = 0L; | ||
| 150 | unsigned long non_purgeable_disk_space = 0L; | ||
| 151 | unsigned long percent_free_space = 0; | ||
| 152 | unsigned long percent_purgeable_space = 0; | ||
| 153 | unsigned long percent_non_purgeable_space = 0; | ||
| 154 | unsigned long current_connections = 0L; | ||
| 155 | unsigned long utilization = 0L; | ||
| 156 | unsigned long cache_hits = 0; | ||
| 157 | unsigned long cache_buffers = 0L; | ||
| 158 | unsigned long lru_time = 0L; | ||
| 159 | unsigned long max_packet_receive_buffers = 0; | ||
| 160 | unsigned long used_packet_receive_buffers = 0; | ||
| 161 | unsigned long percent_used_packet_receive_buffers = 0L; | ||
| 162 | unsigned long sap_entries = 0; | ||
| 163 | char uptime[MAX_INPUT_BUFFER]; | ||
| 164 | |||
| 165 | setlocale(LC_ALL, ""); | ||
| 166 | bindtextdomain(PACKAGE, LOCALEDIR); | ||
| 167 | textdomain(PACKAGE); | ||
| 168 | |||
| 169 | /* Parse extra opts if any */ | ||
| 170 | argv = np_extra_opts(&argc, argv, progname); | ||
| 171 | |||
| 172 | if (process_arguments(argc, argv) == ERROR) | ||
| 173 | usage4(_("Could not parse arguments")); | ||
| 174 | |||
| 175 | /* initialize alarm signal handling */ | ||
| 176 | signal(SIGALRM, socket_timeout_alarm_handler); | ||
| 177 | |||
| 178 | /* set socket timeout */ | ||
| 179 | alarm(socket_timeout); | ||
| 180 | |||
| 181 | /* open connection */ | ||
| 182 | my_tcp_connect(server_address, server_port, &sd); | ||
| 183 | |||
| 184 | /* get OS version string */ | ||
| 185 | if (check_netware_version) { | ||
| 186 | send_buffer = strdup("S19\r\n"); | ||
| 187 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 188 | if (result != STATE_OK) | ||
| 189 | return result; | ||
| 190 | if (!strcmp(recv_buffer, "-1\n")) | ||
| 191 | netware_version = strdup(""); | ||
| 192 | else { | ||
| 193 | recv_buffer[strlen(recv_buffer) - 1] = 0; | ||
| 194 | xasprintf(&netware_version, _("NetWare %s: "), recv_buffer); | ||
| 195 | } | ||
| 196 | } else | ||
| 197 | netware_version = strdup(""); | ||
| 198 | |||
| 199 | /* check CPU load */ | ||
| 200 | if (vars_to_check == LOAD1 || vars_to_check == LOAD5 || vars_to_check == LOAD15) { | ||
| 201 | |||
| 202 | switch (vars_to_check) { | ||
| 203 | case LOAD1: | ||
| 204 | temp_buffer = strdup("1"); | ||
| 205 | break; | ||
| 206 | case LOAD5: | ||
| 207 | temp_buffer = strdup("5"); | ||
| 208 | break; | ||
| 209 | default: | ||
| 210 | temp_buffer = strdup("15"); | ||
| 211 | break; | ||
| 212 | } | ||
| 213 | |||
| 214 | close(sd); | ||
| 215 | my_tcp_connect(server_address, server_port, &sd); | ||
| 216 | |||
| 217 | xasprintf(&send_buffer, "UTIL%s\r\n", temp_buffer); | ||
| 218 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 219 | if (result != STATE_OK) | ||
| 220 | return result; | ||
| 221 | utilization = strtoul(recv_buffer, NULL, 10); | ||
| 222 | |||
| 223 | close(sd); | ||
| 224 | my_tcp_connect(server_address, server_port, &sd); | ||
| 225 | |||
| 226 | send_buffer = strdup("UPTIME\r\n"); | ||
| 227 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 228 | if (result != STATE_OK) | ||
| 229 | return result; | ||
| 230 | recv_buffer[strlen(recv_buffer) - 1] = 0; | ||
| 231 | sprintf(uptime, _("Up %s,"), recv_buffer); | ||
| 232 | |||
| 233 | if (check_critical_value && utilization >= critical_value) | ||
| 234 | result = STATE_CRITICAL; | ||
| 235 | else if (check_warning_value && utilization >= warning_value) | ||
| 236 | result = STATE_WARNING; | ||
| 237 | |||
| 238 | xasprintf(&output_message, _("Load %s - %s %s-min load average = %lu%%|load%s=%lu;%lu;%lu;0;100"), state_text(result), uptime, | ||
| 239 | temp_buffer, utilization, temp_buffer, utilization, warning_value, critical_value); | ||
| 240 | |||
| 241 | /* check number of user connections */ | ||
| 242 | } else if (vars_to_check == CONNS) { | ||
| 243 | |||
| 244 | close(sd); | ||
| 245 | my_tcp_connect(server_address, server_port, &sd); | ||
| 246 | |||
| 247 | send_buffer = strdup("CONNECT\r\n"); | ||
| 248 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 249 | if (result != STATE_OK) | ||
| 250 | return result; | ||
| 251 | current_connections = strtoul(recv_buffer, NULL, 10); | ||
| 252 | |||
| 253 | if (check_critical_value && current_connections >= critical_value) | ||
| 254 | result = STATE_CRITICAL; | ||
| 255 | else if (check_warning_value && current_connections >= warning_value) | ||
| 256 | result = STATE_WARNING; | ||
| 257 | |||
| 258 | xasprintf(&output_message, _("Conns %s - %lu current connections|Conns=%lu;%lu;%lu;;"), state_text(result), current_connections, | ||
| 259 | current_connections, warning_value, critical_value); | ||
| 260 | |||
| 261 | /* check % long term cache hits */ | ||
| 262 | } else if (vars_to_check == LTCH) { | ||
| 263 | |||
| 264 | close(sd); | ||
| 265 | my_tcp_connect(server_address, server_port, &sd); | ||
| 266 | |||
| 267 | send_buffer = strdup("S1\r\n"); | ||
| 268 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 269 | if (result != STATE_OK) | ||
| 270 | return result; | ||
| 271 | cache_hits = atoi(recv_buffer); | ||
| 272 | |||
| 273 | if (check_critical_value && cache_hits <= critical_value) | ||
| 274 | result = STATE_CRITICAL; | ||
| 275 | else if (check_warning_value && cache_hits <= warning_value) | ||
| 276 | result = STATE_WARNING; | ||
| 277 | |||
| 278 | xasprintf(&output_message, _("%s: Long term cache hits = %lu%%"), state_text(result), cache_hits); | ||
| 279 | |||
| 280 | /* check cache buffers */ | ||
| 281 | } else if (vars_to_check == CBUFF) { | ||
| 282 | |||
| 283 | close(sd); | ||
| 284 | my_tcp_connect(server_address, server_port, &sd); | ||
| 285 | |||
| 286 | send_buffer = strdup("S2\r\n"); | ||
| 287 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 288 | if (result != STATE_OK) | ||
| 289 | return result; | ||
| 290 | cache_buffers = strtoul(recv_buffer, NULL, 10); | ||
| 291 | |||
| 292 | if (check_critical_value && cache_buffers <= critical_value) | ||
| 293 | result = STATE_CRITICAL; | ||
| 294 | else if (check_warning_value && cache_buffers <= warning_value) | ||
| 295 | result = STATE_WARNING; | ||
| 296 | |||
| 297 | xasprintf(&output_message, _("%s: Total cache buffers = %lu|Cachebuffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers, | ||
| 298 | cache_buffers, warning_value, critical_value); | ||
| 299 | |||
| 300 | /* check dirty cache buffers */ | ||
| 301 | } else if (vars_to_check == CDBUFF) { | ||
| 302 | |||
| 303 | close(sd); | ||
| 304 | my_tcp_connect(server_address, server_port, &sd); | ||
| 305 | |||
| 306 | send_buffer = strdup("S3\r\n"); | ||
| 307 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 308 | if (result != STATE_OK) | ||
| 309 | return result; | ||
| 310 | cache_buffers = strtoul(recv_buffer, NULL, 10); | ||
| 311 | |||
| 312 | if (check_critical_value && cache_buffers >= critical_value) | ||
| 313 | result = STATE_CRITICAL; | ||
| 314 | else if (check_warning_value && cache_buffers >= warning_value) | ||
| 315 | result = STATE_WARNING; | ||
| 316 | |||
| 317 | xasprintf(&output_message, _("%s: Dirty cache buffers = %lu|Dirty-Cache-Buffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers, | ||
| 318 | cache_buffers, warning_value, critical_value); | ||
| 319 | |||
| 320 | /* check LRU sitting time in minutes */ | ||
| 321 | } else if (vars_to_check == LRUM) { | ||
| 322 | |||
| 323 | close(sd); | ||
| 324 | my_tcp_connect(server_address, server_port, &sd); | ||
| 325 | |||
| 326 | send_buffer = strdup("S5\r\n"); | ||
| 327 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 328 | if (result != STATE_OK) | ||
| 329 | return result; | ||
| 330 | lru_time = strtoul(recv_buffer, NULL, 10); | ||
| 331 | |||
| 332 | if (check_critical_value && lru_time <= critical_value) | ||
| 333 | result = STATE_CRITICAL; | ||
| 334 | else if (check_warning_value && lru_time <= warning_value) | ||
| 335 | result = STATE_WARNING; | ||
| 336 | |||
| 337 | xasprintf(&output_message, _("%s: LRU sitting time = %lu minutes"), state_text(result), lru_time); | ||
| 338 | |||
| 339 | /* check KB free space on volume */ | ||
| 340 | } else if (vars_to_check == VKF) { | ||
| 341 | |||
| 342 | close(sd); | ||
| 343 | my_tcp_connect(server_address, server_port, &sd); | ||
| 344 | |||
| 345 | xasprintf(&send_buffer, "VKF%s\r\n", volume_name); | ||
| 346 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 347 | if (result != STATE_OK) | ||
| 348 | return result; | ||
| 349 | |||
| 350 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 351 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 352 | result = STATE_CRITICAL; | ||
| 353 | } else { | ||
| 354 | free_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 355 | if (check_critical_value && free_disk_space <= critical_value) | ||
| 356 | result = STATE_CRITICAL; | ||
| 357 | else if (check_warning_value && free_disk_space <= warning_value) | ||
| 358 | result = STATE_WARNING; | ||
| 359 | xasprintf(&output_message, _("%s%lu KB free on volume %s|KBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), | ||
| 360 | free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value); | ||
| 361 | } | ||
| 362 | |||
| 363 | /* check MB free space on volume */ | ||
| 364 | } else if (vars_to_check == VMF) { | ||
| 365 | |||
| 366 | xasprintf(&send_buffer, "VMF%s\r\n", volume_name); | ||
| 367 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 368 | if (result != STATE_OK) | ||
| 369 | return result; | ||
| 370 | |||
| 371 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 372 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 373 | result = STATE_CRITICAL; | ||
| 374 | } else { | ||
| 375 | free_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 376 | if (check_critical_value && free_disk_space <= critical_value) | ||
| 377 | result = STATE_CRITICAL; | ||
| 378 | else if (check_warning_value && free_disk_space <= warning_value) | ||
| 379 | result = STATE_WARNING; | ||
| 380 | xasprintf(&output_message, _("%s%lu MB free on volume %s|MBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), | ||
| 381 | free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value); | ||
| 382 | } | ||
| 383 | /* check MB used space on volume */ | ||
| 384 | } else if (vars_to_check == VMU) { | ||
| 385 | |||
| 386 | xasprintf(&send_buffer, "VMU%s\r\n", volume_name); | ||
| 387 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 388 | if (result != STATE_OK) | ||
| 389 | return result; | ||
| 390 | |||
| 391 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 392 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 393 | result = STATE_CRITICAL; | ||
| 394 | } else { | ||
| 395 | free_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 396 | if (check_critical_value && free_disk_space <= critical_value) | ||
| 397 | result = STATE_CRITICAL; | ||
| 398 | else if (check_warning_value && free_disk_space <= warning_value) | ||
| 399 | result = STATE_WARNING; | ||
| 400 | xasprintf(&output_message, _("%s%lu MB used on volume %s|MBUsed%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), | ||
| 401 | free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value); | ||
| 402 | } | ||
| 403 | /* check % used space on volume */ | ||
| 404 | } else if (vars_to_check == VPU) { | ||
| 405 | close(sd); | ||
| 406 | my_tcp_connect(server_address, server_port, &sd); | ||
| 407 | |||
| 408 | asprintf(&send_buffer, "VMU%s\r\n", volume_name); | ||
| 409 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 410 | |||
| 411 | if (result != STATE_OK) | ||
| 412 | return result; | ||
| 413 | |||
| 414 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 415 | asprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 416 | result = STATE_CRITICAL; | ||
| 417 | |||
| 418 | } else { | ||
| 419 | used_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 420 | close(sd); | ||
| 421 | my_tcp_connect(server_address, server_port, &sd); | ||
| 422 | /* get total volume in MB */ | ||
| 423 | asprintf(&send_buffer, "VMS%s\r\n", volume_name); | ||
| 424 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 425 | if (result != STATE_OK) | ||
| 426 | return result; | ||
| 427 | total_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 428 | /* calculate percent used on volume */ | ||
| 429 | percent_used_disk_space = (unsigned long)(((double)used_disk_space / (double)total_disk_space) * 100.0); | ||
| 430 | |||
| 431 | if (check_critical_value && percent_used_disk_space >= critical_value) | ||
| 432 | result = STATE_CRITICAL; | ||
| 433 | else if (check_warning_value && percent_used_disk_space >= warning_value) | ||
| 434 | result = STATE_WARNING; | ||
| 435 | |||
| 436 | asprintf(&output_message, _("%lu MB (%lu%%) used on volume %s - total %lu MB|Used space in percent on %s=%lu;%lu;%lu;0;100"), | ||
| 437 | used_disk_space, percent_used_disk_space, volume_name, total_disk_space, volume_name, percent_used_disk_space, | ||
| 438 | warning_value, critical_value); | ||
| 439 | } | ||
| 440 | |||
| 441 | /* check % free space on volume */ | ||
| 442 | } else if (vars_to_check == VPF) { | ||
| 443 | |||
| 444 | close(sd); | ||
| 445 | my_tcp_connect(server_address, server_port, &sd); | ||
| 446 | |||
| 447 | xasprintf(&send_buffer, "VKF%s\r\n", volume_name); | ||
| 448 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 449 | if (result != STATE_OK) | ||
| 450 | return result; | ||
| 451 | |||
| 452 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 453 | |||
| 454 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 455 | result = STATE_CRITICAL; | ||
| 456 | |||
| 457 | } else { | ||
| 458 | |||
| 459 | free_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 460 | |||
| 461 | close(sd); | ||
| 462 | my_tcp_connect(server_address, server_port, &sd); | ||
| 463 | |||
| 464 | xasprintf(&send_buffer, "VKS%s\r\n", volume_name); | ||
| 465 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 466 | if (result != STATE_OK) | ||
| 467 | return result; | ||
| 468 | total_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 469 | |||
| 470 | percent_free_space = (unsigned long)(((double)free_disk_space / (double)total_disk_space) * 100.0); | ||
| 471 | |||
| 472 | if (check_critical_value && percent_free_space <= critical_value) | ||
| 473 | result = STATE_CRITICAL; | ||
| 474 | else if (check_warning_value && percent_free_space <= warning_value) | ||
| 475 | result = STATE_WARNING; | ||
| 476 | free_disk_space /= 1024; | ||
| 477 | total_disk_space /= 1024; | ||
| 478 | xasprintf(&output_message, _("%lu MB (%lu%%) free on volume %s - total %lu MB|FreeMB%s=%lu;%lu;%lu;0;100"), free_disk_space, | ||
| 479 | percent_free_space, volume_name, total_disk_space, volume_name, percent_free_space, warning_value, critical_value); | ||
| 480 | } | ||
| 481 | |||
| 482 | /* check to see if DS Database is open or closed */ | ||
| 483 | } else if (vars_to_check == DSDB) { | ||
| 484 | |||
| 485 | close(sd); | ||
| 486 | my_tcp_connect(server_address, server_port, &sd); | ||
| 487 | |||
| 488 | send_buffer = strdup("S11\r\n"); | ||
| 489 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 490 | if (result != STATE_OK) | ||
| 491 | return result; | ||
| 492 | if (atoi(recv_buffer) == 1) | ||
| 493 | result = STATE_OK; | ||
| 494 | else | ||
| 495 | result = STATE_WARNING; | ||
| 496 | |||
| 497 | close(sd); | ||
| 498 | my_tcp_connect(server_address, server_port, &sd); | ||
| 499 | |||
| 500 | send_buffer = strdup("S13\r\n"); | ||
| 501 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 502 | temp_buffer = strtok(recv_buffer, "\r\n"); | ||
| 503 | |||
| 504 | xasprintf(&output_message, _("Directory Services Database is %s (DS version %s)"), (result == STATE_OK) ? "open" : "closed", | ||
| 505 | temp_buffer); | ||
| 506 | |||
| 507 | /* check to see if logins are enabled */ | ||
| 508 | } else if (vars_to_check == LOGINS) { | ||
| 509 | |||
| 510 | close(sd); | ||
| 511 | my_tcp_connect(server_address, server_port, &sd); | ||
| 512 | |||
| 513 | send_buffer = strdup("S12\r\n"); | ||
| 514 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 515 | if (result != STATE_OK) | ||
| 516 | return result; | ||
| 517 | if (atoi(recv_buffer) == 1) | ||
| 518 | result = STATE_OK; | ||
| 519 | else | ||
| 520 | result = STATE_WARNING; | ||
| 521 | |||
| 522 | xasprintf(&output_message, _("Logins are %s"), (result == STATE_OK) ? _("enabled") : _("disabled")); | ||
| 523 | |||
| 524 | /* check NRM Health Status Summary*/ | ||
| 525 | } else if (vars_to_check == NRMH) { | ||
| 526 | |||
| 527 | xasprintf(&send_buffer, "NRMH\r\n"); | ||
| 528 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 529 | if (result != STATE_OK) | ||
| 530 | return result; | ||
| 531 | |||
| 532 | nrm_health_status = atoi(recv_buffer); | ||
| 533 | |||
| 534 | if (nrm_health_status == 2) { | ||
| 535 | result = STATE_OK; | ||
| 536 | xasprintf(&output_message, _("CRITICAL - NRM Status is bad!")); | ||
| 537 | } else { | ||
| 538 | if (nrm_health_status == 1) { | ||
| 539 | result = STATE_WARNING; | ||
| 540 | xasprintf(&output_message, _("Warning - NRM Status is suspect!")); | ||
| 541 | } | ||
| 542 | |||
| 543 | xasprintf(&output_message, _("OK - NRM Status is good!")); | ||
| 544 | } | ||
| 545 | |||
| 546 | /* check packet receive buffers */ | ||
| 547 | } else if (vars_to_check == UPRB || vars_to_check == PUPRB) { | ||
| 548 | |||
| 549 | close(sd); | ||
| 550 | my_tcp_connect(server_address, server_port, &sd); | ||
| 551 | |||
| 552 | xasprintf(&send_buffer, "S15\r\n"); | ||
| 553 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 554 | if (result != STATE_OK) | ||
| 555 | return result; | ||
| 556 | |||
| 557 | used_packet_receive_buffers = atoi(recv_buffer); | ||
| 558 | |||
| 559 | close(sd); | ||
| 560 | my_tcp_connect(server_address, server_port, &sd); | ||
| 561 | |||
| 562 | xasprintf(&send_buffer, "S16\r\n"); | ||
| 563 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 564 | if (result != STATE_OK) | ||
| 565 | return result; | ||
| 566 | |||
| 567 | max_packet_receive_buffers = atoi(recv_buffer); | ||
| 568 | |||
| 569 | percent_used_packet_receive_buffers = | ||
| 570 | (unsigned long)(((double)used_packet_receive_buffers / (double)max_packet_receive_buffers) * 100.0); | ||
| 571 | |||
| 572 | if (vars_to_check == UPRB) { | ||
| 573 | if (check_critical_value && used_packet_receive_buffers >= critical_value) | ||
| 574 | result = STATE_CRITICAL; | ||
| 575 | else if (check_warning_value && used_packet_receive_buffers >= warning_value) | ||
| 576 | result = STATE_WARNING; | ||
| 577 | } else { | ||
| 578 | if (check_critical_value && percent_used_packet_receive_buffers >= critical_value) | ||
| 579 | result = STATE_CRITICAL; | ||
| 580 | else if (check_warning_value && percent_used_packet_receive_buffers >= warning_value) | ||
| 581 | result = STATE_WARNING; | ||
| 582 | } | ||
| 583 | |||
| 584 | xasprintf(&output_message, _("%lu of %lu (%lu%%) packet receive buffers used"), used_packet_receive_buffers, | ||
| 585 | max_packet_receive_buffers, percent_used_packet_receive_buffers); | ||
| 586 | |||
| 587 | /* check SAP table entries */ | ||
| 588 | } else if (vars_to_check == SAPENTRIES) { | ||
| 589 | |||
| 590 | close(sd); | ||
| 591 | my_tcp_connect(server_address, server_port, &sd); | ||
| 592 | |||
| 593 | if (sap_number == -1) | ||
| 594 | xasprintf(&send_buffer, "S9\r\n"); | ||
| 595 | else | ||
| 596 | xasprintf(&send_buffer, "S9.%d\r\n", sap_number); | ||
| 597 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 598 | if (result != STATE_OK) | ||
| 599 | return result; | ||
| 600 | |||
| 601 | sap_entries = atoi(recv_buffer); | ||
| 602 | |||
| 603 | if (check_critical_value && sap_entries >= critical_value) | ||
| 604 | result = STATE_CRITICAL; | ||
| 605 | else if (check_warning_value && sap_entries >= warning_value) | ||
| 606 | result = STATE_WARNING; | ||
| 607 | |||
| 608 | if (sap_number == -1) | ||
| 609 | xasprintf(&output_message, _("%lu entries in SAP table"), sap_entries); | ||
| 610 | else | ||
| 611 | xasprintf(&output_message, _("%lu entries in SAP table for SAP type %d"), sap_entries, sap_number); | ||
| 612 | |||
| 613 | /* check KB purgeable space on volume */ | ||
| 614 | } else if (vars_to_check == VKP) { | ||
| 615 | |||
| 616 | close(sd); | ||
| 617 | my_tcp_connect(server_address, server_port, &sd); | ||
| 618 | |||
| 619 | xasprintf(&send_buffer, "VKP%s\r\n", volume_name); | ||
| 620 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 621 | if (result != STATE_OK) | ||
| 622 | return result; | ||
| 623 | |||
| 624 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 625 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 626 | result = STATE_CRITICAL; | ||
| 627 | } else { | ||
| 628 | purgeable_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 629 | if (check_critical_value && purgeable_disk_space >= critical_value) | ||
| 630 | result = STATE_CRITICAL; | ||
| 631 | else if (check_warning_value && purgeable_disk_space >= warning_value) | ||
| 632 | result = STATE_WARNING; | ||
| 633 | xasprintf(&output_message, _("%s%lu KB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), | ||
| 634 | purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value); | ||
| 635 | } | ||
| 636 | /* check MB purgeable space on volume */ | ||
| 637 | } else if (vars_to_check == VMP) { | ||
| 638 | |||
| 639 | xasprintf(&send_buffer, "VMP%s\r\n", volume_name); | ||
| 640 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 641 | if (result != STATE_OK) | ||
| 642 | return result; | ||
| 643 | |||
| 644 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 645 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 646 | result = STATE_CRITICAL; | ||
| 647 | } else { | ||
| 648 | purgeable_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 649 | if (check_critical_value && purgeable_disk_space >= critical_value) | ||
| 650 | result = STATE_CRITICAL; | ||
| 651 | else if (check_warning_value && purgeable_disk_space >= warning_value) | ||
| 652 | result = STATE_WARNING; | ||
| 653 | xasprintf(&output_message, _("%s%lu MB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), | ||
| 654 | purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value); | ||
| 655 | } | ||
| 656 | |||
| 657 | /* check % purgeable space on volume */ | ||
| 658 | } else if (vars_to_check == VPP) { | ||
| 659 | |||
| 660 | close(sd); | ||
| 661 | my_tcp_connect(server_address, server_port, &sd); | ||
| 662 | |||
| 663 | xasprintf(&send_buffer, "VKP%s\r\n", volume_name); | ||
| 664 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 665 | if (result != STATE_OK) | ||
| 666 | return result; | ||
| 667 | |||
| 668 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 669 | |||
| 670 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 671 | result = STATE_CRITICAL; | ||
| 672 | |||
| 673 | } else { | ||
| 674 | |||
| 675 | purgeable_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 676 | |||
| 677 | close(sd); | ||
| 678 | my_tcp_connect(server_address, server_port, &sd); | ||
| 679 | |||
| 680 | xasprintf(&send_buffer, "VKS%s\r\n", volume_name); | ||
| 681 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 682 | if (result != STATE_OK) | ||
| 683 | return result; | ||
| 684 | total_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 685 | |||
| 686 | percent_purgeable_space = (unsigned long)(((double)purgeable_disk_space / (double)total_disk_space) * 100.0); | ||
| 687 | |||
| 688 | if (check_critical_value && percent_purgeable_space >= critical_value) | ||
| 689 | result = STATE_CRITICAL; | ||
| 690 | else if (check_warning_value && percent_purgeable_space >= warning_value) | ||
| 691 | result = STATE_WARNING; | ||
| 692 | purgeable_disk_space /= 1024; | ||
| 693 | xasprintf(&output_message, _("%lu MB (%lu%%) purgeable on volume %s|Purgeable%s=%lu;%lu;%lu;0;100"), purgeable_disk_space, | ||
| 694 | percent_purgeable_space, volume_name, volume_name, percent_purgeable_space, warning_value, critical_value); | ||
| 695 | } | ||
| 696 | |||
| 697 | /* check KB not yet purgeable space on volume */ | ||
| 698 | } else if (vars_to_check == VKNP) { | ||
| 699 | |||
| 700 | close(sd); | ||
| 701 | my_tcp_connect(server_address, server_port, &sd); | ||
| 702 | |||
| 703 | xasprintf(&send_buffer, "VKNP%s\r\n", volume_name); | ||
| 704 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 705 | if (result != STATE_OK) | ||
| 706 | return result; | ||
| 707 | |||
| 708 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 709 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 710 | result = STATE_CRITICAL; | ||
| 711 | } else { | ||
| 712 | non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 713 | if (check_critical_value && non_purgeable_disk_space >= critical_value) | ||
| 714 | result = STATE_CRITICAL; | ||
| 715 | else if (check_warning_value && non_purgeable_disk_space >= warning_value) | ||
| 716 | result = STATE_WARNING; | ||
| 717 | xasprintf(&output_message, _("%s%lu KB not yet purgeable on volume %s"), (result == STATE_OK) ? "" : _("Only "), | ||
| 718 | non_purgeable_disk_space, volume_name); | ||
| 719 | } | ||
| 720 | |||
| 721 | /* check % not yet purgeable space on volume */ | ||
| 722 | } else if (vars_to_check == VPNP) { | ||
| 723 | |||
| 724 | close(sd); | ||
| 725 | my_tcp_connect(server_address, server_port, &sd); | ||
| 726 | |||
| 727 | xasprintf(&send_buffer, "VKNP%s\r\n", volume_name); | ||
| 728 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 729 | if (result != STATE_OK) | ||
| 730 | return result; | ||
| 731 | |||
| 732 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 733 | |||
| 734 | xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); | ||
| 735 | result = STATE_CRITICAL; | ||
| 736 | |||
| 737 | } else { | ||
| 738 | |||
| 739 | non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 740 | |||
| 741 | close(sd); | ||
| 742 | my_tcp_connect(server_address, server_port, &sd); | ||
| 743 | |||
| 744 | xasprintf(&send_buffer, "VKS%s\r\n", volume_name); | ||
| 745 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 746 | if (result != STATE_OK) | ||
| 747 | return result; | ||
| 748 | total_disk_space = strtoul(recv_buffer, NULL, 10); | ||
| 749 | |||
| 750 | percent_non_purgeable_space = (unsigned long)(((double)non_purgeable_disk_space / (double)total_disk_space) * 100.0); | ||
| 751 | |||
| 752 | if (check_critical_value && percent_non_purgeable_space >= critical_value) | ||
| 753 | result = STATE_CRITICAL; | ||
| 754 | else if (check_warning_value && percent_non_purgeable_space >= warning_value) | ||
| 755 | result = STATE_WARNING; | ||
| 756 | purgeable_disk_space /= 1024; | ||
| 757 | xasprintf(&output_message, _("%lu MB (%lu%%) not yet purgeable on volume %s"), non_purgeable_disk_space, | ||
| 758 | percent_non_purgeable_space, volume_name); | ||
| 759 | } | ||
| 760 | |||
| 761 | /* check # of open files */ | ||
| 762 | } else if (vars_to_check == OFILES) { | ||
| 763 | |||
| 764 | close(sd); | ||
| 765 | my_tcp_connect(server_address, server_port, &sd); | ||
| 766 | |||
| 767 | xasprintf(&send_buffer, "S18\r\n"); | ||
| 768 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 769 | if (result != STATE_OK) | ||
| 770 | return result; | ||
| 771 | |||
| 772 | open_files = atoi(recv_buffer); | ||
| 773 | |||
| 774 | if (check_critical_value && open_files >= critical_value) | ||
| 775 | result = STATE_CRITICAL; | ||
| 776 | else if (check_warning_value && open_files >= warning_value) | ||
| 777 | result = STATE_WARNING; | ||
| 778 | |||
| 779 | xasprintf(&output_message, _("%lu open files|Openfiles=%lu;%lu;%lu;0,0"), open_files, open_files, warning_value, critical_value); | ||
| 780 | |||
| 781 | /* check # of abended threads (Netware > 5.x only) */ | ||
| 782 | } else if (vars_to_check == ABENDS) { | ||
| 783 | |||
| 784 | close(sd); | ||
| 785 | my_tcp_connect(server_address, server_port, &sd); | ||
| 786 | |||
| 787 | xasprintf(&send_buffer, "S17\r\n"); | ||
| 788 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 789 | if (result != STATE_OK) | ||
| 790 | return result; | ||
| 791 | |||
| 792 | abended_threads = atoi(recv_buffer); | ||
| 793 | |||
| 794 | if (check_critical_value && abended_threads >= critical_value) | ||
| 795 | result = STATE_CRITICAL; | ||
| 796 | else if (check_warning_value && abended_threads >= warning_value) | ||
| 797 | result = STATE_WARNING; | ||
| 798 | |||
| 799 | xasprintf(&output_message, _("%lu abended threads|Abends=%lu;%lu;%lu;;"), abended_threads, abended_threads, warning_value, | ||
| 800 | critical_value); | ||
| 801 | |||
| 802 | /* check # of current service processes (Netware 5.x only) */ | ||
| 803 | } else if (vars_to_check == CSPROCS) { | ||
| 804 | |||
| 805 | close(sd); | ||
| 806 | my_tcp_connect(server_address, server_port, &sd); | ||
| 807 | |||
| 808 | xasprintf(&send_buffer, "S20\r\n"); | ||
| 809 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 810 | if (result != STATE_OK) | ||
| 811 | return result; | ||
| 812 | |||
| 813 | max_service_processes = atoi(recv_buffer); | ||
| 814 | |||
| 815 | close(sd); | ||
| 816 | my_tcp_connect(server_address, server_port, &sd); | ||
| 817 | |||
| 818 | xasprintf(&send_buffer, "S21\r\n"); | ||
| 819 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 820 | if (result != STATE_OK) | ||
| 821 | return result; | ||
| 822 | |||
| 823 | current_service_processes = atoi(recv_buffer); | ||
| 824 | |||
| 825 | if (check_critical_value && current_service_processes >= critical_value) | ||
| 826 | result = STATE_CRITICAL; | ||
| 827 | else if (check_warning_value && current_service_processes >= warning_value) | ||
| 828 | result = STATE_WARNING; | ||
| 829 | |||
| 830 | xasprintf(&output_message, _("%lu current service processes (%lu max)|Processes=%lu;%lu;%lu;0;%lu"), current_service_processes, | ||
| 831 | max_service_processes, current_service_processes, warning_value, critical_value, max_service_processes); | ||
| 832 | |||
| 833 | /* check # Timesync Status */ | ||
| 834 | } else if (vars_to_check == TSYNC) { | ||
| 835 | |||
| 836 | close(sd); | ||
| 837 | my_tcp_connect(server_address, server_port, &sd); | ||
| 838 | |||
| 839 | xasprintf(&send_buffer, "S22\r\n"); | ||
| 840 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 841 | if (result != STATE_OK) | ||
| 842 | return result; | ||
| 843 | |||
| 844 | time_sync_status = atoi(recv_buffer); | ||
| 845 | |||
| 846 | if (time_sync_status == 0) { | ||
| 847 | result = STATE_CRITICAL; | ||
| 848 | xasprintf(&output_message, _("CRITICAL - Time not in sync with network!")); | ||
| 849 | } else { | ||
| 850 | xasprintf(&output_message, _("OK - Time in sync with network!")); | ||
| 851 | } | ||
| 852 | |||
| 853 | /* check LRU sitting time in secondss */ | ||
| 854 | } else if (vars_to_check == LRUS) { | ||
| 855 | |||
| 856 | close(sd); | ||
| 857 | my_tcp_connect(server_address, server_port, &sd); | ||
| 858 | |||
| 859 | send_buffer = strdup("S4\r\n"); | ||
| 860 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 861 | if (result != STATE_OK) | ||
| 862 | return result; | ||
| 863 | lru_time = strtoul(recv_buffer, NULL, 10); | ||
| 864 | |||
| 865 | if (check_critical_value && lru_time <= critical_value) | ||
| 866 | result = STATE_CRITICAL; | ||
| 867 | else if (check_warning_value && lru_time <= warning_value) | ||
| 868 | result = STATE_WARNING; | ||
| 869 | xasprintf(&output_message, _("LRU sitting time = %lu seconds"), lru_time); | ||
| 870 | |||
| 871 | /* check % dirty cacheobuffers as a percentage of the total*/ | ||
| 872 | } else if (vars_to_check == DCB) { | ||
| 873 | |||
| 874 | close(sd); | ||
| 875 | my_tcp_connect(server_address, server_port, &sd); | ||
| 876 | |||
| 877 | send_buffer = strdup("S6\r\n"); | ||
| 878 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 879 | if (result != STATE_OK) | ||
| 880 | return result; | ||
| 881 | dirty_cache_buffers = atoi(recv_buffer); | ||
| 882 | |||
| 883 | if (check_critical_value && dirty_cache_buffers <= critical_value) | ||
| 884 | result = STATE_CRITICAL; | ||
| 885 | else if (check_warning_value && dirty_cache_buffers <= warning_value) | ||
| 886 | result = STATE_WARNING; | ||
| 887 | xasprintf(&output_message, _("Dirty cache buffers = %lu%% of the total|DCB=%lu;%lu;%lu;0;100"), dirty_cache_buffers, | ||
| 888 | dirty_cache_buffers, warning_value, critical_value); | ||
| 889 | |||
| 890 | /* check % total cache buffers as a percentage of the original*/ | ||
| 891 | } else if (vars_to_check == TCB) { | ||
| 892 | |||
| 893 | close(sd); | ||
| 894 | my_tcp_connect(server_address, server_port, &sd); | ||
| 895 | |||
| 896 | send_buffer = strdup("S7\r\n"); | ||
| 897 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 898 | if (result != STATE_OK) | ||
| 899 | return result; | ||
| 900 | total_cache_buffers = atoi(recv_buffer); | ||
| 901 | |||
| 902 | if (check_critical_value && total_cache_buffers <= critical_value) | ||
| 903 | result = STATE_CRITICAL; | ||
| 904 | else if (check_warning_value && total_cache_buffers <= warning_value) | ||
| 905 | result = STATE_WARNING; | ||
| 906 | xasprintf(&output_message, _("Total cache buffers = %lu%% of the original|TCB=%lu;%lu;%lu;0;100"), total_cache_buffers, | ||
| 907 | total_cache_buffers, warning_value, critical_value); | ||
| 908 | |||
| 909 | } else if (vars_to_check == DSVER) { | ||
| 910 | |||
| 911 | close(sd); | ||
| 912 | my_tcp_connect(server_address, server_port, &sd); | ||
| 913 | |||
| 914 | xasprintf(&send_buffer, "S13\r\n"); | ||
| 915 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 916 | if (result != STATE_OK) | ||
| 917 | return result; | ||
| 918 | |||
| 919 | recv_buffer[strlen(recv_buffer) - 1] = 0; | ||
| 920 | |||
| 921 | xasprintf(&output_message, _("NDS Version %s"), recv_buffer); | ||
| 922 | |||
| 923 | } else if (vars_to_check == UPTIME) { | ||
| 924 | |||
| 925 | close(sd); | ||
| 926 | my_tcp_connect(server_address, server_port, &sd); | ||
| 927 | |||
| 928 | xasprintf(&send_buffer, "UPTIME\r\n"); | ||
| 929 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 930 | if (result != STATE_OK) | ||
| 931 | return result; | ||
| 932 | |||
| 933 | recv_buffer[sizeof(recv_buffer) - 1] = 0; | ||
| 934 | recv_buffer[strlen(recv_buffer) - 1] = 0; | ||
| 935 | |||
| 936 | xasprintf(&output_message, _("Up %s"), recv_buffer); | ||
| 937 | |||
| 938 | } else if (vars_to_check == NLM) { | ||
| 939 | |||
| 940 | close(sd); | ||
| 941 | my_tcp_connect(server_address, server_port, &sd); | ||
| 942 | |||
| 943 | xasprintf(&send_buffer, "S24:%s\r\n", nlm_name); | ||
| 944 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 945 | if (result != STATE_OK) | ||
| 946 | return result; | ||
| 947 | |||
| 948 | recv_buffer[strlen(recv_buffer) - 1] = 0; | ||
| 949 | if (strcmp(recv_buffer, "-1")) { | ||
| 950 | xasprintf(&output_message, _("Module %s version %s is loaded"), nlm_name, recv_buffer); | ||
| 951 | } else { | ||
| 952 | result = STATE_CRITICAL; | ||
| 953 | xasprintf(&output_message, _("Module %s is not loaded"), nlm_name); | ||
| 954 | } | ||
| 955 | } else if (vars_to_check == NRMP) { | ||
| 956 | |||
| 957 | xasprintf(&send_buffer, "NRMP:%s\r\n", nrmp_name); | ||
| 958 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 959 | if (result != STATE_OK) | ||
| 960 | return result; | ||
| 961 | |||
| 962 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 963 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmp_name); | ||
| 964 | result = STATE_CRITICAL; | ||
| 965 | } else { | ||
| 966 | nrmp_value = strtoul(recv_buffer, NULL, 10); | ||
| 967 | if (check_critical_value && nrmp_value <= critical_value) | ||
| 968 | result = STATE_CRITICAL; | ||
| 969 | else if (check_warning_value && nrmp_value <= warning_value) | ||
| 970 | result = STATE_WARNING; | ||
| 971 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmp_name, nrmp_value, nrmp_name, nrmp_value, warning_value, | ||
| 972 | critical_value); | ||
| 973 | } | ||
| 974 | |||
| 975 | } else if (vars_to_check == NRMM) { | ||
| 976 | |||
| 977 | xasprintf(&send_buffer, "NRMM:%s\r\n", nrmm_name); | ||
| 978 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 979 | if (result != STATE_OK) | ||
| 980 | return result; | ||
| 981 | |||
| 982 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 983 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmm_name); | ||
| 984 | result = STATE_CRITICAL; | ||
| 985 | } else { | ||
| 986 | nrmm_value = strtoul(recv_buffer, NULL, 10); | ||
| 987 | if (check_critical_value && nrmm_value <= critical_value) | ||
| 988 | result = STATE_CRITICAL; | ||
| 989 | else if (check_warning_value && nrmm_value <= warning_value) | ||
| 990 | result = STATE_WARNING; | ||
| 991 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmm_name, nrmm_value, nrmm_name, nrmm_value, warning_value, | ||
| 992 | critical_value); | ||
| 993 | } | ||
| 994 | |||
| 995 | } else if (vars_to_check == NRMS) { | ||
| 996 | |||
| 997 | xasprintf(&send_buffer, "NRMS:%s\r\n", nrms_name); | ||
| 998 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 999 | if (result != STATE_OK) | ||
| 1000 | return result; | ||
| 1001 | |||
| 1002 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1003 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrms_name); | ||
| 1004 | result = STATE_CRITICAL; | ||
| 1005 | } else { | ||
| 1006 | nrms_value = strtoul(recv_buffer, NULL, 10); | ||
| 1007 | if (check_critical_value && nrms_value >= critical_value) | ||
| 1008 | result = STATE_CRITICAL; | ||
| 1009 | else if (check_warning_value && nrms_value >= warning_value) | ||
| 1010 | result = STATE_WARNING; | ||
| 1011 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrms_name, nrms_value, nrms_name, nrms_value, warning_value, | ||
| 1012 | critical_value); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | } else if (vars_to_check == NSS1) { | ||
| 1016 | |||
| 1017 | xasprintf(&send_buffer, "NSS1:%s\r\n", nss1_name); | ||
| 1018 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 1019 | if (result != STATE_OK) | ||
| 1020 | return result; | ||
| 1021 | |||
| 1022 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1023 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss1_name); | ||
| 1024 | result = STATE_CRITICAL; | ||
| 1025 | } else { | ||
| 1026 | nss1_value = strtoul(recv_buffer, NULL, 10); | ||
| 1027 | if (check_critical_value && nss1_value >= critical_value) | ||
| 1028 | result = STATE_CRITICAL; | ||
| 1029 | else if (check_warning_value && nss1_value >= warning_value) | ||
| 1030 | result = STATE_WARNING; | ||
| 1031 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss1_name, nss1_value, nss1_name, nss1_value, warning_value, | ||
| 1032 | critical_value); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | } else if (vars_to_check == NSS2) { | ||
| 1036 | |||
| 1037 | xasprintf(&send_buffer, "NSS2:%s\r\n", nss2_name); | ||
| 1038 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 1039 | if (result != STATE_OK) | ||
| 1040 | return result; | ||
| 1041 | |||
| 1042 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1043 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss2_name); | ||
| 1044 | result = STATE_CRITICAL; | ||
| 1045 | } else { | ||
| 1046 | nss2_value = strtoul(recv_buffer, NULL, 10); | ||
| 1047 | if (check_critical_value && nss2_value >= critical_value) | ||
| 1048 | result = STATE_CRITICAL; | ||
| 1049 | else if (check_warning_value && nss2_value >= warning_value) | ||
| 1050 | result = STATE_WARNING; | ||
| 1051 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss2_name, nss2_value, nss2_name, nss2_value, warning_value, | ||
| 1052 | critical_value); | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | } else if (vars_to_check == NSS3) { | ||
| 1056 | |||
| 1057 | xasprintf(&send_buffer, "NSS3:%s\r\n", nss3_name); | ||
| 1058 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 1059 | if (result != STATE_OK) | ||
| 1060 | return result; | ||
| 1061 | |||
| 1062 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1063 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss3_name); | ||
| 1064 | result = STATE_CRITICAL; | ||
| 1065 | } else { | ||
| 1066 | nss3_value = strtoul(recv_buffer, NULL, 10); | ||
| 1067 | if (check_critical_value && nss3_value >= critical_value) | ||
| 1068 | result = STATE_CRITICAL; | ||
| 1069 | else if (check_warning_value && nss3_value >= warning_value) | ||
| 1070 | result = STATE_WARNING; | ||
| 1071 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss3_name, nss3_value, nss3_name, nss3_value, warning_value, | ||
| 1072 | critical_value); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | } else if (vars_to_check == NSS4) { | ||
| 1076 | |||
| 1077 | xasprintf(&send_buffer, "NSS4:%s\r\n", nss4_name); | ||
| 1078 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 1079 | if (result != STATE_OK) | ||
| 1080 | return result; | ||
| 1081 | |||
| 1082 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1083 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss4_name); | ||
| 1084 | result = STATE_CRITICAL; | ||
| 1085 | } else { | ||
| 1086 | nss4_value = strtoul(recv_buffer, NULL, 10); | ||
| 1087 | if (check_critical_value && nss4_value >= critical_value) | ||
| 1088 | result = STATE_CRITICAL; | ||
| 1089 | else if (check_warning_value && nss4_value >= warning_value) | ||
| 1090 | result = STATE_WARNING; | ||
| 1091 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss4_name, nss4_value, nss4_name, nss4_value, warning_value, | ||
| 1092 | critical_value); | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | } else if (vars_to_check == NSS5) { | ||
| 1096 | |||
| 1097 | xasprintf(&send_buffer, "NSS5:%s\r\n", nss5_name); | ||
| 1098 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 1099 | if (result != STATE_OK) | ||
| 1100 | return result; | ||
| 1101 | |||
| 1102 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1103 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss5_name); | ||
| 1104 | result = STATE_CRITICAL; | ||
| 1105 | } else { | ||
| 1106 | nss5_value = strtoul(recv_buffer, NULL, 10); | ||
| 1107 | if (check_critical_value && nss5_value >= critical_value) | ||
| 1108 | result = STATE_CRITICAL; | ||
| 1109 | else if (check_warning_value && nss5_value >= warning_value) | ||
| 1110 | result = STATE_WARNING; | ||
| 1111 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss5_name, nss5_value, nss5_name, nss5_value, warning_value, | ||
| 1112 | critical_value); | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | } else if (vars_to_check == NSS6) { | ||
| 1116 | |||
| 1117 | xasprintf(&send_buffer, "NSS6:%s\r\n", nss6_name); | ||
| 1118 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 1119 | if (result != STATE_OK) | ||
| 1120 | return result; | ||
| 1121 | |||
| 1122 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1123 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss6_name); | ||
| 1124 | result = STATE_CRITICAL; | ||
| 1125 | } else { | ||
| 1126 | nss6_value = strtoul(recv_buffer, NULL, 10); | ||
| 1127 | if (check_critical_value && nss6_value >= critical_value) | ||
| 1128 | result = STATE_CRITICAL; | ||
| 1129 | else if (check_warning_value && nss6_value >= warning_value) | ||
| 1130 | result = STATE_WARNING; | ||
| 1131 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss6_name, nss6_value, nss6_name, nss6_value, warning_value, | ||
| 1132 | critical_value); | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | } else if (vars_to_check == NSS7) { | ||
| 1136 | |||
| 1137 | xasprintf(&send_buffer, "NSS7:%s\r\n", nss7_name); | ||
| 1138 | result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 1139 | if (result != STATE_OK) | ||
| 1140 | return result; | ||
| 1141 | |||
| 1142 | if (!strcmp(recv_buffer, "-1\n")) { | ||
| 1143 | xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss7_name); | ||
| 1144 | result = STATE_CRITICAL; | ||
| 1145 | } else { | ||
| 1146 | nss7_value = strtoul(recv_buffer, NULL, 10); | ||
| 1147 | if (check_critical_value && nss7_value >= critical_value) | ||
| 1148 | result = STATE_CRITICAL; | ||
| 1149 | else if (check_warning_value && nss7_value >= warning_value) | ||
| 1150 | result = STATE_WARNING; | ||
| 1151 | xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss7_name, nss7_value, nss7_name, nss7_value, warning_value, | ||
| 1152 | critical_value); | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | } else { | ||
| 1156 | |||
| 1157 | output_message = strdup(_("Nothing to check!\n")); | ||
| 1158 | result = STATE_UNKNOWN; | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | close(sd); | ||
| 1162 | |||
| 1163 | /* reset timeout */ | ||
| 1164 | alarm(0); | ||
| 1165 | |||
| 1166 | printf("%s%s\n", netware_version, output_message); | ||
| 1167 | |||
| 1168 | return result; | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | /* process command-line arguments */ | ||
| 1172 | int process_arguments(int argc, char **argv) { | ||
| 1173 | int c; | ||
| 1174 | |||
| 1175 | int option = 0; | ||
| 1176 | static struct option longopts[] = {{"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'}, | ||
| 1177 | {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, | ||
| 1178 | {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'}, | ||
| 1179 | {"osversion", no_argument, 0, 'o'}, {"version", no_argument, 0, 'V'}, | ||
| 1180 | {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; | ||
| 1181 | |||
| 1182 | /* no options were supplied */ | ||
| 1183 | if (argc < 2) | ||
| 1184 | return ERROR; | ||
| 1185 | |||
| 1186 | /* backwards compatibility */ | ||
| 1187 | if (!is_option(argv[1])) { | ||
| 1188 | server_address = argv[1]; | ||
| 1189 | argv[1] = argv[0]; | ||
| 1190 | argv = &argv[1]; | ||
| 1191 | argc--; | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | for (c = 1; c < argc; c++) { | ||
| 1195 | if (strcmp("-to", argv[c]) == 0) | ||
| 1196 | strcpy(argv[c], "-t"); | ||
| 1197 | else if (strcmp("-wv", argv[c]) == 0) | ||
| 1198 | strcpy(argv[c], "-w"); | ||
| 1199 | else if (strcmp("-cv", argv[c]) == 0) | ||
| 1200 | strcpy(argv[c], "-c"); | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | while (1) { | ||
| 1204 | c = getopt_long(argc, argv, "+hoVH:t:c:w:p:v:", longopts, &option); | ||
| 1205 | |||
| 1206 | if (c == -1 || c == EOF || c == 1) | ||
| 1207 | break; | ||
| 1208 | |||
| 1209 | switch (c) { | ||
| 1210 | case '?': /* print short usage statement if args not parsable */ | ||
| 1211 | usage5(); | ||
| 1212 | case 'h': /* help */ | ||
| 1213 | print_help(); | ||
| 1214 | exit(STATE_UNKNOWN); | ||
| 1215 | case 'V': /* version */ | ||
| 1216 | print_revision(progname, NP_VERSION); | ||
| 1217 | exit(STATE_UNKNOWN); | ||
| 1218 | case 'H': /* hostname */ | ||
| 1219 | server_address = optarg; | ||
| 1220 | break; | ||
| 1221 | case 'o': /* display nos version */ | ||
| 1222 | check_netware_version = true; | ||
| 1223 | break; | ||
| 1224 | case 'p': /* port */ | ||
| 1225 | if (is_intnonneg(optarg)) | ||
| 1226 | server_port = atoi(optarg); | ||
| 1227 | else | ||
| 1228 | die(STATE_UNKNOWN, _("Server port an integer\n")); | ||
| 1229 | break; | ||
| 1230 | case 'v': | ||
| 1231 | if (strlen(optarg) < 3) | ||
| 1232 | return ERROR; | ||
| 1233 | if (!strcmp(optarg, "LOAD1")) | ||
| 1234 | vars_to_check = LOAD1; | ||
| 1235 | else if (!strcmp(optarg, "LOAD5")) | ||
| 1236 | vars_to_check = LOAD5; | ||
| 1237 | else if (!strcmp(optarg, "LOAD15")) | ||
| 1238 | vars_to_check = LOAD15; | ||
| 1239 | else if (!strcmp(optarg, "CONNS")) | ||
| 1240 | vars_to_check = CONNS; | ||
| 1241 | else if (!strcmp(optarg, "LTCH")) | ||
| 1242 | vars_to_check = LTCH; | ||
| 1243 | else if (!strcmp(optarg, "DCB")) | ||
| 1244 | vars_to_check = DCB; | ||
| 1245 | else if (!strcmp(optarg, "TCB")) | ||
| 1246 | vars_to_check = TCB; | ||
| 1247 | else if (!strcmp(optarg, "CBUFF")) | ||
| 1248 | vars_to_check = CBUFF; | ||
| 1249 | else if (!strcmp(optarg, "CDBUFF")) | ||
| 1250 | vars_to_check = CDBUFF; | ||
| 1251 | else if (!strcmp(optarg, "LRUM")) | ||
| 1252 | vars_to_check = LRUM; | ||
| 1253 | else if (!strcmp(optarg, "LRUS")) | ||
| 1254 | vars_to_check = LRUS; | ||
| 1255 | else if (strncmp(optarg, "VPF", 3) == 0) { | ||
| 1256 | vars_to_check = VPF; | ||
| 1257 | volume_name = strdup(optarg + 3); | ||
| 1258 | if (!strcmp(volume_name, "")) | ||
| 1259 | volume_name = strdup("SYS"); | ||
| 1260 | } else if (strncmp(optarg, "VKF", 3) == 0) { | ||
| 1261 | vars_to_check = VKF; | ||
| 1262 | volume_name = strdup(optarg + 3); | ||
| 1263 | if (!strcmp(volume_name, "")) | ||
| 1264 | volume_name = strdup("SYS"); | ||
| 1265 | } else if (strncmp(optarg, "VMF", 3) == 0) { | ||
| 1266 | vars_to_check = VMF; | ||
| 1267 | volume_name = strdup(optarg + 3); | ||
| 1268 | if (!strcmp(volume_name, "")) | ||
| 1269 | volume_name = strdup("SYS"); | ||
| 1270 | } else if (!strcmp(optarg, "DSDB")) | ||
| 1271 | vars_to_check = DSDB; | ||
| 1272 | else if (!strcmp(optarg, "LOGINS")) | ||
| 1273 | vars_to_check = LOGINS; | ||
| 1274 | else if (!strcmp(optarg, "NRMH")) | ||
| 1275 | vars_to_check = NRMH; | ||
| 1276 | else if (!strcmp(optarg, "UPRB")) | ||
| 1277 | vars_to_check = UPRB; | ||
| 1278 | else if (!strcmp(optarg, "PUPRB")) | ||
| 1279 | vars_to_check = PUPRB; | ||
| 1280 | else if (!strncmp(optarg, "SAPENTRIES", 10)) { | ||
| 1281 | vars_to_check = SAPENTRIES; | ||
| 1282 | if (strlen(optarg) > 10) | ||
| 1283 | sap_number = atoi(optarg + 10); | ||
| 1284 | else | ||
| 1285 | sap_number = -1; | ||
| 1286 | } else if (!strcmp(optarg, "OFILES")) | ||
| 1287 | vars_to_check = OFILES; | ||
| 1288 | else if (strncmp(optarg, "VKP", 3) == 0) { | ||
| 1289 | vars_to_check = VKP; | ||
| 1290 | volume_name = strdup(optarg + 3); | ||
| 1291 | if (!strcmp(volume_name, "")) | ||
| 1292 | volume_name = strdup("SYS"); | ||
| 1293 | } else if (strncmp(optarg, "VMP", 3) == 0) { | ||
| 1294 | vars_to_check = VMP; | ||
| 1295 | volume_name = strdup(optarg + 3); | ||
| 1296 | if (!strcmp(volume_name, "")) | ||
| 1297 | volume_name = strdup("SYS"); | ||
| 1298 | } else if (strncmp(optarg, "VMU", 3) == 0) { | ||
| 1299 | vars_to_check = VMU; | ||
| 1300 | volume_name = strdup(optarg + 3); | ||
| 1301 | if (!strcmp(volume_name, "")) | ||
| 1302 | volume_name = strdup("SYS"); | ||
| 1303 | } else if (strncmp(optarg, "VPU", 3) == 0) { | ||
| 1304 | vars_to_check = VPU; | ||
| 1305 | volume_name = strdup(optarg + 3); | ||
| 1306 | if (!strcmp(volume_name, "")) | ||
| 1307 | volume_name = strdup("SYS"); | ||
| 1308 | } else if (strncmp(optarg, "VPP", 3) == 0) { | ||
| 1309 | vars_to_check = VPP; | ||
| 1310 | volume_name = strdup(optarg + 3); | ||
| 1311 | if (!strcmp(volume_name, "")) | ||
| 1312 | volume_name = strdup("SYS"); | ||
| 1313 | } else if (strncmp(optarg, "VKNP", 4) == 0) { | ||
| 1314 | vars_to_check = VKNP; | ||
| 1315 | volume_name = strdup(optarg + 4); | ||
| 1316 | if (!strcmp(volume_name, "")) | ||
| 1317 | volume_name = strdup("SYS"); | ||
| 1318 | } else if (strncmp(optarg, "VPNP", 4) == 0) { | ||
| 1319 | vars_to_check = VPNP; | ||
| 1320 | volume_name = strdup(optarg + 4); | ||
| 1321 | if (!strcmp(volume_name, "")) | ||
| 1322 | volume_name = strdup("SYS"); | ||
| 1323 | } else if (!strcmp(optarg, "ABENDS")) | ||
| 1324 | vars_to_check = ABENDS; | ||
| 1325 | else if (!strcmp(optarg, "CSPROCS")) | ||
| 1326 | vars_to_check = CSPROCS; | ||
| 1327 | else if (!strcmp(optarg, "TSYNC")) | ||
| 1328 | vars_to_check = TSYNC; | ||
| 1329 | else if (!strcmp(optarg, "DSVER")) | ||
| 1330 | vars_to_check = DSVER; | ||
| 1331 | else if (!strcmp(optarg, "UPTIME")) { | ||
| 1332 | vars_to_check = UPTIME; | ||
| 1333 | } else if (strncmp(optarg, "NLM:", 4) == 0) { | ||
| 1334 | vars_to_check = NLM; | ||
| 1335 | nlm_name = strdup(optarg + 4); | ||
| 1336 | } else if (strncmp(optarg, "NRMP", 4) == 0) { | ||
| 1337 | vars_to_check = NRMP; | ||
| 1338 | nrmp_name = strdup(optarg + 4); | ||
| 1339 | if (!strcmp(nrmp_name, "")) | ||
| 1340 | nrmp_name = strdup("AVAILABLE_MEMORY"); | ||
| 1341 | } else if (strncmp(optarg, "NRMM", 4) == 0) { | ||
| 1342 | vars_to_check = NRMM; | ||
| 1343 | nrmm_name = strdup(optarg + 4); | ||
| 1344 | if (!strcmp(nrmm_name, "")) | ||
| 1345 | nrmm_name = strdup("AVAILABLE_CACHE_MEMORY"); | ||
| 1346 | |||
| 1347 | } | ||
| 1348 | |||
| 1349 | else if (strncmp(optarg, "NRMS", 4) == 0) { | ||
| 1350 | vars_to_check = NRMS; | ||
| 1351 | nrms_name = strdup(optarg + 4); | ||
| 1352 | if (!strcmp(nrms_name, "")) | ||
| 1353 | nrms_name = strdup("USED_SWAP_SPACE"); | ||
| 1354 | |||
| 1355 | } | ||
| 1356 | |||
| 1357 | else if (strncmp(optarg, "NSS1", 4) == 0) { | ||
| 1358 | vars_to_check = NSS1; | ||
| 1359 | nss1_name = strdup(optarg + 4); | ||
| 1360 | if (!strcmp(nss1_name, "")) | ||
| 1361 | nss1_name = strdup("CURRENTBUFFERCACHESIZE"); | ||
| 1362 | |||
| 1363 | } | ||
| 1364 | |||
| 1365 | else if (strncmp(optarg, "NSS2", 4) == 0) { | ||
| 1366 | vars_to_check = NSS2; | ||
| 1367 | nss2_name = strdup(optarg + 4); | ||
| 1368 | if (!strcmp(nss2_name, "")) | ||
| 1369 | nss2_name = strdup("CACHEHITS"); | ||
| 1370 | |||
| 1371 | } | ||
| 1372 | |||
| 1373 | else if (strncmp(optarg, "NSS3", 4) == 0) { | ||
| 1374 | vars_to_check = NSS3; | ||
| 1375 | nss3_name = strdup(optarg + 4); | ||
| 1376 | if (!strcmp(nss3_name, "")) | ||
| 1377 | nss3_name = strdup("CACHEGITPERCENT"); | ||
| 1378 | |||
| 1379 | } | ||
| 1380 | |||
| 1381 | else if (strncmp(optarg, "NSS4", 4) == 0) { | ||
| 1382 | vars_to_check = NSS4; | ||
| 1383 | nss4_name = strdup(optarg + 4); | ||
| 1384 | if (!strcmp(nss4_name, "")) | ||
| 1385 | nss4_name = strdup("CURRENTOPENCOUNT"); | ||
| 1386 | |||
| 1387 | } | ||
| 1388 | |||
| 1389 | else if (strncmp(optarg, "NSS5", 4) == 0) { | ||
| 1390 | vars_to_check = NSS5; | ||
| 1391 | nss5_name = strdup(optarg + 4); | ||
| 1392 | if (!strcmp(nss5_name, "")) | ||
| 1393 | nss5_name = strdup("CACHEMISSES"); | ||
| 1394 | |||
| 1395 | } | ||
| 1396 | |||
| 1397 | else if (strncmp(optarg, "NSS6", 4) == 0) { | ||
| 1398 | vars_to_check = NSS6; | ||
| 1399 | nss6_name = strdup(optarg + 4); | ||
| 1400 | if (!strcmp(nss6_name, "")) | ||
| 1401 | nss6_name = strdup("PENDINGWORKSCOUNT"); | ||
| 1402 | |||
| 1403 | } | ||
| 1404 | |||
| 1405 | else if (strncmp(optarg, "NSS7", 4) == 0) { | ||
| 1406 | vars_to_check = NSS7; | ||
| 1407 | nss7_name = strdup(optarg + 4); | ||
| 1408 | if (!strcmp(nss7_name, "")) | ||
| 1409 | nss7_name = strdup("CACHESIZE"); | ||
| 1410 | |||
| 1411 | } | ||
| 1412 | |||
| 1413 | else | ||
| 1414 | return ERROR; | ||
| 1415 | break; | ||
| 1416 | case 'w': /* warning threshold */ | ||
| 1417 | warning_value = strtoul(optarg, NULL, 10); | ||
| 1418 | check_warning_value = true; | ||
| 1419 | break; | ||
| 1420 | case 'c': /* critical threshold */ | ||
| 1421 | critical_value = strtoul(optarg, NULL, 10); | ||
| 1422 | check_critical_value = true; | ||
| 1423 | break; | ||
| 1424 | case 't': /* timeout */ | ||
| 1425 | socket_timeout = atoi(optarg); | ||
| 1426 | if (socket_timeout <= 0) | ||
| 1427 | return ERROR; | ||
| 1428 | } | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | return OK; | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | void print_help(void) { | ||
| 1435 | char *myport; | ||
| 1436 | xasprintf(&myport, "%d", PORT); | ||
| 1437 | |||
| 1438 | print_revision(progname, NP_VERSION); | ||
| 1439 | |||
| 1440 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); | ||
| 1441 | printf(COPYRIGHT, copyright, email); | ||
| 1442 | |||
| 1443 | printf("%s\n", _("This plugin attempts to contact the MRTGEXT NLM running on a")); | ||
| 1444 | printf("%s\n", _("Novell server to gather the requested system information.")); | ||
| 1445 | |||
| 1446 | printf("\n\n"); | ||
| 1447 | |||
| 1448 | print_usage(); | ||
| 1449 | |||
| 1450 | printf(UT_HELP_VRSN); | ||
| 1451 | printf(UT_EXTRA_OPTS); | ||
| 1452 | |||
| 1453 | printf(UT_HOST_PORT, 'p', myport); | ||
| 1454 | |||
| 1455 | printf(" %s\n", "-v, --variable=STRING"); | ||
| 1456 | printf(" %s\n", _("Variable to check. Valid variables include:")); | ||
| 1457 | printf(" %s\n", _("LOAD1 = 1 minute average CPU load")); | ||
| 1458 | printf(" %s\n", _("LOAD5 = 5 minute average CPU load")); | ||
| 1459 | printf(" %s\n", _("LOAD15 = 15 minute average CPU load")); | ||
| 1460 | printf(" %s\n", _("CSPROCS = number of current service processes (NW 5.x only)")); | ||
| 1461 | printf(" %s\n", _("ABENDS = number of abended threads (NW 5.x only)")); | ||
| 1462 | printf(" %s\n", _("UPTIME = server uptime")); | ||
| 1463 | printf(" %s\n", _("LTCH = percent long term cache hits")); | ||
| 1464 | printf(" %s\n", _("CBUFF = current number of cache buffers")); | ||
| 1465 | printf(" %s\n", _("CDBUFF = current number of dirty cache buffers")); | ||
| 1466 | printf(" %s\n", _("DCB = dirty cache buffers as a percentage of the total")); | ||
| 1467 | printf(" %s\n", _("TCB = dirty cache buffers as a percentage of the original")); | ||
| 1468 | printf(" %s\n", _("OFILES = number of open files")); | ||
| 1469 | printf(" %s\n", _(" VMF<vol> = MB of free space on Volume <vol>")); | ||
| 1470 | printf(" %s\n", _(" VMU<vol> = MB used space on Volume <vol>")); | ||
| 1471 | printf(" %s\n", _(" VPU<vol> = percent used space on Volume <vol>")); | ||
| 1472 | printf(" %s\n", _(" VMP<vol> = MB of purgeable space on Volume <vol>")); | ||
| 1473 | printf(" %s\n", _(" VPF<vol> = percent free space on volume <vol>")); | ||
| 1474 | printf(" %s\n", _(" VKF<vol> = KB of free space on volume <vol>")); | ||
| 1475 | printf(" %s\n", _(" VPP<vol> = percent purgeable space on volume <vol>")); | ||
| 1476 | printf(" %s\n", _(" VKP<vol> = KB of purgeable space on volume <vol>")); | ||
| 1477 | printf(" %s\n", _(" VPNP<vol> = percent not yet purgeable space on volume <vol>")); | ||
| 1478 | printf(" %s\n", _(" VKNP<vol> = KB of not yet purgeable space on volume <vol>")); | ||
| 1479 | printf(" %s\n", _(" LRUM = LRU sitting time in minutes")); | ||
| 1480 | printf(" %s\n", _(" LRUS = LRU sitting time in seconds")); | ||
| 1481 | printf(" %s\n", _(" DSDB = check to see if DS Database is open")); | ||
| 1482 | printf(" %s\n", _(" DSVER = NDS version")); | ||
| 1483 | printf(" %s\n", _(" UPRB = used packet receive buffers")); | ||
| 1484 | printf(" %s\n", _(" PUPRB = percent (of max) used packet receive buffers")); | ||
| 1485 | printf(" %s\n", _(" SAPENTRIES = number of entries in the SAP table")); | ||
| 1486 | printf(" %s\n", _(" SAPENTRIES<n> = number of entries in the SAP table for SAP type <n>")); | ||
| 1487 | printf(" %s\n", _(" TSYNC = timesync status")); | ||
| 1488 | printf(" %s\n", _(" LOGINS = check to see if logins are enabled")); | ||
| 1489 | printf(" %s\n", _(" CONNS = number of currently licensed connections")); | ||
| 1490 | printf(" %s\n", _(" NRMH = NRM Summary Status")); | ||
| 1491 | printf(" %s\n", _(" NRMP<stat> = Returns the current value for a NRM health item")); | ||
| 1492 | printf(" %s\n", _(" NRMM<stat> = Returns the current memory stats from NRM")); | ||
| 1493 | printf(" %s\n", _(" NRMS<stat> = Returns the current Swapfile stats from NRM")); | ||
| 1494 | printf(" %s\n", _(" NSS1<stat> = Statistics from _Admin:Manage_NSS\\GeneralStats.xml")); | ||
| 1495 | printf(" %s\n", _(" NSS3<stat> = Statistics from _Admin:Manage_NSS\\NameCache.xml")); | ||
| 1496 | printf(" %s\n", _(" NSS4<stat> = Statistics from _Admin:Manage_NSS\\FileStats.xml")); | ||
| 1497 | printf(" %s\n", _(" NSS5<stat> = Statistics from _Admin:Manage_NSS\\ObjectCache.xml")); | ||
| 1498 | printf(" %s\n", _(" NSS6<stat> = Statistics from _Admin:Manage_NSS\\Thread.xml")); | ||
| 1499 | printf(" %s\n", _(" NSS7<stat> = Statistics from _Admin:Manage_NSS\\AuthorizationCache.xml")); | ||
| 1500 | printf(" %s\n", _(" NLM:<nlm> = check if NLM is loaded and report version")); | ||
| 1501 | printf(" %s\n", _(" (e.g. NLM:TSANDS.NLM)")); | ||
| 1502 | printf("\n"); | ||
| 1503 | printf(" %s\n", "-w, --warning=INTEGER"); | ||
| 1504 | printf(" %s\n", _("Threshold which will result in a warning status")); | ||
| 1505 | printf(" %s\n", "-c, --critical=INTEGER"); | ||
| 1506 | printf(" %s\n", _("Threshold which will result in a critical status")); | ||
| 1507 | printf(" %s\n", "-o, --osversion"); | ||
| 1508 | printf(" %s\n", _("Include server version string in results")); | ||
| 1509 | |||
| 1510 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
| 1511 | |||
| 1512 | printf("\n"); | ||
| 1513 | printf("%s\n", _("Notes:")); | ||
| 1514 | printf(" %s\n", _("- This plugin requires that the MRTGEXT.NLM file from James Drews' MRTG")); | ||
| 1515 | printf(" %s\n", _(" extension for NetWare be loaded on the Novell servers you wish to check.")); | ||
| 1516 | printf(" %s\n", _(" (available from http://www.engr.wisc.edu/~drews/mrtg/)")); | ||
| 1517 | printf(" %s\n", _("- Values for critical thresholds should be lower than warning thresholds")); | ||
| 1518 | printf(" %s\n", _(" when the following variables are checked: VPF, VKF, LTCH, CBUFF, DCB, ")); | ||
| 1519 | printf(" %s\n", _(" TCB, LRUS and LRUM.")); | ||
| 1520 | |||
| 1521 | printf(UT_SUPPORT); | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | void print_usage(void) { | ||
| 1525 | printf("%s\n", _("Usage:")); | ||
| 1526 | printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname); | ||
| 1527 | } | ||
diff --git a/plugins/check_overcr.c b/plugins/check_overcr.c deleted file mode 100644 index 599540b7..00000000 --- a/plugins/check_overcr.c +++ /dev/null | |||
| @@ -1,417 +0,0 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * Monitoring check_overcr plugin | ||
| 4 | * | ||
| 5 | * License: GPL | ||
| 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team | ||
| 7 | * | ||
| 8 | * Description: | ||
| 9 | * | ||
| 10 | * This file contains the check_overcr plugin | ||
| 11 | * | ||
| 12 | * This plugin attempts to contact the Over-CR collector daemon running on the | ||
| 13 | * remote UNIX server in order to gather the requested system information. | ||
| 14 | * | ||
| 15 | * | ||
| 16 | * This program is free software: you can redistribute it and/or modify | ||
| 17 | * it under the terms of the GNU General Public License as published by | ||
| 18 | * the Free Software Foundation, either version 3 of the License, or | ||
| 19 | * (at your option) any later version. | ||
| 20 | * | ||
| 21 | * This program is distributed in the hope that it will be useful, | ||
| 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 24 | * GNU General Public License for more details. | ||
| 25 | * | ||
| 26 | * You should have received a copy of the GNU General Public License | ||
| 27 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 28 | * | ||
| 29 | * | ||
| 30 | *****************************************************************************/ | ||
| 31 | |||
| 32 | const char *progname = "check_overcr"; | ||
| 33 | const char *copyright = "2000-2024"; | ||
| 34 | const char *email = "devel@monitoring-plugins.org"; | ||
| 35 | |||
| 36 | #include "common.h" | ||
| 37 | #include "netutils.h" | ||
| 38 | #include "utils.h" | ||
| 39 | |||
| 40 | enum checkvar { | ||
| 41 | NONE, | ||
| 42 | LOAD1, | ||
| 43 | LOAD5, | ||
| 44 | LOAD15, | ||
| 45 | DPU, | ||
| 46 | PROCS, | ||
| 47 | NETSTAT, | ||
| 48 | UPTIME | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum { | ||
| 52 | PORT = 2000 | ||
| 53 | }; | ||
| 54 | |||
| 55 | static char *server_address = NULL; | ||
| 56 | static int server_port = PORT; | ||
| 57 | static double warning_value = 0L; | ||
| 58 | static double critical_value = 0L; | ||
| 59 | static bool check_warning_value = false; | ||
| 60 | static bool check_critical_value = false; | ||
| 61 | static enum checkvar vars_to_check = NONE; | ||
| 62 | |||
| 63 | static int netstat_port = 0; | ||
| 64 | static char *disk_name = NULL; | ||
| 65 | static char *process_name = NULL; | ||
| 66 | static char send_buffer[MAX_INPUT_BUFFER]; | ||
| 67 | |||
| 68 | static int process_arguments(int, char **); | ||
| 69 | void print_usage(void); | ||
| 70 | static void print_help(void); | ||
| 71 | |||
| 72 | int main(int argc, char **argv) { | ||
| 73 | int result = STATE_UNKNOWN; | ||
| 74 | char recv_buffer[MAX_INPUT_BUFFER]; | ||
| 75 | char temp_buffer[MAX_INPUT_BUFFER]; | ||
| 76 | char *temp_ptr = NULL; | ||
| 77 | bool found_disk = false; | ||
| 78 | unsigned long percent_used_disk_space = 100; | ||
| 79 | double load; | ||
| 80 | double load_1min; | ||
| 81 | double load_5min; | ||
| 82 | double load_15min; | ||
| 83 | int port_connections = 0; | ||
| 84 | int processes = 0; | ||
| 85 | double uptime_raw_hours; | ||
| 86 | int uptime_raw_minutes = 0; | ||
| 87 | int uptime_days = 0; | ||
| 88 | int uptime_hours = 0; | ||
| 89 | int uptime_minutes = 0; | ||
| 90 | |||
| 91 | setlocale(LC_ALL, ""); | ||
| 92 | bindtextdomain(PACKAGE, LOCALEDIR); | ||
| 93 | textdomain(PACKAGE); | ||
| 94 | |||
| 95 | /* Parse extra opts if any */ | ||
| 96 | argv = np_extra_opts(&argc, argv, progname); | ||
| 97 | |||
| 98 | if (process_arguments(argc, argv) == ERROR) | ||
| 99 | usage4(_("Could not parse arguments")); | ||
| 100 | |||
| 101 | /* initialize alarm signal handling */ | ||
| 102 | signal(SIGALRM, socket_timeout_alarm_handler); | ||
| 103 | |||
| 104 | /* set socket timeout */ | ||
| 105 | alarm(socket_timeout); | ||
| 106 | |||
| 107 | result = process_tcp_request2(server_address, server_port, send_buffer, recv_buffer, sizeof(recv_buffer)); | ||
| 108 | |||
| 109 | switch (vars_to_check) { | ||
| 110 | |||
| 111 | case LOAD1: | ||
| 112 | case LOAD5: | ||
| 113 | case LOAD15: | ||
| 114 | |||
| 115 | if (result != STATE_OK) | ||
| 116 | die(result, _("Unknown error fetching load data\n")); | ||
| 117 | |||
| 118 | temp_ptr = (char *)strtok(recv_buffer, "\r\n"); | ||
| 119 | if (temp_ptr == NULL) | ||
| 120 | die(STATE_CRITICAL, _("Invalid response from server - no load information\n")); | ||
| 121 | else | ||
| 122 | load_1min = strtod(temp_ptr, NULL); | ||
| 123 | |||
| 124 | temp_ptr = (char *)strtok(NULL, "\r\n"); | ||
| 125 | if (temp_ptr == NULL) | ||
| 126 | die(STATE_CRITICAL, _("Invalid response from server after load 1\n")); | ||
| 127 | else | ||
| 128 | load_5min = strtod(temp_ptr, NULL); | ||
| 129 | |||
| 130 | temp_ptr = (char *)strtok(NULL, "\r\n"); | ||
| 131 | if (temp_ptr == NULL) | ||
| 132 | die(STATE_CRITICAL, _("Invalid response from server after load 5\n")); | ||
| 133 | else | ||
| 134 | load_15min = strtod(temp_ptr, NULL); | ||
| 135 | |||
| 136 | switch (vars_to_check) { | ||
| 137 | case LOAD1: | ||
| 138 | strcpy(temp_buffer, "1"); | ||
| 139 | load = load_1min; | ||
| 140 | break; | ||
| 141 | case LOAD5: | ||
| 142 | strcpy(temp_buffer, "5"); | ||
| 143 | load = load_5min; | ||
| 144 | break; | ||
| 145 | default: | ||
| 146 | strcpy(temp_buffer, "15"); | ||
| 147 | load = load_15min; | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | |||
| 151 | if (check_critical_value && (load >= critical_value)) | ||
| 152 | result = STATE_CRITICAL; | ||
| 153 | else if (check_warning_value && (load >= warning_value)) | ||
| 154 | result = STATE_WARNING; | ||
| 155 | |||
| 156 | die(result, _("Load %s - %s-min load average = %0.2f"), state_text(result), temp_buffer, load); | ||
| 157 | |||
| 158 | break; | ||
| 159 | |||
| 160 | case DPU: | ||
| 161 | |||
| 162 | if (result != STATE_OK) | ||
| 163 | die(result, _("Unknown error fetching disk data\n")); | ||
| 164 | |||
| 165 | for (temp_ptr = (char *)strtok(recv_buffer, " "); temp_ptr != NULL; temp_ptr = (char *)strtok(NULL, " ")) { | ||
| 166 | |||
| 167 | if (!strcmp(temp_ptr, disk_name)) { | ||
| 168 | found_disk = true; | ||
| 169 | temp_ptr = (char *)strtok(NULL, "%"); | ||
| 170 | if (temp_ptr == NULL) | ||
| 171 | die(STATE_CRITICAL, _("Invalid response from server\n")); | ||
| 172 | else | ||
| 173 | percent_used_disk_space = strtoul(temp_ptr, NULL, 10); | ||
| 174 | break; | ||
| 175 | } | ||
| 176 | |||
| 177 | temp_ptr = (char *)strtok(NULL, "\r\n"); | ||
| 178 | } | ||
| 179 | |||
| 180 | /* error if we couldn't find the info for the disk */ | ||
| 181 | if (!found_disk) | ||
| 182 | die(STATE_CRITICAL, "CRITICAL - Disk '%s' non-existent or not mounted", disk_name); | ||
| 183 | |||
| 184 | if (check_critical_value && (percent_used_disk_space >= critical_value)) | ||
| 185 | result = STATE_CRITICAL; | ||
| 186 | else if (check_warning_value && (percent_used_disk_space >= warning_value)) | ||
| 187 | result = STATE_WARNING; | ||
| 188 | |||
| 189 | die(result, "Disk %s - %lu%% used on %s", state_text(result), percent_used_disk_space, disk_name); | ||
| 190 | |||
| 191 | break; | ||
| 192 | |||
| 193 | case NETSTAT: | ||
| 194 | |||
| 195 | if (result != STATE_OK) | ||
| 196 | die(result, _("Unknown error fetching network status\n")); | ||
| 197 | else | ||
| 198 | port_connections = strtod(recv_buffer, NULL); | ||
| 199 | |||
| 200 | if (check_critical_value && (port_connections >= critical_value)) | ||
| 201 | result = STATE_CRITICAL; | ||
| 202 | else if (check_warning_value && (port_connections >= warning_value)) | ||
| 203 | result = STATE_WARNING; | ||
| 204 | |||
| 205 | die(result, _("Net %s - %d connection%s on port %d"), state_text(result), port_connections, (port_connections == 1) ? "" : "s", | ||
| 206 | netstat_port); | ||
| 207 | |||
| 208 | break; | ||
| 209 | |||
| 210 | case PROCS: | ||
| 211 | |||
| 212 | if (result != STATE_OK) | ||
| 213 | die(result, _("Unknown error fetching process status\n")); | ||
| 214 | |||
| 215 | temp_ptr = (char *)strtok(recv_buffer, "("); | ||
| 216 | if (temp_ptr == NULL) | ||
| 217 | die(STATE_CRITICAL, _("Invalid response from server\n")); | ||
| 218 | |||
| 219 | temp_ptr = (char *)strtok(NULL, ")"); | ||
| 220 | if (temp_ptr == NULL) | ||
| 221 | die(STATE_CRITICAL, _("Invalid response from server\n")); | ||
| 222 | else | ||
| 223 | processes = strtod(temp_ptr, NULL); | ||
| 224 | |||
| 225 | if (check_critical_value && (processes >= critical_value)) | ||
| 226 | result = STATE_CRITICAL; | ||
| 227 | else if (check_warning_value && (processes >= warning_value)) | ||
| 228 | result = STATE_WARNING; | ||
| 229 | |||
| 230 | die(result, _("Process %s - %d instance%s of %s running"), state_text(result), processes, (processes == 1) ? "" : "s", | ||
| 231 | process_name); | ||
| 232 | break; | ||
| 233 | |||
| 234 | case UPTIME: | ||
| 235 | |||
| 236 | if (result != STATE_OK) | ||
| 237 | return result; | ||
| 238 | |||
| 239 | uptime_raw_hours = strtod(recv_buffer, NULL); | ||
| 240 | uptime_raw_minutes = (unsigned long)(uptime_raw_hours * 60.0); | ||
| 241 | |||
| 242 | if (check_critical_value && (uptime_raw_minutes <= critical_value)) | ||
| 243 | result = STATE_CRITICAL; | ||
| 244 | else if (check_warning_value && (uptime_raw_minutes <= warning_value)) | ||
| 245 | result = STATE_WARNING; | ||
| 246 | |||
| 247 | uptime_days = uptime_raw_minutes / 1440; | ||
| 248 | uptime_raw_minutes %= 1440; | ||
| 249 | uptime_hours = uptime_raw_minutes / 60; | ||
| 250 | uptime_raw_minutes %= 60; | ||
| 251 | uptime_minutes = uptime_raw_minutes; | ||
| 252 | |||
| 253 | die(result, _("Uptime %s - Up %d days %d hours %d minutes"), state_text(result), uptime_days, uptime_hours, uptime_minutes); | ||
| 254 | break; | ||
| 255 | |||
| 256 | default: | ||
| 257 | die(STATE_UNKNOWN, _("Nothing to check!\n")); | ||
| 258 | break; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | /* process command-line arguments */ | ||
| 263 | int process_arguments(int argc, char **argv) { | ||
| 264 | int c; | ||
| 265 | |||
| 266 | int option = 0; | ||
| 267 | static struct option longopts[] = { | ||
| 268 | {"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'}, {"critical", required_argument, 0, 'c'}, | ||
| 269 | {"warning", required_argument, 0, 'w'}, {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'}, | ||
| 270 | {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; | ||
| 271 | |||
| 272 | /* no options were supplied */ | ||
| 273 | if (argc < 2) | ||
| 274 | return ERROR; | ||
| 275 | |||
| 276 | /* backwards compatibility */ | ||
| 277 | if (!is_option(argv[1])) { | ||
| 278 | server_address = argv[1]; | ||
| 279 | argv[1] = argv[0]; | ||
| 280 | argv = &argv[1]; | ||
| 281 | argc--; | ||
| 282 | } | ||
| 283 | |||
| 284 | for (c = 1; c < argc; c++) { | ||
| 285 | if (strcmp("-to", argv[c]) == 0) | ||
| 286 | strcpy(argv[c], "-t"); | ||
| 287 | else if (strcmp("-wv", argv[c]) == 0) | ||
| 288 | strcpy(argv[c], "-w"); | ||
| 289 | else if (strcmp("-cv", argv[c]) == 0) | ||
| 290 | strcpy(argv[c], "-c"); | ||
| 291 | } | ||
| 292 | |||
| 293 | while (1) { | ||
| 294 | c = getopt_long(argc, argv, "+hVH:t:c:w:p:v:", longopts, &option); | ||
| 295 | |||
| 296 | if (c == -1 || c == EOF || c == 1) | ||
| 297 | break; | ||
| 298 | |||
| 299 | switch (c) { | ||
| 300 | case '?': /* print short usage statement if args not parsable */ | ||
| 301 | usage5(); | ||
| 302 | case 'h': /* help */ | ||
| 303 | print_help(); | ||
| 304 | exit(STATE_UNKNOWN); | ||
| 305 | case 'V': /* version */ | ||
| 306 | print_revision(progname, NP_VERSION); | ||
| 307 | exit(STATE_UNKNOWN); | ||
| 308 | case 'H': /* hostname */ | ||
| 309 | server_address = optarg; | ||
| 310 | break; | ||
| 311 | case 'p': /* port */ | ||
| 312 | if (is_intnonneg(optarg)) | ||
| 313 | server_port = atoi(optarg); | ||
| 314 | else | ||
| 315 | die(STATE_UNKNOWN, _("Server port an integer\n")); | ||
| 316 | break; | ||
| 317 | case 'v': /* variable */ | ||
| 318 | if (strcmp(optarg, "LOAD") == 0) { | ||
| 319 | strcpy(send_buffer, "LOAD\r\nQUIT\r\n"); | ||
| 320 | if (strcmp(optarg, "LOAD1") == 0) | ||
| 321 | vars_to_check = LOAD1; | ||
| 322 | else if (strcmp(optarg, "LOAD5") == 0) | ||
| 323 | vars_to_check = LOAD5; | ||
| 324 | else if (strcmp(optarg, "LOAD15") == 0) | ||
| 325 | vars_to_check = LOAD15; | ||
| 326 | } else if (strcmp(optarg, "UPTIME") == 0) { | ||
| 327 | vars_to_check = UPTIME; | ||
| 328 | strcpy(send_buffer, "UPTIME\r\n"); | ||
| 329 | } else if (strstr(optarg, "PROC") == optarg) { | ||
| 330 | vars_to_check = PROCS; | ||
| 331 | process_name = strscpy(process_name, optarg + 4); | ||
| 332 | sprintf(send_buffer, "PROCESS %s\r\n", process_name); | ||
| 333 | } else if (strstr(optarg, "NET") == optarg) { | ||
| 334 | vars_to_check = NETSTAT; | ||
| 335 | netstat_port = atoi(optarg + 3); | ||
| 336 | sprintf(send_buffer, "NETSTAT %d\r\n", netstat_port); | ||
| 337 | } else if (strstr(optarg, "DPU") == optarg) { | ||
| 338 | vars_to_check = DPU; | ||
| 339 | strcpy(send_buffer, "DISKSPACE\r\n"); | ||
| 340 | disk_name = strscpy(disk_name, optarg + 3); | ||
| 341 | } else | ||
| 342 | return ERROR; | ||
| 343 | break; | ||
| 344 | case 'w': /* warning threshold */ | ||
| 345 | warning_value = strtoul(optarg, NULL, 10); | ||
| 346 | check_warning_value = true; | ||
| 347 | break; | ||
| 348 | case 'c': /* critical threshold */ | ||
| 349 | critical_value = strtoul(optarg, NULL, 10); | ||
| 350 | check_critical_value = true; | ||
| 351 | break; | ||
| 352 | case 't': /* timeout */ | ||
| 353 | socket_timeout = atoi(optarg); | ||
| 354 | if (socket_timeout <= 0) | ||
| 355 | return ERROR; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | return OK; | ||
| 359 | } | ||
| 360 | |||
| 361 | void print_help(void) { | ||
| 362 | char *myport; | ||
| 363 | xasprintf(&myport, "%d", PORT); | ||
| 364 | |||
| 365 | print_revision(progname, NP_VERSION); | ||
| 366 | |||
| 367 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); | ||
| 368 | printf(COPYRIGHT, copyright, email); | ||
| 369 | |||
| 370 | printf("%s\n", _("This plugin attempts to contact the Over-CR collector daemon running on the")); | ||
| 371 | printf("%s\n", _("remote UNIX server in order to gather the requested system information.")); | ||
| 372 | |||
| 373 | printf("\n\n"); | ||
| 374 | |||
| 375 | print_usage(); | ||
| 376 | |||
| 377 | printf(UT_HELP_VRSN); | ||
| 378 | printf(UT_EXTRA_OPTS); | ||
| 379 | |||
| 380 | printf(UT_HOST_PORT, 'p', myport); | ||
| 381 | |||
| 382 | printf(" %s\n", "-w, --warning=INTEGER"); | ||
| 383 | printf(" %s\n", _("Threshold which will result in a warning status")); | ||
| 384 | printf(" %s\n", "-c, --critical=INTEGER"); | ||
| 385 | printf(" %s\n", _("Threshold which will result in a critical status")); | ||
| 386 | printf(" %s\n", "-v, --variable=STRING"); | ||
| 387 | printf(" %s\n", _("Variable to check. Valid variables include:")); | ||
| 388 | printf(" %s\n", _("LOAD1 = 1 minute average CPU load")); | ||
| 389 | printf(" %s\n", _("LOAD5 = 5 minute average CPU load")); | ||
| 390 | printf(" %s\n", _("LOAD15 = 15 minute average CPU load")); | ||
| 391 | printf(" %s\n", _("DPU<filesys> = percent used disk space on filesystem <filesys>")); | ||
| 392 | printf(" %s\n", _("PROC<process> = number of running processes with name <process>")); | ||
| 393 | printf(" %s\n", _("NET<port> = number of active connections on TCP port <port>")); | ||
| 394 | printf(" %s\n", _("UPTIME = system uptime in seconds")); | ||
| 395 | |||
| 396 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
| 397 | |||
| 398 | printf(UT_VERBOSE); | ||
| 399 | |||
| 400 | printf("\n"); | ||
| 401 | printf("%s\n", _("This plugin requires that Eric Molitors' Over-CR collector daemon be")); | ||
| 402 | printf("%s\n", _("running on the remote server.")); | ||
| 403 | printf("%s\n", _("Over-CR can be downloaded from http://www.molitor.org/overcr")); | ||
| 404 | printf("%s\n", _("This plugin was tested with version 0.99.53 of the Over-CR collector")); | ||
| 405 | |||
| 406 | printf("\n"); | ||
| 407 | printf("%s\n", _("Notes:")); | ||
| 408 | printf(" %s\n", _("For the available options, the critical threshold value should always be")); | ||
| 409 | printf(" %s\n", _("higher than the warning threshold value, EXCEPT with the uptime variable")); | ||
| 410 | |||
| 411 | printf(UT_SUPPORT); | ||
| 412 | } | ||
| 413 | |||
| 414 | void print_usage(void) { | ||
| 415 | printf("%s\n", _("Usage:")); | ||
| 416 | printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname); | ||
| 417 | } | ||
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index 6613634d..0ce75e0a 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c | |||
| @@ -28,62 +28,63 @@ | |||
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | const char *progname = "check_pgsql"; | 31 | #include "output.h" |
| 32 | const char *copyright = "1999-2024"; | 32 | #include "perfdata.h" |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 33 | #include "states.h" |
| 34 | |||
| 35 | #include "common.h" | 34 | #include "common.h" |
| 36 | #include "utils.h" | 35 | #include "utils.h" |
| 37 | #include "utils_cmd.h" | 36 | #include "utils_cmd.h" |
| 38 | 37 | #include "check_pgsql.d/config.h" | |
| 38 | #include "thresholds.h" | ||
| 39 | #include "netutils.h" | 39 | #include "netutils.h" |
| 40 | #include <libpq-fe.h> | 40 | #include <libpq-fe.h> |
| 41 | #include <pg_config_manual.h> | 41 | #include <pg_config_manual.h> |
| 42 | 42 | ||
| 43 | #define DEFAULT_DB "template1" | 43 | const char *progname = "check_pgsql"; |
| 44 | const char *copyright = "1999-2024"; | ||
| 45 | const char *email = "devel@monitoring-plugins.org"; | ||
| 46 | |||
| 44 | #define DEFAULT_HOST "127.0.0.1" | 47 | #define DEFAULT_HOST "127.0.0.1" |
| 45 | 48 | ||
| 46 | /* return the PSQL server version as a 3-tuple */ | 49 | /* return the PSQL server version as a 3-tuple */ |
| 47 | #define PSQL_SERVER_VERSION3(server_version) \ | 50 | #define PSQL_SERVER_VERSION3(server_version) \ |
| 48 | (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ | 51 | ((server_version) / 10000), \ |
| 49 | (server_version) - (int)((server_version) / 100) * 100 | 52 | (((server_version) / 100) - (int)(((server_version) / 10000) * 100)), \ |
| 53 | (server_version) - (int)(((server_version) / 100) * 100) | ||
| 50 | /* return true if the given host is a UNIX domain socket */ | 54 | /* return true if the given host is a UNIX domain socket */ |
| 51 | #define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) | 55 | #define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) |
| 52 | /* return a 3-tuple identifying a host/port independent of the socket type */ | 56 | /* return a 3-tuple identifying a host/port independent of the socket type */ |
| 53 | #define PSQL_SOCKET3(host, port) \ | 57 | #define PSQL_SOCKET3(host, port) \ |
| 54 | ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port | 58 | ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \ |
| 59 | PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port | ||
| 55 | 60 | ||
| 56 | enum { | 61 | typedef struct { |
| 57 | DEFAULT_PORT = 5432, | 62 | int errorcode; |
| 58 | DEFAULT_WARN = 2, | 63 | check_pgsql_config config; |
| 59 | DEFAULT_CRIT = 8 | 64 | } check_pgsql_config_wrapper; |
| 60 | }; | 65 | static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 61 | 66 | ||
| 62 | static int process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 63 | static void print_help(void); | 67 | static void print_help(void); |
| 64 | static bool is_pg_logname(char * /*username*/); | 68 | static bool is_pg_logname(char * /*username*/); |
| 65 | static int do_query(PGconn * /*conn*/, char * /*query*/); | 69 | |
| 70 | typedef enum { | ||
| 71 | QUERY_OK, | ||
| 72 | ERROR_WITH_QUERY, // critical | ||
| 73 | NO_ROWS_RETURNED, // warning | ||
| 74 | NO_COLUMNS_RETURNED, // warning | ||
| 75 | NO_DATA_RETURNED, // critica/ | ||
| 76 | RESULT_IS_NOT_NUMERIC // critical | ||
| 77 | } do_query_errorcode; | ||
| 78 | |||
| 79 | typedef struct { | ||
| 80 | do_query_errorcode error_code; | ||
| 81 | double numerical_result; | ||
| 82 | } do_query_wrapper; | ||
| 83 | static do_query_wrapper do_query(PGconn * /*conn*/, char * /*query*/); | ||
| 66 | void print_usage(void); | 84 | void print_usage(void); |
| 67 | 85 | ||
| 68 | static char *pghost = NULL; /* host name of the backend server */ | ||
| 69 | static char *pgport = NULL; /* port of the backend server */ | ||
| 70 | static char *pgoptions = NULL; | ||
| 71 | static char *pgtty = NULL; | ||
| 72 | static char dbName[NAMEDATALEN] = DEFAULT_DB; | ||
| 73 | static char *pguser = NULL; | ||
| 74 | static char *pgpasswd = NULL; | ||
| 75 | static char *pgparams = NULL; | ||
| 76 | static double twarn = (double)DEFAULT_WARN; | ||
| 77 | static double tcrit = (double)DEFAULT_CRIT; | ||
| 78 | static char *pgquery = NULL; | ||
| 79 | static char *pgqueryname = NULL; | ||
| 80 | static char *query_warning = NULL; | ||
| 81 | static char *query_critical = NULL; | ||
| 82 | static thresholds *qthresholds = NULL; | ||
| 83 | static int verbose = 0; | 86 | static int verbose = 0; |
| 84 | 87 | ||
| 85 | #define OPTID_QUERYNAME -1000 | ||
| 86 | |||
| 87 | /****************************************************************************** | 88 | /****************************************************************************** |
| 88 | 89 | ||
| 89 | The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@ | 90 | The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@ |
| @@ -139,21 +140,19 @@ int main(int argc, char **argv) { | |||
| 139 | bindtextdomain(PACKAGE, LOCALEDIR); | 140 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 140 | textdomain(PACKAGE); | 141 | textdomain(PACKAGE); |
| 141 | 142 | ||
| 142 | /* begin, by setting the parameters for a backend connection if the | ||
| 143 | * parameters are null, then the system will try to use reasonable | ||
| 144 | * defaults by looking up environment variables or, failing that, | ||
| 145 | * using hardwired constants */ | ||
| 146 | |||
| 147 | pgoptions = NULL; /* special options to start up the backend server */ | ||
| 148 | pgtty = NULL; /* debugging tty for the backend server */ | ||
| 149 | |||
| 150 | /* Parse extra opts if any */ | 143 | /* Parse extra opts if any */ |
| 151 | argv = np_extra_opts(&argc, argv, progname); | 144 | argv = np_extra_opts(&argc, argv, progname); |
| 152 | 145 | ||
| 153 | if (process_arguments(argc, argv) == ERROR) | 146 | check_pgsql_config_wrapper tmp_config = process_arguments(argc, argv); |
| 147 | if (tmp_config.errorcode == ERROR) { | ||
| 154 | usage4(_("Could not parse arguments")); | 148 | usage4(_("Could not parse arguments")); |
| 155 | if (verbose > 2) | 149 | } |
| 156 | printf("Arguments initialized\n"); | 150 | |
| 151 | const check_pgsql_config config = tmp_config.config; | ||
| 152 | |||
| 153 | if (config.output_format_is_set) { | ||
| 154 | mp_set_format(config.output_format); | ||
| 155 | } | ||
| 157 | 156 | ||
| 158 | /* Set signal handling and alarm */ | 157 | /* Set signal handling and alarm */ |
| 159 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 158 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| @@ -162,25 +161,33 @@ int main(int argc, char **argv) { | |||
| 162 | alarm(timeout_interval); | 161 | alarm(timeout_interval); |
| 163 | 162 | ||
| 164 | char *conninfo = NULL; | 163 | char *conninfo = NULL; |
| 165 | if (pgparams) | 164 | if (config.pgparams) { |
| 166 | asprintf(&conninfo, "%s ", pgparams); | 165 | asprintf(&conninfo, "%s ", config.pgparams); |
| 167 | 166 | } | |
| 168 | asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", dbName); | 167 | |
| 169 | if (pghost) | 168 | asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", config.dbName); |
| 170 | asprintf(&conninfo, "%s host = '%s'", conninfo, pghost); | 169 | if (config.pghost) { |
| 171 | if (pgport) | 170 | asprintf(&conninfo, "%s host = '%s'", conninfo, config.pghost); |
| 172 | asprintf(&conninfo, "%s port = '%s'", conninfo, pgport); | 171 | } |
| 173 | if (pgoptions) | 172 | if (config.pgport) { |
| 174 | asprintf(&conninfo, "%s options = '%s'", conninfo, pgoptions); | 173 | asprintf(&conninfo, "%s port = '%s'", conninfo, config.pgport); |
| 174 | } | ||
| 175 | if (config.pgoptions) { | ||
| 176 | asprintf(&conninfo, "%s options = '%s'", conninfo, config.pgoptions); | ||
| 177 | } | ||
| 175 | /* if (pgtty) -- ignored by PQconnectdb */ | 178 | /* if (pgtty) -- ignored by PQconnectdb */ |
| 176 | if (pguser) | 179 | if (config.pguser) { |
| 177 | asprintf(&conninfo, "%s user = '%s'", conninfo, pguser); | 180 | asprintf(&conninfo, "%s user = '%s'", conninfo, config.pguser); |
| 181 | } | ||
| 178 | 182 | ||
| 179 | if (verbose) /* do not include password (see right below) in output */ | 183 | if (verbose) { /* do not include password (see right below) in output */ |
| 180 | printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, pgpasswd ? " password = <hidden>" : ""); | 184 | printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, |
| 185 | config.pgpasswd ? " password = <hidden>" : ""); | ||
| 186 | } | ||
| 181 | 187 | ||
| 182 | if (pgpasswd) | 188 | if (config.pgpasswd) { |
| 183 | asprintf(&conninfo, "%s password = '%s'", conninfo, pgpasswd); | 189 | asprintf(&conninfo, "%s password = '%s'", conninfo, config.pgpasswd); |
| 190 | } | ||
| 184 | 191 | ||
| 185 | /* make a connection to the database */ | 192 | /* make a connection to the database */ |
| 186 | struct timeval start_timeval; | 193 | struct timeval start_timeval; |
| @@ -193,30 +200,52 @@ int main(int argc, char **argv) { | |||
| 193 | --end_timeval.tv_sec; | 200 | --end_timeval.tv_sec; |
| 194 | end_timeval.tv_usec += 1000000; | 201 | end_timeval.tv_usec += 1000000; |
| 195 | } | 202 | } |
| 196 | double elapsed_time = | 203 | double elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) + |
| 197 | (double)(end_timeval.tv_sec - start_timeval.tv_sec) + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0; | 204 | ((double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0); |
| 198 | 205 | ||
| 199 | if (verbose) | 206 | if (verbose) { |
| 200 | printf("Time elapsed: %f\n", elapsed_time); | 207 | printf("Time elapsed: %f\n", elapsed_time); |
| 208 | } | ||
| 201 | 209 | ||
| 202 | /* check to see that the backend connection was successfully made */ | 210 | /* check to see that the backend connection was successfully made */ |
| 203 | if (verbose) | 211 | if (verbose) { |
| 204 | printf("Verifying connection\n"); | 212 | printf("Verifying connection\n"); |
| 205 | if (PQstatus(conn) == CONNECTION_BAD) { | ||
| 206 | printf(_("CRITICAL - no connection to '%s' (%s).\n"), dbName, PQerrorMessage(conn)); | ||
| 207 | PQfinish(conn); | ||
| 208 | return STATE_CRITICAL; | ||
| 209 | } | 213 | } |
| 210 | 214 | ||
| 211 | int status = STATE_UNKNOWN; | 215 | mp_check overall = mp_check_init(); |
| 212 | if (elapsed_time > tcrit) { | 216 | |
| 213 | status = STATE_CRITICAL; | 217 | mp_subcheck sc_connection = mp_subcheck_init(); |
| 214 | } else if (elapsed_time > twarn) { | 218 | |
| 215 | status = STATE_WARNING; | 219 | if (PQstatus(conn) == CONNECTION_BAD) { |
| 220 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL); | ||
| 221 | xasprintf(&sc_connection.output, "no connection to '%s' (%s)", config.dbName, | ||
| 222 | PQerrorMessage(conn)); | ||
| 223 | PQfinish(conn); | ||
| 224 | mp_add_subcheck_to_check(&overall, sc_connection); | ||
| 225 | mp_exit(overall); | ||
| 216 | } else { | 226 | } else { |
| 217 | status = STATE_OK; | 227 | sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK); |
| 228 | xasprintf(&sc_connection.output, "connected to '%s'", config.dbName); | ||
| 229 | mp_add_subcheck_to_check(&overall, sc_connection); | ||
| 218 | } | 230 | } |
| 219 | 231 | ||
| 232 | mp_subcheck sc_connection_time = mp_subcheck_init(); | ||
| 233 | sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_UNKNOWN); | ||
| 234 | |||
| 235 | xasprintf(&sc_connection_time.output, "connection time: %.10g", elapsed_time); | ||
| 236 | |||
| 237 | mp_perfdata pd_connection_time = perfdata_init(); | ||
| 238 | pd_connection_time.label = "time"; | ||
| 239 | pd_connection_time.uom = "s"; | ||
| 240 | pd_connection_time = mp_set_pd_value(pd_connection_time, elapsed_time); | ||
| 241 | pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.time_thresholds); | ||
| 242 | |||
| 243 | mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time); | ||
| 244 | sc_connection_time = | ||
| 245 | mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_connection_time)); | ||
| 246 | |||
| 247 | mp_add_subcheck_to_check(&overall, sc_connection_time); | ||
| 248 | |||
| 220 | if (verbose) { | 249 | if (verbose) { |
| 221 | char *server_host = PQhost(conn); | 250 | char *server_host = PQhost(conn); |
| 222 | int server_version = PQserverVersion(conn); | 251 | int server_version = PQserverVersion(conn); |
| @@ -224,25 +253,91 @@ int main(int argc, char **argv) { | |||
| 224 | printf("Successfully connected to database %s (user %s) " | 253 | printf("Successfully connected to database %s (user %s) " |
| 225 | "at server %s%s%s (server version: %d.%d.%d, " | 254 | "at server %s%s%s (server version: %d.%d.%d, " |
| 226 | "protocol version: %d, pid: %d)\n", | 255 | "protocol version: %d, pid: %d)\n", |
| 227 | PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)), PSQL_SERVER_VERSION3(server_version), | 256 | PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)), |
| 228 | PQprotocolVersion(conn), PQbackendPID(conn)); | 257 | PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn)); |
| 229 | } | 258 | } |
| 230 | 259 | ||
| 231 | printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), dbName, elapsed_time, | 260 | if (config.pgquery) { |
| 232 | fperfdata("time", elapsed_time, "s", !!(twarn > 0.0), twarn, !!(tcrit > 0.0), tcrit, true, 0, false, 0)); | 261 | mp_subcheck sc_query = mp_subcheck_init(); |
| 262 | sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN); | ||
| 263 | if (config.pgqueryname) { | ||
| 264 | xasprintf(&sc_query.output, "query '%s'", config.pgqueryname); | ||
| 265 | } else { | ||
| 266 | xasprintf(&sc_query.output, "query '%s'", config.pgquery); | ||
| 267 | } | ||
| 268 | |||
| 269 | do_query_wrapper query_result = do_query(conn, config.pgquery); | ||
| 270 | |||
| 271 | switch (query_result.error_code) { | ||
| 272 | case QUERY_OK: { | ||
| 273 | // Query was successful and there is a numerical result | ||
| 274 | sc_query = mp_set_subcheck_state(sc_query, STATE_OK); | ||
| 275 | xasprintf(&sc_query.output, "%s succeeded", sc_query.output); | ||
| 276 | |||
| 277 | mp_perfdata pd_query = perfdata_init(); | ||
| 278 | pd_query = mp_set_pd_value(pd_query, query_result.numerical_result); | ||
| 279 | pd_query = mp_pd_set_thresholds(pd_query, config.qthresholds); | ||
| 280 | pd_query.label = "query"; | ||
| 281 | |||
| 282 | mp_subcheck sc_query_compare = mp_subcheck_init(); | ||
| 283 | mp_state_enum query_compare_state = mp_get_pd_status(pd_query); | ||
| 284 | |||
| 285 | sc_query_compare = mp_set_subcheck_state(sc_query_compare, query_compare_state); | ||
| 286 | mp_add_perfdata_to_subcheck(&sc_query_compare, pd_query); | ||
| 287 | |||
| 288 | if (query_compare_state == STATE_OK) { | ||
| 289 | xasprintf(&sc_query_compare.output, "query result '%f' is within thresholds", | ||
| 290 | query_result.numerical_result); | ||
| 291 | } else { | ||
| 292 | xasprintf(&sc_query_compare.output, "query result '%f' is violating thresholds", | ||
| 293 | query_result.numerical_result); | ||
| 294 | } | ||
| 295 | mp_add_subcheck_to_check(&overall, sc_query_compare); | ||
| 296 | |||
| 297 | } break; | ||
| 298 | case ERROR_WITH_QUERY: | ||
| 299 | xasprintf(&sc_query.output, "%s - Error with query: %s", sc_query.output, | ||
| 300 | PQerrorMessage(conn)); | ||
| 301 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 302 | break; | ||
| 303 | case NO_ROWS_RETURNED: | ||
| 304 | xasprintf(&sc_query.output, "%s - no rows were returned by the query", sc_query.output); | ||
| 305 | sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING); | ||
| 306 | break; | ||
| 307 | case NO_COLUMNS_RETURNED: | ||
| 308 | xasprintf(&sc_query.output, "%s - no columns were returned by the query", | ||
| 309 | sc_query.output); | ||
| 310 | sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING); | ||
| 311 | break; | ||
| 312 | case NO_DATA_RETURNED: | ||
| 313 | xasprintf(&sc_query.output, "%s - no data was returned by the query", sc_query.output); | ||
| 314 | sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING); | ||
| 315 | break; | ||
| 316 | case RESULT_IS_NOT_NUMERIC: | ||
| 317 | xasprintf(&sc_query.output, "%s - result of the query is not numeric", sc_query.output); | ||
| 318 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 319 | break; | ||
| 320 | }; | ||
| 233 | 321 | ||
| 234 | int query_status = STATE_UNKNOWN; | 322 | mp_add_subcheck_to_check(&overall, sc_query); |
| 235 | if (pgquery) | 323 | } |
| 236 | query_status = do_query(conn, pgquery); | ||
| 237 | 324 | ||
| 238 | if (verbose) | 325 | if (verbose) { |
| 239 | printf("Closing connection\n"); | 326 | printf("Closing connection\n"); |
| 327 | } | ||
| 240 | PQfinish(conn); | 328 | PQfinish(conn); |
| 241 | return (pgquery && query_status > status) ? query_status : status; | 329 | |
| 330 | mp_exit(overall); | ||
| 242 | } | 331 | } |
| 243 | 332 | ||
| 244 | /* process command-line arguments */ | 333 | /* process command-line arguments */ |
| 245 | int process_arguments(int argc, char **argv) { | 334 | static check_pgsql_config_wrapper process_arguments(int argc, char **argv) { |
| 335 | |||
| 336 | enum { | ||
| 337 | OPTID_QUERYNAME = CHAR_MAX + 1, | ||
| 338 | output_format_index, | ||
| 339 | }; | ||
| 340 | |||
| 246 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, | 341 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, |
| 247 | {"version", no_argument, 0, 'V'}, | 342 | {"version", no_argument, 0, 'V'}, |
| 248 | {"timeout", required_argument, 0, 't'}, | 343 | {"timeout", required_argument, 0, 't'}, |
| @@ -260,16 +355,35 @@ int process_arguments(int argc, char **argv) { | |||
| 260 | {"query_critical", required_argument, 0, 'C'}, | 355 | {"query_critical", required_argument, 0, 'C'}, |
| 261 | {"query_warning", required_argument, 0, 'W'}, | 356 | {"query_warning", required_argument, 0, 'W'}, |
| 262 | {"verbose", no_argument, 0, 'v'}, | 357 | {"verbose", no_argument, 0, 'v'}, |
| 358 | {"output-format", required_argument, 0, output_format_index}, | ||
| 263 | {0, 0, 0, 0}}; | 359 | {0, 0, 0, 0}}; |
| 264 | 360 | ||
| 361 | check_pgsql_config_wrapper result = { | ||
| 362 | .errorcode = OK, | ||
| 363 | .config = check_pgsql_config_init(), | ||
| 364 | }; | ||
| 365 | |||
| 265 | while (true) { | 366 | while (true) { |
| 266 | int option = 0; | 367 | int option = 0; |
| 267 | int option_char = getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option); | 368 | int option_char = |
| 369 | getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option); | ||
| 268 | 370 | ||
| 269 | if (option_char == EOF) | 371 | if (option_char == EOF) { |
| 270 | break; | 372 | break; |
| 373 | } | ||
| 271 | 374 | ||
| 272 | switch (option_char) { | 375 | switch (option_char) { |
| 376 | case output_format_index: { | ||
| 377 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 378 | if (!parser.parsing_success) { | ||
| 379 | printf("Invalid output format: %s\n", optarg); | ||
| 380 | exit(STATE_UNKNOWN); | ||
| 381 | } | ||
| 382 | |||
| 383 | result.config.output_format_is_set = true; | ||
| 384 | result.config.output_format = parser.output_format; | ||
| 385 | break; | ||
| 386 | } | ||
| 273 | case '?': /* usage */ | 387 | case '?': /* usage */ |
| 274 | usage5(); | 388 | usage5(); |
| 275 | case 'h': /* help */ | 389 | case 'h': /* help */ |
| @@ -279,68 +393,89 @@ int process_arguments(int argc, char **argv) { | |||
| 279 | print_revision(progname, NP_VERSION); | 393 | print_revision(progname, NP_VERSION); |
| 280 | exit(STATE_UNKNOWN); | 394 | exit(STATE_UNKNOWN); |
| 281 | case 't': /* timeout period */ | 395 | case 't': /* timeout period */ |
| 282 | if (!is_integer(optarg)) | 396 | if (!is_integer(optarg)) { |
| 283 | usage2(_("Timeout interval must be a positive integer"), optarg); | 397 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 284 | else | 398 | } else { |
| 285 | timeout_interval = atoi(optarg); | 399 | timeout_interval = atoi(optarg); |
| 400 | } | ||
| 286 | break; | 401 | break; |
| 287 | case 'c': /* critical time threshold */ | 402 | case 'c': /* critical time threshold */ { |
| 288 | if (!is_nonnegative(optarg)) | 403 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 289 | usage2(_("Critical threshold must be a positive integer"), optarg); | 404 | if (tmp.error != MP_PARSING_SUCCES) { |
| 290 | else | 405 | die(STATE_UNKNOWN, "failed to parse critical time threshold"); |
| 291 | tcrit = strtod(optarg, NULL); | 406 | } |
| 292 | break; | 407 | result.config.time_thresholds = |
| 293 | case 'w': /* warning time threshold */ | 408 | mp_thresholds_set_crit(result.config.time_thresholds, tmp.range); |
| 294 | if (!is_nonnegative(optarg)) | 409 | } break; |
| 295 | usage2(_("Warning threshold must be a positive integer"), optarg); | 410 | case 'w': /* warning time threshold */ { |
| 296 | else | 411 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 297 | twarn = strtod(optarg, NULL); | 412 | if (tmp.error != MP_PARSING_SUCCES) { |
| 298 | break; | 413 | die(STATE_UNKNOWN, "failed to parse warning time threshold"); |
| 299 | case 'C': /* critical query threshold */ | 414 | } |
| 300 | query_critical = optarg; | 415 | result.config.time_thresholds = |
| 301 | break; | 416 | mp_thresholds_set_warn(result.config.time_thresholds, tmp.range); |
| 302 | case 'W': /* warning query threshold */ | 417 | } break; |
| 303 | query_warning = optarg; | 418 | case 'C': /* critical query threshold */ { |
| 304 | break; | 419 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 420 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 421 | die(STATE_UNKNOWN, "failed to parse critical query threshold"); | ||
| 422 | } | ||
| 423 | |||
| 424 | result.config.qthresholds = | ||
| 425 | mp_thresholds_set_crit(result.config.qthresholds, tmp.range); | ||
| 426 | |||
| 427 | } break; | ||
| 428 | case 'W': /* warning query threshold */ { | ||
| 429 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 430 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 431 | die(STATE_UNKNOWN, "failed to parse warning query threshold"); | ||
| 432 | } | ||
| 433 | result.config.qthresholds = | ||
| 434 | mp_thresholds_set_warn(result.config.qthresholds, tmp.range); | ||
| 435 | } break; | ||
| 305 | case 'H': /* host */ | 436 | case 'H': /* host */ |
| 306 | if ((*optarg != '/') && (!is_host(optarg))) | 437 | if ((*optarg != '/') && (!is_host(optarg))) { |
| 307 | usage2(_("Invalid hostname/address"), optarg); | 438 | usage2(_("Invalid hostname/address"), optarg); |
| 308 | else | 439 | } else { |
| 309 | pghost = optarg; | 440 | result.config.pghost = optarg; |
| 441 | } | ||
| 310 | break; | 442 | break; |
| 311 | case 'P': /* port */ | 443 | case 'P': /* port */ |
| 312 | if (!is_integer(optarg)) | 444 | if (!is_integer(optarg)) { |
| 313 | usage2(_("Port must be a positive integer"), optarg); | 445 | usage2(_("Port must be a positive integer"), optarg); |
| 314 | else | 446 | } else { |
| 315 | pgport = optarg; | 447 | result.config.pgport = optarg; |
| 448 | } | ||
| 316 | break; | 449 | break; |
| 317 | case 'd': /* database name */ | 450 | case 'd': /* database name */ |
| 318 | if (strlen(optarg) >= NAMEDATALEN) { | 451 | if (strlen(optarg) >= NAMEDATALEN) { |
| 319 | usage2(_("Database name exceeds the maximum length"), optarg); | 452 | usage2(_("Database name exceeds the maximum length"), optarg); |
| 320 | } | 453 | } |
| 321 | snprintf(dbName, NAMEDATALEN, "%s", optarg); | 454 | snprintf(result.config.dbName, NAMEDATALEN, "%s", optarg); |
| 322 | break; | 455 | break; |
| 323 | case 'l': /* login name */ | 456 | case 'l': /* login name */ |
| 324 | if (!is_pg_logname(optarg)) | 457 | if (!is_pg_logname(optarg)) { |
| 325 | usage2(_("User name is not valid"), optarg); | 458 | usage2(_("User name is not valid"), optarg); |
| 326 | else | 459 | } else { |
| 327 | pguser = optarg; | 460 | result.config.pguser = optarg; |
| 461 | } | ||
| 328 | break; | 462 | break; |
| 329 | case 'p': /* authentication password */ | 463 | case 'p': /* authentication password */ |
| 330 | case 'a': | 464 | case 'a': |
| 331 | pgpasswd = optarg; | 465 | result.config.pgpasswd = optarg; |
| 332 | break; | 466 | break; |
| 333 | case 'o': | 467 | case 'o': |
| 334 | if (pgparams) | 468 | if (result.config.pgparams) { |
| 335 | asprintf(&pgparams, "%s %s", pgparams, optarg); | 469 | asprintf(&result.config.pgparams, "%s %s", result.config.pgparams, optarg); |
| 336 | else | 470 | } else { |
| 337 | asprintf(&pgparams, "%s", optarg); | 471 | asprintf(&result.config.pgparams, "%s", optarg); |
| 472 | } | ||
| 338 | break; | 473 | break; |
| 339 | case 'q': | 474 | case 'q': |
| 340 | pgquery = optarg; | 475 | result.config.pgquery = optarg; |
| 341 | break; | 476 | break; |
| 342 | case OPTID_QUERYNAME: | 477 | case OPTID_QUERYNAME: |
| 343 | pgqueryname = optarg; | 478 | result.config.pgqueryname = optarg; |
| 344 | break; | 479 | break; |
| 345 | case 'v': | 480 | case 'v': |
| 346 | verbose++; | 481 | verbose++; |
| @@ -348,9 +483,7 @@ int process_arguments(int argc, char **argv) { | |||
| 348 | } | 483 | } |
| 349 | } | 484 | } |
| 350 | 485 | ||
| 351 | set_thresholds(&qthresholds, query_warning, query_critical); | 486 | return result; |
| 352 | |||
| 353 | return OK; | ||
| 354 | } | 487 | } |
| 355 | 488 | ||
| 356 | /** | 489 | /** |
| @@ -377,9 +510,10 @@ should be added.</para> | |||
| 377 | -@@ | 510 | -@@ |
| 378 | ******************************************************************************/ | 511 | ******************************************************************************/ |
| 379 | 512 | ||
| 380 | bool is_pg_logname(char *username) { | 513 | static bool is_pg_logname(char *username) { |
| 381 | if (strlen(username) > NAMEDATALEN - 1) | 514 | if (strlen(username) > NAMEDATALEN - 1) { |
| 382 | return (false); | 515 | return (false); |
| 516 | } | ||
| 383 | return (true); | 517 | return (true); |
| 384 | } | 518 | } |
| 385 | 519 | ||
| @@ -394,7 +528,7 @@ bool is_pg_logname(char *username) { | |||
| 394 | void print_help(void) { | 528 | void print_help(void) { |
| 395 | char *myport; | 529 | char *myport; |
| 396 | 530 | ||
| 397 | xasprintf(&myport, "%d", DEFAULT_PORT); | 531 | xasprintf(&myport, "%d", 5432); |
| 398 | 532 | ||
| 399 | print_revision(progname, NP_VERSION); | 533 | print_revision(progname, NP_VERSION); |
| 400 | 534 | ||
| @@ -430,12 +564,13 @@ void print_help(void) { | |||
| 430 | printf(" %s\n", "--queryname=STRING"); | 564 | printf(" %s\n", "--queryname=STRING"); |
| 431 | printf(" %s\n", _("A name for the query, this string is used instead of the query")); | 565 | printf(" %s\n", _("A name for the query, this string is used instead of the query")); |
| 432 | printf(" %s\n", _("in the long output of the plugin")); | 566 | printf(" %s\n", _("in the long output of the plugin")); |
| 433 | printf(" %s\n", "-W, --query-warning=RANGE"); | 567 | printf(" %s\n", "-W, --query_warning=RANGE"); |
| 434 | printf(" %s\n", _("SQL query value to result in warning status (double)")); | 568 | printf(" %s\n", _("SQL query value to result in warning status (double)")); |
| 435 | printf(" %s\n", "-C, --query-critical=RANGE"); | 569 | printf(" %s\n", "-C, --query_critical=RANGE"); |
| 436 | printf(" %s\n", _("SQL query value to result in critical status (double)")); | 570 | printf(" %s\n", _("SQL query value to result in critical status (double)")); |
| 437 | 571 | ||
| 438 | printf(UT_VERBOSE); | 572 | printf(UT_VERBOSE); |
| 573 | printf(UT_OUTPUT_FORMAT); | ||
| 439 | 574 | ||
| 440 | printf("\n"); | 575 | printf("\n"); |
| 441 | printf(" %s\n", _("All parameters are optional.")); | 576 | printf(" %s\n", _("All parameters are optional.")); |
| @@ -447,29 +582,39 @@ void print_help(void) { | |||
| 447 | 582 | ||
| 448 | printf(" %s\n", _("If a query is specified using the -q option, it will be executed after")); | 583 | printf(" %s\n", _("If a query is specified using the -q option, it will be executed after")); |
| 449 | printf(" %s\n", _("connecting to the server. The result from the query has to be numeric.")); | 584 | printf(" %s\n", _("connecting to the server. The result from the query has to be numeric.")); |
| 450 | printf(" %s\n", _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); | 585 | printf(" %s\n", |
| 586 | _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); | ||
| 451 | printf(" %s\n", _("of the last command is taken into account only. The value of the first")); | 587 | printf(" %s\n", _("of the last command is taken into account only. The value of the first")); |
| 452 | printf(" %s\n", _("column in the first row is used as the check result. If a second column is")); | 588 | printf(" %s\n", |
| 589 | _("column in the first row is used as the check result. If a second column is")); | ||
| 453 | printf(" %s\n", _("present in the result set, this is added to the plugin output with a")); | 590 | printf(" %s\n", _("present in the result set, this is added to the plugin output with a")); |
| 454 | printf(" %s\n", _("prefix of \"Extra Info:\". This information can be displayed in the system")); | 591 | printf(" %s\n", |
| 592 | _("prefix of \"Extra Info:\". This information can be displayed in the system")); | ||
| 455 | printf(" %s\n\n", _("executing the plugin.")); | 593 | printf(" %s\n\n", _("executing the plugin.")); |
| 456 | 594 | ||
| 457 | printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); | 595 | printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); |
| 458 | printf(" %s\n\n", _("for details about how to access internal statistics of the database server.")); | 596 | printf(" %s\n\n", |
| 597 | _("for details about how to access internal statistics of the database server.")); | ||
| 459 | 598 | ||
| 460 | printf(" %s\n", _("For a list of available connection parameters which may be used with the -o")); | 599 | printf(" %s\n", |
| 461 | printf(" %s\n", _("command line option, see the documentation for PQconnectdb() in the chapter")); | 600 | _("For a list of available connection parameters which may be used with the -o")); |
| 601 | printf(" %s\n", | ||
| 602 | _("command line option, see the documentation for PQconnectdb() in the chapter")); | ||
| 462 | printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); | 603 | printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); |
| 463 | printf(" %s\n", _("used to specify a service name in pg_service.conf to be used for additional")); | 604 | printf(" %s\n", |
| 605 | _("used to specify a service name in pg_service.conf to be used for additional")); | ||
| 464 | printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); | 606 | printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); |
| 465 | printf(" %s\n\n", _("-o 'sslmode=require'.")); | 607 | printf(" %s\n\n", _("-o 'sslmode=require'.")); |
| 466 | 608 | ||
| 467 | printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); | 609 | printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); |
| 468 | printf(" %s\n", _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); | 610 | printf(" %s\n", |
| 611 | _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); | ||
| 469 | printf(" %s\n\n", _("connections (start the postmaster with the -i option).")); | 612 | printf(" %s\n\n", _("connections (start the postmaster with the -i option).")); |
| 470 | 613 | ||
| 471 | printf(" %s\n", _("Typically, the monitoring user (unless the --logname option is used) should be")); | 614 | printf(" %s\n", |
| 472 | printf(" %s\n", _("able to connect to the database without a password. The plugin can also send")); | 615 | _("Typically, the monitoring user (unless the --logname option is used) should be")); |
| 616 | printf(" %s\n", | ||
| 617 | _("able to connect to the database without a password. The plugin can also send")); | ||
| 473 | printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); | 618 | printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); |
| 474 | 619 | ||
| 475 | printf(UT_SUPPORT); | 620 | printf(UT_SUPPORT); |
| @@ -482,64 +627,66 @@ void print_usage(void) { | |||
| 482 | "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); | 627 | "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); |
| 483 | } | 628 | } |
| 484 | 629 | ||
| 485 | int do_query(PGconn *conn, char *query) { | 630 | static do_query_wrapper do_query(PGconn *conn, char *query) { |
| 486 | if (verbose) | 631 | if (verbose) { |
| 487 | printf("Executing SQL query \"%s\".\n", query); | 632 | printf("Executing SQL query \"%s\".\n", query); |
| 633 | } | ||
| 488 | PGresult *res = PQexec(conn, query); | 634 | PGresult *res = PQexec(conn, query); |
| 489 | 635 | ||
| 636 | do_query_wrapper result = { | ||
| 637 | .error_code = QUERY_OK, | ||
| 638 | }; | ||
| 639 | |||
| 490 | if (PGRES_TUPLES_OK != PQresultStatus(res)) { | 640 | if (PGRES_TUPLES_OK != PQresultStatus(res)) { |
| 491 | printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), PQerrorMessage(conn)); | 641 | // TODO |
| 492 | return STATE_CRITICAL; | 642 | // printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), |
| 643 | // PQerrorMessage(conn)); | ||
| 644 | result.error_code = ERROR_WITH_QUERY; | ||
| 645 | return result; | ||
| 493 | } | 646 | } |
| 494 | 647 | ||
| 495 | if (PQntuples(res) < 1) { | 648 | if (PQntuples(res) < 1) { |
| 496 | printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); | 649 | // TODO |
| 497 | return STATE_WARNING; | 650 | // printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); |
| 651 | result.error_code = NO_ROWS_RETURNED; | ||
| 652 | return result; | ||
| 498 | } | 653 | } |
| 499 | 654 | ||
| 500 | if (PQnfields(res) < 1) { | 655 | if (PQnfields(res) < 1) { |
| 501 | printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); | 656 | // TODO |
| 502 | return STATE_WARNING; | 657 | // printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); |
| 658 | result.error_code = NO_COLUMNS_RETURNED; | ||
| 659 | return result; | ||
| 503 | } | 660 | } |
| 504 | 661 | ||
| 505 | char *val_str = PQgetvalue(res, 0, 0); | 662 | char *val_str = PQgetvalue(res, 0, 0); |
| 506 | if (!val_str) { | 663 | if (!val_str) { |
| 507 | printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); | 664 | // TODO |
| 508 | return STATE_CRITICAL; | 665 | // printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); |
| 666 | result.error_code = NO_DATA_RETURNED; | ||
| 667 | return result; | ||
| 509 | } | 668 | } |
| 510 | 669 | ||
| 511 | char *endptr = NULL; | 670 | char *endptr = NULL; |
| 512 | double value = strtod(val_str, &endptr); | 671 | double value = strtod(val_str, &endptr); |
| 513 | if (verbose) | 672 | if (verbose) { |
| 514 | printf("Query result: %f\n", value); | 673 | printf("Query result: %f\n", value); |
| 674 | } | ||
| 515 | 675 | ||
| 516 | if (endptr == val_str) { | 676 | if (endptr == val_str) { |
| 517 | printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); | 677 | // TODO |
| 518 | return STATE_CRITICAL; | 678 | // printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); |
| 679 | result.error_code = RESULT_IS_NOT_NUMERIC; | ||
| 680 | return result; | ||
| 519 | } | 681 | } |
| 520 | 682 | ||
| 521 | if ((endptr != NULL) && (*endptr != '\0')) { | 683 | if ((endptr != NULL) && (*endptr != '\0')) { |
| 522 | if (verbose) | 684 | if (verbose) { |
| 523 | printf("Garbage after value: %s.\n", endptr); | 685 | printf("Garbage after value: %s.\n", endptr); |
| 686 | } | ||
| 524 | } | 687 | } |
| 525 | 688 | ||
| 526 | int my_status = get_status(value, qthresholds); | 689 | result.numerical_result = value; |
| 527 | printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK") | ||
| 528 | : (my_status == STATE_WARNING) ? _("WARNING") | ||
| 529 | : (my_status == STATE_CRITICAL) ? _("CRITICAL") | ||
| 530 | : _("UNKNOWN")); | ||
| 531 | if (pgqueryname) { | ||
| 532 | printf(_("%s returned %f"), pgqueryname, value); | ||
| 533 | } else { | ||
| 534 | printf(_("'%s' returned %f"), query, value); | ||
| 535 | } | ||
| 536 | 690 | ||
| 537 | printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", query_critical ? query_critical : ""); | 691 | return result; |
| 538 | if (PQnfields(res) > 1) { | ||
| 539 | char *extra_info = PQgetvalue(res, 0, 1); | ||
| 540 | if (extra_info != NULL) { | ||
| 541 | printf("Extra Info: %s\n", extra_info); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | return my_status; | ||
| 545 | } | 692 | } |
diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h new file mode 100644 index 00000000..7cf0637b --- /dev/null +++ b/plugins/check_pgsql.d/config.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "perfdata.h" | ||
| 6 | #include "thresholds.h" | ||
| 7 | #include <stddef.h> | ||
| 8 | #include <pg_config_manual.h> | ||
| 9 | |||
| 10 | #define DEFAULT_DB "template1" | ||
| 11 | |||
| 12 | enum { | ||
| 13 | DEFAULT_WARN = 2, | ||
| 14 | DEFAULT_CRIT = 8, | ||
| 15 | }; | ||
| 16 | |||
| 17 | typedef struct { | ||
| 18 | char *pghost; /* host name of the backend server */ | ||
| 19 | char *pgport; /* port of the backend server */ | ||
| 20 | char *pgoptions; /* special options to start up the backend server */ | ||
| 21 | char *pgtty; /* debugging tty for the backend server */ | ||
| 22 | char dbName[NAMEDATALEN]; | ||
| 23 | char *pguser; | ||
| 24 | char *pgpasswd; | ||
| 25 | char *pgparams; | ||
| 26 | char *pgquery; | ||
| 27 | char *pgqueryname; | ||
| 28 | |||
| 29 | mp_thresholds time_thresholds; | ||
| 30 | mp_thresholds qthresholds; | ||
| 31 | |||
| 32 | bool output_format_is_set; | ||
| 33 | mp_output_format output_format; | ||
| 34 | } check_pgsql_config; | ||
| 35 | |||
| 36 | /* begin, by setting the parameters for a backend connection if the | ||
| 37 | * parameters are null, then the system will try to use reasonable | ||
| 38 | * defaults by looking up environment variables or, failing that, | ||
| 39 | * using hardwired constants | ||
| 40 | * this targets .pgoptions and .pgtty | ||
| 41 | */ | ||
| 42 | |||
| 43 | check_pgsql_config check_pgsql_config_init() { | ||
| 44 | check_pgsql_config tmp = { | ||
| 45 | .pghost = NULL, | ||
| 46 | .pgport = NULL, | ||
| 47 | .pgoptions = NULL, | ||
| 48 | .pgtty = NULL, | ||
| 49 | .dbName = DEFAULT_DB, | ||
| 50 | .pguser = NULL, | ||
| 51 | .pgpasswd = NULL, | ||
| 52 | .pgparams = NULL, | ||
| 53 | .pgquery = NULL, | ||
| 54 | .pgqueryname = NULL, | ||
| 55 | |||
| 56 | .time_thresholds = mp_thresholds_init(), | ||
| 57 | .qthresholds = mp_thresholds_init(), | ||
| 58 | |||
| 59 | .output_format_is_set = false, | ||
| 60 | }; | ||
| 61 | |||
| 62 | mp_range tmp_range = mp_range_init(); | ||
| 63 | tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_WARN)); | ||
| 64 | tmp.time_thresholds = mp_thresholds_set_warn(tmp.time_thresholds, tmp_range); | ||
| 65 | |||
| 66 | tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_CRIT)); | ||
| 67 | tmp.time_thresholds = mp_thresholds_set_crit(tmp.time_thresholds, tmp_range); | ||
| 68 | |||
| 69 | return tmp; | ||
| 70 | } | ||
diff --git a/plugins/check_ping.c b/plugins/check_ping.c index 4aafaf41..61feb958 100644 --- a/plugins/check_ping.c +++ b/plugins/check_ping.c | |||
| @@ -36,61 +36,52 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 36 | #include "netutils.h" | 36 | #include "netutils.h" |
| 37 | #include "popen.h" | 37 | #include "popen.h" |
| 38 | #include "utils.h" | 38 | #include "utils.h" |
| 39 | #include "check_ping.d/config.h" | ||
| 40 | #include "../lib/states.h" | ||
| 39 | 41 | ||
| 40 | #include <signal.h> | 42 | #include <signal.h> |
| 41 | 43 | ||
| 42 | #define WARN_DUPLICATES "DUPLICATES FOUND! " | 44 | #define WARN_DUPLICATES "DUPLICATES FOUND! " |
| 43 | #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */ | ||
| 44 | 45 | ||
| 45 | enum { | 46 | typedef struct { |
| 46 | UNKNOWN_PACKET_LOSS = 200, /* 200% */ | 47 | int errorcode; |
| 47 | DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */ | 48 | check_ping_config config; |
| 48 | }; | 49 | } check_ping_config_wrapper; |
| 50 | static check_ping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 51 | static check_ping_config_wrapper validate_arguments(check_ping_config_wrapper /*config_wrapper*/); | ||
| 49 | 52 | ||
| 50 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 53 | static int get_threshold(char * /*arg*/, double * /*trta*/, int * /*tpl*/); |
| 51 | static int get_threshold(char * /*arg*/, float * /*trta*/, int * /*tpl*/); | 54 | |
| 52 | static int validate_arguments(void); | 55 | typedef struct { |
| 53 | static int run_ping(const char *cmd, const char *addr); | 56 | mp_state_enum state; |
| 54 | static int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr); | 57 | double round_trip_average; |
| 58 | int packet_loss; | ||
| 59 | } ping_result; | ||
| 60 | static ping_result run_ping(const char *cmd, const char *addr, double /*crta*/); | ||
| 61 | |||
| 62 | static mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr); | ||
| 55 | static void print_help(void); | 63 | static void print_help(void); |
| 56 | void print_usage(void); | 64 | void print_usage(void); |
| 57 | 65 | ||
| 58 | static bool display_html = false; | ||
| 59 | static int wpl = UNKNOWN_PACKET_LOSS; | ||
| 60 | static int cpl = UNKNOWN_PACKET_LOSS; | ||
| 61 | static float wrta = UNKNOWN_TRIP_TIME; | ||
| 62 | static float crta = UNKNOWN_TRIP_TIME; | ||
| 63 | static char **addresses = NULL; | ||
| 64 | static int n_addresses = 0; | ||
| 65 | static int max_addr = 1; | ||
| 66 | static int max_packets = -1; | ||
| 67 | static int verbose = 0; | 66 | static int verbose = 0; |
| 68 | 67 | ||
| 69 | static float rta = UNKNOWN_TRIP_TIME; | ||
| 70 | static int pl = UNKNOWN_PACKET_LOSS; | ||
| 71 | |||
| 72 | static char *warn_text; | 68 | static char *warn_text; |
| 73 | 69 | ||
| 74 | int main(int argc, char **argv) { | 70 | int main(int argc, char **argv) { |
| 75 | char *cmd = NULL; | ||
| 76 | char *rawcmd = NULL; | ||
| 77 | int result = STATE_UNKNOWN; | ||
| 78 | int this_result = STATE_UNKNOWN; | ||
| 79 | int i; | ||
| 80 | |||
| 81 | setlocale(LC_ALL, ""); | 71 | setlocale(LC_ALL, ""); |
| 82 | setlocale(LC_NUMERIC, "C"); | 72 | setlocale(LC_NUMERIC, "C"); |
| 83 | bindtextdomain(PACKAGE, LOCALEDIR); | 73 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 84 | textdomain(PACKAGE); | 74 | textdomain(PACKAGE); |
| 85 | 75 | ||
| 86 | addresses = malloc(sizeof(char *) * max_addr); | ||
| 87 | addresses[0] = NULL; | ||
| 88 | |||
| 89 | /* Parse extra opts if any */ | 76 | /* Parse extra opts if any */ |
| 90 | argv = np_extra_opts(&argc, argv, progname); | 77 | argv = np_extra_opts(&argc, argv, progname); |
| 91 | 78 | ||
| 92 | if (process_arguments(argc, argv) == ERROR) | 79 | check_ping_config_wrapper tmp_config = process_arguments(argc, argv); |
| 80 | if (tmp_config.errorcode == ERROR) { | ||
| 93 | usage4(_("Could not parse arguments")); | 81 | usage4(_("Could not parse arguments")); |
| 82 | } | ||
| 83 | |||
| 84 | const check_ping_config config = tmp_config.config; | ||
| 94 | 85 | ||
| 95 | /* Set signal handling and alarm */ | 86 | /* Set signal handling and alarm */ |
| 96 | if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { | 87 | if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { |
| @@ -105,71 +96,90 @@ int main(int argc, char **argv) { | |||
| 105 | alarm(timeout_interval); | 96 | alarm(timeout_interval); |
| 106 | #endif | 97 | #endif |
| 107 | 98 | ||
| 108 | for (i = 0; i < n_addresses; i++) { | 99 | int result = STATE_UNKNOWN; |
| 109 | 100 | char *rawcmd = NULL; | |
| 101 | for (size_t i = 0; i < config.n_addresses; i++) { | ||
| 110 | #ifdef PING6_COMMAND | 102 | #ifdef PING6_COMMAND |
| 111 | if (address_family != AF_INET && is_inet6_addr(addresses[i])) | 103 | if (address_family != AF_INET && is_inet6_addr(config.addresses[i])) { |
| 112 | rawcmd = strdup(PING6_COMMAND); | 104 | rawcmd = strdup(PING6_COMMAND); |
| 113 | else | 105 | } else { |
| 114 | rawcmd = strdup(PING_COMMAND); | 106 | rawcmd = strdup(PING_COMMAND); |
| 107 | } | ||
| 115 | #else | 108 | #else |
| 116 | rawcmd = strdup(PING_COMMAND); | 109 | rawcmd = strdup(PING_COMMAND); |
| 117 | #endif | 110 | #endif |
| 118 | 111 | ||
| 119 | /* does the host address of number of packets argument come first? */ | 112 | char *cmd = NULL; |
| 113 | |||
| 114 | /* does the host address of number of packets argument come first? */ | ||
| 120 | #ifdef PING_PACKETS_FIRST | 115 | #ifdef PING_PACKETS_FIRST |
| 121 | # ifdef PING_HAS_TIMEOUT | 116 | # ifdef PING_HAS_TIMEOUT |
| 122 | xasprintf(&cmd, rawcmd, timeout_interval, max_packets, addresses[i]); | 117 | xasprintf(&cmd, rawcmd, timeout_interval, config.max_packets, config.addresses[i]); |
| 123 | # else | 118 | # else |
| 124 | xasprintf(&cmd, rawcmd, max_packets, addresses[i]); | 119 | xasprintf(&cmd, rawcmd, config.max_packets, config.addresses[i]); |
| 125 | # endif | 120 | # endif |
| 126 | #else | 121 | #else |
| 127 | xasprintf(&cmd, rawcmd, addresses[i], max_packets); | 122 | xasprintf(&cmd, rawcmd, config.addresses[i], config.max_packets); |
| 128 | #endif | 123 | #endif |
| 129 | 124 | ||
| 130 | if (verbose >= 2) | 125 | if (verbose >= 2) { |
| 131 | printf("CMD: %s\n", cmd); | 126 | printf("CMD: %s\n", cmd); |
| 127 | } | ||
| 132 | 128 | ||
| 133 | /* run the command */ | 129 | /* run the command */ |
| 134 | this_result = run_ping(cmd, addresses[i]); | ||
| 135 | 130 | ||
| 136 | if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) { | 131 | ping_result pinged = run_ping(cmd, config.addresses[i], config.crta); |
| 132 | |||
| 133 | if (pinged.packet_loss == UNKNOWN_PACKET_LOSS || pinged.round_trip_average < 0.0) { | ||
| 137 | printf("%s\n", cmd); | 134 | printf("%s\n", cmd); |
| 138 | die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n")); | 135 | die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n")); |
| 139 | } | 136 | } |
| 140 | 137 | ||
| 141 | if (pl >= cpl || rta >= crta || rta < 0) | 138 | if (pinged.packet_loss >= config.cpl || pinged.round_trip_average >= config.crta || |
| 142 | this_result = STATE_CRITICAL; | 139 | pinged.round_trip_average < 0) { |
| 143 | else if (pl >= wpl || rta >= wrta) | 140 | pinged.state = STATE_CRITICAL; |
| 144 | this_result = STATE_WARNING; | 141 | } else if (pinged.packet_loss >= config.wpl || pinged.round_trip_average >= config.wrta) { |
| 145 | else if (pl >= 0 && rta >= 0) | 142 | pinged.state = STATE_WARNING; |
| 146 | this_result = max_state(STATE_OK, this_result); | 143 | } else if (pinged.packet_loss >= 0 && pinged.round_trip_average >= 0) { |
| 147 | 144 | pinged.state = max_state(STATE_OK, pinged.state); | |
| 148 | if (n_addresses > 1 && this_result != STATE_UNKNOWN) | 145 | } |
| 149 | die(STATE_OK, "%s is alive\n", addresses[i]); | 146 | |
| 150 | 147 | if (config.n_addresses > 1 && pinged.state != STATE_UNKNOWN) { | |
| 151 | if (display_html == true) | 148 | die(STATE_OK, "%s is alive\n", config.addresses[i]); |
| 152 | printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]); | 149 | } |
| 153 | if (pl == 100) | 150 | |
| 154 | printf(_("PING %s - %sPacket loss = %d%%"), state_text(this_result), warn_text, pl); | 151 | if (config.display_html) { |
| 155 | else | 152 | printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, config.addresses[i]); |
| 156 | printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(this_result), warn_text, pl, rta); | 153 | } |
| 157 | if (display_html == true) | 154 | if (pinged.packet_loss == 100) { |
| 155 | printf(_("PING %s - %sPacket loss = %d%%"), state_text(pinged.state), warn_text, | ||
| 156 | pinged.packet_loss); | ||
| 157 | } else { | ||
| 158 | printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(pinged.state), | ||
| 159 | warn_text, pinged.packet_loss, pinged.round_trip_average); | ||
| 160 | } | ||
| 161 | if (config.display_html) { | ||
| 158 | printf("</A>"); | 162 | printf("</A>"); |
| 163 | } | ||
| 159 | 164 | ||
| 160 | /* Print performance data */ | 165 | /* Print performance data */ |
| 161 | if (pl != 100) { | 166 | if (pinged.packet_loss != 100) { |
| 162 | printf("|%s", | 167 | printf("|%s", |
| 163 | fperfdata("rta", (double)rta, "ms", wrta > 0 ? true : false, wrta, crta > 0 ? true : false, crta, true, 0, false, 0)); | 168 | fperfdata("rta", pinged.round_trip_average, "ms", (bool)(config.wrta > 0), |
| 169 | config.wrta, (bool)(config.crta > 0), config.crta, true, 0, false, 0)); | ||
| 164 | } else { | 170 | } else { |
| 165 | printf("| rta=U;%f;%f;;", wrta, crta); | 171 | printf("| rta=U;%f;%f;;", config.wrta, config.crta); |
| 166 | } | 172 | } |
| 167 | printf(" %s\n", perfdata("pl", (long)pl, "%", wpl > 0 ? true : false, wpl, cpl > 0 ? true : false, cpl, true, 0, false, 0)); | ||
| 168 | 173 | ||
| 169 | if (verbose >= 2) | 174 | printf(" %s\n", |
| 170 | printf("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl); | 175 | perfdata("pl", (long)pinged.packet_loss, "%", (bool)(config.wpl > 0), config.wpl, |
| 176 | (bool)(config.cpl > 0), config.cpl, true, 0, false, 0)); | ||
| 177 | |||
| 178 | if (verbose >= 2) { | ||
| 179 | printf("%f:%d%% %f:%d%%\n", config.wrta, config.wpl, config.crta, config.cpl); | ||
| 180 | } | ||
| 171 | 181 | ||
| 172 | result = max_state(result, this_result); | 182 | result = max_state(result, pinged.state); |
| 173 | free(rawcmd); | 183 | free(rawcmd); |
| 174 | free(cmd); | 184 | free(cmd); |
| 175 | } | 185 | } |
| @@ -178,11 +188,7 @@ int main(int argc, char **argv) { | |||
| 178 | } | 188 | } |
| 179 | 189 | ||
| 180 | /* process command-line arguments */ | 190 | /* process command-line arguments */ |
| 181 | int process_arguments(int argc, char **argv) { | 191 | check_ping_config_wrapper process_arguments(int argc, char **argv) { |
| 182 | int c = 1; | ||
| 183 | char *ptr; | ||
| 184 | |||
| 185 | int option = 0; | ||
| 186 | static struct option longopts[] = {STD_LONG_OPTS, | 192 | static struct option longopts[] = {STD_LONG_OPTS, |
| 187 | {"packets", required_argument, 0, 'p'}, | 193 | {"packets", required_argument, 0, 'p'}, |
| 188 | {"nohtml", no_argument, 0, 'n'}, | 194 | {"nohtml", no_argument, 0, 'n'}, |
| @@ -191,23 +197,35 @@ int process_arguments(int argc, char **argv) { | |||
| 191 | {"use-ipv6", no_argument, 0, '6'}, | 197 | {"use-ipv6", no_argument, 0, '6'}, |
| 192 | {0, 0, 0, 0}}; | 198 | {0, 0, 0, 0}}; |
| 193 | 199 | ||
| 194 | if (argc < 2) | 200 | check_ping_config_wrapper result = { |
| 195 | return ERROR; | 201 | .errorcode = OK, |
| 202 | .config = check_ping_config_init(), | ||
| 203 | }; | ||
| 204 | |||
| 205 | if (argc < 2) { | ||
| 206 | result.errorcode = ERROR; | ||
| 207 | return result; | ||
| 208 | } | ||
| 196 | 209 | ||
| 197 | for (c = 1; c < argc; c++) { | 210 | for (int index = 1; index < argc; index++) { |
| 198 | if (strcmp("-to", argv[c]) == 0) | 211 | if (strcmp("-to", argv[index]) == 0) { |
| 199 | strcpy(argv[c], "-t"); | 212 | strcpy(argv[index], "-t"); |
| 200 | if (strcmp("-nohtml", argv[c]) == 0) | 213 | } |
| 201 | strcpy(argv[c], "-n"); | 214 | if (strcmp("-nohtml", argv[index]) == 0) { |
| 215 | strcpy(argv[index], "-n"); | ||
| 216 | } | ||
| 202 | } | 217 | } |
| 203 | 218 | ||
| 204 | while (1) { | 219 | int option = 0; |
| 205 | c = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option); | 220 | size_t max_addr = MAX_ADDR_START; |
| 221 | while (true) { | ||
| 222 | int option_index = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option); | ||
| 206 | 223 | ||
| 207 | if (c == -1 || c == EOF) | 224 | if (option_index == -1 || option_index == EOF) { |
| 208 | break; | 225 | break; |
| 226 | } | ||
| 209 | 227 | ||
| 210 | switch (c) { | 228 | switch (option_index) { |
| 211 | case '?': /* usage */ | 229 | case '?': /* usage */ |
| 212 | usage5(); | 230 | usage5(); |
| 213 | case 'h': /* help */ | 231 | case 'h': /* help */ |
| @@ -234,17 +252,19 @@ int process_arguments(int argc, char **argv) { | |||
| 234 | usage(_("IPv6 support not available\n")); | 252 | usage(_("IPv6 support not available\n")); |
| 235 | #endif | 253 | #endif |
| 236 | break; | 254 | break; |
| 237 | case 'H': /* hostname */ | 255 | case 'H': /* hostname */ { |
| 238 | ptr = optarg; | 256 | char *ptr = optarg; |
| 239 | while (1) { | 257 | while (true) { |
| 240 | n_addresses++; | 258 | result.config.n_addresses++; |
| 241 | if (n_addresses > max_addr) { | 259 | if (result.config.n_addresses > max_addr) { |
| 242 | max_addr *= 2; | 260 | max_addr *= 2; |
| 243 | addresses = realloc(addresses, sizeof(char *) * max_addr); | 261 | result.config.addresses = |
| 244 | if (addresses == NULL) | 262 | realloc(result.config.addresses, sizeof(char *) * max_addr); |
| 263 | if (result.config.addresses == NULL) { | ||
| 245 | die(STATE_UNKNOWN, _("Could not realloc() addresses\n")); | 264 | die(STATE_UNKNOWN, _("Could not realloc() addresses\n")); |
| 265 | } | ||
| 246 | } | 266 | } |
| 247 | addresses[n_addresses - 1] = ptr; | 267 | result.config.addresses[result.config.n_addresses - 1] = ptr; |
| 248 | if ((ptr = index(ptr, ','))) { | 268 | if ((ptr = index(ptr, ','))) { |
| 249 | strcpy(ptr, ""); | 269 | strcpy(ptr, ""); |
| 250 | ptr += sizeof(char); | 270 | ptr += sizeof(char); |
| @@ -252,219 +272,302 @@ int process_arguments(int argc, char **argv) { | |||
| 252 | break; | 272 | break; |
| 253 | } | 273 | } |
| 254 | } | 274 | } |
| 255 | break; | 275 | } break; |
| 256 | case 'p': /* number of packets to send */ | 276 | case 'p': /* number of packets to send */ |
| 257 | if (is_intnonneg(optarg)) | 277 | if (is_intnonneg(optarg)) { |
| 258 | max_packets = atoi(optarg); | 278 | result.config.max_packets = atoi(optarg); |
| 259 | else | 279 | } else { |
| 260 | usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg); | 280 | usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg); |
| 281 | } | ||
| 261 | break; | 282 | break; |
| 262 | case 'n': /* no HTML */ | 283 | case 'n': /* no HTML */ |
| 263 | display_html = false; | 284 | result.config.display_html = false; |
| 264 | break; | 285 | break; |
| 265 | case 'L': /* show HTML */ | 286 | case 'L': /* show HTML */ |
| 266 | display_html = true; | 287 | result.config.display_html = true; |
| 267 | break; | 288 | break; |
| 268 | case 'c': | 289 | case 'c': |
| 269 | get_threshold(optarg, &crta, &cpl); | 290 | get_threshold(optarg, &result.config.crta, &result.config.cpl); |
| 270 | break; | 291 | break; |
| 271 | case 'w': | 292 | case 'w': |
| 272 | get_threshold(optarg, &wrta, &wpl); | 293 | get_threshold(optarg, &result.config.wrta, &result.config.wpl); |
| 273 | break; | 294 | break; |
| 274 | } | 295 | } |
| 275 | } | 296 | } |
| 276 | 297 | ||
| 277 | c = optind; | 298 | int arg_counter = optind; |
| 278 | if (c == argc) | 299 | if (arg_counter == argc) { |
| 279 | return validate_arguments(); | 300 | return validate_arguments(result); |
| 301 | } | ||
| 280 | 302 | ||
| 281 | if (addresses[0] == NULL) { | 303 | if (result.config.addresses[0] == NULL) { |
| 282 | if (!is_host(argv[c])) { | 304 | if (!is_host(argv[arg_counter])) { |
| 283 | usage2(_("Invalid hostname/address"), argv[c]); | 305 | usage2(_("Invalid hostname/address"), argv[arg_counter]); |
| 284 | } else { | 306 | } else { |
| 285 | addresses[0] = argv[c++]; | 307 | result.config.addresses[0] = argv[arg_counter++]; |
| 286 | n_addresses++; | 308 | result.config.n_addresses++; |
| 287 | if (c == argc) | 309 | if (arg_counter == argc) { |
| 288 | return validate_arguments(); | 310 | return validate_arguments(result); |
| 311 | } | ||
| 289 | } | 312 | } |
| 290 | } | 313 | } |
| 291 | 314 | ||
| 292 | if (wpl == UNKNOWN_PACKET_LOSS) { | 315 | if (result.config.wpl == UNKNOWN_PACKET_LOSS) { |
| 293 | if (!is_intpercent(argv[c])) { | 316 | if (!is_intpercent(argv[arg_counter])) { |
| 294 | printf(_("<wpl> (%s) must be an integer percentage\n"), argv[c]); | 317 | printf(_("<wpl> (%s) must be an integer percentage\n"), argv[arg_counter]); |
| 295 | return ERROR; | 318 | result.errorcode = ERROR; |
| 296 | } else { | 319 | return result; |
| 297 | wpl = atoi(argv[c++]); | 320 | } |
| 298 | if (c == argc) | 321 | result.config.wpl = atoi(argv[arg_counter++]); |
| 299 | return validate_arguments(); | 322 | if (arg_counter == argc) { |
| 323 | return validate_arguments(result); | ||
| 300 | } | 324 | } |
| 301 | } | 325 | } |
| 302 | 326 | ||
| 303 | if (cpl == UNKNOWN_PACKET_LOSS) { | 327 | if (result.config.cpl == UNKNOWN_PACKET_LOSS) { |
| 304 | if (!is_intpercent(argv[c])) { | 328 | if (!is_intpercent(argv[arg_counter])) { |
| 305 | printf(_("<cpl> (%s) must be an integer percentage\n"), argv[c]); | 329 | printf(_("<cpl> (%s) must be an integer percentage\n"), argv[arg_counter]); |
| 306 | return ERROR; | 330 | result.errorcode = ERROR; |
| 307 | } else { | 331 | return result; |
| 308 | cpl = atoi(argv[c++]); | 332 | } |
| 309 | if (c == argc) | 333 | result.config.cpl = atoi(argv[arg_counter++]); |
| 310 | return validate_arguments(); | 334 | if (arg_counter == argc) { |
| 335 | return validate_arguments(result); | ||
| 311 | } | 336 | } |
| 312 | } | 337 | } |
| 313 | 338 | ||
| 314 | if (wrta < 0.0) { | 339 | if (result.config.wrta < 0.0) { |
| 315 | if (is_negative(argv[c])) { | 340 | if (is_negative(argv[arg_counter])) { |
| 316 | printf(_("<wrta> (%s) must be a non-negative number\n"), argv[c]); | 341 | printf(_("<wrta> (%s) must be a non-negative number\n"), argv[arg_counter]); |
| 317 | return ERROR; | 342 | result.errorcode = ERROR; |
| 318 | } else { | 343 | return result; |
| 319 | wrta = atof(argv[c++]); | 344 | } |
| 320 | if (c == argc) | 345 | result.config.wrta = atof(argv[arg_counter++]); |
| 321 | return validate_arguments(); | 346 | if (arg_counter == argc) { |
| 347 | return validate_arguments(result); | ||
| 322 | } | 348 | } |
| 323 | } | 349 | } |
| 324 | 350 | ||
| 325 | if (crta < 0.0) { | 351 | if (result.config.crta < 0.0) { |
| 326 | if (is_negative(argv[c])) { | 352 | if (is_negative(argv[arg_counter])) { |
| 327 | printf(_("<crta> (%s) must be a non-negative number\n"), argv[c]); | 353 | printf(_("<crta> (%s) must be a non-negative number\n"), argv[arg_counter]); |
| 328 | return ERROR; | 354 | result.errorcode = ERROR; |
| 329 | } else { | 355 | return result; |
| 330 | crta = atof(argv[c++]); | 356 | } |
| 331 | if (c == argc) | 357 | result.config.crta = atof(argv[arg_counter++]); |
| 332 | return validate_arguments(); | 358 | if (arg_counter == argc) { |
| 359 | return validate_arguments(result); | ||
| 333 | } | 360 | } |
| 334 | } | 361 | } |
| 335 | 362 | ||
| 336 | if (max_packets == -1) { | 363 | if (result.config.max_packets == -1) { |
| 337 | if (is_intnonneg(argv[c])) { | 364 | if (is_intnonneg(argv[arg_counter])) { |
| 338 | max_packets = atoi(argv[c++]); | 365 | result.config.max_packets = atoi(argv[arg_counter++]); |
| 339 | } else { | 366 | } else { |
| 340 | printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[c]); | 367 | printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[arg_counter]); |
| 341 | return ERROR; | 368 | result.errorcode = ERROR; |
| 369 | return result; | ||
| 342 | } | 370 | } |
| 343 | } | 371 | } |
| 344 | 372 | ||
| 345 | return validate_arguments(); | 373 | return validate_arguments(result); |
| 346 | } | 374 | } |
| 347 | 375 | ||
| 348 | int get_threshold(char *arg, float *trta, int *tpl) { | 376 | int get_threshold(char *arg, double *trta, int *tpl) { |
| 349 | if (is_intnonneg(arg) && sscanf(arg, "%f", trta) == 1) | 377 | if (is_intnonneg(arg) && sscanf(arg, "%lf", trta) == 1) { |
| 350 | return OK; | 378 | return OK; |
| 351 | else if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%f%*[:,]%d%%", trta, tpl) == 2) | 379 | } |
| 380 | |||
| 381 | if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%lf%*[:,]%d%%", trta, tpl) == 2) { | ||
| 352 | return OK; | 382 | return OK; |
| 353 | else if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) | 383 | } |
| 384 | |||
| 385 | if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) { | ||
| 354 | return OK; | 386 | return OK; |
| 387 | } | ||
| 355 | 388 | ||
| 356 | usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg); | 389 | usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg); |
| 357 | return STATE_UNKNOWN; | 390 | return STATE_UNKNOWN; |
| 358 | } | 391 | } |
| 359 | 392 | ||
| 360 | int validate_arguments() { | 393 | check_ping_config_wrapper validate_arguments(check_ping_config_wrapper config_wrapper) { |
| 361 | float max_seconds; | 394 | if (config_wrapper.config.wrta < 0.0) { |
| 362 | int i; | ||
| 363 | |||
| 364 | if (wrta < 0.0) { | ||
| 365 | printf(_("<wrta> was not set\n")); | 395 | printf(_("<wrta> was not set\n")); |
| 366 | return ERROR; | 396 | config_wrapper.errorcode = ERROR; |
| 367 | } else if (crta < 0.0) { | 397 | return config_wrapper; |
| 398 | } | ||
| 399 | |||
| 400 | if (config_wrapper.config.crta < 0.0) { | ||
| 368 | printf(_("<crta> was not set\n")); | 401 | printf(_("<crta> was not set\n")); |
| 369 | return ERROR; | 402 | config_wrapper.errorcode = ERROR; |
| 370 | } else if (wpl == UNKNOWN_PACKET_LOSS) { | 403 | return config_wrapper; |
| 404 | } | ||
| 405 | |||
| 406 | if (config_wrapper.config.wpl == UNKNOWN_PACKET_LOSS) { | ||
| 371 | printf(_("<wpl> was not set\n")); | 407 | printf(_("<wpl> was not set\n")); |
| 372 | return ERROR; | 408 | config_wrapper.errorcode = ERROR; |
| 373 | } else if (cpl == UNKNOWN_PACKET_LOSS) { | 409 | return config_wrapper; |
| 410 | } | ||
| 411 | |||
| 412 | if (config_wrapper.config.cpl == UNKNOWN_PACKET_LOSS) { | ||
| 374 | printf(_("<cpl> was not set\n")); | 413 | printf(_("<cpl> was not set\n")); |
| 375 | return ERROR; | 414 | config_wrapper.errorcode = ERROR; |
| 376 | } else if (wrta > crta) { | 415 | return config_wrapper; |
| 377 | printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta); | 416 | } |
| 378 | return ERROR; | 417 | |
| 379 | } else if (wpl > cpl) { | 418 | if (config_wrapper.config.wrta > config_wrapper.config.crta) { |
| 380 | printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl); | 419 | printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), config_wrapper.config.wrta, |
| 381 | return ERROR; | 420 | config_wrapper.config.crta); |
| 421 | config_wrapper.errorcode = ERROR; | ||
| 422 | return config_wrapper; | ||
| 382 | } | 423 | } |
| 383 | 424 | ||
| 384 | if (max_packets == -1) | 425 | if (config_wrapper.config.wpl > config_wrapper.config.cpl) { |
| 385 | max_packets = DEFAULT_MAX_PACKETS; | 426 | printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), config_wrapper.config.wpl, |
| 427 | config_wrapper.config.cpl); | ||
| 428 | config_wrapper.errorcode = ERROR; | ||
| 429 | return config_wrapper; | ||
| 430 | } | ||
| 386 | 431 | ||
| 387 | max_seconds = crta / 1000.0 * max_packets + max_packets; | 432 | if (config_wrapper.config.max_packets == -1) { |
| 388 | if (max_seconds > timeout_interval) | 433 | config_wrapper.config.max_packets = DEFAULT_MAX_PACKETS; |
| 389 | timeout_interval = (int)max_seconds; | 434 | } |
| 390 | 435 | ||
| 391 | for (i = 0; i < n_addresses; i++) { | 436 | double max_seconds = (config_wrapper.config.crta / 1000.0 * config_wrapper.config.max_packets) + |
| 392 | if (!is_host(addresses[i])) | 437 | config_wrapper.config.max_packets; |
| 393 | usage2(_("Invalid hostname/address"), addresses[i]); | 438 | if (max_seconds > timeout_interval) { |
| 439 | timeout_interval = (unsigned int)max_seconds; | ||
| 440 | } | ||
| 441 | |||
| 442 | for (size_t i = 0; i < config_wrapper.config.n_addresses; i++) { | ||
| 443 | if (!is_host(config_wrapper.config.addresses[i])) { | ||
| 444 | usage2(_("Invalid hostname/address"), config_wrapper.config.addresses[i]); | ||
| 445 | } | ||
| 394 | } | 446 | } |
| 395 | 447 | ||
| 396 | if (n_addresses == 0) { | 448 | if (config_wrapper.config.n_addresses == 0) { |
| 397 | usage(_("You must specify a server address or host name")); | 449 | usage(_("You must specify a server address or host name")); |
| 398 | } | 450 | } |
| 399 | 451 | ||
| 400 | return OK; | 452 | return config_wrapper; |
| 401 | } | 453 | } |
| 402 | 454 | ||
| 403 | int run_ping(const char *cmd, const char *addr) { | 455 | ping_result run_ping(const char *cmd, const char *addr, double crta) { |
| 404 | char buf[MAX_INPUT_BUFFER]; | 456 | if ((child_process = spopen(cmd)) == NULL) { |
| 405 | int result = STATE_UNKNOWN; | ||
| 406 | int match; | ||
| 407 | |||
| 408 | if ((child_process = spopen(cmd)) == NULL) | ||
| 409 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); | 457 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); |
| 458 | } | ||
| 410 | 459 | ||
| 411 | child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); | 460 | child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); |
| 412 | if (child_stderr == NULL) | 461 | if (child_stderr == NULL) { |
| 413 | printf(_("Cannot open stderr for %s\n"), cmd); | 462 | printf(_("Cannot open stderr for %s\n"), cmd); |
| 463 | } | ||
| 414 | 464 | ||
| 415 | while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) { | 465 | char buf[MAX_INPUT_BUFFER]; |
| 466 | ping_result result = { | ||
| 467 | .state = STATE_UNKNOWN, | ||
| 468 | .packet_loss = UNKNOWN_PACKET_LOSS, | ||
| 469 | .round_trip_average = UNKNOWN_TRIP_TIME, | ||
| 470 | }; | ||
| 416 | 471 | ||
| 417 | if (verbose >= 3) | 472 | while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) { |
| 473 | if (verbose >= 3) { | ||
| 418 | printf("Output: %s", buf); | 474 | printf("Output: %s", buf); |
| 475 | } | ||
| 419 | 476 | ||
| 420 | result = max_state(result, error_scan(buf, addr)); | 477 | result.state = max_state(result.state, error_scan(buf, addr)); |
| 421 | 478 | ||
| 422 | /* get the percent loss statistics */ | 479 | /* get the percent loss statistics */ |
| 423 | match = 0; | 480 | int match = 0; |
| 424 | if ((sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || | 481 | if ((sscanf( |
| 425 | (sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || | 482 | buf, |
| 426 | (sscanf(buf, "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || | 483 | "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n", |
| 427 | (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n", &pl, &match) && match) || | 484 | &result.packet_loss, &match) == 1 && |
| 428 | (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n", &pl, &match) && match) || | 485 | match) || |
| 429 | (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n", &pl, &match) && match) || | 486 | (sscanf(buf, |
| 430 | (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n", &pl, &match) && match) || | 487 | "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet " |
| 431 | (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || | 488 | "loss%n", |
| 432 | (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || | 489 | &result.packet_loss, &match) == 1 && |
| 433 | (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &pl, &match) && match)) | 490 | match) || |
| 491 | (sscanf(buf, | ||
| 492 | "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n", | ||
| 493 | &result.packet_loss, &match) == 1 && | ||
| 494 | match) || | ||
| 495 | (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n", | ||
| 496 | &result.packet_loss, &match) == 1 && | ||
| 497 | match) || | ||
| 498 | (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n", | ||
| 499 | &result.packet_loss, &match) == 1 && | ||
| 500 | match) || | ||
| 501 | (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n", | ||
| 502 | &result.packet_loss, &match) == 1 && | ||
| 503 | match) || | ||
| 504 | (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n", | ||
| 505 | &result.packet_loss, &match) == 1 && | ||
| 506 | match) == 1 || | ||
| 507 | (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n", | ||
| 508 | &result.packet_loss, &match) == 1 && | ||
| 509 | match) || | ||
| 510 | (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n", | ||
| 511 | &result.packet_loss, &match) == 1 && | ||
| 512 | match) || | ||
| 513 | (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &result.packet_loss, &match) == 1 && match)) { | ||
| 434 | continue; | 514 | continue; |
| 515 | } | ||
| 435 | 516 | ||
| 436 | /* get the round trip average */ | 517 | /* get the round trip average */ |
| 437 | else if ((sscanf(buf, "round-trip min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || | 518 | if ((sscanf(buf, "round-trip min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average, |
| 438 | (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || | 519 | &match) == 1 && |
| 439 | (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || | 520 | match) || |
| 440 | (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || | 521 | (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%lf/%*f/%*f%n", |
| 441 | (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || | 522 | &result.round_trip_average, &match) == 1 && |
| 442 | (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || | 523 | match) || |
| 443 | (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || | 524 | (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%lf/%*f/%*f%n", |
| 444 | (sscanf(buf, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms%n", &rta, &match) && match) || | 525 | &result.round_trip_average, &match) == 1 && |
| 445 | (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %fms%n", &rta, &match) && match)) | 526 | match) || |
| 527 | (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%lf/%*f/%*f%n", | ||
| 528 | &result.round_trip_average, &match) == 1 && | ||
| 529 | match) || | ||
| 530 | (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%lf/%*f/%*f%n", | ||
| 531 | &result.round_trip_average, &match) == 1 && | ||
| 532 | match) || | ||
| 533 | (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average, | ||
| 534 | &match) == 1 && | ||
| 535 | match) || | ||
| 536 | (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%lf/%*f/%*f%n", | ||
| 537 | &result.round_trip_average, &match) == 1 && | ||
| 538 | match) || | ||
| 539 | (sscanf(buf, "rtt min/avg/max/mdev = %*f/%lf/%*f/%*f ms%n", &result.round_trip_average, | ||
| 540 | &match) == 1 && | ||
| 541 | match) || | ||
| 542 | (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %lfms%n", | ||
| 543 | &result.round_trip_average, &match) == 1 && | ||
| 544 | match)) { | ||
| 446 | continue; | 545 | continue; |
| 546 | } | ||
| 447 | } | 547 | } |
| 448 | 548 | ||
| 449 | /* this is needed because there is no rta if all packets are lost */ | 549 | /* this is needed because there is no rta if all packets are lost */ |
| 450 | if (pl == 100) | 550 | if (result.packet_loss == 100) { |
| 451 | rta = crta; | 551 | result.round_trip_average = crta; |
| 552 | } | ||
| 452 | 553 | ||
| 453 | /* check stderr, setting at least WARNING if there is output here */ | 554 | /* check stderr, setting at least WARNING if there is output here */ |
| 454 | /* Add warning into warn_text */ | 555 | /* Add warning into warn_text */ |
| 455 | while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) { | 556 | while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) { |
| 456 | if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") && !strstr(buf, "Warning: time of day goes back") | 557 | if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") && |
| 558 | !strstr(buf, "Warning: time of day goes back") | ||
| 457 | 559 | ||
| 458 | ) { | 560 | ) { |
| 459 | if (verbose >= 3) { | 561 | if (verbose >= 3) { |
| 460 | printf("Got stderr: %s", buf); | 562 | printf("Got stderr: %s", buf); |
| 461 | } | 563 | } |
| 462 | if ((result = error_scan(buf, addr)) == STATE_OK) { | 564 | if ((result.state = error_scan(buf, addr)) == STATE_OK) { |
| 463 | result = STATE_WARNING; | 565 | result.state = STATE_WARNING; |
| 464 | if (warn_text == NULL) { | 566 | if (warn_text == NULL) { |
| 465 | warn_text = strdup(_("System call sent warnings to stderr ")); | 567 | warn_text = strdup(_("System call sent warnings to stderr ")); |
| 466 | } else { | 568 | } else { |
| 467 | xasprintf(&warn_text, "%s %s", warn_text, _("System call sent warnings to stderr ")); | 569 | xasprintf(&warn_text, "%s %s", warn_text, |
| 570 | _("System call sent warnings to stderr ")); | ||
| 468 | } | 571 | } |
| 469 | } | 572 | } |
| 470 | } | 573 | } |
| @@ -474,43 +577,48 @@ int run_ping(const char *cmd, const char *addr) { | |||
| 474 | 577 | ||
| 475 | spclose(child_process); | 578 | spclose(child_process); |
| 476 | 579 | ||
| 477 | if (warn_text == NULL) | 580 | if (warn_text == NULL) { |
| 478 | warn_text = strdup(""); | 581 | warn_text = strdup(""); |
| 582 | } | ||
| 479 | 583 | ||
| 480 | return result; | 584 | return result; |
| 481 | } | 585 | } |
| 482 | 586 | ||
| 483 | int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) { | 587 | mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) { |
| 484 | if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") || strstr(buf, "No route")) | 588 | if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") || |
| 589 | strstr(buf, "No route")) { | ||
| 485 | die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr); | 590 | die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr); |
| 486 | else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) | 591 | } else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) { |
| 487 | die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr); | 592 | die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr); |
| 488 | else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) | 593 | } else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) { |
| 489 | die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr); | 594 | die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr); |
| 490 | else if (strstr(buf, "Destination Protocol Unreachable")) | 595 | } else if (strstr(buf, "Destination Protocol Unreachable")) { |
| 491 | die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr); | 596 | die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr); |
| 492 | else if (strstr(buf, "Destination Net Prohibited")) | 597 | } else if (strstr(buf, "Destination Net Prohibited")) { |
| 493 | die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr); | 598 | die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr); |
| 494 | else if (strstr(buf, "Destination Host Prohibited")) | 599 | } else if (strstr(buf, "Destination Host Prohibited")) { |
| 495 | die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr); | 600 | die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr); |
| 496 | else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) | 601 | } else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) { |
| 497 | die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr); | 602 | die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr); |
| 498 | else if (strstr(buf, "unknown host")) | 603 | } else if (strstr(buf, "unknown host")) { |
| 499 | die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr); | 604 | die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr); |
| 500 | else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) | 605 | } else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) { |
| 501 | die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr); | 606 | die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr); |
| 502 | else if (strstr(buf, "Destination unreachable: ")) | 607 | } else if (strstr(buf, "Destination unreachable: ")) { |
| 503 | die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr); | 608 | die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr); |
| 609 | } | ||
| 504 | 610 | ||
| 505 | if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) { | 611 | if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) { |
| 506 | if (warn_text == NULL) | 612 | if (warn_text == NULL) { |
| 507 | warn_text = strdup(_(WARN_DUPLICATES)); | 613 | warn_text = strdup(_(WARN_DUPLICATES)); |
| 508 | else if (!strstr(warn_text, _(WARN_DUPLICATES)) && xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) | 614 | } else if (!strstr(warn_text, _(WARN_DUPLICATES)) && |
| 615 | xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) { | ||
| 509 | die(STATE_UNKNOWN, _("Unable to realloc warn_text\n")); | 616 | die(STATE_UNKNOWN, _("Unable to realloc warn_text\n")); |
| 510 | return (STATE_WARNING); | 617 | } |
| 618 | return STATE_WARNING; | ||
| 511 | } | 619 | } |
| 512 | 620 | ||
| 513 | return (STATE_OK); | 621 | return STATE_OK; |
| 514 | } | 622 | } |
| 515 | 623 | ||
| 516 | void print_help(void) { | 624 | void print_help(void) { |
| @@ -550,10 +658,10 @@ void print_help(void) { | |||
| 550 | printf("%s\n", _("percentage of packet loss to trigger an alarm state.")); | 658 | printf("%s\n", _("percentage of packet loss to trigger an alarm state.")); |
| 551 | 659 | ||
| 552 | printf("\n"); | 660 | printf("\n"); |
| 553 | printf("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss")); | 661 | printf("%s\n", |
| 554 | printf("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output")); | 662 | _("This plugin uses the ping command to probe the specified host for packet loss")); |
| 555 | printf("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in")); | 663 | printf("%s\n", |
| 556 | printf("%s\n", _("the contrib area of the downloads section at http://www.nagios.org/")); | 664 | _("(percentage) and round trip average (milliseconds). It can produce HTML output.")); |
| 557 | 665 | ||
| 558 | printf(UT_SUPPORT); | 666 | printf(UT_SUPPORT); |
| 559 | } | 667 | } |
diff --git a/plugins/check_ping.d/config.h b/plugins/check_ping.d/config.h new file mode 100644 index 00000000..eb2735a7 --- /dev/null +++ b/plugins/check_ping.d/config.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | |||
| 7 | enum { | ||
| 8 | UNKNOWN_PACKET_LOSS = 200, /* 200% */ | ||
| 9 | DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */ | ||
| 10 | }; | ||
| 11 | |||
| 12 | #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */ | ||
| 13 | |||
| 14 | #define MAX_ADDR_START 1 | ||
| 15 | |||
| 16 | typedef struct { | ||
| 17 | bool display_html; | ||
| 18 | int max_packets; | ||
| 19 | |||
| 20 | char **addresses; | ||
| 21 | size_t n_addresses; | ||
| 22 | |||
| 23 | int wpl; | ||
| 24 | int cpl; | ||
| 25 | double wrta; | ||
| 26 | double crta; | ||
| 27 | } check_ping_config; | ||
| 28 | |||
| 29 | check_ping_config check_ping_config_init() { | ||
| 30 | check_ping_config tmp = { | ||
| 31 | .display_html = false, | ||
| 32 | .max_packets = -1, | ||
| 33 | |||
| 34 | .addresses = NULL, | ||
| 35 | .n_addresses = 0, | ||
| 36 | |||
| 37 | .wpl = UNKNOWN_PACKET_LOSS, | ||
| 38 | .cpl = UNKNOWN_PACKET_LOSS, | ||
| 39 | .wrta = UNKNOWN_TRIP_TIME, | ||
| 40 | .crta = UNKNOWN_TRIP_TIME, | ||
| 41 | }; | ||
| 42 | |||
| 43 | tmp.addresses = calloc(MAX_ADDR_START, sizeof(char *)); | ||
| 44 | tmp.addresses[0] = NULL; | ||
| 45 | return tmp; | ||
| 46 | } | ||
diff --git a/plugins/check_procs.c b/plugins/check_procs.c index 1d78ccee..ae6e9c23 100644 --- a/plugins/check_procs.c +++ b/plugins/check_procs.c | |||
| @@ -1,41 +1,41 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_procs plugin | 3 | * Monitoring check_procs plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team | 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the check_procs plugin | 10 | * This file contains the check_procs plugin |
| 11 | * | 11 | * |
| 12 | * Checks all processes and generates WARNING or CRITICAL states if the | 12 | * Checks all processes and generates WARNING or CRITICAL states if the |
| 13 | * specified metric is outside the required threshold ranges. The metric | 13 | * specified metric is outside the required threshold ranges. The metric |
| 14 | * defaults to number of processes. Search filters can be applied to limit | 14 | * defaults to number of processes. Search filters can be applied to limit |
| 15 | * the processes to check. | 15 | * the processes to check. |
| 16 | * | 16 | * |
| 17 | * The parent process, check_procs itself and any child process of | 17 | * The parent process, check_procs itself and any child process of |
| 18 | * check_procs (ps) are excluded from any checks to prevent false positives. | 18 | * check_procs (ps) are excluded from any checks to prevent false positives. |
| 19 | * | 19 | * |
| 20 | * | 20 | * |
| 21 | * This program is free software: you can redistribute it and/or modify | 21 | * This program is free software: you can redistribute it and/or modify |
| 22 | * it under the terms of the GNU General Public License as published by | 22 | * it under the terms of the GNU General Public License as published by |
| 23 | * the Free Software Foundation, either version 3 of the License, or | 23 | * the Free Software Foundation, either version 3 of the License, or |
| 24 | * (at your option) any later version. | 24 | * (at your option) any later version. |
| 25 | * | 25 | * |
| 26 | * This program is distributed in the hope that it will be useful, | 26 | * This program is distributed in the hope that it will be useful, |
| 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 29 | * GNU General Public License for more details. | 29 | * GNU General Public License for more details. |
| 30 | * | 30 | * |
| 31 | * You should have received a copy of the GNU General Public License | 31 | * You should have received a copy of the GNU General Public License |
| 32 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 32 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 33 | * | 33 | * |
| 34 | * | 34 | * |
| 35 | *****************************************************************************/ | 35 | *****************************************************************************/ |
| 36 | 36 | ||
| 37 | const char *progname = "check_procs"; | 37 | const char *progname = "check_procs"; |
| 38 | const char *program_name = "check_procs"; /* Required for coreutils libs */ | 38 | const char *program_name = "check_procs"; /* Required for coreutils libs */ |
| 39 | const char *copyright = "2000-2024"; | 39 | const char *copyright = "2000-2024"; |
| 40 | const char *email = "devel@monitoring-plugins.org"; | 40 | const char *email = "devel@monitoring-plugins.org"; |
| 41 | 41 | ||
| @@ -43,313 +43,297 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 43 | #include "utils.h" | 43 | #include "utils.h" |
| 44 | #include "utils_cmd.h" | 44 | #include "utils_cmd.h" |
| 45 | #include "regex.h" | 45 | #include "regex.h" |
| 46 | #include "states.h" | ||
| 47 | #include "check_procs.d/config.h" | ||
| 46 | 48 | ||
| 47 | #include <pwd.h> | 49 | #include <pwd.h> |
| 48 | #include <errno.h> | 50 | #include <errno.h> |
| 49 | 51 | ||
| 50 | #ifdef HAVE_SYS_STAT_H | 52 | #ifdef HAVE_SYS_STAT_H |
| 51 | #include <sys/stat.h> | 53 | # include <sys/stat.h> |
| 52 | #endif | 54 | #endif |
| 53 | 55 | ||
| 54 | static int process_arguments (int /*argc*/, char ** /*argv*/); | 56 | typedef struct { |
| 55 | static int validate_arguments (void); | 57 | int errorcode; |
| 56 | static int convert_to_seconds (char * /*etime*/); | 58 | check_procs_config config; |
| 57 | static void print_help (void); | 59 | } check_procs_config_wrapper; |
| 58 | void print_usage (void); | 60 | static check_procs_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 59 | 61 | static check_procs_config_wrapper validate_arguments(check_procs_config_wrapper /*config_wrapper*/); | |
| 60 | static char *warning_range = NULL; | 62 | |
| 61 | static char *critical_range = NULL; | 63 | static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/); |
| 62 | static thresholds *procs_thresholds = NULL; | 64 | static void print_help(void); |
| 63 | 65 | void print_usage(void); | |
| 64 | static 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 */ | ||
| 82 | char *metric_name; | ||
| 83 | enum metric { | ||
| 84 | METRIC_PROCS, | ||
| 85 | METRIC_VSZ, | ||
| 86 | METRIC_RSS, | ||
| 87 | METRIC_CPU, | ||
| 88 | METRIC_ELAPSED | ||
| 89 | }; | ||
| 90 | enum metric metric = METRIC_PROCS; | ||
| 91 | 83 | ||
| 92 | static int verbose = 0; | 84 | static int verbose = 0; |
| 93 | static int uid; | 85 | |
| 94 | static pid_t ppid; | 86 | static int stat_exe(const pid_t pid, struct stat *buf) { |
| 95 | static int vsz; | ||
| 96 | static int rss; | ||
| 97 | static float pcpu; | ||
| 98 | static char *statopts; | ||
| 99 | static char *prog; | ||
| 100 | static char *exclude_progs; | ||
| 101 | static char **exclude_progs_arr = NULL; | ||
| 102 | static char exclude_progs_counter = 0; | ||
| 103 | static char *args; | ||
| 104 | static char *input_filename = NULL; | ||
| 105 | static regex_t re_args; | ||
| 106 | static char *fmt; | ||
| 107 | static char *fails; | ||
| 108 | static char tmp[MAX_INPUT_BUFFER]; | ||
| 109 | static int kthread_filter = 0; | ||
| 110 | static int usepid = 0; /* whether to test for pid or /proc/pid/exe */ | ||
| 111 | |||
| 112 | static int | ||
| 113 | stat_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 | 94 | int main(int argc, char **argv) { | |
| 123 | int | 95 | setlocale(LC_ALL, ""); |
| 124 | main (int argc, char **argv) | ||
| 125 | { | ||
| 126 | char *input_buffer; | ||
| 127 | char *input_line; | ||
| 128 | char *procprog; | ||
| 129 | |||
| 130 | pid_t mypid = 0; | ||
| 131 | pid_t myppid = 0; | ||
| 132 | struct stat statbuf; | ||
| 133 | dev_t mydev = 0; | ||
| 134 | ino_t myino = 0; | ||
| 135 | int procuid = 0; | ||
| 136 | pid_t procpid = 0; | ||
| 137 | pid_t procppid = 0; | ||
| 138 | pid_t kthread_ppid = 0; | ||
| 139 | int procvsz = 0; | ||
| 140 | int procrss = 0; | ||
| 141 | int procseconds = 0; | ||
| 142 | float procpcpu = 0; | ||
| 143 | char procstat[8]; | ||
| 144 | char procetime[MAX_INPUT_BUFFER] = { '\0' }; | ||
| 145 | char *procargs; | ||
| 146 | |||
| 147 | const char *zombie = "Z"; | ||
| 148 | |||
| 149 | int resultsum = 0; /* bitmask of the filter criteria met by a process */ | ||
| 150 | int found = 0; /* counter for number of lines returned in `ps` output */ | ||
| 151 | int procs = 0; /* counter for number of processes meeting filter criteria */ | ||
| 152 | int pos; /* number of spaces before 'args' in `ps` output */ | ||
| 153 | int cols; /* number of columns in ps output */ | ||
| 154 | int expected_cols = PS_COLS - 1; | ||
| 155 | int warn = 0; /* number of processes in warn state */ | ||
| 156 | int crit = 0; /* number of processes in crit state */ | ||
| 157 | int i = 0; | ||
| 158 | int result = STATE_UNKNOWN; | ||
| 159 | int ret = 0; | ||
| 160 | output chld_out, chld_err; | ||
| 161 | |||
| 162 | setlocale (LC_ALL, ""); | ||
| 163 | bindtextdomain (PACKAGE, LOCALEDIR); | ||
| 164 | textdomain (PACKAGE); | ||
| 165 | setlocale(LC_NUMERIC, "POSIX"); | 96 | setlocale(LC_NUMERIC, "POSIX"); |
| 166 | 97 | bindtextdomain(PACKAGE, LOCALEDIR); | |
| 167 | input_buffer = malloc (MAX_INPUT_BUFFER); | 98 | textdomain(PACKAGE); |
| 168 | procprog = malloc (MAX_INPUT_BUFFER); | ||
| 169 | |||
| 170 | xasprintf (&metric_name, "PROCS"); | ||
| 171 | metric = METRIC_PROCS; | ||
| 172 | 99 | ||
| 173 | /* Parse extra opts if any */ | 100 | /* Parse extra opts if any */ |
| 174 | argv=np_extra_opts (&argc, argv, progname); | 101 | argv = np_extra_opts(&argc, argv, progname); |
| 102 | |||
| 103 | check_procs_config_wrapper tmp_config = process_arguments(argc, argv); | ||
| 104 | if (tmp_config.errorcode == ERROR) { | ||
| 105 | usage4(_("Could not parse arguments")); | ||
| 106 | } | ||
| 175 | 107 | ||
| 176 | if (process_arguments (argc, argv) == ERROR) | 108 | check_procs_config config = tmp_config.config; |
| 177 | usage4 (_("Could not parse arguments")); | ||
| 178 | 109 | ||
| 179 | /* find ourself */ | 110 | /* find ourself */ |
| 180 | mypid = getpid(); | 111 | pid_t mypid = getpid(); |
| 181 | myppid = getppid(); | 112 | pid_t myppid = getppid(); |
| 182 | if (usepid || stat_exe(mypid, &statbuf) == -1) { | 113 | dev_t mydev = 0; |
| 114 | ino_t myino = 0; | ||
| 115 | struct stat statbuf; | ||
| 116 | if (config.usepid || stat_exe(mypid, &statbuf) == -1) { | ||
| 183 | /* usepid might have been set by -T */ | 117 | /* usepid might have been set by -T */ |
| 184 | usepid = 1; | 118 | config.usepid = true; |
| 185 | } else { | 119 | } else { |
| 186 | usepid = 0; | 120 | config.usepid = false; |
| 187 | mydev = statbuf.st_dev; | 121 | mydev = statbuf.st_dev; |
| 188 | myino = statbuf.st_ino; | 122 | myino = statbuf.st_ino; |
| 189 | } | 123 | } |
| 190 | 124 | ||
| 191 | /* Set signal handling and alarm timeout */ | 125 | /* Set signal handling and alarm timeout */ |
| 192 | if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 126 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| 193 | die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); | 127 | die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); |
| 194 | } | 128 | } |
| 195 | (void) alarm ((unsigned) timeout_interval); | 129 | (void)alarm(timeout_interval); |
| 196 | 130 | ||
| 197 | if (verbose >= 2) | 131 | if (verbose >= 2) { |
| 198 | printf (_("CMD: %s\n"), PS_COMMAND); | 132 | printf(_("CMD: %s\n"), PS_COMMAND); |
| 133 | } | ||
| 199 | 134 | ||
| 200 | if (input_filename == NULL) { | 135 | output chld_out; |
| 201 | result = cmd_run( PS_COMMAND, &chld_out, &chld_err, 0); | 136 | output chld_err; |
| 137 | mp_state_enum result = STATE_UNKNOWN; | ||
| 138 | if (config.input_filename == NULL) { | ||
| 139 | result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0); | ||
| 202 | if (chld_err.lines > 0) { | 140 | if (chld_err.lines > 0) { |
| 203 | printf ("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); | 141 | printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); |
| 204 | exit(STATE_WARNING); | 142 | exit(STATE_WARNING); |
| 205 | } | 143 | } |
| 206 | } else { | 144 | } else { |
| 207 | result = cmd_file_read( input_filename, &chld_out, 0); | 145 | result = cmd_file_read(config.input_filename, &chld_out, 0); |
| 208 | } | 146 | } |
| 209 | 147 | ||
| 148 | int pos; /* number of spaces before 'args' in `ps` output */ | ||
| 149 | uid_t procuid = 0; | ||
| 150 | pid_t procpid = 0; | ||
| 151 | pid_t procppid = 0; | ||
| 152 | pid_t kthread_ppid = 0; | ||
| 153 | int warn = 0; /* number of processes in warn state */ | ||
| 154 | int crit = 0; /* number of processes in crit state */ | ||
| 155 | int procvsz = 0; | ||
| 156 | int procrss = 0; | ||
| 157 | int procseconds = 0; | ||
| 158 | float procpcpu = 0; | ||
| 159 | char procstat[8]; | ||
| 160 | char procetime[MAX_INPUT_BUFFER] = {'\0'}; | ||
| 161 | int resultsum = 0; /* bitmask of the filter criteria met by a process */ | ||
| 162 | int found = 0; /* counter for number of lines returned in `ps` output */ | ||
| 163 | int procs = 0; /* counter for number of processes meeting filter criteria */ | ||
| 164 | char *input_buffer = malloc(MAX_INPUT_BUFFER); | ||
| 165 | char *procprog = malloc(MAX_INPUT_BUFFER); | ||
| 166 | const int expected_cols = PS_COLS - 1; | ||
| 167 | |||
| 210 | /* flush first line: j starts at 1 */ | 168 | /* flush first line: j starts at 1 */ |
| 211 | for (size_t j = 1; j < chld_out.lines; j++) { | 169 | for (size_t j = 1; j < chld_out.lines; j++) { |
| 212 | input_line = chld_out.line[j]; | 170 | char *input_line = chld_out.line[j]; |
| 213 | 171 | ||
| 214 | if (verbose >= 3) | 172 | if (verbose >= 3) { |
| 215 | printf ("%s", input_line); | 173 | printf("%s", input_line); |
| 174 | } | ||
| 216 | 175 | ||
| 217 | strcpy (procprog, ""); | 176 | strcpy(procprog, ""); |
| 218 | xasprintf (&procargs, "%s", ""); | 177 | char *procargs; |
| 178 | xasprintf(&procargs, "%s", ""); | ||
| 219 | 179 | ||
| 220 | cols = sscanf (input_line, PS_FORMAT, PS_VARLIST); | 180 | /* number of columns in ps output */ |
| 181 | int cols = sscanf(input_line, PS_FORMAT, PS_VARLIST); | ||
| 221 | 182 | ||
| 222 | /* Zombie processes do not give a procprog command */ | 183 | /* Zombie processes do not give a procprog command */ |
| 223 | if ( cols < expected_cols && strstr(procstat, zombie) ) { | 184 | const char *zombie = "Z"; |
| 185 | if (cols < expected_cols && strstr(procstat, zombie)) { | ||
| 224 | cols = expected_cols; | 186 | cols = expected_cols; |
| 225 | } | 187 | } |
| 226 | if ( cols >= expected_cols ) { | 188 | if (cols >= expected_cols) { |
| 227 | resultsum = 0; | 189 | resultsum = 0; |
| 228 | xasprintf (&procargs, "%s", input_line + pos); | 190 | xasprintf(&procargs, "%s", input_line + pos); |
| 229 | strip (procargs); | 191 | strip(procargs); |
| 230 | 192 | ||
| 231 | /* Some ps return full pathname for command. This removes path */ | 193 | /* Some ps return full pathname for command. This removes path */ |
| 232 | strcpy(procprog, base_name(procprog)); | 194 | strcpy(procprog, base_name(procprog)); |
| 233 | 195 | ||
| 234 | /* we need to convert the elapsed time to seconds */ | 196 | /* we need to convert the elapsed time to seconds */ |
| 235 | procseconds = convert_to_seconds(procetime); | 197 | procseconds = convert_to_seconds(procetime, config.metric); |
| 236 | 198 | ||
| 237 | if (verbose >= 3) | 199 | if (verbose >= 3) { |
| 238 | printf ("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", | 200 | printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s " |
| 239 | procs, procuid, procvsz, procrss, | 201 | "prog=%s args=%s\n", |
| 240 | procpid, procppid, procpcpu, procstat, | 202 | procs, procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat, |
| 241 | procetime, procprog, procargs); | 203 | procetime, procprog, procargs); |
| 204 | } | ||
| 242 | 205 | ||
| 243 | /* Ignore self */ | 206 | /* Ignore self */ |
| 244 | if ((usepid && mypid == procpid) || | 207 | int ret = 0; |
| 245 | ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || | 208 | if ((config.usepid && mypid == procpid) || |
| 246 | (ret == -1 && errno == ENOENT)) | 209 | (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && |
| 247 | ) { | 210 | statbuf.st_dev == mydev && statbuf.st_ino == myino)) || |
| 248 | if (verbose >= 3) | 211 | (ret == -1 && errno == ENOENT))) { |
| 249 | printf("not considering - is myself or gone\n"); | 212 | if (verbose >= 3) { |
| 213 | printf("not considering - is myself or gone\n"); | ||
| 214 | } | ||
| 250 | continue; | 215 | continue; |
| 251 | } | 216 | } |
| 252 | /* Ignore parent*/ | 217 | /* Ignore parent*/ |
| 253 | else if (myppid == procpid) { | 218 | if (myppid == procpid) { |
| 254 | if (verbose >= 3) | 219 | if (verbose >= 3) { |
| 255 | printf("not considering - is parent\n"); | 220 | printf("not considering - is parent\n"); |
| 221 | } | ||
| 256 | continue; | 222 | continue; |
| 257 | } | 223 | } |
| 258 | 224 | ||
| 259 | /* Ignore our own children */ | 225 | /* Ignore our own children */ |
| 260 | if (procppid == mypid) { | 226 | if (procppid == mypid) { |
| 261 | if (verbose >= 3) | 227 | if (verbose >= 3) { |
| 262 | printf("not considering - is our child\n"); | 228 | printf("not considering - is our child\n"); |
| 229 | } | ||
| 263 | continue; | 230 | continue; |
| 264 | } | 231 | } |
| 265 | 232 | ||
| 266 | /* Ignore excluded processes by name */ | 233 | /* Ignore excluded processes by name */ |
| 267 | if(options & EXCLUDE_PROGS) { | 234 | if (config.options & EXCLUDE_PROGS) { |
| 268 | int found = 0; | 235 | bool found = false; |
| 269 | int i = 0; | 236 | for (int i = 0; i < (config.exclude_progs_counter); i++) { |
| 270 | 237 | if (!strcmp(procprog, config.exclude_progs_arr[i])) { | |
| 271 | for(i=0; i < (exclude_progs_counter); i++) { | 238 | found = true; |
| 272 | if(!strcmp(procprog, exclude_progs_arr[i])) { | 239 | } |
| 273 | found = 1; | 240 | } |
| 274 | } | 241 | if (!found) { |
| 275 | } | 242 | resultsum |= EXCLUDE_PROGS; |
| 276 | if(found == 0) { | 243 | } else { |
| 277 | resultsum |= EXCLUDE_PROGS; | 244 | if (verbose >= 3) { |
| 278 | } else | 245 | printf("excluding - by ignorelist\n"); |
| 279 | { | 246 | } |
| 280 | if(verbose >= 3) | 247 | } |
| 281 | printf("excluding - by ignorelist\n"); | ||
| 282 | } | ||
| 283 | } | 248 | } |
| 284 | 249 | ||
| 285 | /* filter kernel threads (children of KTHREAD_PARENT)*/ | 250 | /* filter kernel threads (children of KTHREAD_PARENT)*/ |
| 286 | /* TODO adapt for other OSes than GNU/Linux | 251 | /* TODO adapt for other OSes than GNU/Linux |
| 287 | sorry for not doing that, but I've no other OSes to test :-( */ | 252 | sorry for not doing that, but I've no other OSes to test :-( */ |
| 288 | if (kthread_filter == 1) { | 253 | if (config.kthread_filter) { |
| 289 | /* get pid KTHREAD_PARENT */ | 254 | /* get pid KTHREAD_PARENT */ |
| 290 | if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) | 255 | if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) { |
| 291 | kthread_ppid = procpid; | 256 | kthread_ppid = procpid; |
| 257 | } | ||
| 292 | 258 | ||
| 293 | if (kthread_ppid == procppid) { | 259 | if (kthread_ppid == procppid) { |
| 294 | if (verbose >= 2) | 260 | if (verbose >= 2) { |
| 295 | printf ("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); | 261 | printf("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, |
| 262 | procppid, procprog, procargs); | ||
| 263 | } | ||
| 296 | continue; | 264 | continue; |
| 297 | } | 265 | } |
| 298 | } | 266 | } |
| 299 | 267 | ||
| 300 | if ((options & STAT) && (strstr (procstat, statopts))) | 268 | if ((config.options & STAT) && (strstr(procstat, config.statopts))) { |
| 301 | resultsum |= STAT; | 269 | resultsum |= STAT; |
| 302 | if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) | 270 | } |
| 271 | if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) { | ||
| 303 | resultsum |= ARGS; | 272 | resultsum |= ARGS; |
| 304 | if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t) 0, NULL, 0) == 0)) | 273 | } |
| 274 | if ((config.options & EREG_ARGS) && procargs && | ||
| 275 | (regexec(&config.re_args, procargs, (size_t)0, NULL, 0) == 0)) { | ||
| 305 | resultsum |= EREG_ARGS; | 276 | resultsum |= EREG_ARGS; |
| 306 | if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) | 277 | } |
| 278 | if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) { | ||
| 307 | resultsum |= PROG; | 279 | resultsum |= PROG; |
| 308 | if ((options & PPID) && (procppid == ppid)) | 280 | } |
| 281 | if ((config.options & PPID) && (procppid == config.ppid)) { | ||
| 309 | resultsum |= PPID; | 282 | resultsum |= PPID; |
| 310 | if ((options & USER) && (procuid == uid)) | 283 | } |
| 284 | if ((config.options & USER) && (procuid == config.uid)) { | ||
| 311 | resultsum |= USER; | 285 | resultsum |= USER; |
| 312 | if ((options & VSZ) && (procvsz >= vsz)) | 286 | } |
| 287 | if ((config.options & VSZ) && (procvsz >= config.vsz)) { | ||
| 313 | resultsum |= VSZ; | 288 | resultsum |= VSZ; |
| 314 | if ((options & RSS) && (procrss >= rss)) | 289 | } |
| 290 | if ((config.options & RSS) && (procrss >= config.rss)) { | ||
| 315 | resultsum |= RSS; | 291 | resultsum |= RSS; |
| 316 | if ((options & PCPU) && (procpcpu >= pcpu)) | 292 | } |
| 293 | if ((config.options & PCPU) && (procpcpu >= config.pcpu)) { | ||
| 317 | resultsum |= PCPU; | 294 | resultsum |= PCPU; |
| 295 | } | ||
| 318 | 296 | ||
| 319 | found++; | 297 | found++; |
| 320 | 298 | ||
| 321 | /* Next line if filters not matched */ | 299 | /* Next line if filters not matched */ |
| 322 | if (!(options == resultsum || options == ALL)) | 300 | if (!(config.options == resultsum || config.options == ALL)) { |
| 323 | continue; | 301 | continue; |
| 302 | } | ||
| 324 | 303 | ||
| 325 | procs++; | 304 | procs++; |
| 326 | if (verbose >= 2) { | 305 | if (verbose >= 2) { |
| 327 | printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", | 306 | printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s " |
| 328 | procuid, procvsz, procrss, | 307 | "prog=%s args=%s\n", |
| 329 | procpid, procppid, procpcpu, procstat, | 308 | procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat, procetime, |
| 330 | procetime, procprog, procargs); | 309 | procprog, procargs); |
| 331 | } | 310 | } |
| 332 | 311 | ||
| 333 | if (metric == METRIC_VSZ) | 312 | mp_state_enum temporary_result = STATE_OK; |
| 334 | i = get_status ((double)procvsz, procs_thresholds); | 313 | if (config.metric == METRIC_VSZ) { |
| 335 | else if (metric == METRIC_RSS) | 314 | temporary_result = get_status((double)procvsz, config.procs_thresholds); |
| 336 | i = get_status ((double)procrss, procs_thresholds); | 315 | } else if (config.metric == METRIC_RSS) { |
| 316 | temporary_result = get_status((double)procrss, config.procs_thresholds); | ||
| 317 | } | ||
| 337 | /* TODO? float thresholds for --metric=CPU */ | 318 | /* TODO? float thresholds for --metric=CPU */ |
| 338 | else if (metric == METRIC_CPU) | 319 | else if (config.metric == METRIC_CPU) { |
| 339 | i = get_status (procpcpu, procs_thresholds); | 320 | temporary_result = get_status(procpcpu, config.procs_thresholds); |
| 340 | else if (metric == METRIC_ELAPSED) | 321 | } else if (config.metric == METRIC_ELAPSED) { |
| 341 | i = get_status ((double)procseconds, procs_thresholds); | 322 | temporary_result = get_status((double)procseconds, config.procs_thresholds); |
| 323 | } | ||
| 342 | 324 | ||
| 343 | if (metric != METRIC_PROCS) { | 325 | if (config.metric != METRIC_PROCS) { |
| 344 | if (i == STATE_WARNING) { | 326 | if (temporary_result == STATE_WARNING) { |
| 345 | warn++; | 327 | warn++; |
| 346 | xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); | 328 | xasprintf(&config.fails, "%s%s%s", config.fails, |
| 347 | result = max_state (result, i); | 329 | (strcmp(config.fails, "") ? ", " : ""), procprog); |
| 330 | result = max_state(result, temporary_result); | ||
| 348 | } | 331 | } |
| 349 | if (i == STATE_CRITICAL) { | 332 | if (temporary_result == STATE_CRITICAL) { |
| 350 | crit++; | 333 | crit++; |
| 351 | xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); | 334 | xasprintf(&config.fails, "%s%s%s", config.fails, |
| 352 | result = max_state (result, i); | 335 | (strcmp(config.fails, "") ? ", " : ""), procprog); |
| 336 | result = max_state(result, temporary_result); | ||
| 353 | } | 337 | } |
| 354 | } | 338 | } |
| 355 | } | 339 | } |
| @@ -359,339 +343,366 @@ main (int argc, char **argv) | |||
| 359 | } | 343 | } |
| 360 | } | 344 | } |
| 361 | 345 | ||
| 362 | if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ | 346 | if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ |
| 363 | printf (_("Unable to read output\n")); | 347 | printf(_("Unable to read output\n")); |
| 364 | return STATE_UNKNOWN; | 348 | return STATE_UNKNOWN; |
| 365 | } | 349 | } |
| 366 | 350 | ||
| 367 | if ( result == STATE_UNKNOWN ) | 351 | if (result == STATE_UNKNOWN) { |
| 368 | result = STATE_OK; | 352 | result = STATE_OK; |
| 353 | } | ||
| 369 | 354 | ||
| 370 | /* Needed if procs found, but none match filter */ | 355 | /* Needed if procs found, but none match filter */ |
| 371 | if ( metric == METRIC_PROCS ) { | 356 | if (config.metric == METRIC_PROCS) { |
| 372 | result = max_state (result, get_status ((double)procs, procs_thresholds) ); | 357 | result = max_state(result, get_status((double)procs, config.procs_thresholds)); |
| 373 | } | 358 | } |
| 374 | 359 | ||
| 375 | if ( result == STATE_OK ) { | 360 | if (result == STATE_OK) { |
| 376 | printf ("%s %s: ", metric_name, _("OK")); | 361 | printf("%s %s: ", config.metric_name, _("OK")); |
| 377 | } else if (result == STATE_WARNING) { | 362 | } else if (result == STATE_WARNING) { |
| 378 | printf ("%s %s: ", metric_name, _("WARNING")); | 363 | printf("%s %s: ", config.metric_name, _("WARNING")); |
| 379 | if ( metric != METRIC_PROCS ) { | 364 | if (config.metric != METRIC_PROCS) { |
| 380 | printf (_("%d warn out of "), warn); | 365 | printf(_("%d warn out of "), warn); |
| 381 | } | 366 | } |
| 382 | } else if (result == STATE_CRITICAL) { | 367 | } else if (result == STATE_CRITICAL) { |
| 383 | printf ("%s %s: ", metric_name, _("CRITICAL")); | 368 | printf("%s %s: ", config.metric_name, _("CRITICAL")); |
| 384 | if (metric != METRIC_PROCS) { | 369 | if (config.metric != METRIC_PROCS) { |
| 385 | printf (_("%d crit, %d warn out of "), crit, warn); | 370 | printf(_("%d crit, %d warn out of "), crit, warn); |
| 386 | } | 371 | } |
| 387 | } | 372 | } |
| 388 | printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); | 373 | printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs); |
| 389 | 374 | ||
| 390 | if (strcmp(fmt,"") != 0) { | 375 | if (strcmp(config.fmt, "") != 0) { |
| 391 | printf (_(" with %s"), fmt); | 376 | printf(_(" with %s"), config.fmt); |
| 392 | } | 377 | } |
| 393 | 378 | ||
| 394 | if ( verbose >= 1 && strcmp(fails,"") ) | 379 | if (verbose >= 1 && strcmp(config.fails, "")) { |
| 395 | printf (" [%s]", fails); | 380 | printf(" [%s]", config.fails); |
| 381 | } | ||
| 396 | 382 | ||
| 397 | if (metric == METRIC_PROCS) | 383 | if (config.metric == METRIC_PROCS) { |
| 398 | printf (" | procs=%d;%s;%s;0;", procs, | 384 | printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "", |
| 399 | warning_range ? warning_range : "", | 385 | config.critical_range ? config.critical_range : ""); |
| 400 | critical_range ? critical_range : ""); | 386 | } else { |
| 401 | else | 387 | printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); |
| 402 | printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); | 388 | } |
| 403 | 389 | ||
| 404 | printf ("\n"); | 390 | printf("\n"); |
| 405 | return result; | 391 | exit(result); |
| 406 | } | 392 | } |
| 407 | 393 | ||
| 408 | |||
| 409 | |||
| 410 | /* process command-line arguments */ | 394 | /* process command-line arguments */ |
| 411 | int | 395 | check_procs_config_wrapper process_arguments(int argc, char **argv) { |
| 412 | process_arguments (int argc, char **argv) | 396 | static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, |
| 413 | { | 397 | {"critical", required_argument, 0, 'c'}, |
| 414 | int c = 1; | 398 | {"metric", required_argument, 0, 'm'}, |
| 415 | char *user; | 399 | {"timeout", required_argument, 0, 't'}, |
| 416 | struct passwd *pw; | 400 | {"status", required_argument, 0, 's'}, |
| 417 | int option = 0; | 401 | {"ppid", required_argument, 0, 'p'}, |
| 418 | int err; | 402 | {"user", required_argument, 0, 'u'}, |
| 419 | int cflags = REG_NOSUB | REG_EXTENDED; | 403 | {"command", required_argument, 0, 'C'}, |
| 420 | char errbuf[MAX_INPUT_BUFFER]; | 404 | {"vsz", required_argument, 0, 'z'}, |
| 421 | char *temp_string; | 405 | {"rss", required_argument, 0, 'r'}, |
| 422 | int i=0; | 406 | {"pcpu", required_argument, 0, 'P'}, |
| 423 | static struct option longopts[] = { | 407 | {"elapsed", required_argument, 0, 'e'}, |
| 424 | {"warning", required_argument, 0, 'w'}, | 408 | {"argument-array", required_argument, 0, 'a'}, |
| 425 | {"critical", required_argument, 0, 'c'}, | 409 | {"help", no_argument, 0, 'h'}, |
| 426 | {"metric", required_argument, 0, 'm'}, | 410 | {"version", no_argument, 0, 'V'}, |
| 427 | {"timeout", required_argument, 0, 't'}, | 411 | {"verbose", no_argument, 0, 'v'}, |
| 428 | {"status", required_argument, 0, 's'}, | 412 | {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1}, |
| 429 | {"ppid", required_argument, 0, 'p'}, | 413 | {"input-file", required_argument, 0, CHAR_MAX + 2}, |
| 430 | {"user", required_argument, 0, 'u'}, | 414 | {"no-kthreads", required_argument, 0, 'k'}, |
| 431 | {"command", required_argument, 0, 'C'}, | 415 | {"traditional-filter", no_argument, 0, 'T'}, |
| 432 | {"vsz", required_argument, 0, 'z'}, | 416 | {"exclude-process", required_argument, 0, 'X'}, |
| 433 | {"rss", required_argument, 0, 'r'}, | 417 | {0, 0, 0, 0}}; |
| 434 | {"pcpu", required_argument, 0, 'P'}, | 418 | |
| 435 | {"elapsed", required_argument, 0, 'e'}, | 419 | for (int index = 1; index < argc; index++) { |
| 436 | {"argument-array", required_argument, 0, 'a'}, | 420 | if (strcmp("-to", argv[index]) == 0) { |
| 437 | {"help", no_argument, 0, 'h'}, | 421 | strcpy(argv[index], "-t"); |
| 438 | {"version", no_argument, 0, 'V'}, | 422 | } |
| 439 | {"verbose", no_argument, 0, 'v'}, | 423 | } |
| 440 | {"ereg-argument-array", required_argument, 0, CHAR_MAX+1}, | ||
| 441 | {"input-file", required_argument, 0, CHAR_MAX+2}, | ||
| 442 | {"no-kthreads", required_argument, 0, 'k'}, | ||
| 443 | {"traditional-filter", no_argument, 0, 'T'}, | ||
| 444 | {"exclude-process", required_argument, 0, 'X'}, | ||
| 445 | {0, 0, 0, 0} | ||
| 446 | }; | ||
| 447 | 424 | ||
| 448 | for (c = 1; c < argc; c++) | 425 | check_procs_config_wrapper result = { |
| 449 | if (strcmp ("-to", argv[c]) == 0) | 426 | .errorcode = OK, |
| 450 | strcpy (argv[c], "-t"); | 427 | .config = check_procs_config_init(), |
| 428 | }; | ||
| 451 | 429 | ||
| 452 | while (1) { | 430 | while (true) { |
| 453 | c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", | 431 | int option = 0; |
| 454 | longopts, &option); | 432 | int option_index = |
| 433 | getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option); | ||
| 455 | 434 | ||
| 456 | if (c == -1 || c == EOF) | 435 | if (option_index == -1 || option_index == EOF) { |
| 457 | break; | 436 | break; |
| 437 | } | ||
| 458 | 438 | ||
| 459 | switch (c) { | 439 | switch (option_index) { |
| 460 | case '?': /* help */ | 440 | case '?': /* help */ |
| 461 | usage5 (); | 441 | usage5(); |
| 462 | case 'h': /* help */ | 442 | case 'h': /* help */ |
| 463 | print_help (); | 443 | print_help(); |
| 464 | exit (STATE_UNKNOWN); | 444 | exit(STATE_UNKNOWN); |
| 465 | case 'V': /* version */ | 445 | case 'V': /* version */ |
| 466 | print_revision (progname, NP_VERSION); | 446 | print_revision(progname, NP_VERSION); |
| 467 | exit (STATE_UNKNOWN); | 447 | exit(STATE_UNKNOWN); |
| 468 | case 't': /* timeout period */ | 448 | case 't': /* timeout period */ |
| 469 | if (!is_integer (optarg)) | 449 | if (!is_integer(optarg)) { |
| 470 | usage2 (_("Timeout interval must be a positive integer"), optarg); | 450 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 471 | else | 451 | } else { |
| 472 | timeout_interval = atoi (optarg); | 452 | timeout_interval = atoi(optarg); |
| 453 | } | ||
| 473 | break; | 454 | break; |
| 474 | case 'c': /* critical threshold */ | 455 | case 'c': /* critical threshold */ |
| 475 | critical_range = optarg; | 456 | result.config.critical_range = optarg; |
| 476 | break; | 457 | break; |
| 477 | case 'w': /* warning threshold */ | 458 | case 'w': /* warning threshold */ |
| 478 | warning_range = optarg; | 459 | result.config.warning_range = optarg; |
| 479 | break; | 460 | break; |
| 480 | case 'p': /* process id */ | 461 | case 'p': { /* process id */ |
| 481 | if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { | 462 | static char tmp[MAX_INPUT_BUFFER]; |
| 482 | xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); | 463 | if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) { |
| 483 | options |= PPID; | 464 | xasprintf(&result.config.fmt, "%s%sPPID = %d", |
| 465 | (result.config.fmt ? result.config.fmt : ""), | ||
| 466 | (result.config.options ? ", " : ""), result.config.ppid); | ||
| 467 | result.config.options |= PPID; | ||
| 484 | break; | 468 | break; |
| 485 | } | 469 | } |
| 486 | usage4 (_("Parent Process ID must be an integer!")); | 470 | usage4(_("Parent Process ID must be an integer!")); |
| 487 | case 's': /* status */ | 471 | } |
| 488 | if (statopts) | 472 | case 's': /* status */ |
| 473 | if (result.config.statopts) { | ||
| 489 | break; | 474 | break; |
| 490 | else | 475 | } else { |
| 491 | statopts = optarg; | 476 | result.config.statopts = optarg; |
| 492 | xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); | 477 | } |
| 493 | options |= STAT; | 478 | xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), |
| 479 | (result.config.fmt ? result.config.fmt : ""), | ||
| 480 | (result.config.options ? ", " : ""), result.config.statopts); | ||
| 481 | result.config.options |= STAT; | ||
| 494 | break; | 482 | break; |
| 495 | case 'u': /* user or user id */ | 483 | case 'u': /* user or user id */ { |
| 496 | if (is_integer (optarg)) { | 484 | struct passwd *pw; |
| 497 | uid = atoi (optarg); | 485 | if (is_integer(optarg)) { |
| 498 | pw = getpwuid ((uid_t) uid); | 486 | result.config.uid = atoi(optarg); |
| 487 | pw = getpwuid(result.config.uid); | ||
| 499 | /* check to be sure user exists */ | 488 | /* check to be sure user exists */ |
| 500 | if (pw == NULL) | 489 | if (pw == NULL) { |
| 501 | usage2 (_("UID was not found"), optarg); | 490 | usage2(_("UID was not found"), optarg); |
| 502 | } | 491 | } |
| 503 | else { | 492 | } else { |
| 504 | pw = getpwnam (optarg); | 493 | pw = getpwnam(optarg); |
| 505 | /* check to be sure user exists */ | 494 | /* check to be sure user exists */ |
| 506 | if (pw == NULL) | 495 | if (pw == NULL) { |
| 507 | usage2 (_("User name was not found"), optarg); | 496 | usage2(_("User name was not found"), optarg); |
| 497 | } | ||
| 508 | /* then get uid */ | 498 | /* then get uid */ |
| 509 | uid = pw->pw_uid; | 499 | result.config.uid = pw->pw_uid; |
| 510 | } | 500 | } |
| 511 | user = pw->pw_name; | 501 | |
| 512 | xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), | 502 | char *user = pw->pw_name; |
| 513 | uid, user); | 503 | xasprintf(&result.config.fmt, "%s%sUID = %d (%s)", |
| 514 | options |= USER; | 504 | (result.config.fmt ? result.config.fmt : ""), |
| 515 | break; | 505 | (result.config.options ? ", " : ""), result.config.uid, user); |
| 516 | case 'C': /* command */ | 506 | result.config.options |= USER; |
| 507 | } break; | ||
| 508 | case 'C': /* command */ | ||
| 517 | /* TODO: allow this to be passed in with --metric */ | 509 | /* TODO: allow this to be passed in with --metric */ |
| 518 | if (prog) | 510 | if (result.config.prog) { |
| 519 | break; | 511 | break; |
| 520 | else | 512 | } else { |
| 521 | prog = optarg; | 513 | result.config.prog = optarg; |
| 522 | xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), | 514 | } |
| 523 | prog); | 515 | xasprintf(&result.config.fmt, _("%s%scommand name '%s'"), |
| 524 | options |= PROG; | 516 | (result.config.fmt ? result.config.fmt : ""), |
| 517 | (result.config.options ? ", " : ""), result.config.prog); | ||
| 518 | result.config.options |= PROG; | ||
| 525 | break; | 519 | break; |
| 526 | case 'X': | 520 | case 'X': |
| 527 | if(exclude_progs) | 521 | if (result.config.exclude_progs) { |
| 528 | break; | 522 | break; |
| 529 | else | 523 | } else { |
| 530 | exclude_progs = optarg; | 524 | result.config.exclude_progs = optarg; |
| 531 | xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), | 525 | } |
| 532 | exclude_progs); | 526 | xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"), |
| 533 | char *p = strtok(exclude_progs, ","); | 527 | (result.config.fmt ? result.config.fmt : ""), |
| 534 | 528 | (result.config.options ? ", " : ""), result.config.exclude_progs); | |
| 535 | while(p){ | 529 | char *tmp_pointer = strtok(result.config.exclude_progs, ","); |
| 536 | exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); | 530 | |
| 537 | exclude_progs_arr[exclude_progs_counter-1] = p; | 531 | while (tmp_pointer) { |
| 538 | p = strtok(NULL, ","); | 532 | result.config.exclude_progs_arr = |
| 533 | realloc(result.config.exclude_progs_arr, | ||
| 534 | sizeof(char *) * ++result.config.exclude_progs_counter); | ||
| 535 | result.config.exclude_progs_arr[result.config.exclude_progs_counter - 1] = | ||
| 536 | tmp_pointer; | ||
| 537 | tmp_pointer = strtok(NULL, ","); | ||
| 539 | } | 538 | } |
| 540 | 539 | ||
| 541 | options |= EXCLUDE_PROGS; | 540 | result.config.options |= EXCLUDE_PROGS; |
| 542 | break; | 541 | break; |
| 543 | case 'a': /* args (full path name with args) */ | 542 | case 'a': /* args (full path name with args) */ |
| 544 | /* TODO: allow this to be passed in with --metric */ | 543 | /* TODO: allow this to be passed in with --metric */ |
| 545 | if (args) | 544 | if (result.config.args) { |
| 546 | break; | 545 | break; |
| 547 | else | 546 | } else { |
| 548 | args = optarg; | 547 | result.config.args = optarg; |
| 549 | xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); | 548 | } |
| 550 | options |= ARGS; | 549 | xasprintf(&result.config.fmt, "%s%sargs '%s'", |
| 550 | (result.config.fmt ? result.config.fmt : ""), | ||
| 551 | (result.config.options ? ", " : ""), result.config.args); | ||
| 552 | result.config.options |= ARGS; | ||
| 551 | break; | 553 | break; |
| 552 | case CHAR_MAX+1: | 554 | case CHAR_MAX + 1: { |
| 553 | err = regcomp(&re_args, optarg, cflags); | 555 | int cflags = REG_NOSUB | REG_EXTENDED; |
| 556 | int err = regcomp(&result.config.re_args, optarg, cflags); | ||
| 554 | if (err != 0) { | 557 | if (err != 0) { |
| 555 | regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); | 558 | char errbuf[MAX_INPUT_BUFFER]; |
| 556 | die (STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); | 559 | regerror(err, &result.config.re_args, errbuf, MAX_INPUT_BUFFER); |
| 560 | die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), | ||
| 561 | _("Could not compile regular expression"), errbuf); | ||
| 557 | } | 562 | } |
| 558 | /* Strip off any | within the regex optarg */ | 563 | /* Strip off any | within the regex optarg */ |
| 559 | temp_string = strdup(optarg); | 564 | char *temp_string = strdup(optarg); |
| 560 | while(temp_string[i]!='\0'){ | 565 | int index = 0; |
| 561 | if(temp_string[i]=='|') | 566 | while (temp_string[index] != '\0') { |
| 562 | temp_string[i]=','; | 567 | if (temp_string[index] == '|') { |
| 563 | i++; | 568 | temp_string[index] = ','; |
| 564 | } | 569 | } |
| 565 | xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); | 570 | index++; |
| 566 | options |= EREG_ARGS; | 571 | } |
| 567 | break; | 572 | xasprintf(&result.config.fmt, "%s%sregex args '%s'", |
| 568 | case 'r': /* RSS */ | 573 | (result.config.fmt ? result.config.fmt : ""), |
| 569 | if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { | 574 | (result.config.options ? ", " : ""), temp_string); |
| 570 | xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); | 575 | result.config.options |= EREG_ARGS; |
| 571 | options |= RSS; | 576 | } break; |
| 577 | case 'r': { /* RSS */ | ||
| 578 | static char tmp[MAX_INPUT_BUFFER]; | ||
| 579 | if (sscanf(optarg, "%d%[^0-9]", &result.config.rss, tmp) == 1) { | ||
| 580 | xasprintf(&result.config.fmt, "%s%sRSS >= %d", | ||
| 581 | (result.config.fmt ? result.config.fmt : ""), | ||
| 582 | (result.config.options ? ", " : ""), result.config.rss); | ||
| 583 | result.config.options |= RSS; | ||
| 572 | break; | 584 | break; |
| 573 | } | 585 | } |
| 574 | usage4 (_("RSS must be an integer!")); | 586 | usage4(_("RSS must be an integer!")); |
| 575 | case 'z': /* VSZ */ | 587 | } |
| 576 | if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { | 588 | case 'z': { /* VSZ */ |
| 577 | xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); | 589 | static char tmp[MAX_INPUT_BUFFER]; |
| 578 | options |= VSZ; | 590 | if (sscanf(optarg, "%d%[^0-9]", &result.config.vsz, tmp) == 1) { |
| 591 | xasprintf(&result.config.fmt, "%s%sVSZ >= %d", | ||
| 592 | (result.config.fmt ? result.config.fmt : ""), | ||
| 593 | (result.config.options ? ", " : ""), result.config.vsz); | ||
| 594 | result.config.options |= VSZ; | ||
| 579 | break; | 595 | break; |
| 580 | } | 596 | } |
| 581 | usage4 (_("VSZ must be an integer!")); | 597 | usage4(_("VSZ must be an integer!")); |
| 582 | case 'P': /* PCPU */ | 598 | } |
| 599 | case 'P': { /* PCPU */ | ||
| 583 | /* TODO: -P 1.5.5 is accepted */ | 600 | /* TODO: -P 1.5.5 is accepted */ |
| 584 | if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { | 601 | static char tmp[MAX_INPUT_BUFFER]; |
| 585 | xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); | 602 | if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) { |
| 586 | options |= PCPU; | 603 | xasprintf(&result.config.fmt, "%s%sPCPU >= %.2f", |
| 604 | (result.config.fmt ? result.config.fmt : ""), | ||
| 605 | (result.config.options ? ", " : ""), result.config.pcpu); | ||
| 606 | result.config.options |= PCPU; | ||
| 587 | break; | 607 | break; |
| 588 | } | 608 | } |
| 589 | usage4 (_("PCPU must be a float!")); | 609 | usage4(_("PCPU must be a float!")); |
| 610 | } | ||
| 590 | case 'm': | 611 | case 'm': |
| 591 | xasprintf (&metric_name, "%s", optarg); | 612 | xasprintf(&result.config.metric_name, "%s", optarg); |
| 592 | if ( strcmp(optarg, "PROCS") == 0) { | 613 | if (strcmp(optarg, "PROCS") == 0) { |
| 593 | metric = METRIC_PROCS; | 614 | result.config.metric = METRIC_PROCS; |
| 594 | break; | 615 | break; |
| 595 | } | 616 | } |
| 596 | else if ( strcmp(optarg, "VSZ") == 0) { | 617 | if (strcmp(optarg, "VSZ") == 0) { |
| 597 | metric = METRIC_VSZ; | 618 | result.config.metric = METRIC_VSZ; |
| 598 | break; | 619 | break; |
| 599 | } | 620 | } |
| 600 | else if ( strcmp(optarg, "RSS") == 0 ) { | 621 | if (strcmp(optarg, "RSS") == 0) { |
| 601 | metric = METRIC_RSS; | 622 | result.config.metric = METRIC_RSS; |
| 602 | break; | 623 | break; |
| 603 | } | 624 | } |
| 604 | else if ( strcmp(optarg, "CPU") == 0 ) { | 625 | if (strcmp(optarg, "CPU") == 0) { |
| 605 | metric = METRIC_CPU; | 626 | result.config.metric = METRIC_CPU; |
| 606 | break; | 627 | break; |
| 607 | } | 628 | } |
| 608 | else if ( strcmp(optarg, "ELAPSED") == 0) { | 629 | if (strcmp(optarg, "ELAPSED") == 0) { |
| 609 | metric = METRIC_ELAPSED; | 630 | result.config.metric = METRIC_ELAPSED; |
| 610 | break; | 631 | break; |
| 611 | } | 632 | } |
| 612 | 633 | ||
| 613 | usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); | 634 | usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); |
| 614 | case 'k': /* linux kernel thread filter */ | 635 | case 'k': /* linux kernel thread filter */ |
| 615 | kthread_filter = 1; | 636 | result.config.kthread_filter = true; |
| 616 | break; | 637 | break; |
| 617 | case 'v': /* command */ | 638 | case 'v': /* command */ |
| 618 | verbose++; | 639 | verbose++; |
| 619 | break; | 640 | break; |
| 620 | case 'T': | 641 | case 'T': |
| 621 | usepid = 1; | 642 | result.config.usepid = true; |
| 622 | break; | 643 | break; |
| 623 | case CHAR_MAX+2: | 644 | case CHAR_MAX + 2: |
| 624 | input_filename = optarg; | 645 | result.config.input_filename = optarg; |
| 625 | break; | 646 | break; |
| 626 | } | 647 | } |
| 627 | } | 648 | } |
| 628 | 649 | ||
| 629 | c = optind; | 650 | int index = optind; |
| 630 | if ((! warning_range) && argv[c]) | 651 | if ((!result.config.warning_range) && argv[index]) { |
| 631 | warning_range = argv[c++]; | 652 | result.config.warning_range = argv[index++]; |
| 632 | if ((! critical_range) && argv[c]) | 653 | } |
| 633 | critical_range = argv[c++]; | 654 | if ((!result.config.critical_range) && argv[index]) { |
| 634 | if (statopts == NULL && argv[c]) { | 655 | result.config.critical_range = argv[index++]; |
| 635 | xasprintf (&statopts, "%s", argv[c++]); | 656 | } |
| 636 | xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); | 657 | if (result.config.statopts == NULL && argv[index]) { |
| 637 | options |= STAT; | 658 | xasprintf(&result.config.statopts, "%s", argv[index++]); |
| 659 | xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), | ||
| 660 | (result.config.fmt ? result.config.fmt : ""), (result.config.options ? ", " : ""), | ||
| 661 | result.config.statopts); | ||
| 662 | result.config.options |= STAT; | ||
| 638 | } | 663 | } |
| 639 | 664 | ||
| 640 | /* this will abort in case of invalid ranges */ | 665 | /* this will abort in case of invalid ranges */ |
| 641 | set_thresholds (&procs_thresholds, warning_range, critical_range); | 666 | set_thresholds(&result.config.procs_thresholds, result.config.warning_range, |
| 667 | result.config.critical_range); | ||
| 642 | 668 | ||
| 643 | return validate_arguments (); | 669 | return validate_arguments(result); |
| 644 | } | 670 | } |
| 645 | 671 | ||
| 672 | check_procs_config_wrapper validate_arguments(check_procs_config_wrapper config_wrapper) { | ||
| 673 | if (config_wrapper.config.options == 0) { | ||
| 674 | config_wrapper.config.options = ALL; | ||
| 675 | } | ||
| 646 | 676 | ||
| 677 | if (config_wrapper.config.statopts == NULL) { | ||
| 678 | config_wrapper.config.statopts = strdup(""); | ||
| 679 | } | ||
| 647 | 680 | ||
| 648 | int | 681 | if (config_wrapper.config.prog == NULL) { |
| 649 | validate_arguments () | 682 | config_wrapper.config.prog = strdup(""); |
| 650 | { | 683 | } |
| 651 | if (options == 0) | ||
| 652 | options = ALL; | ||
| 653 | |||
| 654 | if (statopts==NULL) | ||
| 655 | statopts = strdup(""); | ||
| 656 | |||
| 657 | if (prog==NULL) | ||
| 658 | prog = strdup(""); | ||
| 659 | 684 | ||
| 660 | if (args==NULL) | 685 | if (config_wrapper.config.args == NULL) { |
| 661 | args = strdup(""); | 686 | config_wrapper.config.args = strdup(""); |
| 687 | } | ||
| 662 | 688 | ||
| 663 | if (fmt==NULL) | 689 | if (config_wrapper.config.fmt == NULL) { |
| 664 | fmt = strdup(""); | 690 | config_wrapper.config.fmt = strdup(""); |
| 691 | } | ||
| 665 | 692 | ||
| 666 | if (fails==NULL) | 693 | if (config_wrapper.config.fails == NULL) { |
| 667 | fails = strdup(""); | 694 | config_wrapper.config.fails = strdup(""); |
| 695 | } | ||
| 668 | 696 | ||
| 669 | return options; | 697 | // return options; |
| 698 | return config_wrapper; | ||
| 670 | } | 699 | } |
| 671 | 700 | ||
| 672 | |||
| 673 | /* convert the elapsed time to seconds */ | 701 | /* convert the elapsed time to seconds */ |
| 674 | int | 702 | int convert_to_seconds(char *etime, enum metric metric) { |
| 675 | convert_to_seconds(char *etime) { | 703 | int hyphcnt = 0; |
| 676 | 704 | int coloncnt = 0; | |
| 677 | char *ptr; | 705 | for (char *ptr = etime; *ptr != '\0'; ptr++) { |
| 678 | int total; | ||
| 679 | |||
| 680 | int hyphcnt; | ||
| 681 | int coloncnt; | ||
| 682 | int days; | ||
| 683 | int hours; | ||
| 684 | int minutes; | ||
| 685 | int seconds; | ||
| 686 | |||
| 687 | hyphcnt = 0; | ||
| 688 | coloncnt = 0; | ||
| 689 | days = 0; | ||
| 690 | hours = 0; | ||
| 691 | minutes = 0; | ||
| 692 | seconds = 0; | ||
| 693 | |||
| 694 | for (ptr = etime; *ptr != '\0'; ptr++) { | ||
| 695 | 706 | ||
| 696 | if (*ptr == '-') { | 707 | if (*ptr == '-') { |
| 697 | hyphcnt++; | 708 | hyphcnt++; |
| @@ -703,9 +714,12 @@ convert_to_seconds(char *etime) { | |||
| 703 | } | 714 | } |
| 704 | } | 715 | } |
| 705 | 716 | ||
| 717 | int days = 0; | ||
| 718 | int hours = 0; | ||
| 719 | int minutes = 0; | ||
| 720 | int seconds = 0; | ||
| 706 | if (hyphcnt > 0) { | 721 | if (hyphcnt > 0) { |
| 707 | sscanf(etime, "%d-%d:%d:%d", | 722 | sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds); |
| 708 | &days, &hours, &minutes, &seconds); | ||
| 709 | /* linux 2.6.5/2.6.6 reporting some processes with infinite | 723 | /* linux 2.6.5/2.6.6 reporting some processes with infinite |
| 710 | * elapsed times for some reason */ | 724 | * elapsed times for some reason */ |
| 711 | if (days == 49710) { | 725 | if (days == 49710) { |
| @@ -713,135 +727,129 @@ convert_to_seconds(char *etime) { | |||
| 713 | } | 727 | } |
| 714 | } else { | 728 | } else { |
| 715 | if (coloncnt == 2) { | 729 | if (coloncnt == 2) { |
| 716 | sscanf(etime, "%d:%d:%d", | 730 | sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds); |
| 717 | &hours, &minutes, &seconds); | ||
| 718 | } else if (coloncnt == 1) { | 731 | } else if (coloncnt == 1) { |
| 719 | sscanf(etime, "%d:%d", | 732 | sscanf(etime, "%d:%d", &minutes, &seconds); |
| 720 | &minutes, &seconds); | ||
| 721 | } | 733 | } |
| 722 | } | 734 | } |
| 723 | 735 | ||
| 724 | total = (days * 86400) + | 736 | int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds; |
| 725 | (hours * 3600) + | ||
| 726 | (minutes * 60) + | ||
| 727 | seconds; | ||
| 728 | 737 | ||
| 729 | if (verbose >= 3 && metric == METRIC_ELAPSED) { | 738 | if (verbose >= 3 && metric == METRIC_ELAPSED) { |
| 730 | printf("seconds: %d\n", total); | 739 | printf("seconds: %d\n", total); |
| 731 | } | 740 | } |
| 732 | return total; | 741 | return total; |
| 733 | } | 742 | } |
| 734 | 743 | ||
| 735 | 744 | void print_help(void) { | |
| 736 | void | 745 | print_revision(progname, NP_VERSION); |
| 737 | print_help (void) | 746 | |
| 738 | { | 747 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); |
| 739 | print_revision (progname, NP_VERSION); | 748 | printf(COPYRIGHT, copyright, email); |
| 740 | 749 | ||
| 741 | printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); | 750 | printf("%s\n", |
| 742 | printf (COPYRIGHT, copyright, email); | 751 | _("Checks all processes and generates WARNING or CRITICAL states if the specified")); |
| 743 | 752 | printf("%s\n", | |
| 744 | printf ("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); | 753 | _("metric is outside the required threshold ranges. The metric defaults to number")); |
| 745 | printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); | 754 | printf("%s\n", |
| 746 | printf ("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); | 755 | _("of processes. Search filters can be applied to limit the processes to check.")); |
| 747 | 756 | ||
| 748 | printf ("\n\n"); | 757 | printf("\n\n"); |
| 749 | 758 | ||
| 750 | printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); | 759 | printf("%s\n", |
| 751 | printf ("%s\n", _("are excluded from any checks to prevent false positives.")); | 760 | _("The parent process, check_procs itself and any child process of check_procs (ps)")); |
| 752 | 761 | printf("%s\n", _("are excluded from any checks to prevent false positives.")); | |
| 753 | printf ("\n\n"); | 762 | |
| 754 | 763 | printf("\n\n"); | |
| 755 | print_usage (); | 764 | |
| 756 | 765 | print_usage(); | |
| 757 | printf (UT_HELP_VRSN); | 766 | |
| 758 | printf (UT_EXTRA_OPTS); | 767 | printf(UT_HELP_VRSN); |
| 759 | printf (" %s\n", "-w, --warning=RANGE"); | 768 | printf(UT_EXTRA_OPTS); |
| 760 | printf (" %s\n", _("Generate warning state if metric is outside this range")); | 769 | printf(" %s\n", "-w, --warning=RANGE"); |
| 761 | printf (" %s\n", "-c, --critical=RANGE"); | 770 | printf(" %s\n", _("Generate warning state if metric is outside this range")); |
| 762 | printf (" %s\n", _("Generate critical state if metric is outside this range")); | 771 | printf(" %s\n", "-c, --critical=RANGE"); |
| 763 | printf (" %s\n", "-m, --metric=TYPE"); | 772 | printf(" %s\n", _("Generate critical state if metric is outside this range")); |
| 764 | printf (" %s\n", _("Check thresholds against metric. Valid types:")); | 773 | printf(" %s\n", "-m, --metric=TYPE"); |
| 765 | printf (" %s\n", _("PROCS - number of processes (default)")); | 774 | printf(" %s\n", _("Check thresholds against metric. Valid types:")); |
| 766 | printf (" %s\n", _("VSZ - virtual memory size")); | 775 | printf(" %s\n", _("PROCS - number of processes (default)")); |
| 767 | printf (" %s\n", _("RSS - resident set memory size")); | 776 | printf(" %s\n", _("VSZ - virtual memory size")); |
| 768 | printf (" %s\n", _("CPU - percentage CPU")); | 777 | printf(" %s\n", _("RSS - resident set memory size")); |
| 778 | printf(" %s\n", _("CPU - percentage CPU")); | ||
| 769 | /* only linux etime is support currently */ | 779 | /* only linux etime is support currently */ |
| 770 | #if defined( __linux__ ) | 780 | #if defined(__linux__) |
| 771 | printf (" %s\n", _("ELAPSED - time elapsed in seconds")); | 781 | printf(" %s\n", _("ELAPSED - time elapsed in seconds")); |
| 772 | #endif /* defined(__linux__) */ | 782 | #endif /* defined(__linux__) */ |
| 773 | printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 783 | printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 774 | 784 | ||
| 775 | printf (" %s\n", "-v, --verbose"); | 785 | printf(" %s\n", "-v, --verbose"); |
| 776 | printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); | 786 | printf(" %s\n", _("Extra information. Up to 3 verbosity levels")); |
| 777 | 787 | ||
| 778 | printf (" %s\n", "-T, --traditional"); | 788 | printf(" %s\n", "-T, --traditional"); |
| 779 | printf (" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); | 789 | printf(" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); |
| 780 | 790 | ||
| 781 | printf ("\n"); | 791 | printf("\n"); |
| 782 | printf ("%s\n", "Filters:"); | 792 | printf("%s\n", "Filters:"); |
| 783 | printf (" %s\n", "-s, --state=STATUSFLAGS"); | 793 | printf(" %s\n", "-s, --state=STATUSFLAGS"); |
| 784 | printf (" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); | 794 | printf(" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); |
| 785 | printf (" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); | 795 | printf(" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); |
| 786 | printf (" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); | 796 | printf(" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); |
| 787 | printf (" %s\n", "-p, --ppid=PPID"); | 797 | printf(" %s\n", "-p, --ppid=PPID"); |
| 788 | printf (" %s\n", _("Only scan for children of the parent process ID indicated.")); | 798 | printf(" %s\n", _("Only scan for children of the parent process ID indicated.")); |
| 789 | printf (" %s\n", "-z, --vsz=VSZ"); | 799 | printf(" %s\n", "-z, --vsz=VSZ"); |
| 790 | printf (" %s\n", _("Only scan for processes with VSZ higher than indicated.")); | 800 | printf(" %s\n", _("Only scan for processes with VSZ higher than indicated.")); |
| 791 | printf (" %s\n", "-r, --rss=RSS"); | 801 | printf(" %s\n", "-r, --rss=RSS"); |
| 792 | printf (" %s\n", _("Only scan for processes with RSS higher than indicated.")); | 802 | printf(" %s\n", _("Only scan for processes with RSS higher than indicated.")); |
| 793 | printf (" %s\n", "-P, --pcpu=PCPU"); | 803 | printf(" %s\n", "-P, --pcpu=PCPU"); |
| 794 | printf (" %s\n", _("Only scan for processes with PCPU higher than indicated.")); | 804 | printf(" %s\n", _("Only scan for processes with PCPU higher than indicated.")); |
| 795 | printf (" %s\n", "-u, --user=USER"); | 805 | printf(" %s\n", "-u, --user=USER"); |
| 796 | printf (" %s\n", _("Only scan for processes with user name or ID indicated.")); | 806 | printf(" %s\n", _("Only scan for processes with user name or ID indicated.")); |
| 797 | printf (" %s\n", "-a, --argument-array=STRING"); | 807 | printf(" %s\n", "-a, --argument-array=STRING"); |
| 798 | printf (" %s\n", _("Only scan for processes with args that contain STRING.")); | 808 | printf(" %s\n", _("Only scan for processes with args that contain STRING.")); |
| 799 | printf (" %s\n", "--ereg-argument-array=STRING"); | 809 | printf(" %s\n", "--ereg-argument-array=STRING"); |
| 800 | printf (" %s\n", _("Only scan for processes with args that contain the regex STRING.")); | 810 | printf(" %s\n", _("Only scan for processes with args that contain the regex STRING.")); |
| 801 | printf (" %s\n", "-C, --command=COMMAND"); | 811 | printf(" %s\n", "-C, --command=COMMAND"); |
| 802 | printf (" %s\n", _("Only scan for exact matches of COMMAND (without path).")); | 812 | printf(" %s\n", _("Only scan for exact matches of COMMAND (without path).")); |
| 803 | printf (" %s\n", "-X, --exclude-process"); | 813 | printf(" %s\n", "-X, --exclude-process"); |
| 804 | printf (" %s\n", _("Exclude processes which match this comma separated list")); | 814 | printf(" %s\n", _("Exclude processes which match this comma separated list")); |
| 805 | printf (" %s\n", "-k, --no-kthreads"); | 815 | printf(" %s\n", "-k, --no-kthreads"); |
| 806 | printf (" %s\n", _("Only scan for non kernel threads (works on Linux only).")); | 816 | printf(" %s\n", _("Only scan for non kernel threads (works on Linux only).")); |
| 807 | 817 | ||
| 808 | printf(_("\n\ | 818 | printf(_("\n\ |
| 809 | RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ | 819 | RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ |
| 810 | specified 'max:min', a warning status will be generated if the\n\ | 820 | specified 'max:min', a warning status will be generated if the\n\ |
| 811 | count is inside the specified range\n\n")); | 821 | count is inside the specified range\n\n")); |
| 812 | 822 | ||
| 813 | printf(_("\ | 823 | printf(_("\ |
| 814 | This plugin checks the number of currently running processes and\n\ | 824 | This plugin checks the number of currently running processes and\n\ |
| 815 | generates WARNING or CRITICAL states if the process count is outside\n\ | 825 | generates WARNING or CRITICAL states if the process count is outside\n\ |
| 816 | the specified threshold ranges. The process count can be filtered by\n\ | 826 | the specified threshold ranges. The process count can be filtered by\n\ |
| 817 | process owner, parent process PID, current state (e.g., 'Z'), or may\n\ | 827 | process owner, parent process PID, current state (e.g., 'Z'), or may\n\ |
| 818 | be the total number of running processes\n\n")); | 828 | be the total number of running processes\n\n")); |
| 819 | 829 | ||
| 820 | printf ("%s\n", _("Examples:")); | 830 | printf("%s\n", _("Examples:")); |
| 821 | printf (" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); | 831 | printf(" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); |
| 822 | printf (" %s\n", _("Warning if not two processes with command name portsentry.")); | 832 | printf(" %s\n", _("Warning if not two processes with command name portsentry.")); |
| 823 | printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); | 833 | printf(" %s\n\n", _("Critical if < 2 or > 1024 processes")); |
| 824 | printf (" %s\n", "check_procs -c 1: -C sshd"); | 834 | printf(" %s\n", "check_procs -c 1: -C sshd"); |
| 825 | printf (" %s\n", _("Critical if not at least 1 process with command sshd")); | 835 | printf(" %s\n", _("Critical if not at least 1 process with command sshd")); |
| 826 | printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); | 836 | printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd"); |
| 827 | printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); | 837 | printf(" %s\n", _("Warning if > 1024 processes with command name sshd.")); |
| 828 | printf (" %s\n\n", _("Critical if < 1 processes with command name sshd.")); | 838 | printf(" %s\n\n", _("Critical if < 1 processes with command name sshd.")); |
| 829 | printf (" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); | 839 | printf(" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); |
| 830 | printf (" %s\n", _("Warning alert if > 10 processes with command arguments containing")); | 840 | printf(" %s\n", _("Warning alert if > 10 processes with command arguments containing")); |
| 831 | printf (" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); | 841 | printf(" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); |
| 832 | printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); | 842 | printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); |
| 833 | printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); | 843 | printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); |
| 834 | printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); | 844 | printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); |
| 835 | printf (" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); | 845 | printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); |
| 836 | 846 | ||
| 837 | printf (UT_SUPPORT); | 847 | printf(UT_SUPPORT); |
| 838 | } | 848 | } |
| 839 | 849 | ||
| 840 | void | 850 | void print_usage(void) { |
| 841 | print_usage (void) | 851 | printf("%s\n", _("Usage:")); |
| 842 | { | 852 | printf("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); |
| 843 | printf ("%s\n", _("Usage:")); | 853 | printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n"); |
| 844 | printf ("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); | 854 | printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n"); |
| 845 | printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n"); | ||
| 846 | printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n"); | ||
| 847 | } | 855 | } |
diff --git a/plugins/check_procs.d/config.h b/plugins/check_procs.d/config.h new file mode 100644 index 00000000..e32ca066 --- /dev/null +++ b/plugins/check_procs.d/config.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "regex.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include <sys/types.h> | ||
| 9 | |||
| 10 | enum metric { | ||
| 11 | METRIC_PROCS, | ||
| 12 | METRIC_VSZ, | ||
| 13 | METRIC_RSS, | ||
| 14 | METRIC_CPU, | ||
| 15 | METRIC_ELAPSED | ||
| 16 | }; | ||
| 17 | |||
| 18 | typedef 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 | |||
| 46 | check_procs_config check_procs_config_init() { | ||
| 47 | check_procs_config tmp = { | ||
| 48 | .options = 0, | ||
| 49 | .metric = METRIC_PROCS, | ||
| 50 | .metric_name = strdup("PROCS"), | ||
| 51 | .input_filename = NULL, | ||
| 52 | .prog = NULL, | ||
| 53 | .args = NULL, | ||
| 54 | .fmt = NULL, | ||
| 55 | .fails = NULL, | ||
| 56 | .exclude_progs = NULL, | ||
| 57 | .exclude_progs_arr = NULL, | ||
| 58 | .exclude_progs_counter = 0, | ||
| 59 | .re_args = {0}, | ||
| 60 | |||
| 61 | .kthread_filter = false, | ||
| 62 | .usepid = false, | ||
| 63 | .uid = 0, | ||
| 64 | .ppid = 0, | ||
| 65 | .vsz = 0, | ||
| 66 | .rss = 0, | ||
| 67 | .pcpu = 0, | ||
| 68 | .statopts = NULL, | ||
| 69 | |||
| 70 | .warning_range = NULL, | ||
| 71 | .critical_range = NULL, | ||
| 72 | .procs_thresholds = NULL, | ||
| 73 | }; | ||
| 74 | return tmp; | ||
| 75 | } | ||
diff --git a/plugins/check_radius.c b/plugins/check_radius.c index d9ff8fa7..d26f7cf3 100644 --- a/plugins/check_radius.c +++ b/plugins/check_radius.c | |||
| @@ -1,32 +1,32 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_radius plugin | 3 | * Monitoring check_radius plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team | 6 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the check_radius plugin | 10 | * This file contains the check_radius plugin |
| 11 | * | 11 | * |
| 12 | * Tests to see if a radius server is accepting connections. | 12 | * Tests to see if a radius server is accepting connections. |
| 13 | * | 13 | * |
| 14 | * | 14 | * |
| 15 | * This program is free software: you can redistribute it and/or modify | 15 | * This program is free software: you can redistribute it and/or modify |
| 16 | * it under the terms of the GNU General Public License as published by | 16 | * it under the terms of the GNU General Public License as published by |
| 17 | * the Free Software Foundation, either version 3 of the License, or | 17 | * the Free Software Foundation, either version 3 of the License, or |
| 18 | * (at your option) any later version. | 18 | * (at your option) any later version. |
| 19 | * | 19 | * |
| 20 | * This program is distributed in the hope that it will be useful, | 20 | * This program is distributed in the hope that it will be useful, |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | * GNU General Public License for more details. | 23 | * GNU General Public License for more details. |
| 24 | * | 24 | * |
| 25 | * You should have received a copy of the GNU General Public License | 25 | * You should have received a copy of the GNU General Public License |
| 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | * | 27 | * |
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | const char *progname = "check_radius"; | 31 | const char *progname = "check_radius"; |
| 32 | const char *copyright = "2000-2024"; | 32 | const char *copyright = "2000-2024"; |
| @@ -35,64 +35,58 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 35 | #include "common.h" | 35 | #include "common.h" |
| 36 | #include "utils.h" | 36 | #include "utils.h" |
| 37 | #include "netutils.h" | 37 | #include "netutils.h" |
| 38 | #include "states.h" | ||
| 39 | #include "check_radius.d/config.h" | ||
| 38 | 40 | ||
| 39 | #if defined(HAVE_LIBRADCLI) | 41 | #if defined(HAVE_LIBRADCLI) |
| 40 | #include <radcli/radcli.h> | 42 | # include <radcli/radcli.h> |
| 41 | #elif defined(HAVE_LIBFREERADIUS_CLIENT) | 43 | #elif defined(HAVE_LIBFREERADIUS_CLIENT) |
| 42 | #include <freeradius-client.h> | 44 | # include <freeradius-client.h> |
| 43 | #elif defined(HAVE_LIBRADIUSCLIENT_NG) | 45 | #elif defined(HAVE_LIBRADIUSCLIENT_NG) |
| 44 | #include <radiusclient-ng.h> | 46 | # include <radiusclient-ng.h> |
| 45 | #else | 47 | #else |
| 46 | #include <radiusclient.h> | 48 | # include <radiusclient.h> |
| 47 | #endif | 49 | #endif |
| 48 | 50 | ||
| 49 | static int process_arguments (int /*argc*/, char ** /*argv*/); | 51 | typedef struct { |
| 50 | static void print_help (void); | 52 | int errorcode; |
| 51 | void print_usage (void); | 53 | check_radius_config config; |
| 52 | 54 | } check_radius_config_wrapper; | |
| 53 | #if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) | 55 | static check_radius_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 54 | #define my_rc_conf_str(a) rc_conf_str(rch,a) | 56 | static void print_help(void); |
| 55 | #if defined(HAVE_LIBRADCLI) | 57 | void print_usage(void); |
| 56 | #define my_rc_send_server(a,b) rc_send_server(rch,a,b,AUTH) | 58 | |
| 57 | #else | 59 | #if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \ |
| 58 | #define my_rc_send_server(a,b) rc_send_server(rch,a,b) | 60 | defined(HAVE_LIBRADCLI) |
| 59 | #endif | 61 | # define my_rc_conf_str(a) rc_conf_str(rch, a) |
| 60 | #if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI) | 62 | # if defined(HAVE_LIBRADCLI) |
| 61 | #define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,(a)->secret,e,f) | 63 | # define my_rc_send_server(a, b) rc_send_server(rch, a, b, AUTH) |
| 64 | # else | ||
| 65 | # define my_rc_send_server(a, b) rc_send_server(rch, a, b) | ||
| 66 | # endif | ||
| 67 | # if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI) | ||
| 68 | # define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, (a)->secret, e, f) | ||
| 69 | # else | ||
| 70 | # define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, e, f) | ||
| 71 | # endif | ||
| 72 | # define my_rc_avpair_add(a, b, c, d) rc_avpair_add(rch, a, b, c, -1, d) | ||
| 73 | # define my_rc_read_dictionary(a) rc_read_dictionary(rch, a) | ||
| 62 | #else | 74 | #else |
| 63 | #define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,e,f) | 75 | # define my_rc_conf_str(a) rc_conf_str(a) |
| 64 | #endif | 76 | # define my_rc_send_server(a, b) rc_send_server(a, b) |
| 65 | #define my_rc_avpair_add(a,b,c,d) rc_avpair_add(rch,a,b,c,-1,d) | 77 | # define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(a, b, c, d, e, f) |
| 66 | #define my_rc_read_dictionary(a) rc_read_dictionary(rch, a) | 78 | # define my_rc_avpair_add(a, b, c, d) rc_avpair_add(a, b, c, d) |
| 67 | #else | 79 | # define my_rc_read_dictionary(a) rc_read_dictionary(a) |
| 68 | #define my_rc_conf_str(a) rc_conf_str(a) | ||
| 69 | #define my_rc_send_server(a,b) rc_send_server(a, b) | ||
| 70 | #define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(a,b,c,d,e,f) | ||
| 71 | #define my_rc_avpair_add(a,b,c,d) rc_avpair_add(a, b, c, d) | ||
| 72 | #define my_rc_read_dictionary(a) rc_read_dictionary(a) | ||
| 73 | #endif | 80 | #endif |
| 74 | 81 | ||
| 75 | /* REJECT_RC is only defined in some version of radiusclient. It has | 82 | /* REJECT_RC is only defined in some version of radiusclient. It has |
| 76 | * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */ | 83 | * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */ |
| 77 | #ifndef REJECT_RC | 84 | #ifndef REJECT_RC |
| 78 | #define REJECT_RC BADRESP_RC | 85 | # define REJECT_RC BADRESP_RC |
| 79 | #endif | 86 | #endif |
| 80 | 87 | ||
| 81 | static int my_rc_read_config(char * /*a*/); | 88 | static int my_rc_read_config(char * /*a*/, rc_handle ** /*rch*/); |
| 82 | |||
| 83 | #if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) | ||
| 84 | static rc_handle *rch = NULL; | ||
| 85 | #endif | ||
| 86 | 89 | ||
| 87 | static char *server = NULL; | ||
| 88 | static char *username = NULL; | ||
| 89 | static char *password = NULL; | ||
| 90 | static char *nasid = NULL; | ||
| 91 | static char *nasipaddress = NULL; | ||
| 92 | static char *expect = NULL; | ||
| 93 | static char *config_file = NULL; | ||
| 94 | static unsigned short port = PW_AUTH_UDP_PORT; | ||
| 95 | static int retries = 1; | ||
| 96 | static bool verbose = false; | 90 | static bool verbose = false; |
| 97 | 91 | ||
| 98 | /****************************************************************************** | 92 | /****************************************************************************** |
| @@ -148,149 +142,171 @@ Please note that all tags must be lowercase to use the DocBook XML DTD. | |||
| 148 | -@@ | 142 | -@@ |
| 149 | ******************************************************************************/ | 143 | ******************************************************************************/ |
| 150 | 144 | ||
| 145 | int main(int argc, char **argv) { | ||
| 146 | setlocale(LC_ALL, ""); | ||
| 147 | bindtextdomain(PACKAGE, LOCALEDIR); | ||
| 148 | textdomain(PACKAGE); | ||
| 151 | 149 | ||
| 150 | /* Parse extra opts if any */ | ||
| 151 | argv = np_extra_opts(&argc, argv, progname); | ||
| 152 | |||
| 153 | check_radius_config_wrapper tmp_config = process_arguments(argc, argv); | ||
| 154 | |||
| 155 | if (tmp_config.errorcode == ERROR) { | ||
| 156 | usage4(_("Could not parse arguments")); | ||
| 157 | } | ||
| 158 | |||
| 159 | check_radius_config config = tmp_config.config; | ||
| 160 | |||
| 161 | #if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \ | ||
| 162 | defined(HAVE_LIBRADCLI) | ||
| 163 | rc_handle *rch = NULL; | ||
| 164 | #endif | ||
| 165 | |||
| 166 | char *str = strdup("dictionary"); | ||
| 167 | if ((config.config_file && my_rc_read_config(config.config_file, &rch)) || | ||
| 168 | my_rc_read_dictionary(my_rc_conf_str(str))) { | ||
| 169 | die(STATE_UNKNOWN, _("Config file error\n")); | ||
| 170 | } | ||
| 171 | |||
| 172 | uint32_t service = PW_AUTHENTICATE_ONLY; | ||
| 173 | |||
| 174 | SEND_DATA data; | ||
| 175 | memset(&data, 0, sizeof(data)); | ||
| 176 | if (!(my_rc_avpair_add(&data.send_pairs, PW_SERVICE_TYPE, &service, 0) && | ||
| 177 | my_rc_avpair_add(&data.send_pairs, PW_USER_NAME, config.username, 0) && | ||
| 178 | my_rc_avpair_add(&data.send_pairs, PW_USER_PASSWORD, config.password, 0))) { | ||
| 179 | die(STATE_UNKNOWN, _("Out of Memory?\n")); | ||
| 180 | } | ||
| 181 | |||
| 182 | if (config.nas_id != NULL) { | ||
| 183 | if (!(my_rc_avpair_add(&data.send_pairs, PW_NAS_IDENTIFIER, config.nas_id, 0))) { | ||
| 184 | die(STATE_UNKNOWN, _("Invalid NAS-Identifier\n")); | ||
| 185 | } | ||
| 186 | } | ||
| 152 | 187 | ||
| 153 | int | ||
| 154 | main (int argc, char **argv) | ||
| 155 | { | ||
| 156 | struct sockaddr_storage ss; | ||
| 157 | char name[HOST_NAME_MAX]; | 188 | char name[HOST_NAME_MAX]; |
| 189 | if (config.nas_ip_address == NULL) { | ||
| 190 | if (gethostname(name, sizeof(name)) != 0) { | ||
| 191 | die(STATE_UNKNOWN, _("gethostname() failed!\n")); | ||
| 192 | } | ||
| 193 | config.nas_ip_address = name; | ||
| 194 | } | ||
| 195 | |||
| 196 | struct sockaddr_storage radius_server_socket; | ||
| 197 | if (!dns_lookup(config.nas_ip_address, &radius_server_socket, AF_UNSPEC)) { | ||
| 198 | die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n")); | ||
| 199 | } | ||
| 200 | |||
| 201 | uint32_t client_id = ntohl(((struct sockaddr_in *)&radius_server_socket)->sin_addr.s_addr); | ||
| 202 | if (my_rc_avpair_add(&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) { | ||
| 203 | die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n")); | ||
| 204 | } | ||
| 205 | |||
| 206 | my_rc_buildreq(&data, PW_ACCESS_REQUEST, config.server, config.port, (int)timeout_interval, | ||
| 207 | config.retries); | ||
| 208 | |||
| 158 | #ifdef RC_BUFFER_LEN | 209 | #ifdef RC_BUFFER_LEN |
| 159 | char msg[RC_BUFFER_LEN]; | 210 | char msg[RC_BUFFER_LEN]; |
| 160 | #else | 211 | #else |
| 161 | char msg[BUFFER_LEN]; | 212 | char msg[BUFFER_LEN]; |
| 162 | #endif | 213 | #endif |
| 163 | SEND_DATA data; | ||
| 164 | int result = STATE_UNKNOWN; | ||
| 165 | uint32_t client_id, service; | ||
| 166 | char *str; | ||
| 167 | 214 | ||
| 168 | setlocale (LC_ALL, ""); | 215 | int result = my_rc_send_server(&data, msg); |
| 169 | bindtextdomain (PACKAGE, LOCALEDIR); | 216 | rc_avpair_free(data.send_pairs); |
| 170 | textdomain (PACKAGE); | 217 | if (data.receive_pairs) { |
| 171 | 218 | rc_avpair_free(data.receive_pairs); | |
| 172 | /* Parse extra opts if any */ | 219 | } |
| 173 | argv=np_extra_opts (&argc, argv, progname); | ||
| 174 | 220 | ||
| 175 | if (process_arguments (argc, argv) == ERROR) | 221 | if (result == TIMEOUT_RC) { |
| 176 | usage4 (_("Could not parse arguments")); | 222 | printf("Timeout\n"); |
| 223 | exit(STATE_CRITICAL); | ||
| 224 | } | ||
| 177 | 225 | ||
| 178 | str = strdup ("dictionary"); | 226 | if (result == ERROR_RC) { |
| 179 | if ((config_file && my_rc_read_config (config_file)) || | 227 | printf(_("Auth Error\n")); |
| 180 | my_rc_read_dictionary (my_rc_conf_str (str))) | 228 | exit(STATE_CRITICAL); |
| 181 | die (STATE_UNKNOWN, _("Config file error\n")); | 229 | } |
| 182 | 230 | ||
| 183 | service = PW_AUTHENTICATE_ONLY; | 231 | if (result == REJECT_RC) { |
| 232 | printf(_("Auth Failed\n")); | ||
| 233 | exit(STATE_WARNING); | ||
| 234 | } | ||
| 184 | 235 | ||
| 185 | memset (&data, 0, sizeof(data)); | 236 | if (result == BADRESP_RC) { |
| 186 | if (!(my_rc_avpair_add (&data.send_pairs, PW_SERVICE_TYPE, &service, 0) && | 237 | printf(_("Bad Response\n")); |
| 187 | my_rc_avpair_add (&data.send_pairs, PW_USER_NAME, username, 0) && | 238 | exit(STATE_WARNING); |
| 188 | my_rc_avpair_add (&data.send_pairs, PW_USER_PASSWORD, password, 0) | 239 | } |
| 189 | )) | ||
| 190 | die (STATE_UNKNOWN, _("Out of Memory?\n")); | ||
| 191 | 240 | ||
| 192 | if (nasid != NULL) { | 241 | if (config.expect && !strstr(msg, config.expect)) { |
| 193 | if (!(my_rc_avpair_add (&data.send_pairs, PW_NAS_IDENTIFIER, nasid, 0))) | 242 | printf("%s\n", msg); |
| 194 | die (STATE_UNKNOWN, _("Invalid NAS-Identifier\n")); | 243 | exit(STATE_WARNING); |
| 195 | } | 244 | } |
| 196 | 245 | ||
| 197 | if (nasipaddress == NULL) { | 246 | if (result == OK_RC) { |
| 198 | if (gethostname (name, sizeof(name)) != 0) | 247 | printf(_("Auth OK\n")); |
| 199 | die (STATE_UNKNOWN, _("gethostname() failed!\n")); | 248 | exit(STATE_OK); |
| 200 | nasipaddress = name; | ||
| 201 | } | 249 | } |
| 202 | if (!dns_lookup (nasipaddress, &ss, AF_INET)) /* TODO: Support IPv6. */ | 250 | |
| 203 | die (STATE_UNKNOWN, _("Invalid NAS-IP-Address\n")); | ||
| 204 | client_id = ntohl (((struct sockaddr_in *)&ss)->sin_addr.s_addr); | ||
| 205 | if (my_rc_avpair_add (&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) | ||
| 206 | die (STATE_UNKNOWN, _("Invalid NAS-IP-Address\n")); | ||
| 207 | |||
| 208 | my_rc_buildreq (&data, PW_ACCESS_REQUEST, server, port, (int)timeout_interval, | ||
| 209 | retries); | ||
| 210 | |||
| 211 | result = my_rc_send_server (&data, msg); | ||
| 212 | rc_avpair_free (data.send_pairs); | ||
| 213 | if (data.receive_pairs) | ||
| 214 | rc_avpair_free (data.receive_pairs); | ||
| 215 | |||
| 216 | if (result == TIMEOUT_RC) | ||
| 217 | die (STATE_CRITICAL, _("Timeout\n")); | ||
| 218 | if (result == ERROR_RC) | ||
| 219 | die (STATE_CRITICAL, _("Auth Error\n")); | ||
| 220 | if (result == REJECT_RC) | ||
| 221 | die (STATE_WARNING, _("Auth Failed\n")); | ||
| 222 | if (result == BADRESP_RC) | ||
| 223 | die (STATE_WARNING, _("Bad Response\n")); | ||
| 224 | if (expect && !strstr (msg, expect)) | ||
| 225 | die (STATE_WARNING, "%s\n", msg); | ||
| 226 | if (result == OK_RC) | ||
| 227 | die (STATE_OK, _("Auth OK\n")); | ||
| 228 | (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result); | 251 | (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result); |
| 229 | die (STATE_UNKNOWN, "%s\n", msg); | 252 | printf("%s\n", msg); |
| 253 | exit(STATE_UNKNOWN); | ||
| 230 | } | 254 | } |
| 231 | 255 | ||
| 232 | |||
| 233 | |||
| 234 | /* process command-line arguments */ | 256 | /* process command-line arguments */ |
| 235 | int | 257 | check_radius_config_wrapper process_arguments(int argc, char **argv) { |
| 236 | process_arguments (int argc, char **argv) | ||
| 237 | { | ||
| 238 | int c; | ||
| 239 | |||
| 240 | int option = 0; | ||
| 241 | static struct option longopts[] = { | 258 | static struct option longopts[] = { |
| 242 | {"hostname", required_argument, 0, 'H'}, | 259 | {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'P'}, |
| 243 | {"port", required_argument, 0, 'P'}, | 260 | {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'}, |
| 244 | {"username", required_argument, 0, 'u'}, | 261 | {"nas-id", required_argument, 0, 'n'}, {"nas-ip-address", required_argument, 0, 'N'}, |
| 245 | {"password", required_argument, 0, 'p'}, | 262 | {"filename", required_argument, 0, 'F'}, {"expect", required_argument, 0, 'e'}, |
| 246 | {"nas-id", required_argument, 0, 'n'}, | 263 | {"retries", required_argument, 0, 'r'}, {"timeout", required_argument, 0, 't'}, |
| 247 | {"nas-ip-address", required_argument, 0, 'N'}, | 264 | {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, |
| 248 | {"filename", required_argument, 0, 'F'}, | 265 | {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; |
| 249 | {"expect", required_argument, 0, 'e'}, | 266 | |
| 250 | {"retries", required_argument, 0, 'r'}, | 267 | check_radius_config_wrapper result = { |
| 251 | {"timeout", required_argument, 0, 't'}, | 268 | .errorcode = OK, |
| 252 | {"verbose", no_argument, 0, 'v'}, | 269 | .config = check_radius_config_init(), |
| 253 | {"version", no_argument, 0, 'V'}, | ||
| 254 | {"help", no_argument, 0, 'h'}, | ||
| 255 | {0, 0, 0, 0} | ||
| 256 | }; | 270 | }; |
| 257 | 271 | ||
| 258 | while (1) { | 272 | while (true) { |
| 259 | c = getopt_long (argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, | 273 | int option = 0; |
| 260 | &option); | 274 | int option_index = getopt_long(argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, &option); |
| 261 | 275 | ||
| 262 | if (c == -1 || c == EOF || c == 1) | 276 | if (option_index == -1 || option_index == EOF || option_index == 1) { |
| 263 | break; | 277 | break; |
| 278 | } | ||
| 264 | 279 | ||
| 265 | switch (c) { | 280 | switch (option_index) { |
| 266 | case '?': /* print short usage statement if args not parsable */ | 281 | case '?': /* print short usage statement if args not parsable */ |
| 267 | usage5 (); | 282 | usage5(); |
| 268 | case 'h': /* help */ | 283 | case 'h': /* help */ |
| 269 | print_help (); | 284 | print_help(); |
| 270 | exit (STATE_UNKNOWN); | 285 | exit(STATE_UNKNOWN); |
| 271 | case 'V': /* version */ | 286 | case 'V': /* version */ |
| 272 | print_revision (progname, NP_VERSION); | 287 | print_revision(progname, NP_VERSION); |
| 273 | exit (STATE_UNKNOWN); | 288 | exit(STATE_UNKNOWN); |
| 274 | case 'v': /* verbose mode */ | 289 | case 'v': /* verbose mode */ |
| 275 | verbose = true; | 290 | verbose = true; |
| 276 | break; | 291 | break; |
| 277 | case 'H': /* hostname */ | 292 | case 'H': /* hostname */ |
| 278 | if (!is_host (optarg)) { | 293 | if (!is_host(optarg)) { |
| 279 | usage2 (_("Invalid hostname/address"), optarg); | 294 | usage2(_("Invalid hostname/address"), optarg); |
| 280 | } | 295 | } |
| 281 | server = optarg; | 296 | result.config.server = optarg; |
| 282 | break; | 297 | break; |
| 283 | case 'P': /* port */ | 298 | case 'P': /* port */ |
| 284 | if (is_intnonneg (optarg)) | 299 | if (is_intnonneg(optarg)) { |
| 285 | port = (unsigned short)atoi (optarg); | 300 | result.config.port = (unsigned short)atoi(optarg); |
| 286 | else | 301 | } else { |
| 287 | usage4 (_("Port must be a positive integer")); | 302 | usage4(_("Port must be a positive integer")); |
| 303 | } | ||
| 288 | break; | 304 | break; |
| 289 | case 'u': /* username */ | 305 | case 'u': /* username */ |
| 290 | username = optarg; | 306 | result.config.username = optarg; |
| 291 | break; | 307 | break; |
| 292 | case 'p': /* password */ | 308 | case 'p': /* password */ |
| 293 | password = strdup(optarg); | 309 | result.config.password = strdup(optarg); |
| 294 | 310 | ||
| 295 | /* Delete the password from process list */ | 311 | /* Delete the password from process list */ |
| 296 | while (*optarg != '\0') { | 312 | while (*optarg != '\0') { |
| @@ -298,119 +314,118 @@ process_arguments (int argc, char **argv) | |||
| 298 | optarg++; | 314 | optarg++; |
| 299 | } | 315 | } |
| 300 | break; | 316 | break; |
| 301 | case 'n': /* nas id */ | 317 | case 'n': /* nas id */ |
| 302 | nasid = optarg; | 318 | result.config.nas_id = optarg; |
| 303 | break; | 319 | break; |
| 304 | case 'N': /* nas ip address */ | 320 | case 'N': /* nas ip address */ |
| 305 | nasipaddress = optarg; | 321 | result.config.nas_ip_address = optarg; |
| 306 | break; | 322 | break; |
| 307 | case 'F': /* configuration file */ | 323 | case 'F': /* configuration file */ |
| 308 | config_file = optarg; | 324 | result.config.config_file = optarg; |
| 309 | break; | 325 | break; |
| 310 | case 'e': /* expect */ | 326 | case 'e': /* expect */ |
| 311 | expect = optarg; | 327 | result.config.expect = optarg; |
| 312 | break; | 328 | break; |
| 313 | case 'r': /* retries */ | 329 | case 'r': /* retries */ |
| 314 | if (is_intpos (optarg)) | 330 | if (is_intpos(optarg)) { |
| 315 | retries = atoi (optarg); | 331 | result.config.retries = atoi(optarg); |
| 316 | else | 332 | } else { |
| 317 | usage4 (_("Number of retries must be a positive integer")); | 333 | usage4(_("Number of retries must be a positive integer")); |
| 334 | } | ||
| 318 | break; | 335 | break; |
| 319 | case 't': /* timeout */ | 336 | case 't': /* timeout */ |
| 320 | if (is_intpos (optarg)) | 337 | if (is_intpos(optarg)) { |
| 321 | timeout_interval = (unsigned)atoi (optarg); | 338 | timeout_interval = (unsigned)atoi(optarg); |
| 322 | else | 339 | } else { |
| 323 | usage2 (_("Timeout interval must be a positive integer"), optarg); | 340 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 341 | } | ||
| 324 | break; | 342 | break; |
| 325 | } | 343 | } |
| 326 | } | 344 | } |
| 327 | 345 | ||
| 328 | if (server == NULL) | 346 | if (result.config.server == NULL) { |
| 329 | usage4 (_("Hostname was not supplied")); | 347 | usage4(_("Hostname was not supplied")); |
| 330 | if (username == NULL) | 348 | } |
| 331 | usage4 (_("User not specified")); | 349 | if (result.config.username == NULL) { |
| 332 | if (password == NULL) | 350 | usage4(_("User not specified")); |
| 333 | usage4 (_("Password not specified")); | 351 | } |
| 334 | if (config_file == NULL) | 352 | if (result.config.password == NULL) { |
| 335 | usage4 (_("Configuration file not specified")); | 353 | usage4(_("Password not specified")); |
| 354 | } | ||
| 355 | if (result.config.config_file == NULL) { | ||
| 356 | usage4(_("Configuration file not specified")); | ||
| 357 | } | ||
| 336 | 358 | ||
| 337 | return OK; | 359 | return result; |
| 338 | } | 360 | } |
| 339 | 361 | ||
| 340 | 362 | void print_help(void) { | |
| 341 | |||
| 342 | void | ||
| 343 | print_help (void) | ||
| 344 | { | ||
| 345 | char *myport; | 363 | char *myport; |
| 346 | xasprintf (&myport, "%d", PW_AUTH_UDP_PORT); | 364 | xasprintf(&myport, "%d", PW_AUTH_UDP_PORT); |
| 347 | 365 | ||
| 348 | print_revision (progname, NP_VERSION); | 366 | print_revision(progname, NP_VERSION); |
| 349 | 367 | ||
| 350 | printf ("Copyright (c) 1999 Robert August Vincent II\n"); | 368 | printf("Copyright (c) 1999 Robert August Vincent II\n"); |
| 351 | printf (COPYRIGHT, copyright, email); | 369 | printf(COPYRIGHT, copyright, email); |
| 352 | 370 | ||
| 353 | printf("%s\n", _("Tests to see if a RADIUS server is accepting connections.")); | 371 | printf("%s\n", _("Tests to see if a RADIUS server is accepting connections.")); |
| 354 | 372 | ||
| 355 | printf ("\n\n"); | 373 | printf("\n\n"); |
| 356 | 374 | ||
| 357 | print_usage (); | 375 | print_usage(); |
| 358 | 376 | ||
| 359 | printf (UT_HELP_VRSN); | 377 | printf(UT_HELP_VRSN); |
| 360 | printf (UT_EXTRA_OPTS); | 378 | printf(UT_EXTRA_OPTS); |
| 361 | 379 | ||
| 362 | printf (UT_HOST_PORT, 'P', myport); | 380 | printf(UT_HOST_PORT, 'P', myport); |
| 363 | 381 | ||
| 364 | printf (" %s\n", "-u, --username=STRING"); | 382 | printf(" %s\n", "-u, --username=STRING"); |
| 365 | printf (" %s\n", _("The user to authenticate")); | 383 | printf(" %s\n", _("The user to authenticate")); |
| 366 | printf (" %s\n", "-p, --password=STRING"); | 384 | printf(" %s\n", "-p, --password=STRING"); |
| 367 | printf (" %s\n", _("Password for authentication (SECURITY RISK)")); | 385 | printf(" %s\n", _("Password for authentication (SECURITY RISK)")); |
| 368 | printf (" %s\n", "-n, --nas-id=STRING"); | 386 | printf(" %s\n", "-n, --nas-id=STRING"); |
| 369 | printf (" %s\n", _("NAS identifier")); | 387 | printf(" %s\n", _("NAS identifier")); |
| 370 | printf (" %s\n", "-N, --nas-ip-address=STRING"); | 388 | printf(" %s\n", "-N, --nas-ip-address=STRING"); |
| 371 | printf (" %s\n", _("NAS IP Address")); | 389 | printf(" %s\n", _("NAS IP Address")); |
| 372 | printf (" %s\n", "-F, --filename=STRING"); | 390 | printf(" %s\n", "-F, --filename=STRING"); |
| 373 | printf (" %s\n", _("Configuration file")); | 391 | printf(" %s\n", _("Configuration file")); |
| 374 | printf (" %s\n", "-e, --expect=STRING"); | 392 | printf(" %s\n", "-e, --expect=STRING"); |
| 375 | printf (" %s\n", _("Response string to expect from the server")); | 393 | printf(" %s\n", _("Response string to expect from the server")); |
| 376 | printf (" %s\n", "-r, --retries=INTEGER"); | 394 | printf(" %s\n", "-r, --retries=INTEGER"); |
| 377 | printf (" %s\n", _("Number of times to retry a failed connection")); | 395 | printf(" %s\n", _("Number of times to retry a failed connection")); |
| 378 | 396 | ||
| 379 | printf (UT_CONN_TIMEOUT, timeout_interval); | 397 | printf(UT_CONN_TIMEOUT, timeout_interval); |
| 380 | 398 | ||
| 381 | printf ("\n"); | 399 | printf("\n"); |
| 382 | printf ("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections.")); | 400 | printf("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections.")); |
| 383 | printf ("%s\n", _("The server to test must be specified in the invocation, as well as a user")); | 401 | printf("%s\n", _("The server to test must be specified in the invocation, as well as a user")); |
| 384 | printf ("%s\n", _("name and password. A configuration file must be present. The format of")); | 402 | printf("%s\n", _("name and password. A configuration file must be present. The format of")); |
| 385 | printf ("%s\n", _("the configuration file is described in the radiusclient library sources.")); | 403 | printf("%s\n", _("the configuration file is described in the radiusclient library sources.")); |
| 386 | printf ("%s\n", _("The password option presents a substantial security issue because the")); | 404 | printf("%s\n", _("The password option presents a substantial security issue because the")); |
| 387 | printf ("%s\n", _("password can possibly be determined by careful watching of the command line")); | 405 | printf("%s\n", |
| 388 | printf ("%s\n", _("in a process listing. This risk is exacerbated because the plugin will")); | 406 | _("password can possibly be determined by careful watching of the command line")); |
| 389 | printf ("%s\n", _("typically be executed at regular predictable intervals. Please be sure that")); | 407 | printf("%s\n", _("in a process listing. This risk is exacerbated because the plugin will")); |
| 390 | printf ("%s\n", _("the password used does not allow access to sensitive system resources.")); | 408 | printf("%s\n", |
| 391 | 409 | _("typically be executed at regular predictable intervals. Please be sure that")); | |
| 392 | printf (UT_SUPPORT); | 410 | printf("%s\n", _("the password used does not allow access to sensitive system resources.")); |
| 411 | |||
| 412 | printf(UT_SUPPORT); | ||
| 393 | } | 413 | } |
| 394 | 414 | ||
| 395 | 415 | void print_usage(void) { | |
| 396 | 416 | printf("%s\n", _("Usage:")); | |
| 397 | void | 417 | printf("%s -H host -F config_file -u username -p password\n\ |
| 398 | print_usage (void) | ||
| 399 | { | ||
| 400 | printf ("%s\n", _("Usage:")); | ||
| 401 | printf ("%s -H host -F config_file -u username -p password\n\ | ||
| 402 | [-P port] [-t timeout] [-r retries] [-e expect]\n\ | 418 | [-P port] [-t timeout] [-r retries] [-e expect]\n\ |
| 403 | [-n nas-id] [-N nas-ip-addr]\n", progname); | 419 | [-n nas-id] [-N nas-ip-addr]\n", |
| 420 | progname); | ||
| 404 | } | 421 | } |
| 405 | 422 | ||
| 406 | 423 | int my_rc_read_config(char *config_file_name, rc_handle **rch) { | |
| 407 | 424 | #if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \ | |
| 408 | int my_rc_read_config(char * a) | 425 | defined(HAVE_LIBRADCLI) |
| 409 | { | 426 | *rch = rc_read_config(config_file_name); |
| 410 | #if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) | ||
| 411 | rch = rc_read_config(a); | ||
| 412 | return (rch == NULL) ? 1 : 0; | 427 | return (rch == NULL) ? 1 : 0; |
| 413 | #else | 428 | #else |
| 414 | return rc_read_config(a); | 429 | return rc_read_config(config_file_name); |
| 415 | #endif | 430 | #endif |
| 416 | } | 431 | } |
diff --git a/plugins/check_radius.d/config.h b/plugins/check_radius.d/config.h new file mode 100644 index 00000000..b27d31e7 --- /dev/null +++ b/plugins/check_radius.d/config.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | #if defined(HAVE_LIBRADCLI) | ||
| 6 | # include <radcli/radcli.h> | ||
| 7 | #elif defined(HAVE_LIBFREERADIUS_CLIENT) | ||
| 8 | # include <freeradius-client.h> | ||
| 9 | #elif defined(HAVE_LIBRADIUSCLIENT_NG) | ||
| 10 | # include <radiusclient-ng.h> | ||
| 11 | #else | ||
| 12 | # include <radiusclient.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | typedef struct { | ||
| 16 | char *server; | ||
| 17 | char *username; | ||
| 18 | char *password; | ||
| 19 | char *config_file; | ||
| 20 | char *nas_id; | ||
| 21 | char *nas_ip_address; | ||
| 22 | int retries; | ||
| 23 | unsigned short port; | ||
| 24 | |||
| 25 | char *expect; | ||
| 26 | } check_radius_config; | ||
| 27 | |||
| 28 | check_radius_config check_radius_config_init() { | ||
| 29 | check_radius_config tmp = { | ||
| 30 | .server = NULL, | ||
| 31 | .username = NULL, | ||
| 32 | .password = NULL, | ||
| 33 | .config_file = NULL, | ||
| 34 | .nas_id = NULL, | ||
| 35 | .nas_ip_address = NULL, | ||
| 36 | .retries = 1, | ||
| 37 | .port = PW_AUTH_UDP_PORT, | ||
| 38 | |||
| 39 | .expect = NULL, | ||
| 40 | }; | ||
| 41 | return tmp; | ||
| 42 | } | ||
diff --git a/plugins/check_real.c b/plugins/check_real.c index 369a88b1..66d07f8f 100644 --- a/plugins/check_real.c +++ b/plugins/check_real.c | |||
| @@ -28,6 +28,8 @@ | |||
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | #include "states.h" | ||
| 32 | #include <stdio.h> | ||
| 31 | const char *progname = "check_real"; | 33 | const char *progname = "check_real"; |
| 32 | const char *copyright = "2000-2024"; | 34 | const char *copyright = "2000-2024"; |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 35 | const char *email = "devel@monitoring-plugins.org"; |
| @@ -35,27 +37,20 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 35 | #include "common.h" | 37 | #include "common.h" |
| 36 | #include "netutils.h" | 38 | #include "netutils.h" |
| 37 | #include "utils.h" | 39 | #include "utils.h" |
| 38 | 40 | #include "check_real.d/config.h" | |
| 39 | enum { | ||
| 40 | PORT = 554 | ||
| 41 | }; | ||
| 42 | 41 | ||
| 43 | #define EXPECT "RTSP/1." | 42 | #define EXPECT "RTSP/1." |
| 44 | #define URL "" | 43 | #define URL "" |
| 45 | 44 | ||
| 46 | static int process_arguments(int, char **); | 45 | typedef struct { |
| 46 | int errorcode; | ||
| 47 | check_real_config config; | ||
| 48 | } check_real_config_wrapper; | ||
| 49 | static check_real_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 50 | |||
| 47 | static void print_help(void); | 51 | static void print_help(void); |
| 48 | void print_usage(void); | 52 | void print_usage(void); |
| 49 | 53 | ||
| 50 | static int server_port = PORT; | ||
| 51 | static char *server_address; | ||
| 52 | static char *host_name; | ||
| 53 | static char *server_url = NULL; | ||
| 54 | static char *server_expect; | ||
| 55 | static int warning_time = 0; | ||
| 56 | static bool check_warning_time = false; | ||
| 57 | static int critical_time = 0; | ||
| 58 | static bool check_critical_time = false; | ||
| 59 | static bool verbose = false; | 54 | static bool verbose = false; |
| 60 | 55 | ||
| 61 | int main(int argc, char **argv) { | 56 | int main(int argc, char **argv) { |
| @@ -66,8 +61,12 @@ int main(int argc, char **argv) { | |||
| 66 | /* Parse extra opts if any */ | 61 | /* Parse extra opts if any */ |
| 67 | argv = np_extra_opts(&argc, argv, progname); | 62 | argv = np_extra_opts(&argc, argv, progname); |
| 68 | 63 | ||
| 69 | if (process_arguments(argc, argv) == ERROR) | 64 | check_real_config_wrapper tmp_config = process_arguments(argc, argv); |
| 65 | if (tmp_config.errorcode == ERROR) { | ||
| 70 | usage4(_("Could not parse arguments")); | 66 | usage4(_("Could not parse arguments")); |
| 67 | } | ||
| 68 | |||
| 69 | const check_real_config config = tmp_config.config; | ||
| 71 | 70 | ||
| 72 | /* initialize alarm signal handling */ | 71 | /* initialize alarm signal handling */ |
| 73 | signal(SIGALRM, socket_timeout_alarm_handler); | 72 | signal(SIGALRM, socket_timeout_alarm_handler); |
| @@ -78,38 +77,52 @@ int main(int argc, char **argv) { | |||
| 78 | 77 | ||
| 79 | /* try to connect to the host at the given port number */ | 78 | /* try to connect to the host at the given port number */ |
| 80 | int socket; | 79 | int socket; |
| 81 | if (my_tcp_connect(server_address, server_port, &socket) != STATE_OK) | 80 | if (my_tcp_connect(config.server_address, config.server_port, &socket) != STATE_OK) { |
| 82 | die(STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), server_address, server_port); | 81 | die(STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), config.server_address, |
| 82 | config.server_port); | ||
| 83 | } | ||
| 83 | 84 | ||
| 84 | /* Part I - Server Check */ | 85 | /* Part I - Server Check */ |
| 85 | 86 | ||
| 86 | /* send the OPTIONS request */ | 87 | /* send the OPTIONS request */ |
| 87 | char buffer[MAX_INPUT_BUFFER]; | 88 | char buffer[MAX_INPUT_BUFFER]; |
| 88 | sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", host_name, server_port); | 89 | sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", config.host_name, config.server_port); |
| 89 | int result = send(socket, buffer, strlen(buffer), 0); | 90 | ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0); |
| 91 | if (sent_bytes == -1) { | ||
| 92 | die(STATE_CRITICAL, _("Sending options to %s failed\n"), config.host_name); | ||
| 93 | } | ||
| 90 | 94 | ||
| 91 | /* send the header sync */ | 95 | /* send the header sync */ |
| 92 | sprintf(buffer, "CSeq: 1\r\n"); | 96 | sprintf(buffer, "CSeq: 1\r\n"); |
| 93 | result = send(socket, buffer, strlen(buffer), 0); | 97 | sent_bytes = send(socket, buffer, strlen(buffer), 0); |
| 98 | if (sent_bytes == -1) { | ||
| 99 | die(STATE_CRITICAL, _("Sending header sync to %s failed\n"), config.host_name); | ||
| 100 | } | ||
| 94 | 101 | ||
| 95 | /* send a newline so the server knows we're done with the request */ | 102 | /* send a newline so the server knows we're done with the request */ |
| 96 | sprintf(buffer, "\r\n"); | 103 | sprintf(buffer, "\r\n"); |
| 97 | result = send(socket, buffer, strlen(buffer), 0); | 104 | sent_bytes = send(socket, buffer, strlen(buffer), 0); |
| 105 | if (sent_bytes == -1) { | ||
| 106 | die(STATE_CRITICAL, _("Sending newline to %s failed\n"), config.host_name); | ||
| 107 | } | ||
| 98 | 108 | ||
| 99 | /* watch for the REAL connection string */ | 109 | /* watch for the REAL connection string */ |
| 100 | result = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); | 110 | ssize_t received_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); |
| 101 | 111 | ||
| 102 | /* return a CRITICAL status if we couldn't read any data */ | 112 | /* return a CRITICAL status if we couldn't read any data */ |
| 103 | if (result == -1) | 113 | if (received_bytes == -1) { |
| 104 | die(STATE_CRITICAL, _("No data received from %s\n"), host_name); | 114 | die(STATE_CRITICAL, _("No data received from %s\n"), config.host_name); |
| 115 | } | ||
| 105 | 116 | ||
| 117 | mp_state_enum result = STATE_OK; | ||
| 106 | char *status_line = NULL; | 118 | char *status_line = NULL; |
| 107 | /* make sure we find the response we are looking for */ | 119 | /* make sure we find the response we are looking for */ |
| 108 | if (!strstr(buffer, server_expect)) { | 120 | if (!strstr(buffer, config.server_expect)) { |
| 109 | if (server_port == PORT) | 121 | if (config.server_port == PORT) { |
| 110 | printf("%s\n", _("Invalid REAL response received from host")); | 122 | printf("%s\n", _("Invalid REAL response received from host")); |
| 111 | else | 123 | } else { |
| 112 | printf(_("Invalid REAL response received from host on port %d\n"), server_port); | 124 | printf(_("Invalid REAL response received from host on port %d\n"), config.server_port); |
| 125 | } | ||
| 113 | } else { | 126 | } else { |
| 114 | /* else we got the REAL string, so check the return code */ | 127 | /* else we got the REAL string, so check the return code */ |
| 115 | 128 | ||
| @@ -117,69 +130,81 @@ int main(int argc, char **argv) { | |||
| 117 | 130 | ||
| 118 | result = STATE_OK; | 131 | result = STATE_OK; |
| 119 | 132 | ||
| 120 | status_line = (char *)strtok(buffer, "\n"); | 133 | status_line = strtok(buffer, "\n"); |
| 121 | 134 | ||
| 122 | if (strstr(status_line, "200")) | 135 | if (strstr(status_line, "200")) { |
| 123 | result = STATE_OK; | 136 | result = STATE_OK; |
| 137 | } | ||
| 124 | 138 | ||
| 125 | /* client errors result in a warning state */ | 139 | /* client errors result in a warning state */ |
| 126 | else if (strstr(status_line, "400")) | 140 | else if (strstr(status_line, "400")) { |
| 127 | result = STATE_WARNING; | 141 | result = STATE_WARNING; |
| 128 | else if (strstr(status_line, "401")) | 142 | } else if (strstr(status_line, "401")) { |
| 129 | result = STATE_WARNING; | 143 | result = STATE_WARNING; |
| 130 | else if (strstr(status_line, "402")) | 144 | } else if (strstr(status_line, "402")) { |
| 131 | result = STATE_WARNING; | 145 | result = STATE_WARNING; |
| 132 | else if (strstr(status_line, "403")) | 146 | } else if (strstr(status_line, "403")) { |
| 133 | result = STATE_WARNING; | 147 | result = STATE_WARNING; |
| 134 | else if (strstr(status_line, "404")) | 148 | } else if (strstr(status_line, "404")) { |
| 135 | result = STATE_WARNING; | 149 | result = STATE_WARNING; |
| 136 | 150 | } else if (strstr(status_line, "500")) { | |
| 137 | /* server errors result in a critical state */ | 151 | /* server errors result in a critical state */ |
| 138 | else if (strstr(status_line, "500")) | ||
| 139 | result = STATE_CRITICAL; | 152 | result = STATE_CRITICAL; |
| 140 | else if (strstr(status_line, "501")) | 153 | } else if (strstr(status_line, "501")) { |
| 141 | result = STATE_CRITICAL; | 154 | result = STATE_CRITICAL; |
| 142 | else if (strstr(status_line, "502")) | 155 | } else if (strstr(status_line, "502")) { |
| 143 | result = STATE_CRITICAL; | 156 | result = STATE_CRITICAL; |
| 144 | else if (strstr(status_line, "503")) | 157 | } else if (strstr(status_line, "503")) { |
| 145 | result = STATE_CRITICAL; | 158 | result = STATE_CRITICAL; |
| 146 | 159 | } else { | |
| 147 | else | ||
| 148 | result = STATE_UNKNOWN; | 160 | result = STATE_UNKNOWN; |
| 161 | } | ||
| 149 | } | 162 | } |
| 150 | 163 | ||
| 151 | /* Part II - Check stream exists and is ok */ | 164 | /* Part II - Check stream exists and is ok */ |
| 152 | if ((result == STATE_OK) && (server_url != NULL)) { | 165 | if ((result == STATE_OK) && (config.server_url != NULL)) { |
| 153 | 166 | ||
| 154 | /* Part I - Server Check */ | 167 | /* Part I - Server Check */ |
| 155 | 168 | ||
| 156 | /* send the DESCRIBE request */ | 169 | /* send the DESCRIBE request */ |
| 157 | sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", host_name, server_port, server_url); | 170 | sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", config.host_name, |
| 158 | result = send(socket, buffer, strlen(buffer), 0); | 171 | config.server_port, config.server_url); |
| 172 | |||
| 173 | ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0); | ||
| 174 | if (sent_bytes == -1) { | ||
| 175 | die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name); | ||
| 176 | } | ||
| 159 | 177 | ||
| 160 | /* send the header sync */ | 178 | /* send the header sync */ |
| 161 | sprintf(buffer, "CSeq: 2\r\n"); | 179 | sprintf(buffer, "CSeq: 2\r\n"); |
| 162 | result = send(socket, buffer, strlen(buffer), 0); | 180 | sent_bytes = send(socket, buffer, strlen(buffer), 0); |
| 181 | if (sent_bytes == -1) { | ||
| 182 | die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name); | ||
| 183 | } | ||
| 163 | 184 | ||
| 164 | /* send a newline so the server knows we're done with the request */ | 185 | /* send a newline so the server knows we're done with the request */ |
| 165 | sprintf(buffer, "\r\n"); | 186 | sprintf(buffer, "\r\n"); |
| 166 | result = send(socket, buffer, strlen(buffer), 0); | 187 | sent_bytes = send(socket, buffer, strlen(buffer), 0); |
| 188 | if (sent_bytes == -1) { | ||
| 189 | die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name); | ||
| 190 | } | ||
| 167 | 191 | ||
| 168 | /* watch for the REAL connection string */ | 192 | /* watch for the REAL connection string */ |
| 169 | result = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); | 193 | ssize_t recv_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); |
| 170 | buffer[result] = '\0'; /* null terminate received buffer */ | 194 | if (recv_bytes == -1) { |
| 171 | 195 | /* return a CRITICAL status if we couldn't read any data */ | |
| 172 | /* return a CRITICAL status if we couldn't read any data */ | ||
| 173 | if (result == -1) { | ||
| 174 | printf(_("No data received from host\n")); | 196 | printf(_("No data received from host\n")); |
| 175 | result = STATE_CRITICAL; | 197 | result = STATE_CRITICAL; |
| 176 | } else { | 198 | } else { |
| 199 | buffer[result] = '\0'; /* null terminate received buffer */ | ||
| 177 | /* make sure we find the response we are looking for */ | 200 | /* make sure we find the response we are looking for */ |
| 178 | if (!strstr(buffer, server_expect)) { | 201 | if (!strstr(buffer, config.server_expect)) { |
| 179 | if (server_port == PORT) | 202 | if (config.server_port == PORT) { |
| 180 | printf("%s\n", _("Invalid REAL response received from host")); | 203 | printf("%s\n", _("Invalid REAL response received from host")); |
| 181 | else | 204 | } else { |
| 182 | printf(_("Invalid REAL response received from host on port %d\n"), server_port); | 205 | printf(_("Invalid REAL response received from host on port %d\n"), |
| 206 | config.server_port); | ||
| 207 | } | ||
| 183 | } else { | 208 | } else { |
| 184 | 209 | ||
| 185 | /* else we got the REAL string, so check the return code */ | 210 | /* else we got the REAL string, so check the return code */ |
| @@ -188,51 +213,57 @@ int main(int argc, char **argv) { | |||
| 188 | 213 | ||
| 189 | result = STATE_OK; | 214 | result = STATE_OK; |
| 190 | 215 | ||
| 191 | status_line = (char *)strtok(buffer, "\n"); | 216 | status_line = strtok(buffer, "\n"); |
| 192 | 217 | ||
| 193 | if (strstr(status_line, "200")) | 218 | if (strstr(status_line, "200")) { |
| 194 | result = STATE_OK; | 219 | result = STATE_OK; |
| 220 | } | ||
| 195 | 221 | ||
| 196 | /* client errors result in a warning state */ | 222 | /* client errors result in a warning state */ |
| 197 | else if (strstr(status_line, "400")) | 223 | else if (strstr(status_line, "400")) { |
| 198 | result = STATE_WARNING; | 224 | result = STATE_WARNING; |
| 199 | else if (strstr(status_line, "401")) | 225 | } else if (strstr(status_line, "401")) { |
| 200 | result = STATE_WARNING; | 226 | result = STATE_WARNING; |
| 201 | else if (strstr(status_line, "402")) | 227 | } else if (strstr(status_line, "402")) { |
| 202 | result = STATE_WARNING; | 228 | result = STATE_WARNING; |
| 203 | else if (strstr(status_line, "403")) | 229 | } else if (strstr(status_line, "403")) { |
| 204 | result = STATE_WARNING; | 230 | result = STATE_WARNING; |
| 205 | else if (strstr(status_line, "404")) | 231 | } else if (strstr(status_line, "404")) { |
| 206 | result = STATE_WARNING; | 232 | result = STATE_WARNING; |
| 233 | } | ||
| 207 | 234 | ||
| 208 | /* server errors result in a critical state */ | 235 | /* server errors result in a critical state */ |
| 209 | else if (strstr(status_line, "500")) | 236 | else if (strstr(status_line, "500")) { |
| 210 | result = STATE_CRITICAL; | 237 | result = STATE_CRITICAL; |
| 211 | else if (strstr(status_line, "501")) | 238 | } else if (strstr(status_line, "501")) { |
| 212 | result = STATE_CRITICAL; | 239 | result = STATE_CRITICAL; |
| 213 | else if (strstr(status_line, "502")) | 240 | } else if (strstr(status_line, "502")) { |
| 214 | result = STATE_CRITICAL; | 241 | result = STATE_CRITICAL; |
| 215 | else if (strstr(status_line, "503")) | 242 | } else if (strstr(status_line, "503")) { |
| 216 | result = STATE_CRITICAL; | 243 | result = STATE_CRITICAL; |
| 244 | } | ||
| 217 | 245 | ||
| 218 | else | 246 | else { |
| 219 | result = STATE_UNKNOWN; | 247 | result = STATE_UNKNOWN; |
| 248 | } | ||
| 220 | } | 249 | } |
| 221 | } | 250 | } |
| 222 | } | 251 | } |
| 223 | 252 | ||
| 224 | /* Return results */ | 253 | /* Return results */ |
| 225 | if (result == STATE_OK) { | 254 | if (result == STATE_OK) { |
| 226 | 255 | if (config.check_critical_time && (end_time - start_time) > config.critical_time) { | |
| 227 | if (check_critical_time && (end_time - start_time) > critical_time) | ||
| 228 | result = STATE_CRITICAL; | 256 | result = STATE_CRITICAL; |
| 229 | else if (check_warning_time && (end_time - start_time) > warning_time) | 257 | } else if (config.check_warning_time && (end_time - start_time) > config.warning_time) { |
| 230 | result = STATE_WARNING; | 258 | result = STATE_WARNING; |
| 259 | } | ||
| 231 | 260 | ||
| 232 | /* Put some HTML in here to create a dynamic link */ | 261 | /* Put some HTML in here to create a dynamic link */ |
| 233 | printf(_("REAL %s - %d second response time\n"), state_text(result), (int)(end_time - start_time)); | 262 | printf(_("REAL %s - %d second response time\n"), state_text(result), |
| 234 | } else | 263 | (int)(end_time - start_time)); |
| 264 | } else { | ||
| 235 | printf("%s\n", status_line); | 265 | printf("%s\n", status_line); |
| 266 | } | ||
| 236 | 267 | ||
| 237 | /* close the connection */ | 268 | /* close the connection */ |
| 238 | close(socket); | 269 | close(socket); |
| @@ -240,73 +271,83 @@ int main(int argc, char **argv) { | |||
| 240 | /* reset the alarm */ | 271 | /* reset the alarm */ |
| 241 | alarm(0); | 272 | alarm(0); |
| 242 | 273 | ||
| 243 | return result; | 274 | exit(result); |
| 244 | } | 275 | } |
| 245 | 276 | ||
| 246 | /* process command-line arguments */ | 277 | /* process command-line arguments */ |
| 247 | int process_arguments(int argc, char **argv) { | 278 | check_real_config_wrapper process_arguments(int argc, char **argv) { |
| 248 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'}, | 279 | static struct option longopts[] = { |
| 249 | {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'}, | 280 | {"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'}, |
| 250 | {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'}, | 281 | {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'}, |
| 251 | {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'}, | 282 | {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'}, |
| 252 | {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, | 283 | {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'}, |
| 253 | {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; | 284 | {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, |
| 254 | 285 | {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; | |
| 255 | if (argc < 2) | 286 | |
| 256 | return ERROR; | 287 | check_real_config_wrapper result = { |
| 288 | .errorcode = OK, | ||
| 289 | .config = check_real_config_init(), | ||
| 290 | }; | ||
| 291 | |||
| 292 | if (argc < 2) { | ||
| 293 | result.errorcode = ERROR; | ||
| 294 | return result; | ||
| 295 | } | ||
| 257 | 296 | ||
| 258 | for (int i = 1; i < argc; i++) { | 297 | for (int i = 1; i < argc; i++) { |
| 259 | if (strcmp("-to", argv[i]) == 0) | 298 | if (strcmp("-to", argv[i]) == 0) { |
| 260 | strcpy(argv[i], "-t"); | 299 | strcpy(argv[i], "-t"); |
| 261 | else if (strcmp("-wt", argv[i]) == 0) | 300 | } else if (strcmp("-wt", argv[i]) == 0) { |
| 262 | strcpy(argv[i], "-w"); | 301 | strcpy(argv[i], "-w"); |
| 263 | else if (strcmp("-ct", argv[i]) == 0) | 302 | } else if (strcmp("-ct", argv[i]) == 0) { |
| 264 | strcpy(argv[i], "-c"); | 303 | strcpy(argv[i], "-c"); |
| 304 | } | ||
| 265 | } | 305 | } |
| 266 | 306 | ||
| 267 | int option_char; | ||
| 268 | while (true) { | 307 | while (true) { |
| 269 | int option = 0; | 308 | int option = 0; |
| 270 | option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option); | 309 | int option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option); |
| 271 | 310 | ||
| 272 | if (option_char == -1 || option_char == EOF) | 311 | if (option_char == -1 || option_char == EOF) { |
| 273 | break; | 312 | break; |
| 313 | } | ||
| 274 | 314 | ||
| 275 | switch (option_char) { | 315 | switch (option_char) { |
| 276 | case 'I': /* hostname */ | 316 | case 'I': /* hostname */ |
| 277 | case 'H': /* hostname */ | 317 | case 'H': /* hostname */ |
| 278 | if (server_address) | 318 | if (result.config.server_address) { |
| 279 | break; | 319 | break; |
| 280 | else if (is_host(optarg)) | 320 | } else if (is_host(optarg)) { |
| 281 | server_address = optarg; | 321 | result.config.server_address = optarg; |
| 282 | else | 322 | } else { |
| 283 | usage2(_("Invalid hostname/address"), optarg); | 323 | usage2(_("Invalid hostname/address"), optarg); |
| 324 | } | ||
| 284 | break; | 325 | break; |
| 285 | case 'e': /* string to expect in response header */ | 326 | case 'e': /* string to expect in response header */ |
| 286 | server_expect = optarg; | 327 | result.config.server_expect = optarg; |
| 287 | break; | 328 | break; |
| 288 | case 'u': /* server URL */ | 329 | case 'u': /* server URL */ |
| 289 | server_url = optarg; | 330 | result.config.server_url = optarg; |
| 290 | break; | 331 | break; |
| 291 | case 'p': /* port */ | 332 | case 'p': /* port */ |
| 292 | if (is_intpos(optarg)) { | 333 | if (is_intpos(optarg)) { |
| 293 | server_port = atoi(optarg); | 334 | result.config.server_port = atoi(optarg); |
| 294 | } else { | 335 | } else { |
| 295 | usage4(_("Port must be a positive integer")); | 336 | usage4(_("Port must be a positive integer")); |
| 296 | } | 337 | } |
| 297 | break; | 338 | break; |
| 298 | case 'w': /* warning time threshold */ | 339 | case 'w': /* warning time threshold */ |
| 299 | if (is_intnonneg(optarg)) { | 340 | if (is_intnonneg(optarg)) { |
| 300 | warning_time = atoi(optarg); | 341 | result.config.warning_time = atoi(optarg); |
| 301 | check_warning_time = true; | 342 | result.config.check_warning_time = true; |
| 302 | } else { | 343 | } else { |
| 303 | usage4(_("Warning time must be a positive integer")); | 344 | usage4(_("Warning time must be a positive integer")); |
| 304 | } | 345 | } |
| 305 | break; | 346 | break; |
| 306 | case 'c': /* critical time threshold */ | 347 | case 'c': /* critical time threshold */ |
| 307 | if (is_intnonneg(optarg)) { | 348 | if (is_intnonneg(optarg)) { |
| 308 | critical_time = atoi(optarg); | 349 | result.config.critical_time = atoi(optarg); |
| 309 | check_critical_time = true; | 350 | result.config.check_critical_time = true; |
| 310 | } else { | 351 | } else { |
| 311 | usage4(_("Critical time must be a positive integer")); | 352 | usage4(_("Critical time must be a positive integer")); |
| 312 | } | 353 | } |
| @@ -332,25 +373,28 @@ int process_arguments(int argc, char **argv) { | |||
| 332 | } | 373 | } |
| 333 | } | 374 | } |
| 334 | 375 | ||
| 335 | option_char = optind; | 376 | int option_char = optind; |
| 336 | if (server_address == NULL && argc > option_char) { | 377 | if (result.config.server_address == NULL && argc > option_char) { |
| 337 | if (is_host(argv[option_char])) { | 378 | if (is_host(argv[option_char])) { |
| 338 | server_address = argv[option_char++]; | 379 | result.config.server_address = argv[option_char++]; |
| 339 | } else { | 380 | } else { |
| 340 | usage2(_("Invalid hostname/address"), argv[option_char]); | 381 | usage2(_("Invalid hostname/address"), argv[option_char]); |
| 341 | } | 382 | } |
| 342 | } | 383 | } |
| 343 | 384 | ||
| 344 | if (server_address == NULL) | 385 | if (result.config.server_address == NULL) { |
| 345 | usage4(_("You must provide a server to check")); | 386 | usage4(_("You must provide a server to check")); |
| 387 | } | ||
| 346 | 388 | ||
| 347 | if (host_name == NULL) | 389 | if (result.config.host_name == NULL) { |
| 348 | host_name = strdup(server_address); | 390 | result.config.host_name = strdup(result.config.server_address); |
| 391 | } | ||
| 349 | 392 | ||
| 350 | if (server_expect == NULL) | 393 | if (result.config.server_expect == NULL) { |
| 351 | server_expect = strdup(EXPECT); | 394 | result.config.server_expect = strdup(EXPECT); |
| 395 | } | ||
| 352 | 396 | ||
| 353 | return OK; | 397 | return result; |
| 354 | } | 398 | } |
| 355 | 399 | ||
| 356 | void print_help(void) { | 400 | void print_help(void) { |
| @@ -388,7 +432,8 @@ void print_help(void) { | |||
| 388 | printf("%s\n", _("This plugin will attempt to open an RTSP connection with the host.")); | 432 | printf("%s\n", _("This plugin will attempt to open an RTSP connection with the host.")); |
| 389 | printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); | 433 | printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); |
| 390 | printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,")); | 434 | printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,")); |
| 391 | printf("%s\n", _("but incorrect response messages from the host result in STATE_WARNING return")); | 435 | printf("%s\n", |
| 436 | _("but incorrect response messages from the host result in STATE_WARNING return")); | ||
| 392 | printf("%s\n", _("values.")); | 437 | printf("%s\n", _("values.")); |
| 393 | 438 | ||
| 394 | printf(UT_SUPPORT); | 439 | printf(UT_SUPPORT); |
diff --git a/plugins/check_real.d/config.h b/plugins/check_real.d/config.h new file mode 100644 index 00000000..c4663cf9 --- /dev/null +++ b/plugins/check_real.d/config.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | enum { | ||
| 7 | PORT = 554 | ||
| 8 | }; | ||
| 9 | |||
| 10 | typedef struct { | ||
| 11 | char *server_address; | ||
| 12 | char *host_name; | ||
| 13 | int server_port; | ||
| 14 | char *server_url; | ||
| 15 | |||
| 16 | char *server_expect; | ||
| 17 | int warning_time; | ||
| 18 | bool check_warning_time; | ||
| 19 | int critical_time; | ||
| 20 | bool check_critical_time; | ||
| 21 | } check_real_config; | ||
| 22 | |||
| 23 | check_real_config check_real_config_init() { | ||
| 24 | check_real_config tmp = { | ||
| 25 | .server_address = NULL, | ||
| 26 | .host_name = NULL, | ||
| 27 | .server_port = PORT, | ||
| 28 | .server_url = NULL, | ||
| 29 | |||
| 30 | .server_expect = NULL, | ||
| 31 | .warning_time = 0, | ||
| 32 | .check_warning_time = false, | ||
| 33 | .critical_time = 0, | ||
| 34 | .check_critical_time = false, | ||
| 35 | }; | ||
| 36 | return tmp; | ||
| 37 | } | ||
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index e6369e63..e806ad29 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c | |||
| @@ -1,263 +1,302 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_smtp plugin | 3 | * Monitoring check_smtp plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team | 6 | * Copyright (c) 2000-2024 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the check_smtp plugin | 10 | * This file contains the check_smtp plugin |
| 11 | * | 11 | * |
| 12 | * This plugin will attempt to open an SMTP connection with the host. | 12 | * This plugin will attempt to open an SMTP connection with the host. |
| 13 | * | 13 | * |
| 14 | * | 14 | * |
| 15 | * This program is free software: you can redistribute it and/or modify | 15 | * This program is free software: you can redistribute it and/or modify |
| 16 | * it under the terms of the GNU General Public License as published by | 16 | * it under the terms of the GNU General Public License as published by |
| 17 | * the Free Software Foundation, either version 3 of the License, or | 17 | * the Free Software Foundation, either version 3 of the License, or |
| 18 | * (at your option) any later version. | 18 | * (at your option) any later version. |
| 19 | * | 19 | * |
| 20 | * This program is distributed in the hope that it will be useful, | 20 | * This program is distributed in the hope that it will be useful, |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | * GNU General Public License for more details. | 23 | * GNU General Public License for more details. |
| 24 | * | 24 | * |
| 25 | * You should have received a copy of the GNU General Public License | 25 | * You should have received a copy of the GNU General Public License |
| 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | * | 27 | * |
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | |||
| 31 | const char *progname = "check_smtp"; | ||
| 32 | const char *copyright = "2000-2024"; | ||
| 33 | const char *email = "devel@monitoring-plugins.org"; | ||
| 34 | 30 | ||
| 35 | #include "common.h" | 31 | #include "common.h" |
| 36 | #include "netutils.h" | 32 | #include "netutils.h" |
| 33 | #include "output.h" | ||
| 34 | #include "perfdata.h" | ||
| 35 | #include "thresholds.h" | ||
| 37 | #include "utils.h" | 36 | #include "utils.h" |
| 38 | #include "base64.h" | 37 | #include "base64.h" |
| 38 | #include "regex.h" | ||
| 39 | 39 | ||
| 40 | #include <bits/getopt_ext.h> | ||
| 40 | #include <ctype.h> | 41 | #include <ctype.h> |
| 42 | #include <string.h> | ||
| 43 | #include "check_smtp.d/config.h" | ||
| 44 | #include "../lib/states.h" | ||
| 41 | 45 | ||
| 46 | const char *progname = "check_smtp"; | ||
| 47 | const char *copyright = "2000-2024"; | ||
| 48 | const char *email = "devel@monitoring-plugins.org"; | ||
| 49 | |||
| 50 | #define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" | ||
| 51 | #define SMTP_HELO "HELO " | ||
| 52 | #define SMTP_EHLO "EHLO " | ||
| 53 | #define SMTP_LHLO "LHLO " | ||
| 54 | #define SMTP_QUIT "QUIT\r\n" | ||
| 55 | #define SMTP_STARTTLS "STARTTLS\r\n" | ||
| 56 | #define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n" | ||
| 57 | |||
| 58 | #define EHLO_SUPPORTS_STARTTLS 1 | ||
| 59 | |||
| 60 | typedef struct { | ||
| 61 | int errorcode; | ||
| 62 | check_smtp_config config; | ||
| 63 | } check_smtp_config_wrapper; | ||
| 64 | static check_smtp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 65 | |||
| 66 | int my_recv(check_smtp_config config, void *buf, int num, int socket_descriptor, | ||
| 67 | bool ssl_established) { | ||
| 42 | #ifdef HAVE_SSL | 68 | #ifdef HAVE_SSL |
| 43 | static bool check_cert = false; | 69 | if ((config.use_starttls || config.use_ssl) && ssl_established) { |
| 44 | static int days_till_exp_warn, days_till_exp_crit; | 70 | return np_net_ssl_read(buf, num); |
| 45 | # define my_recv(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) | 71 | } |
| 46 | # define my_send(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) | 72 | return (int)read(socket_descriptor, buf, (size_t)num); |
| 47 | #else /* ifndef HAVE_SSL */ | 73 | #else /* ifndef HAVE_SSL */ |
| 48 | # define my_recv(buf, len) read(sd, buf, len) | 74 | return read(socket_descriptor, buf, len) |
| 49 | # define my_send(buf, len) send(sd, buf, len, 0) | ||
| 50 | #endif | 75 | #endif |
| 76 | } | ||
| 51 | 77 | ||
| 52 | enum { | 78 | int my_send(check_smtp_config config, void *buf, int num, int socket_descriptor, |
| 53 | SMTP_PORT = 25, | 79 | bool ssl_established) { |
| 54 | SMTPS_PORT = 465 | 80 | #ifdef HAVE_SSL |
| 55 | }; | 81 | if ((config.use_starttls || config.use_ssl) && ssl_established) { |
| 56 | #define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" | ||
| 57 | #define SMTP_EXPECT "220" | ||
| 58 | #define SMTP_HELO "HELO " | ||
| 59 | #define SMTP_EHLO "EHLO " | ||
| 60 | #define SMTP_LHLO "LHLO " | ||
| 61 | #define SMTP_QUIT "QUIT\r\n" | ||
| 62 | #define SMTP_STARTTLS "STARTTLS\r\n" | ||
| 63 | #define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n" | ||
| 64 | 82 | ||
| 65 | #define EHLO_SUPPORTS_STARTTLS 1 | 83 | return np_net_ssl_write(buf, num); |
| 84 | } | ||
| 85 | return (int)send(socket_descriptor, buf, (size_t)num, 0); | ||
| 86 | #else /* ifndef HAVE_SSL */ | ||
| 87 | return send(socket_descriptor, buf, len, 0); | ||
| 88 | #endif | ||
| 89 | } | ||
| 66 | 90 | ||
| 67 | static int process_arguments (int, char **); | 91 | static void print_help(void); |
| 68 | static int validate_arguments (void); | 92 | void print_usage(void); |
| 69 | static void print_help (void); | 93 | static char *smtp_quit(check_smtp_config /*config*/, char /*buffer*/[MAX_INPUT_BUFFER], |
| 70 | void print_usage (void); | 94 | int /*socket_descriptor*/, bool /*ssl_established*/); |
| 71 | static void smtp_quit(void); | 95 | static int recvline(char * /*buf*/, size_t /*bufsize*/, check_smtp_config /*config*/, |
| 72 | static int recvline(char *, size_t); | 96 | int /*socket_descriptor*/, bool /*ssl_established*/); |
| 73 | static int recvlines(char *, size_t); | 97 | static int recvlines(check_smtp_config /*config*/, char * /*buf*/, size_t /*bufsize*/, |
| 74 | static int my_close(void); | 98 | int /*socket_descriptor*/, bool /*ssl_established*/); |
| 99 | static int my_close(int /*socket_descriptor*/); | ||
| 75 | 100 | ||
| 76 | #include "regex.h" | ||
| 77 | static regex_t preg; | ||
| 78 | static regmatch_t pmatch[10]; | ||
| 79 | static char errbuf[MAX_INPUT_BUFFER]; | ||
| 80 | static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; | ||
| 81 | static int eflags = 0; | ||
| 82 | static int errcode, excode; | ||
| 83 | |||
| 84 | static int server_port = SMTP_PORT; | ||
| 85 | static int server_port_option = 0; | ||
| 86 | static char *server_address = NULL; | ||
| 87 | static char *server_expect = NULL; | ||
| 88 | static char *mail_command = NULL; | ||
| 89 | static char *from_arg = NULL; | ||
| 90 | static int send_mail_from=0; | ||
| 91 | static int ncommands=0; | ||
| 92 | static int command_size=0; | ||
| 93 | static int nresponses=0; | ||
| 94 | static int response_size=0; | ||
| 95 | static char **commands = NULL; | ||
| 96 | static char **responses = NULL; | ||
| 97 | static char *authtype = NULL; | ||
| 98 | static char *authuser = NULL; | ||
| 99 | static char *authpass = NULL; | ||
| 100 | static double warning_time = 0; | ||
| 101 | static bool check_warning_time = false; | ||
| 102 | static double critical_time = 0; | ||
| 103 | static bool check_critical_time = false; | ||
| 104 | static int verbose = 0; | 101 | static int verbose = 0; |
| 105 | static bool use_ssl = false; | ||
| 106 | static bool use_starttls = false; | ||
| 107 | static bool use_sni = false; | ||
| 108 | static bool use_proxy_prefix = false; | ||
| 109 | static bool use_ehlo = false; | ||
| 110 | static bool use_lhlo = false; | ||
| 111 | static bool ssl_established = false; | ||
| 112 | static char *localhostname = NULL; | ||
| 113 | static int sd; | ||
| 114 | static char buffer[MAX_INPUT_BUFFER]; | ||
| 115 | enum { | ||
| 116 | TCP_PROTOCOL = 1, | ||
| 117 | UDP_PROTOCOL = 2, | ||
| 118 | }; | ||
| 119 | static bool ignore_send_quit_failure = false; | ||
| 120 | |||
| 121 | |||
| 122 | int | ||
| 123 | main (int argc, char **argv) | ||
| 124 | { | ||
| 125 | bool supports_tls = false; | ||
| 126 | int n = 0; | ||
| 127 | double elapsed_time; | ||
| 128 | long microsec; | ||
| 129 | int result = STATE_UNKNOWN; | ||
| 130 | char *cmd_str = NULL; | ||
| 131 | char *helocmd = NULL; | ||
| 132 | char *error_msg = ""; | ||
| 133 | char *server_response = NULL; | ||
| 134 | struct timeval tv; | ||
| 135 | |||
| 136 | /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ | ||
| 137 | (void) signal (SIGPIPE, SIG_IGN); | ||
| 138 | 102 | ||
| 139 | setlocale (LC_ALL, ""); | 103 | int main(int argc, char **argv) { |
| 140 | bindtextdomain (PACKAGE, LOCALEDIR); | 104 | setlocale(LC_ALL, ""); |
| 141 | textdomain (PACKAGE); | 105 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 106 | textdomain(PACKAGE); | ||
| 142 | 107 | ||
| 143 | /* Parse extra opts if any */ | 108 | /* Parse extra opts if any */ |
| 144 | argv=np_extra_opts (&argc, argv, progname); | 109 | argv = np_extra_opts(&argc, argv, progname); |
| 145 | 110 | ||
| 146 | if (process_arguments (argc, argv) == ERROR) | 111 | check_smtp_config_wrapper tmp_config = process_arguments(argc, argv); |
| 147 | usage4 (_("Could not parse arguments")); | 112 | |
| 113 | if (tmp_config.errorcode == ERROR) { | ||
| 114 | usage4(_("Could not parse arguments")); | ||
| 115 | } | ||
| 116 | |||
| 117 | const check_smtp_config config = tmp_config.config; | ||
| 118 | |||
| 119 | if (config.output_format_is_set) { | ||
| 120 | mp_set_format(config.output_format); | ||
| 121 | } | ||
| 148 | 122 | ||
| 149 | /* If localhostname not set on command line, use gethostname to set */ | 123 | /* If localhostname not set on command line, use gethostname to set */ |
| 150 | if(! localhostname){ | 124 | char *localhostname = config.localhostname; |
| 151 | localhostname = malloc (HOST_MAX_BYTES); | 125 | if (!localhostname) { |
| 152 | if(!localhostname){ | 126 | localhostname = malloc(HOST_MAX_BYTES); |
| 127 | if (!localhostname) { | ||
| 153 | printf(_("malloc() failed!\n")); | 128 | printf(_("malloc() failed!\n")); |
| 154 | return STATE_CRITICAL; | 129 | exit(STATE_CRITICAL); |
| 155 | } | 130 | } |
| 156 | if(gethostname(localhostname, HOST_MAX_BYTES)){ | 131 | if (gethostname(localhostname, HOST_MAX_BYTES)) { |
| 157 | printf(_("gethostname() failed!\n")); | 132 | printf(_("gethostname() failed!\n")); |
| 158 | return STATE_CRITICAL; | 133 | exit(STATE_CRITICAL); |
| 159 | } | 134 | } |
| 160 | } | 135 | } |
| 161 | if(use_lhlo) | ||
| 162 | xasprintf (&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); | ||
| 163 | else if(use_ehlo) | ||
| 164 | xasprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); | ||
| 165 | else | ||
| 166 | xasprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); | ||
| 167 | 136 | ||
| 168 | if (verbose) | 137 | char *helocmd = NULL; |
| 138 | if (config.use_lhlo) { | ||
| 139 | xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); | ||
| 140 | } else if (config.use_ehlo) { | ||
| 141 | xasprintf(&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); | ||
| 142 | } else { | ||
| 143 | xasprintf(&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); | ||
| 144 | } | ||
| 145 | |||
| 146 | if (verbose) { | ||
| 169 | printf("HELOCMD: %s", helocmd); | 147 | printf("HELOCMD: %s", helocmd); |
| 148 | } | ||
| 170 | 149 | ||
| 150 | char *mail_command = strdup("MAIL "); | ||
| 151 | char *cmd_str = NULL; | ||
| 171 | /* initialize the MAIL command with optional FROM command */ | 152 | /* initialize the MAIL command with optional FROM command */ |
| 172 | xasprintf (&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n"); | 153 | xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, config.from_arg, "\r\n"); |
| 154 | |||
| 155 | if (verbose && config.send_mail_from) { | ||
| 156 | printf("FROM CMD: %s", cmd_str); | ||
| 157 | } | ||
| 173 | 158 | ||
| 174 | if (verbose && send_mail_from) | 159 | /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ |
| 175 | printf ("FROM CMD: %s", cmd_str); | 160 | (void)signal(SIGPIPE, SIG_IGN); |
| 176 | 161 | ||
| 177 | /* initialize alarm signal handling */ | 162 | /* initialize alarm signal handling */ |
| 178 | (void) signal (SIGALRM, socket_timeout_alarm_handler); | 163 | (void)signal(SIGALRM, socket_timeout_alarm_handler); |
| 179 | 164 | ||
| 180 | /* set socket timeout */ | 165 | /* set socket timeout */ |
| 181 | (void) alarm (socket_timeout); | 166 | (void)alarm(socket_timeout); |
| 182 | 167 | ||
| 168 | struct timeval start_time; | ||
| 183 | /* start timer */ | 169 | /* start timer */ |
| 184 | gettimeofday (&tv, NULL); | 170 | gettimeofday(&start_time, NULL); |
| 171 | |||
| 172 | int socket_descriptor = 0; | ||
| 185 | 173 | ||
| 186 | /* try to connect to the host at the given port number */ | 174 | /* try to connect to the host at the given port number */ |
| 187 | result = my_tcp_connect (server_address, server_port, &sd); | 175 | mp_state_enum tcp_result = |
| 188 | 176 | my_tcp_connect(config.server_address, config.server_port, &socket_descriptor); | |
| 189 | if (result == STATE_OK) { /* we connected */ | 177 | |
| 190 | /* If requested, send PROXY header */ | 178 | mp_check overall = mp_check_init(); |
| 191 | if (use_proxy_prefix) { | 179 | mp_subcheck sc_tcp_connect = mp_subcheck_init(); |
| 192 | if (verbose) | 180 | char buffer[MAX_INPUT_BUFFER]; |
| 193 | printf ("Sending header %s\n", PROXY_PREFIX); | 181 | bool ssl_established = false; |
| 194 | my_send(PROXY_PREFIX, strlen(PROXY_PREFIX)); | 182 | |
| 183 | if (tcp_result != STATE_OK) { | ||
| 184 | // Connect failed | ||
| 185 | sc_tcp_connect = mp_set_subcheck_state(sc_tcp_connect, STATE_CRITICAL); | ||
| 186 | xasprintf(&sc_tcp_connect.output, "TCP connect to '%s' failed", config.server_address); | ||
| 187 | mp_add_subcheck_to_check(&overall, sc_tcp_connect); | ||
| 188 | mp_exit(overall); | ||
| 189 | } | ||
| 190 | |||
| 191 | /* we connected */ | ||
| 192 | /* If requested, send PROXY header */ | ||
| 193 | if (config.use_proxy_prefix) { | ||
| 194 | if (verbose) { | ||
| 195 | printf("Sending header %s\n", PROXY_PREFIX); | ||
| 195 | } | 196 | } |
| 197 | my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established); | ||
| 198 | } | ||
| 196 | 199 | ||
| 197 | #ifdef HAVE_SSL | 200 | #ifdef HAVE_SSL |
| 198 | if (use_ssl) { | 201 | if (config.use_ssl) { |
| 199 | result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); | 202 | int tls_result = np_net_ssl_init_with_hostname( |
| 200 | if (result != STATE_OK) { | 203 | socket_descriptor, (config.use_sni ? config.server_address : NULL)); |
| 201 | printf (_("CRITICAL - Cannot create SSL context.\n")); | 204 | |
| 202 | close(sd); | 205 | mp_subcheck sc_tls_connection = mp_subcheck_init(); |
| 203 | np_net_ssl_cleanup(); | 206 | |
| 204 | return STATE_CRITICAL; | 207 | if (tls_result != STATE_OK) { |
| 205 | } else { | 208 | close(socket_descriptor); |
| 206 | ssl_established = 1; | 209 | np_net_ssl_cleanup(); |
| 207 | } | 210 | |
| 211 | sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_CRITICAL); | ||
| 212 | xasprintf(&sc_tls_connection.output, "cannot create TLS context"); | ||
| 213 | mp_add_subcheck_to_check(&overall, sc_tls_connection); | ||
| 214 | mp_exit(overall); | ||
| 208 | } | 215 | } |
| 216 | |||
| 217 | sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_OK); | ||
| 218 | xasprintf(&sc_tls_connection.output, "TLS context established"); | ||
| 219 | mp_add_subcheck_to_check(&overall, sc_tls_connection); | ||
| 220 | ssl_established = true; | ||
| 221 | } | ||
| 209 | #endif | 222 | #endif |
| 210 | 223 | ||
| 211 | /* watch for the SMTP connection string and */ | 224 | /* watch for the SMTP connection string and */ |
| 212 | /* return a WARNING status if we couldn't read any data */ | 225 | /* return a WARNING status if we couldn't read any data */ |
| 213 | if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { | 226 | if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { |
| 214 | printf (_("recv() failed\n")); | 227 | mp_subcheck sc_read_data = mp_subcheck_init(); |
| 215 | return STATE_WARNING; | 228 | sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING); |
| 229 | xasprintf(&sc_read_data.output, "recv() failed"); | ||
| 230 | mp_add_subcheck_to_check(&overall, sc_read_data); | ||
| 231 | mp_exit(overall); | ||
| 232 | } | ||
| 233 | |||
| 234 | char *server_response = NULL; | ||
| 235 | /* save connect return (220 hostname ..) for later use */ | ||
| 236 | xasprintf(&server_response, "%s", buffer); | ||
| 237 | |||
| 238 | /* send the HELO/EHLO command */ | ||
| 239 | my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established); | ||
| 240 | |||
| 241 | /* allow for response to helo command to reach us */ | ||
| 242 | if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { | ||
| 243 | mp_subcheck sc_read_data = mp_subcheck_init(); | ||
| 244 | sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING); | ||
| 245 | xasprintf(&sc_read_data.output, "recv() failed"); | ||
| 246 | mp_add_subcheck_to_check(&overall, sc_read_data); | ||
| 247 | mp_exit(overall); | ||
| 248 | } | ||
| 249 | |||
| 250 | bool supports_tls = false; | ||
| 251 | if (config.use_ehlo || config.use_lhlo) { | ||
| 252 | if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) { | ||
| 253 | supports_tls = true; | ||
| 216 | } | 254 | } |
| 255 | } | ||
| 217 | 256 | ||
| 218 | /* save connect return (220 hostname ..) for later use */ | 257 | if (config.use_starttls && !supports_tls) { |
| 219 | xasprintf(&server_response, "%s", buffer); | 258 | smtp_quit(config, buffer, socket_descriptor, ssl_established); |
| 220 | 259 | ||
| 221 | /* send the HELO/EHLO command */ | 260 | mp_subcheck sc_read_data = mp_subcheck_init(); |
| 222 | my_send(helocmd, strlen(helocmd)); | 261 | sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING); |
| 262 | xasprintf(&sc_read_data.output, "StartTLS not supported by server"); | ||
| 263 | mp_add_subcheck_to_check(&overall, sc_read_data); | ||
| 264 | mp_exit(overall); | ||
| 265 | } | ||
| 223 | 266 | ||
| 224 | /* allow for response to helo command to reach us */ | 267 | #ifdef HAVE_SSL |
| 225 | if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { | 268 | if (config.use_starttls) { |
| 226 | printf (_("recv() failed\n")); | 269 | /* send the STARTTLS command */ |
| 227 | return STATE_WARNING; | 270 | send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); |
| 228 | } else if(use_ehlo || use_lhlo){ | 271 | |
| 229 | if(strstr(buffer, "250 STARTTLS") != NULL || | 272 | mp_subcheck sc_starttls_init = mp_subcheck_init(); |
| 230 | strstr(buffer, "250-STARTTLS") != NULL){ | 273 | recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, |
| 231 | supports_tls=true; | 274 | ssl_established); /* wait for it */ |
| 232 | } | 275 | if (!strstr(buffer, SMTP_EXPECT)) { |
| 276 | smtp_quit(config, buffer, socket_descriptor, ssl_established); | ||
| 277 | |||
| 278 | xasprintf(&sc_starttls_init.output, "StartTLS not supported by server"); | ||
| 279 | sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_UNKNOWN); | ||
| 280 | mp_add_subcheck_to_check(&overall, sc_starttls_init); | ||
| 281 | mp_exit(overall); | ||
| 233 | } | 282 | } |
| 234 | 283 | ||
| 235 | if(use_starttls && ! supports_tls){ | 284 | mp_state_enum starttls_result = np_net_ssl_init_with_hostname( |
| 236 | printf(_("WARNING - TLS not supported by server\n")); | 285 | socket_descriptor, (config.use_sni ? config.server_address : NULL)); |
| 237 | smtp_quit(); | 286 | if (starttls_result != STATE_OK) { |
| 238 | return STATE_WARNING; | 287 | close(socket_descriptor); |
| 288 | np_net_ssl_cleanup(); | ||
| 289 | |||
| 290 | sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_CRITICAL); | ||
| 291 | xasprintf(&sc_starttls_init.output, "failed to create StartTLS context"); | ||
| 292 | mp_add_subcheck_to_check(&overall, sc_starttls_init); | ||
| 293 | mp_exit(overall); | ||
| 239 | } | 294 | } |
| 295 | sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_OK); | ||
| 296 | xasprintf(&sc_starttls_init.output, "created StartTLS context"); | ||
| 297 | mp_add_subcheck_to_check(&overall, sc_starttls_init); | ||
| 240 | 298 | ||
| 241 | #ifdef HAVE_SSL | 299 | ssl_established = true; |
| 242 | if(use_starttls) { | ||
| 243 | /* send the STARTTLS command */ | ||
| 244 | send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); | ||
| 245 | |||
| 246 | recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */ | ||
| 247 | if (!strstr (buffer, SMTP_EXPECT)) { | ||
| 248 | printf (_("Server does not support STARTTLS\n")); | ||
| 249 | smtp_quit(); | ||
| 250 | return STATE_UNKNOWN; | ||
| 251 | } | ||
| 252 | result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); | ||
| 253 | if(result != STATE_OK) { | ||
| 254 | printf (_("CRITICAL - Cannot create SSL context.\n")); | ||
| 255 | close(sd); | ||
| 256 | np_net_ssl_cleanup(); | ||
| 257 | return STATE_CRITICAL; | ||
| 258 | } else { | ||
| 259 | ssl_established = 1; | ||
| 260 | } | ||
| 261 | 300 | ||
| 262 | /* | 301 | /* |
| 263 | * Resend the EHLO command. | 302 | * Resend the EHLO command. |
| @@ -270,218 +309,287 @@ main (int argc, char **argv) | |||
| 270 | * reason, some MTAs will not allow an AUTH LOGIN command before | 309 | * reason, some MTAs will not allow an AUTH LOGIN command before |
| 271 | * we resent EHLO via TLS. | 310 | * we resent EHLO via TLS. |
| 272 | */ | 311 | */ |
| 273 | if (my_send(helocmd, strlen(helocmd)) <= 0) { | 312 | if (my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established) <= |
| 274 | printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); | 313 | 0) { |
| 275 | my_close(); | 314 | my_close(socket_descriptor); |
| 276 | return STATE_UNKNOWN; | 315 | |
| 316 | mp_subcheck sc_ehlo = mp_subcheck_init(); | ||
| 317 | sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN); | ||
| 318 | xasprintf(&sc_ehlo.output, "cannot send EHLO command via StartTLS"); | ||
| 319 | mp_add_subcheck_to_check(&overall, sc_ehlo); | ||
| 320 | mp_exit(overall); | ||
| 277 | } | 321 | } |
| 278 | if (verbose) | 322 | |
| 323 | if (verbose) { | ||
| 279 | printf(_("sent %s"), helocmd); | 324 | printf(_("sent %s"), helocmd); |
| 280 | if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { | ||
| 281 | printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); | ||
| 282 | my_close(); | ||
| 283 | return STATE_UNKNOWN; | ||
| 284 | } | 325 | } |
| 326 | |||
| 327 | if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { | ||
| 328 | my_close(socket_descriptor); | ||
| 329 | |||
| 330 | mp_subcheck sc_ehlo = mp_subcheck_init(); | ||
| 331 | sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN); | ||
| 332 | xasprintf(&sc_ehlo.output, "cannot read EHLO response via StartTLS"); | ||
| 333 | mp_add_subcheck_to_check(&overall, sc_ehlo); | ||
| 334 | mp_exit(overall); | ||
| 335 | } | ||
| 336 | |||
| 285 | if (verbose) { | 337 | if (verbose) { |
| 286 | printf("%s", buffer); | 338 | printf("%s", buffer); |
| 287 | } | 339 | } |
| 340 | } | ||
| 341 | |||
| 342 | # ifdef USE_OPENSSL | ||
| 343 | if (ssl_established) { | ||
| 344 | net_ssl_check_cert_result cert_check_result = | ||
| 345 | np_net_ssl_check_cert2(config.days_till_exp_warn, config.days_till_exp_crit); | ||
| 346 | |||
| 347 | mp_subcheck sc_cert_check = mp_subcheck_init(); | ||
| 348 | |||
| 349 | switch (cert_check_result.errors) { | ||
| 350 | case ALL_OK: { | ||
| 351 | |||
| 352 | if (cert_check_result.result_state != STATE_OK && | ||
| 353 | config.ignore_certificate_expiration) { | ||
| 354 | xasprintf(&sc_cert_check.output, | ||
| 355 | "Remaining certificate lifetime: %d days. Expiration will be ignored", | ||
| 356 | (int)(cert_check_result.remaining_seconds / 86400)); | ||
| 357 | sc_cert_check = mp_set_subcheck_state(sc_cert_check, STATE_OK); | ||
| 358 | } else { | ||
| 359 | xasprintf(&sc_cert_check.output, "Remaining certificate lifetime: %d days", | ||
| 360 | (int)(cert_check_result.remaining_seconds / 86400)); | ||
| 361 | sc_cert_check = | ||
| 362 | mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); | ||
| 363 | } | ||
| 364 | } break; | ||
| 365 | case NO_SERVER_CERTIFICATE_PRESENT: { | ||
| 366 | xasprintf(&sc_cert_check.output, "no server certificate present"); | ||
| 367 | sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); | ||
| 368 | } break; | ||
| 369 | case UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT: { | ||
| 370 | xasprintf(&sc_cert_check.output, "can not retrieve certificate subject"); | ||
| 371 | sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); | ||
| 372 | } break; | ||
| 373 | case WRONG_TIME_FORMAT_IN_CERTIFICATE: { | ||
| 374 | xasprintf(&sc_cert_check.output, "wrong time format in certificate"); | ||
| 375 | sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state); | ||
| 376 | } break; | ||
| 377 | }; | ||
| 378 | |||
| 379 | mp_add_subcheck_to_check(&overall, sc_cert_check); | ||
| 380 | } | ||
| 381 | # endif /* USE_OPENSSL */ | ||
| 288 | 382 | ||
| 289 | # ifdef USE_OPENSSL | ||
| 290 | if ( check_cert ) { | ||
| 291 | result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); | ||
| 292 | smtp_quit(); | ||
| 293 | my_close(); | ||
| 294 | return result; | ||
| 295 | } | ||
| 296 | # endif /* USE_OPENSSL */ | ||
| 297 | } | ||
| 298 | #endif | 383 | #endif |
| 299 | 384 | ||
| 300 | if (verbose) | 385 | if (verbose) { |
| 301 | printf ("%s", buffer); | 386 | printf("%s", buffer); |
| 302 | 387 | } | |
| 303 | /* save buffer for later use */ | 388 | |
| 304 | xasprintf(&server_response, "%s%s", server_response, buffer); | 389 | /* save buffer for later use */ |
| 305 | /* strip the buffer of carriage returns */ | 390 | xasprintf(&server_response, "%s%s", server_response, buffer); |
| 306 | strip (server_response); | 391 | /* strip the buffer of carriage returns */ |
| 307 | 392 | strip(server_response); | |
| 308 | /* make sure we find the droids we are looking for */ | 393 | |
| 309 | if (!strstr (server_response, server_expect)) { | 394 | /* make sure we find the droids we are looking for */ |
| 310 | if (server_port == SMTP_PORT) | 395 | mp_subcheck sc_expect_response = mp_subcheck_init(); |
| 311 | printf (_("Invalid SMTP response received from host: %s\n"), server_response); | 396 | |
| 312 | else | 397 | if (!strstr(server_response, config.server_expect)) { |
| 313 | printf (_("Invalid SMTP response received from host on port %d: %s\n"), | 398 | sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_WARNING); |
| 314 | server_port, server_response); | 399 | if (config.server_port == SMTP_PORT) { |
| 315 | return STATE_WARNING; | 400 | xasprintf(&sc_expect_response.output, _("invalid SMTP response received from host: %s"), |
| 401 | server_response); | ||
| 402 | } else { | ||
| 403 | xasprintf(&sc_expect_response.output, | ||
| 404 | _("invalid SMTP response received from host on port %d: %s"), | ||
| 405 | config.server_port, server_response); | ||
| 316 | } | 406 | } |
| 407 | exit(STATE_WARNING); | ||
| 408 | } else { | ||
| 409 | xasprintf(&sc_expect_response.output, "received valid SMTP response '%s' from host: '%s'", | ||
| 410 | config.server_expect, server_response); | ||
| 411 | sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_OK); | ||
| 412 | } | ||
| 413 | |||
| 414 | mp_add_subcheck_to_check(&overall, sc_expect_response); | ||
| 317 | 415 | ||
| 318 | if (send_mail_from) { | 416 | if (config.send_mail_from) { |
| 319 | my_send(cmd_str, strlen(cmd_str)); | 417 | my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); |
| 320 | if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) | 418 | if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 && |
| 321 | printf("%s", buffer); | 419 | verbose) { |
| 420 | printf("%s", buffer); | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | size_t counter = 0; | ||
| 425 | while (counter < config.ncommands) { | ||
| 426 | xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n"); | ||
| 427 | my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); | ||
| 428 | if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 && | ||
| 429 | verbose) { | ||
| 430 | printf("%s", buffer); | ||
| 322 | } | 431 | } |
| 323 | 432 | ||
| 324 | n = 0; | 433 | strip(buffer); |
| 325 | while (n < ncommands) { | 434 | |
| 326 | xasprintf (&cmd_str, "%s%s", commands[n], "\r\n"); | 435 | if (counter < config.nresponses) { |
| 327 | my_send(cmd_str, strlen(cmd_str)); | 436 | int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; |
| 328 | if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) | 437 | regex_t preg; |
| 329 | printf("%s", buffer); | 438 | int errcode = regcomp(&preg, config.responses[counter], cflags); |
| 330 | strip (buffer); | 439 | char errbuf[MAX_INPUT_BUFFER]; |
| 331 | if (n < nresponses) { | 440 | if (errcode != 0) { |
| 332 | cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; | 441 | regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); |
| 333 | errcode = regcomp (&preg, responses[n], cflags); | 442 | printf(_("Could Not Compile Regular Expression")); |
| 334 | if (errcode != 0) { | 443 | exit(STATE_UNKNOWN); |
| 335 | regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); | 444 | } |
| 336 | printf (_("Could Not Compile Regular Expression")); | 445 | |
| 337 | return ERROR; | 446 | regmatch_t pmatch[10]; |
| 447 | int eflags = 0; | ||
| 448 | int excode = regexec(&preg, buffer, 10, pmatch, eflags); | ||
| 449 | mp_subcheck sc_expected_responses = mp_subcheck_init(); | ||
| 450 | if (excode == 0) { | ||
| 451 | xasprintf(&sc_expected_responses.output, "valid response '%s' to command '%s'", | ||
| 452 | buffer, config.commands[counter]); | ||
| 453 | sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_OK); | ||
| 454 | } else if (excode == REG_NOMATCH) { | ||
| 455 | sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_WARNING); | ||
| 456 | xasprintf(&sc_expected_responses.output, "invalid response '%s' to command '%s'", | ||
| 457 | buffer, config.commands[counter]); | ||
| 458 | } else { | ||
| 459 | regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER); | ||
| 460 | xasprintf(&sc_expected_responses.output, "regexec execute error: %s", errbuf); | ||
| 461 | sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_UNKNOWN); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | counter++; | ||
| 465 | } | ||
| 466 | |||
| 467 | if (config.authtype != NULL) { | ||
| 468 | mp_subcheck sc_auth = mp_subcheck_init(); | ||
| 469 | |||
| 470 | if (strcmp(config.authtype, "LOGIN") == 0) { | ||
| 471 | char *abuf; | ||
| 472 | int ret; | ||
| 473 | do { | ||
| 474 | /* send AUTH LOGIN */ | ||
| 475 | my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor, | ||
| 476 | ssl_established); | ||
| 477 | |||
| 478 | if (verbose) { | ||
| 479 | printf(_("sent %s\n"), "AUTH LOGIN"); | ||
| 338 | } | 480 | } |
| 339 | excode = regexec (&preg, buffer, 10, pmatch, eflags); | 481 | |
| 340 | if (excode == 0) { | 482 | if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, |
| 341 | result = STATE_OK; | 483 | ssl_established)) <= 0) { |
| 484 | xasprintf(&sc_auth.output, _("recv() failed after AUTH LOGIN")); | ||
| 485 | sc_auth = mp_set_subcheck_state(sc_auth, STATE_WARNING); | ||
| 486 | break; | ||
| 342 | } | 487 | } |
| 343 | else if (excode == REG_NOMATCH) { | 488 | |
| 344 | result = STATE_WARNING; | 489 | if (verbose) { |
| 345 | printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result), buffer, commands[n]); | 490 | printf(_("received %s\n"), buffer); |
| 346 | } | 491 | } |
| 347 | else { | 492 | |
| 348 | regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER); | 493 | if (strncmp(buffer, "334", 3) != 0) { |
| 349 | printf (_("Execute Error: %s\n"), errbuf); | 494 | xasprintf(&sc_auth.output, "invalid response received after AUTH LOGIN"); |
| 350 | result = STATE_UNKNOWN; | 495 | sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); |
| 496 | break; | ||
| 497 | } | ||
| 498 | |||
| 499 | /* encode authuser with base64 */ | ||
| 500 | base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf); | ||
| 501 | xasprintf(&abuf, "%s\r\n", abuf); | ||
| 502 | my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); | ||
| 503 | if (verbose) { | ||
| 504 | printf(_("sent %s\n"), abuf); | ||
| 351 | } | 505 | } |
| 352 | } | ||
| 353 | n++; | ||
| 354 | } | ||
| 355 | 506 | ||
| 356 | if (authtype != NULL) { | 507 | if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, |
| 357 | if (strcmp (authtype, "LOGIN") == 0) { | 508 | ssl_established)) <= 0) { |
| 358 | char *abuf; | 509 | xasprintf(&sc_auth.output, "recv() failed after sending authuser"); |
| 359 | int ret; | 510 | sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); |
| 360 | do { | ||
| 361 | if (authuser == NULL) { | ||
| 362 | result = STATE_CRITICAL; | ||
| 363 | xasprintf(&error_msg, _("no authuser specified, ")); | ||
| 364 | break; | ||
| 365 | } | ||
| 366 | if (authpass == NULL) { | ||
| 367 | result = STATE_CRITICAL; | ||
| 368 | xasprintf(&error_msg, _("no authpass specified, ")); | ||
| 369 | break; | ||
| 370 | } | ||
| 371 | |||
| 372 | /* send AUTH LOGIN */ | ||
| 373 | my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN)); | ||
| 374 | if (verbose) | ||
| 375 | printf (_("sent %s\n"), "AUTH LOGIN"); | ||
| 376 | |||
| 377 | if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { | ||
| 378 | xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, ")); | ||
| 379 | result = STATE_WARNING; | ||
| 380 | break; | ||
| 381 | } | ||
| 382 | if (verbose) | ||
| 383 | printf (_("received %s\n"), buffer); | ||
| 384 | |||
| 385 | if (strncmp (buffer, "334", 3) != 0) { | ||
| 386 | result = STATE_CRITICAL; | ||
| 387 | xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, ")); | ||
| 388 | break; | ||
| 389 | } | ||
| 390 | |||
| 391 | /* encode authuser with base64 */ | ||
| 392 | base64_encode_alloc (authuser, strlen(authuser), &abuf); | ||
| 393 | xasprintf(&abuf, "%s\r\n", abuf); | ||
| 394 | my_send(abuf, strlen(abuf)); | ||
| 395 | if (verbose) | ||
| 396 | printf (_("sent %s\n"), abuf); | ||
| 397 | |||
| 398 | if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { | ||
| 399 | result = STATE_CRITICAL; | ||
| 400 | xasprintf(&error_msg, _("recv() failed after sending authuser, ")); | ||
| 401 | break; | ||
| 402 | } | ||
| 403 | if (verbose) { | ||
| 404 | printf (_("received %s\n"), buffer); | ||
| 405 | } | ||
| 406 | if (strncmp (buffer, "334", 3) != 0) { | ||
| 407 | result = STATE_CRITICAL; | ||
| 408 | xasprintf(&error_msg, _("invalid response received after authuser, ")); | ||
| 409 | break; | ||
| 410 | } | ||
| 411 | /* encode authpass with base64 */ | ||
| 412 | base64_encode_alloc (authpass, strlen(authpass), &abuf); | ||
| 413 | xasprintf(&abuf, "%s\r\n", abuf); | ||
| 414 | my_send(abuf, strlen(abuf)); | ||
| 415 | if (verbose) { | ||
| 416 | printf (_("sent %s\n"), abuf); | ||
| 417 | } | ||
| 418 | if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { | ||
| 419 | result = STATE_CRITICAL; | ||
| 420 | xasprintf(&error_msg, _("recv() failed after sending authpass, ")); | ||
| 421 | break; | ||
| 422 | } | ||
| 423 | if (verbose) { | ||
| 424 | printf (_("received %s\n"), buffer); | ||
| 425 | } | ||
| 426 | if (strncmp (buffer, "235", 3) != 0) { | ||
| 427 | result = STATE_CRITICAL; | ||
| 428 | xasprintf(&error_msg, _("invalid response received after authpass, ")); | ||
| 429 | break; | ||
| 430 | } | ||
| 431 | break; | 511 | break; |
| 432 | } while (0); | 512 | } |
| 433 | } else { | ||
| 434 | result = STATE_CRITICAL; | ||
| 435 | xasprintf(&error_msg, _("only authtype LOGIN is supported, ")); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | 513 | ||
| 439 | /* tell the server we're done */ | 514 | if (verbose) { |
| 440 | smtp_quit(); | 515 | printf(_("received %s\n"), buffer); |
| 516 | } | ||
| 441 | 517 | ||
| 442 | /* finally close the connection */ | 518 | if (strncmp(buffer, "334", 3) != 0) { |
| 443 | close (sd); | 519 | xasprintf(&sc_auth.output, "invalid response received after authuser"); |
| 444 | } | 520 | sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); |
| 521 | break; | ||
| 522 | } | ||
| 445 | 523 | ||
| 446 | /* reset the alarm */ | 524 | /* encode authpass with base64 */ |
| 447 | alarm (0); | 525 | base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf); |
| 526 | xasprintf(&abuf, "%s\r\n", abuf); | ||
| 527 | my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established); | ||
| 528 | |||
| 529 | if (verbose) { | ||
| 530 | printf(_("sent %s\n"), abuf); | ||
| 531 | } | ||
| 532 | |||
| 533 | if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, | ||
| 534 | ssl_established)) <= 0) { | ||
| 535 | xasprintf(&sc_auth.output, "recv() failed after sending authpass"); | ||
| 536 | sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); | ||
| 537 | break; | ||
| 538 | } | ||
| 448 | 539 | ||
| 449 | microsec = deltime (tv); | 540 | if (verbose) { |
| 450 | elapsed_time = (double)microsec / 1.0e6; | 541 | printf(_("received %s\n"), buffer); |
| 542 | } | ||
| 543 | |||
| 544 | if (strncmp(buffer, "235", 3) != 0) { | ||
| 545 | xasprintf(&sc_auth.output, "invalid response received after authpass"); | ||
| 546 | sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); | ||
| 547 | break; | ||
| 548 | } | ||
| 549 | break; | ||
| 550 | } while (false); | ||
| 551 | } else { | ||
| 552 | sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL); | ||
| 553 | xasprintf(&sc_auth.output, "only authtype LOGIN is supported"); | ||
| 554 | } | ||
| 451 | 555 | ||
| 452 | if (result == STATE_OK) { | 556 | mp_add_subcheck_to_check(&overall, sc_auth); |
| 453 | if (check_critical_time && elapsed_time > critical_time) | ||
| 454 | result = STATE_CRITICAL; | ||
| 455 | else if (check_warning_time && elapsed_time > warning_time) | ||
| 456 | result = STATE_WARNING; | ||
| 457 | } | 557 | } |
| 458 | 558 | ||
| 459 | printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), | 559 | /* tell the server we're done */ |
| 460 | state_text (result), | 560 | smtp_quit(config, buffer, socket_descriptor, ssl_established); |
| 461 | error_msg, | ||
| 462 | elapsed_time, | ||
| 463 | verbose?", ":"", verbose?buffer:"", | ||
| 464 | fperfdata ("time", elapsed_time, "s", | ||
| 465 | (int)check_warning_time, warning_time, | ||
| 466 | (int)check_critical_time, critical_time, | ||
| 467 | true, 0, false, 0)); | ||
| 468 | 561 | ||
| 469 | return result; | 562 | /* finally close the connection */ |
| 470 | } | 563 | close(socket_descriptor); |
| 471 | 564 | ||
| 565 | /* reset the alarm */ | ||
| 566 | alarm(0); | ||
| 472 | 567 | ||
| 568 | long microsec = deltime(start_time); | ||
| 569 | double elapsed_time = (double)microsec / 1.0e6; | ||
| 473 | 570 | ||
| 474 | /* process command-line arguments */ | 571 | mp_perfdata pd_elapsed_time = perfdata_init(); |
| 475 | int | 572 | pd_elapsed_time = mp_set_pd_value(pd_elapsed_time, elapsed_time); |
| 476 | process_arguments (int argc, char **argv) | 573 | pd_elapsed_time.label = "time"; |
| 477 | { | 574 | pd_elapsed_time.uom = "s"; |
| 478 | int c; | ||
| 479 | char* temp; | ||
| 480 | 575 | ||
| 481 | bool implicit_tls = false; | 576 | pd_elapsed_time = mp_pd_set_thresholds(pd_elapsed_time, config.connection_time); |
| 577 | |||
| 578 | mp_subcheck sc_connection_time = mp_subcheck_init(); | ||
| 579 | xasprintf(&sc_connection_time.output, "connection time: %.3gs", elapsed_time); | ||
| 580 | sc_connection_time = | ||
| 581 | mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_elapsed_time)); | ||
| 582 | mp_add_subcheck_to_check(&overall, sc_connection_time); | ||
| 583 | |||
| 584 | mp_exit(overall); | ||
| 585 | } | ||
| 482 | 586 | ||
| 587 | /* process command-line arguments */ | ||
| 588 | check_smtp_config_wrapper process_arguments(int argc, char **argv) { | ||
| 483 | enum { | 589 | enum { |
| 484 | SNI_OPTION | 590 | SNI_OPTION = CHAR_MAX + 1, |
| 591 | output_format_index, | ||
| 592 | ignore_certificate_expiration_index, | ||
| 485 | }; | 593 | }; |
| 486 | 594 | ||
| 487 | int option = 0; | 595 | int option = 0; |
| @@ -507,173 +615,194 @@ process_arguments (int argc, char **argv) | |||
| 507 | {"lmtp", no_argument, 0, 'L'}, | 615 | {"lmtp", no_argument, 0, 'L'}, |
| 508 | {"ssl", no_argument, 0, 's'}, | 616 | {"ssl", no_argument, 0, 's'}, |
| 509 | {"tls", no_argument, 0, 's'}, | 617 | {"tls", no_argument, 0, 's'}, |
| 510 | {"starttls",no_argument,0,'S'}, | 618 | {"starttls", no_argument, 0, 'S'}, |
| 511 | {"sni", no_argument, 0, SNI_OPTION}, | 619 | {"sni", no_argument, 0, SNI_OPTION}, |
| 512 | {"certificate",required_argument,0,'D'}, | 620 | {"certificate", required_argument, 0, 'D'}, |
| 513 | {"ignore-quit-failure",no_argument,0,'q'}, | 621 | {"ignore-quit-failure", no_argument, 0, 'q'}, |
| 514 | {"proxy",no_argument,0,'r'}, | 622 | {"proxy", no_argument, 0, 'r'}, |
| 515 | {0, 0, 0, 0} | 623 | {"ignore-certificate-expiration", no_argument, 0, ignore_certificate_expiration_index}, |
| 624 | {"output-format", required_argument, 0, output_format_index}, | ||
| 625 | {0, 0, 0, 0}}; | ||
| 626 | |||
| 627 | check_smtp_config_wrapper result = { | ||
| 628 | .config = check_smtp_config_init(), | ||
| 629 | .errorcode = OK, | ||
| 516 | }; | 630 | }; |
| 517 | 631 | ||
| 518 | if (argc < 2) | 632 | if (argc < 2) { |
| 519 | return ERROR; | 633 | result.errorcode = ERROR; |
| 634 | return result; | ||
| 635 | } | ||
| 520 | 636 | ||
| 521 | for (c = 1; c < argc; c++) { | 637 | for (int index = 1; index < argc; index++) { |
| 522 | if (strcmp ("-to", argv[c]) == 0) | 638 | if (strcmp("-to", argv[index]) == 0) { |
| 523 | strcpy (argv[c], "-t"); | 639 | strcpy(argv[index], "-t"); |
| 524 | else if (strcmp ("-wt", argv[c]) == 0) | 640 | } else if (strcmp("-wt", argv[index]) == 0) { |
| 525 | strcpy (argv[c], "-w"); | 641 | strcpy(argv[index], "-w"); |
| 526 | else if (strcmp ("-ct", argv[c]) == 0) | 642 | } else if (strcmp("-ct", argv[index]) == 0) { |
| 527 | strcpy (argv[c], "-c"); | 643 | strcpy(argv[index], "-c"); |
| 644 | } | ||
| 528 | } | 645 | } |
| 529 | 646 | ||
| 530 | while (1) { | 647 | unsigned long command_size = 0; |
| 531 | c = getopt_long (argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", | 648 | unsigned long response_size = 0; |
| 532 | longopts, &option); | 649 | bool implicit_tls = false; |
| 650 | int server_port_option = 0; | ||
| 651 | while (true) { | ||
| 652 | int opt_index = | ||
| 653 | getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option); | ||
| 533 | 654 | ||
| 534 | if (c == -1 || c == EOF) | 655 | if (opt_index == -1 || opt_index == EOF) { |
| 535 | break; | 656 | break; |
| 657 | } | ||
| 536 | 658 | ||
| 537 | switch (c) { | 659 | switch (opt_index) { |
| 538 | case 'H': /* hostname */ | 660 | case 'H': /* hostname */ |
| 539 | if (is_host (optarg)) { | 661 | if (is_host(optarg)) { |
| 540 | server_address = optarg; | 662 | result.config.server_address = optarg; |
| 541 | } | 663 | } else { |
| 542 | else { | 664 | usage2(_("Invalid hostname/address"), optarg); |
| 543 | usage2 (_("Invalid hostname/address"), optarg); | ||
| 544 | } | 665 | } |
| 545 | break; | 666 | break; |
| 546 | case 'p': /* port */ | 667 | case 'p': /* port */ |
| 547 | if (is_intpos (optarg)) | 668 | if (is_intpos(optarg)) { |
| 548 | server_port_option = atoi (optarg); | 669 | server_port_option = atoi(optarg); |
| 549 | else | 670 | } else { |
| 550 | usage4 (_("Port must be a positive integer")); | 671 | usage4(_("Port must be a positive integer")); |
| 672 | } | ||
| 551 | break; | 673 | break; |
| 552 | case 'F': | 674 | case 'F': |
| 553 | /* localhostname */ | 675 | /* localhostname */ |
| 554 | localhostname = strdup(optarg); | 676 | result.config.localhostname = strdup(optarg); |
| 555 | break; | 677 | break; |
| 556 | case 'f': /* from argument */ | 678 | case 'f': /* from argument */ |
| 557 | from_arg = optarg + strspn(optarg, "<"); | 679 | result.config.from_arg = optarg + strspn(optarg, "<"); |
| 558 | from_arg = strndup(from_arg, strcspn(from_arg, ">")); | 680 | result.config.from_arg = |
| 559 | send_mail_from = 1; | 681 | strndup(result.config.from_arg, strcspn(result.config.from_arg, ">")); |
| 682 | result.config.send_mail_from = true; | ||
| 560 | break; | 683 | break; |
| 561 | case 'A': | 684 | case 'A': |
| 562 | authtype = optarg; | 685 | result.config.authtype = optarg; |
| 563 | use_ehlo = true; | 686 | result.config.use_ehlo = true; |
| 564 | break; | 687 | break; |
| 565 | case 'U': | 688 | case 'U': |
| 566 | authuser = optarg; | 689 | result.config.authuser = optarg; |
| 567 | break; | 690 | break; |
| 568 | case 'P': | 691 | case 'P': |
| 569 | authpass = optarg; | 692 | result.config.authpass = optarg; |
| 570 | break; | 693 | break; |
| 571 | case 'e': /* server expect string on 220 */ | 694 | case 'e': /* server expect string on 220 */ |
| 572 | server_expect = optarg; | 695 | result.config.server_expect = optarg; |
| 573 | break; | 696 | break; |
| 574 | case 'C': /* commands */ | 697 | case 'C': /* commands */ |
| 575 | if (ncommands >= command_size) { | 698 | if (result.config.ncommands >= command_size) { |
| 576 | command_size+=8; | 699 | command_size += 8; |
| 577 | commands = realloc (commands, sizeof(char *) * command_size); | 700 | result.config.commands = |
| 578 | if (commands == NULL) | 701 | realloc(result.config.commands, sizeof(char *) * command_size); |
| 579 | die (STATE_UNKNOWN, | 702 | if (result.config.commands == NULL) { |
| 580 | _("Could not realloc() units [%d]\n"), ncommands); | 703 | die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"), |
| 704 | result.config.ncommands); | ||
| 705 | } | ||
| 581 | } | 706 | } |
| 582 | commands[ncommands] = (char *) malloc (sizeof(char) * 255); | 707 | result.config.commands[result.config.ncommands] = (char *)malloc(sizeof(char) * 255); |
| 583 | strncpy (commands[ncommands], optarg, 255); | 708 | strncpy(result.config.commands[result.config.ncommands], optarg, 255); |
| 584 | ncommands++; | 709 | result.config.ncommands++; |
| 585 | break; | 710 | break; |
| 586 | case 'R': /* server responses */ | 711 | case 'R': /* server responses */ |
| 587 | if (nresponses >= response_size) { | 712 | if (result.config.nresponses >= response_size) { |
| 588 | response_size += 8; | 713 | response_size += 8; |
| 589 | responses = realloc (responses, sizeof(char *) * response_size); | 714 | result.config.responses = |
| 590 | if (responses == NULL) | 715 | realloc(result.config.responses, sizeof(char *) * response_size); |
| 591 | die (STATE_UNKNOWN, | 716 | if (result.config.responses == NULL) { |
| 592 | _("Could not realloc() units [%d]\n"), nresponses); | 717 | die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"), |
| 718 | result.config.nresponses); | ||
| 719 | } | ||
| 593 | } | 720 | } |
| 594 | responses[nresponses] = (char *) malloc (sizeof(char) * 255); | 721 | result.config.responses[result.config.nresponses] = (char *)malloc(sizeof(char) * 255); |
| 595 | strncpy (responses[nresponses], optarg, 255); | 722 | strncpy(result.config.responses[result.config.nresponses], optarg, 255); |
| 596 | nresponses++; | 723 | result.config.nresponses++; |
| 597 | break; | 724 | break; |
| 598 | case 'c': /* critical time threshold */ | 725 | case 'c': /* critical time threshold */ { |
| 599 | if (!is_nonnegative (optarg)) | 726 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 600 | usage4 (_("Critical time must be a positive")); | 727 | if (tmp.error != MP_PARSING_SUCCES) { |
| 601 | else { | 728 | die(STATE_UNKNOWN, "failed to parse critical time threshold"); |
| 602 | critical_time = strtod (optarg, NULL); | ||
| 603 | check_critical_time = true; | ||
| 604 | } | 729 | } |
| 605 | break; | 730 | result.config.connection_time = |
| 606 | case 'w': /* warning time threshold */ | 731 | mp_thresholds_set_warn(result.config.connection_time, tmp.range); |
| 607 | if (!is_nonnegative (optarg)) | 732 | } break; |
| 608 | usage4 (_("Warning time must be a positive")); | 733 | case 'w': /* warning time threshold */ { |
| 609 | else { | 734 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 610 | warning_time = strtod (optarg, NULL); | 735 | if (tmp.error != MP_PARSING_SUCCES) { |
| 611 | check_warning_time = true; | 736 | die(STATE_UNKNOWN, "failed to parse warning time threshold"); |
| 612 | } | 737 | } |
| 613 | break; | 738 | result.config.connection_time = |
| 614 | case 'v': /* verbose */ | 739 | mp_thresholds_set_crit(result.config.connection_time, tmp.range); |
| 740 | } break; | ||
| 741 | case 'v': /* verbose */ | ||
| 615 | verbose++; | 742 | verbose++; |
| 616 | break; | 743 | break; |
| 617 | case 'q': | 744 | case 'q': |
| 618 | ignore_send_quit_failure = true; /* ignore problem sending QUIT */ | 745 | result.config.ignore_send_quit_failure = true; /* ignore problem sending QUIT */ |
| 619 | break; | 746 | break; |
| 620 | case 't': /* timeout */ | 747 | case 't': /* timeout */ |
| 621 | if (is_intnonneg (optarg)) { | 748 | if (is_intnonneg(optarg)) { |
| 622 | socket_timeout = atoi (optarg); | 749 | socket_timeout = atoi(optarg); |
| 623 | } | 750 | } else { |
| 624 | else { | 751 | usage4(_("Timeout interval must be a positive integer")); |
| 625 | usage4 (_("Timeout interval must be a positive integer")); | ||
| 626 | } | 752 | } |
| 627 | break; | 753 | break; |
| 628 | case 'D': | 754 | case 'D': { |
| 629 | /* Check SSL cert validity */ | 755 | /* Check SSL cert validity */ |
| 630 | #ifdef USE_OPENSSL | 756 | #ifdef USE_OPENSSL |
| 631 | if ((temp=strchr(optarg,','))!=NULL) { | 757 | char *temp; |
| 632 | *temp='\0'; | 758 | if ((temp = strchr(optarg, ',')) != NULL) { |
| 633 | if (!is_intnonneg (optarg)) | 759 | *temp = '\0'; |
| 634 | usage2 ("Invalid certificate expiration period", optarg); | 760 | if (!is_intnonneg(optarg)) { |
| 635 | days_till_exp_warn = atoi(optarg); | 761 | usage2("Invalid certificate expiration period", optarg); |
| 636 | *temp=','; | 762 | } |
| 637 | temp++; | 763 | result.config.days_till_exp_warn = atoi(optarg); |
| 638 | if (!is_intnonneg (temp)) | 764 | *temp = ','; |
| 639 | usage2 (_("Invalid certificate expiration period"), temp); | 765 | temp++; |
| 640 | days_till_exp_crit = atoi (temp); | 766 | if (!is_intnonneg(temp)) { |
| 641 | } | 767 | usage2(_("Invalid certificate expiration period"), temp); |
| 642 | else { | 768 | } |
| 643 | days_till_exp_crit=0; | 769 | result.config.days_till_exp_crit = atoi(temp); |
| 644 | if (!is_intnonneg (optarg)) | 770 | } else { |
| 645 | usage2 ("Invalid certificate expiration period", optarg); | 771 | result.config.days_till_exp_crit = 0; |
| 646 | days_till_exp_warn = atoi (optarg); | 772 | if (!is_intnonneg(optarg)) { |
| 647 | } | 773 | usage2("Invalid certificate expiration period", optarg); |
| 648 | check_cert = true; | 774 | } |
| 649 | ignore_send_quit_failure = true; | 775 | result.config.days_till_exp_warn = atoi(optarg); |
| 776 | } | ||
| 777 | result.config.ignore_send_quit_failure = true; | ||
| 650 | #else | 778 | #else |
| 651 | usage (_("SSL support not available - install OpenSSL and recompile")); | 779 | usage(_("SSL support not available - install OpenSSL and recompile")); |
| 652 | #endif | 780 | #endif |
| 653 | implicit_tls = true; | 781 | implicit_tls = true; |
| 654 | // fallthrough | 782 | // fallthrough |
| 655 | case 's': | 783 | case 's': |
| 656 | /* ssl */ | 784 | /* ssl */ |
| 657 | use_ssl = true; | 785 | result.config.use_ssl = true; |
| 658 | server_port = SMTPS_PORT; | 786 | result.config.server_port = SMTPS_PORT; |
| 659 | break; | 787 | break; |
| 660 | case 'S': | 788 | case 'S': |
| 661 | /* starttls */ | 789 | /* starttls */ |
| 662 | use_starttls = true; | 790 | result.config.use_starttls = true; |
| 663 | use_ehlo = true; | 791 | result.config.use_ehlo = true; |
| 664 | break; | 792 | break; |
| 793 | } | ||
| 665 | case SNI_OPTION: | 794 | case SNI_OPTION: |
| 666 | #ifdef HAVE_SSL | 795 | #ifdef HAVE_SSL |
| 667 | use_sni = true; | 796 | result.config.use_sni = true; |
| 668 | #else | 797 | #else |
| 669 | usage (_("SSL support not available - install OpenSSL and recompile")); | 798 | usage(_("SSL support not available - install OpenSSL and recompile")); |
| 670 | #endif | 799 | #endif |
| 671 | break; | 800 | break; |
| 672 | case 'r': | 801 | case 'r': |
| 673 | use_proxy_prefix = true; | 802 | result.config.use_proxy_prefix = true; |
| 674 | break; | 803 | break; |
| 675 | case 'L': | 804 | case 'L': |
| 676 | use_lhlo = true; | 805 | result.config.use_lhlo = true; |
| 677 | break; | 806 | break; |
| 678 | case '4': | 807 | case '4': |
| 679 | address_family = AF_INET; | 808 | address_family = AF_INET; |
| @@ -682,102 +811,109 @@ process_arguments (int argc, char **argv) | |||
| 682 | #ifdef USE_IPV6 | 811 | #ifdef USE_IPV6 |
| 683 | address_family = AF_INET6; | 812 | address_family = AF_INET6; |
| 684 | #else | 813 | #else |
| 685 | usage4 (_("IPv6 support not available")); | 814 | usage4(_("IPv6 support not available")); |
| 686 | #endif | 815 | #endif |
| 687 | break; | 816 | break; |
| 688 | case 'V': /* version */ | 817 | case 'V': /* version */ |
| 689 | print_revision (progname, NP_VERSION); | 818 | print_revision(progname, NP_VERSION); |
| 690 | exit (STATE_UNKNOWN); | 819 | exit(STATE_UNKNOWN); |
| 691 | case 'h': /* help */ | 820 | case 'h': /* help */ |
| 692 | print_help (); | 821 | print_help(); |
| 693 | exit (STATE_UNKNOWN); | 822 | exit(STATE_UNKNOWN); |
| 694 | case '?': /* help */ | 823 | case '?': /* help */ |
| 695 | usage5 (); | 824 | usage5(); |
| 825 | case output_format_index: { | ||
| 826 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 827 | if (!parser.parsing_success) { | ||
| 828 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 829 | printf("Invalid output format: %s\n", optarg); | ||
| 830 | exit(STATE_UNKNOWN); | ||
| 831 | } | ||
| 832 | |||
| 833 | result.config.output_format_is_set = true; | ||
| 834 | result.config.output_format = parser.output_format; | ||
| 835 | break; | ||
| 836 | } | ||
| 837 | case ignore_certificate_expiration_index: { | ||
| 838 | result.config.ignore_certificate_expiration = true; | ||
| 839 | } | ||
| 696 | } | 840 | } |
| 697 | } | 841 | } |
| 698 | 842 | ||
| 699 | c = optind; | 843 | int c = optind; |
| 700 | if (server_address == NULL) { | 844 | if (result.config.server_address == NULL) { |
| 701 | if (argv[c]) { | 845 | if (argv[c]) { |
| 702 | if (is_host (argv[c])) | 846 | if (is_host(argv[c])) { |
| 703 | server_address = argv[c]; | 847 | result.config.server_address = argv[c]; |
| 704 | else | 848 | } else { |
| 705 | usage2 (_("Invalid hostname/address"), argv[c]); | 849 | usage2(_("Invalid hostname/address"), argv[c]); |
| 706 | } | 850 | } |
| 707 | else { | 851 | } else { |
| 708 | xasprintf (&server_address, "127.0.0.1"); | 852 | result.config.server_address = strdup("localhost"); |
| 709 | } | 853 | } |
| 710 | } | 854 | } |
| 711 | 855 | ||
| 712 | if (server_expect == NULL) | 856 | if (result.config.use_starttls && result.config.use_ssl) { |
| 713 | server_expect = strdup (SMTP_EXPECT); | ||
| 714 | |||
| 715 | if (mail_command == NULL) | ||
| 716 | mail_command = strdup("MAIL "); | ||
| 717 | |||
| 718 | if (from_arg==NULL) | ||
| 719 | from_arg = strdup(" "); | ||
| 720 | |||
| 721 | if (use_starttls && use_ssl) { | ||
| 722 | if (implicit_tls) { | 857 | if (implicit_tls) { |
| 723 | use_ssl = false; | 858 | result.config.use_ssl = false; |
| 724 | server_port = SMTP_PORT; | ||
| 725 | } else { | 859 | } else { |
| 726 | usage4 (_("Set either -s/--ssl/--tls or -S/--starttls")); | 860 | usage4(_("Set either -s/--ssl/--tls or -S/--starttls")); |
| 727 | } | 861 | } |
| 728 | } | 862 | } |
| 729 | 863 | ||
| 730 | if (server_port_option != 0) { | 864 | if (server_port_option != 0) { |
| 731 | server_port = server_port_option; | 865 | result.config.server_port = server_port_option; |
| 732 | } | 866 | } |
| 733 | 867 | ||
| 734 | return validate_arguments (); | 868 | if (result.config.authtype) { |
| 735 | } | 869 | if (strcmp(result.config.authtype, "LOGIN") == 0) { |
| 736 | 870 | if (result.config.authuser == NULL) { | |
| 737 | 871 | usage4("no authuser specified"); | |
| 872 | } | ||
| 873 | if (result.config.authpass == NULL) { | ||
| 874 | usage4("no authpass specified"); | ||
| 875 | } | ||
| 876 | } else { | ||
| 877 | usage4("only authtype LOGIN is supported"); | ||
| 878 | } | ||
| 879 | } | ||
| 738 | 880 | ||
| 739 | int | 881 | return result; |
| 740 | validate_arguments (void) | ||
| 741 | { | ||
| 742 | return OK; | ||
| 743 | } | 882 | } |
| 744 | 883 | ||
| 745 | 884 | char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int socket_descriptor, | |
| 746 | void | 885 | bool ssl_established) { |
| 747 | smtp_quit(void) | 886 | int sent_bytes = |
| 748 | { | 887 | my_send(config, SMTP_QUIT, strlen(SMTP_QUIT), socket_descriptor, ssl_established); |
| 749 | int bytes; | 888 | if (sent_bytes < 0) { |
| 750 | int n; | 889 | if (config.ignore_send_quit_failure) { |
| 751 | 890 | if (verbose) { | |
| 752 | n = my_send(SMTP_QUIT, strlen(SMTP_QUIT)); | ||
| 753 | if(n < 0) { | ||
| 754 | if(ignore_send_quit_failure) { | ||
| 755 | if(verbose) { | ||
| 756 | printf(_("Connection closed by server before sending QUIT command\n")); | 891 | printf(_("Connection closed by server before sending QUIT command\n")); |
| 757 | } | 892 | } |
| 758 | return; | 893 | return buffer; |
| 759 | } | 894 | } |
| 760 | die (STATE_UNKNOWN, | 895 | die(STATE_UNKNOWN, _("Connection closed by server before sending QUIT command\n")); |
| 761 | _("Connection closed by server before sending QUIT command\n")); | ||
| 762 | } | 896 | } |
| 763 | 897 | ||
| 764 | if (verbose) | 898 | if (verbose) { |
| 765 | printf(_("sent %s\n"), "QUIT"); | 899 | printf(_("sent %s\n"), "QUIT"); |
| 900 | } | ||
| 766 | 901 | ||
| 767 | /* read the response but don't care about problems */ | 902 | /* read the response but don't care about problems */ |
| 768 | bytes = recvlines(buffer, MAX_INPUT_BUFFER); | 903 | int bytes = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established); |
| 769 | if (verbose) { | 904 | if (verbose) { |
| 770 | if (bytes < 0) | 905 | if (bytes < 0) { |
| 771 | printf(_("recv() failed after QUIT.")); | 906 | printf(_("recv() failed after QUIT.")); |
| 772 | else if (bytes == 0) | 907 | } else if (bytes == 0) { |
| 773 | printf(_("Connection reset by peer.")); | 908 | printf(_("Connection reset by peer.")); |
| 774 | else { | 909 | } else { |
| 775 | buffer[bytes] = '\0'; | 910 | buffer[bytes] = '\0'; |
| 776 | printf(_("received %s\n"), buffer); | 911 | printf(_("received %s\n"), buffer); |
| 777 | } | 912 | } |
| 778 | } | 913 | } |
| 779 | } | ||
| 780 | 914 | ||
| 915 | return buffer; | ||
| 916 | } | ||
| 781 | 917 | ||
| 782 | /* | 918 | /* |
| 783 | * Receive one line, copy it into buf and nul-terminate it. Returns the | 919 | * Receive one line, copy it into buf and nul-terminate it. Returns the |
| @@ -788,24 +924,23 @@ smtp_quit(void) | |||
| 788 | * function which buffers the data, move that to netutils.c and change | 924 | * function which buffers the data, move that to netutils.c and change |
| 789 | * check_smtp and other plugins to use that. Also, remove (\r)\n. | 925 | * check_smtp and other plugins to use that. Also, remove (\r)\n. |
| 790 | */ | 926 | */ |
| 791 | int | 927 | int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor, |
| 792 | recvline(char *buf, size_t bufsize) | 928 | bool ssl_established) { |
| 793 | { | ||
| 794 | int result; | 929 | int result; |
| 795 | unsigned i; | 930 | size_t counter; |
| 796 | 931 | ||
| 797 | for (i = result = 0; i < bufsize - 1; i++) { | 932 | for (counter = result = 0; counter < bufsize - 1; counter++) { |
| 798 | if ((result = my_recv(&buf[i], 1)) != 1) | 933 | if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) { |
| 799 | break; | 934 | break; |
| 800 | if (buf[i] == '\n') { | 935 | } |
| 801 | buf[++i] = '\0'; | 936 | if (buf[counter] == '\n') { |
| 802 | return i; | 937 | buf[++counter] = '\0'; |
| 938 | return (int)counter; | ||
| 803 | } | 939 | } |
| 804 | } | 940 | } |
| 805 | return (result == 1 || i == 0) ? -2 : result; /* -2 if out of space */ | 941 | return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */ |
| 806 | } | 942 | } |
| 807 | 943 | ||
| 808 | |||
| 809 | /* | 944 | /* |
| 810 | * Receive one or more lines, copy them into buf and nul-terminate it. Returns | 945 | * Receive one or more lines, copy them into buf and nul-terminate it. Returns |
| 811 | * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on | 946 | * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on |
| @@ -820,117 +955,114 @@ recvline(char *buf, size_t bufsize) | |||
| 820 | * | 955 | * |
| 821 | * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n. | 956 | * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n. |
| 822 | */ | 957 | */ |
| 823 | int | 958 | int recvlines(check_smtp_config config, char *buf, size_t bufsize, int socket_descriptor, |
| 824 | recvlines(char *buf, size_t bufsize) | 959 | bool ssl_established) { |
| 825 | { | 960 | int result; |
| 826 | int result, i; | 961 | int counter; |
| 827 | 962 | ||
| 828 | for (i = 0; /* forever */; i += result) | 963 | for (counter = 0; /* forever */; counter += result) { |
| 829 | if (!((result = recvline(buf + i, bufsize - i)) > 3 && | 964 | if (!((result = recvline(buf + counter, bufsize - counter, config, socket_descriptor, |
| 830 | isdigit((int)buf[i]) && | 965 | ssl_established)) > 3 && |
| 831 | isdigit((int)buf[i + 1]) && | 966 | isdigit((int)buf[counter]) && isdigit((int)buf[counter + 1]) && |
| 832 | isdigit((int)buf[i + 2]) && | 967 | isdigit((int)buf[counter + 2]) && buf[counter + 3] == '-')) { |
| 833 | buf[i + 3] == '-')) | ||
| 834 | break; | 968 | break; |
| 969 | } | ||
| 970 | } | ||
| 835 | 971 | ||
| 836 | return (result <= 0) ? result : result + i; | 972 | return (result <= 0) ? result : result + counter; |
| 837 | } | 973 | } |
| 838 | 974 | ||
| 839 | 975 | int my_close(int socket_descriptor) { | |
| 840 | int | ||
| 841 | my_close (void) | ||
| 842 | { | ||
| 843 | int result; | 976 | int result; |
| 844 | result = close(sd); | 977 | result = close(socket_descriptor); |
| 845 | #ifdef HAVE_SSL | 978 | #ifdef HAVE_SSL |
| 846 | np_net_ssl_cleanup(); | 979 | np_net_ssl_cleanup(); |
| 847 | #endif | 980 | #endif |
| 848 | return result; | 981 | return result; |
| 849 | } | 982 | } |
| 850 | 983 | ||
| 851 | 984 | void print_help(void) { | |
| 852 | void | ||
| 853 | print_help (void) | ||
| 854 | { | ||
| 855 | char *myport; | 985 | char *myport; |
| 856 | xasprintf (&myport, "%d", SMTP_PORT); | 986 | xasprintf(&myport, "%d", SMTP_PORT); |
| 857 | 987 | ||
| 858 | print_revision (progname, NP_VERSION); | 988 | print_revision(progname, NP_VERSION); |
| 859 | 989 | ||
| 860 | printf ("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n"); | 990 | printf("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n"); |
| 861 | printf (COPYRIGHT, copyright, email); | 991 | printf(COPYRIGHT, copyright, email); |
| 862 | 992 | ||
| 863 | printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host.")); | 993 | printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host.")); |
| 864 | 994 | ||
| 865 | printf ("\n\n"); | 995 | printf("\n\n"); |
| 866 | 996 | ||
| 867 | print_usage (); | 997 | print_usage(); |
| 868 | 998 | ||
| 869 | printf (UT_HELP_VRSN); | 999 | printf(UT_HELP_VRSN); |
| 870 | printf (UT_EXTRA_OPTS); | 1000 | printf(UT_EXTRA_OPTS); |
| 871 | 1001 | ||
| 872 | printf (UT_HOST_PORT, 'p', myport); | 1002 | printf(UT_HOST_PORT, 'p', myport); |
| 873 | 1003 | ||
| 874 | printf (UT_IPv46); | 1004 | printf(UT_IPv46); |
| 875 | 1005 | ||
| 876 | printf (" %s\n", "-e, --expect=STRING"); | 1006 | printf(" %s\n", "-e, --expect=STRING"); |
| 877 | printf (_(" String to expect in first line of server response (default: '%s')\n"), SMTP_EXPECT); | 1007 | printf(_(" String to expect in first line of server response (default: '%s')\n"), |
| 878 | printf (" %s\n", "-C, --command=STRING"); | 1008 | SMTP_EXPECT); |
| 879 | printf (" %s\n", _("SMTP command (may be used repeatedly)")); | 1009 | printf(" %s\n", "-C, --command=STRING"); |
| 880 | printf (" %s\n", "-R, --response=STRING"); | 1010 | printf(" %s\n", _("SMTP command (may be used repeatedly)")); |
| 881 | printf (" %s\n", _("Expected response to command (may be used repeatedly)")); | 1011 | printf(" %s\n", "-R, --response=STRING"); |
| 882 | printf (" %s\n", "-f, --from=STRING"); | 1012 | printf(" %s\n", _("Expected response to command (may be used repeatedly)")); |
| 883 | printf (" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), | 1013 | printf(" %s\n", "-f, --from=STRING"); |
| 884 | printf (" %s\n", "-F, --fqdn=STRING"); | 1014 | printf(" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), |
| 885 | printf (" %s\n", _("FQDN used for HELO")); | 1015 | printf(" %s\n", "-F, --fqdn=STRING"); |
| 886 | printf (" %s\n", "-r, --proxy"); | 1016 | printf(" %s\n", _("FQDN used for HELO")); |
| 887 | printf (" %s\n", _("Use PROXY protocol prefix for the connection.")); | 1017 | printf(" %s\n", "-r, --proxy"); |
| 1018 | printf(" %s\n", _("Use PROXY protocol prefix for the connection.")); | ||
| 888 | #ifdef HAVE_SSL | 1019 | #ifdef HAVE_SSL |
| 889 | printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); | 1020 | printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); |
| 890 | printf (" %s\n", _("Minimum number of days a certificate has to be valid.")); | 1021 | printf(" %s\n", _("Minimum number of days a certificate has to be valid.")); |
| 891 | printf (" %s\n", "-s, --ssl, --tls"); | 1022 | printf(" %s\n", "-s, --ssl, --tls"); |
| 892 | printf (" %s\n", _("Use SSL/TLS for the connection.")); | 1023 | printf(" %s\n", _("Use SSL/TLS for the connection.")); |
| 893 | printf (_(" Sets default port to %d.\n"), SMTPS_PORT); | 1024 | printf(_(" Sets default port to %d.\n"), SMTPS_PORT); |
| 894 | printf (" %s\n", "-S, --starttls"); | 1025 | printf(" %s\n", "-S, --starttls"); |
| 895 | printf (" %s\n", _("Use STARTTLS for the connection.")); | 1026 | printf(" %s\n", _("Use STARTTLS for the connection.")); |
| 896 | printf (" %s\n", "--sni"); | 1027 | printf(" %s\n", "--sni"); |
| 897 | printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); | 1028 | printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); |
| 898 | #endif | 1029 | #endif |
| 899 | 1030 | ||
| 900 | printf (" %s\n", "-A, --authtype=STRING"); | 1031 | printf(" %s\n", "-A, --authtype=STRING"); |
| 901 | printf (" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)")); | 1032 | printf(" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)")); |
| 902 | printf (" %s\n", "-U, --authuser=STRING"); | 1033 | printf(" %s\n", "-U, --authuser=STRING"); |
| 903 | printf (" %s\n", _("SMTP AUTH username")); | 1034 | printf(" %s\n", _("SMTP AUTH username")); |
| 904 | printf (" %s\n", "-P, --authpass=STRING"); | 1035 | printf(" %s\n", "-P, --authpass=STRING"); |
| 905 | printf (" %s\n", _("SMTP AUTH password")); | 1036 | printf(" %s\n", _("SMTP AUTH password")); |
| 906 | printf (" %s\n", "-L, --lmtp"); | 1037 | printf(" %s\n", "-L, --lmtp"); |
| 907 | printf (" %s\n", _("Send LHLO instead of HELO/EHLO")); | 1038 | printf(" %s\n", _("Send LHLO instead of HELO/EHLO")); |
| 908 | printf (" %s\n", "-q, --ignore-quit-failure"); | 1039 | printf(" %s\n", "-q, --ignore-quit-failure"); |
| 909 | printf (" %s\n", _("Ignore failure when sending QUIT command to server")); | 1040 | printf(" %s\n", _("Ignore failure when sending QUIT command to server")); |
| 910 | 1041 | printf(" %s\n", "--ignore-certificate-expiration"); | |
| 911 | printf (UT_WARN_CRIT); | 1042 | printf(" %s\n", _("Ignore certificate expiration")); |
| 912 | |||
| 913 | printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
| 914 | 1043 | ||
| 915 | printf (UT_VERBOSE); | 1044 | printf(UT_WARN_CRIT); |
| 916 | 1045 | ||
| 917 | printf("\n"); | 1046 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 918 | printf ("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); | ||
| 919 | printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful")); | ||
| 920 | printf ("%s\n", _("connects, but incorrect response messages from the host result in")); | ||
| 921 | printf ("%s\n", _("STATE_WARNING return values.")); | ||
| 922 | 1047 | ||
| 923 | printf (UT_SUPPORT); | 1048 | printf(UT_OUTPUT_FORMAT); |
| 924 | } | ||
| 925 | 1049 | ||
| 1050 | printf(UT_VERBOSE); | ||
| 926 | 1051 | ||
| 1052 | printf("\n"); | ||
| 1053 | printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); | ||
| 1054 | printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful")); | ||
| 1055 | printf("%s\n", _("connects, but incorrect response messages from the host result in")); | ||
| 1056 | printf("%s\n", _("STATE_WARNING return values.")); | ||
| 927 | 1057 | ||
| 928 | void | 1058 | printf(UT_SUPPORT); |
| 929 | print_usage (void) | ||
| 930 | { | ||
| 931 | printf ("%s\n", _("Usage:")); | ||
| 932 | printf ("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", progname); | ||
| 933 | printf ("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n"); | ||
| 934 | printf ("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] [-v] \n"); | ||
| 935 | } | 1059 | } |
| 936 | 1060 | ||
| 1061 | void print_usage(void) { | ||
| 1062 | printf("%s\n", _("Usage:")); | ||
| 1063 | printf("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", | ||
| 1064 | progname); | ||
| 1065 | printf("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n"); | ||
| 1066 | printf("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] " | ||
| 1067 | "[-v] \n"); | ||
| 1068 | } | ||
diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h new file mode 100644 index 00000000..b0d42ed1 --- /dev/null +++ b/plugins/check_smtp.d/config.h | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "thresholds.h" | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <string.h> | ||
| 8 | |||
| 9 | enum { | ||
| 10 | SMTP_PORT = 25, | ||
| 11 | SMTPS_PORT = 465 | ||
| 12 | }; | ||
| 13 | |||
| 14 | #define SMTP_EXPECT "220" | ||
| 15 | |||
| 16 | typedef struct { | ||
| 17 | int server_port; | ||
| 18 | char *server_address; | ||
| 19 | char *localhostname; | ||
| 20 | char *server_expect; | ||
| 21 | bool ignore_send_quit_failure; | ||
| 22 | |||
| 23 | mp_thresholds connection_time; | ||
| 24 | |||
| 25 | bool use_ehlo; | ||
| 26 | bool use_lhlo; | ||
| 27 | |||
| 28 | char *from_arg; | ||
| 29 | bool send_mail_from; | ||
| 30 | |||
| 31 | unsigned long ncommands; | ||
| 32 | char **commands; | ||
| 33 | |||
| 34 | unsigned long nresponses; | ||
| 35 | char **responses; | ||
| 36 | |||
| 37 | char *authtype; | ||
| 38 | char *authuser; | ||
| 39 | char *authpass; | ||
| 40 | |||
| 41 | bool use_proxy_prefix; | ||
| 42 | #ifdef HAVE_SSL | ||
| 43 | int days_till_exp_warn; | ||
| 44 | int days_till_exp_crit; | ||
| 45 | bool use_ssl; | ||
| 46 | bool use_starttls; | ||
| 47 | bool use_sni; | ||
| 48 | |||
| 49 | bool ignore_certificate_expiration; | ||
| 50 | #endif | ||
| 51 | |||
| 52 | bool output_format_is_set; | ||
| 53 | mp_output_format output_format; | ||
| 54 | } check_smtp_config; | ||
| 55 | |||
| 56 | check_smtp_config check_smtp_config_init() { | ||
| 57 | check_smtp_config tmp = { | ||
| 58 | .server_port = SMTP_PORT, | ||
| 59 | .server_address = NULL, | ||
| 60 | .localhostname = NULL, | ||
| 61 | |||
| 62 | .server_expect = SMTP_EXPECT, | ||
| 63 | .ignore_send_quit_failure = false, | ||
| 64 | |||
| 65 | .connection_time = mp_thresholds_init(), | ||
| 66 | .use_ehlo = false, | ||
| 67 | .use_lhlo = false, | ||
| 68 | |||
| 69 | .from_arg = strdup(" "), | ||
| 70 | .send_mail_from = false, | ||
| 71 | |||
| 72 | .ncommands = 0, | ||
| 73 | .commands = NULL, | ||
| 74 | |||
| 75 | .nresponses = 0, | ||
| 76 | .responses = NULL, | ||
| 77 | |||
| 78 | .authtype = NULL, | ||
| 79 | .authuser = NULL, | ||
| 80 | .authpass = NULL, | ||
| 81 | |||
| 82 | .use_proxy_prefix = false, | ||
| 83 | #ifdef HAVE_SSL | ||
| 84 | .days_till_exp_warn = 0, | ||
| 85 | .days_till_exp_crit = 0, | ||
| 86 | .use_ssl = false, | ||
| 87 | .use_starttls = false, | ||
| 88 | .use_sni = false, | ||
| 89 | |||
| 90 | .ignore_certificate_expiration = false, | ||
| 91 | #endif | ||
| 92 | |||
| 93 | .output_format_is_set = false, | ||
| 94 | }; | ||
| 95 | return tmp; | ||
| 96 | } | ||
diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index c1d8e2dd..f470d222 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c | |||
| @@ -32,716 +32,492 @@ const char *progname = "check_snmp"; | |||
| 32 | const char *copyright = "1999-2024"; | 32 | const char *copyright = "1999-2024"; |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 33 | const char *email = "devel@monitoring-plugins.org"; |
| 34 | 34 | ||
| 35 | #include "common.h" | 35 | #include "./common.h" |
| 36 | #include "runcmd.h" | 36 | #include "./runcmd.h" |
| 37 | #include "utils.h" | 37 | #include "./utils.h" |
| 38 | #include "utils_cmd.h" | 38 | #include "../lib/states.h" |
| 39 | 39 | ||
| 40 | #define DEFAULT_COMMUNITY "public" | 40 | #include "../lib/utils_base.h" |
| 41 | #define DEFAULT_PORT "161" | 41 | #include "../lib/output.h" |
| 42 | #define DEFAULT_MIBLIST "ALL" | 42 | #include "check_snmp.d/check_snmp_helpers.h" |
| 43 | #define DEFAULT_PROTOCOL "1" | 43 | |
| 44 | #define DEFAULT_RETRIES 5 | 44 | #include <strings.h> |
| 45 | #define DEFAULT_AUTH_PROTOCOL "MD5" | 45 | #include <stdint.h> |
| 46 | #define DEFAULT_PRIV_PROTOCOL "DES" | 46 | |
| 47 | #define DEFAULT_DELIMITER "=" | 47 | #include "check_snmp.d/config.h" |
| 48 | #define DEFAULT_OUTPUT_DELIMITER " " | 48 | #include <stdlib.h> |
| 49 | #define DEFAULT_BUFFER_SIZE 100 | 49 | #include <arpa/inet.h> |
| 50 | 50 | #include <net-snmp/library/parse.h> | |
| 51 | #define mark(a) ((a) != 0 ? "*" : "") | 51 | #include <net-snmp/net-snmp-config.h> |
| 52 | 52 | #include <net-snmp/net-snmp-includes.h> | |
| 53 | #define CHECK_UNDEF 0 | 53 | #include <net-snmp/library/snmp.h> |
| 54 | #define CRIT_PRESENT 1 | 54 | #include <net-snmp/library/keytools.h> |
| 55 | #define CRIT_STRING 2 | 55 | #include <net-snmp/library/snmp_api.h> |
| 56 | #define CRIT_REGEX 4 | 56 | #include <net-snmp/session_api.h> |
| 57 | #define WARN_PRESENT 8 | 57 | #include <net-snmp/definitions.h> |
| 58 | 58 | #include <net-snmp/library/asn1.h> | |
| 59 | #define OID_COUNT_STEP 8 | 59 | #include <net-snmp/mib_api.h> |
| 60 | 60 | #include <net-snmp/library/snmp_impl.h> | |
| 61 | /* Longopts only arguments */ | 61 | #include <string.h> |
| 62 | #define L_CALCULATE_RATE CHAR_MAX + 1 | 62 | #include "../gl/regex.h" |
| 63 | #define L_RATE_MULTIPLIER CHAR_MAX + 2 | 63 | #include "../gl/base64.h" |
| 64 | #define L_INVERT_SEARCH CHAR_MAX + 3 | 64 | #include <assert.h> |
| 65 | #define L_OFFSET CHAR_MAX + 4 | 65 | |
| 66 | #define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 | 66 | const char DEFAULT_COMMUNITY[] = "public"; |
| 67 | 67 | const char DEFAULT_MIBLIST[] = "ALL"; | |
| 68 | /* Gobble to string - stop incrementing c when c[0] match one of the | 68 | #define DEFAULT_AUTH_PROTOCOL "MD5" |
| 69 | * characters in s */ | 69 | |
| 70 | #define GOBBLE_TOS(c, s) \ | 70 | #ifdef HAVE_USM_DES_PRIV_PROTOCOL |
| 71 | while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \ | 71 | # define DEFAULT_PRIV_PROTOCOL "DES" |
| 72 | c++; \ | 72 | #else |
| 73 | # define DEFAULT_PRIV_PROTOCOL "AES" | ||
| 74 | #endif | ||
| 75 | |||
| 76 | typedef struct proces_arguments_wrapper { | ||
| 77 | int errorcode; | ||
| 78 | check_snmp_config config; | ||
| 79 | } process_arguments_wrapper; | ||
| 80 | |||
| 81 | static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 82 | static char *trim_whitespaces_and_check_quoting(char *str); | ||
| 83 | static char *get_next_argument(char *str); | ||
| 84 | void print_usage(void); | ||
| 85 | void print_help(void); | ||
| 86 | |||
| 87 | int verbose = 0; | ||
| 88 | |||
| 89 | typedef struct { | ||
| 90 | int errorcode; | ||
| 91 | char *state_string; | ||
| 92 | } gen_state_string_type; | ||
| 93 | gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) { | ||
| 94 | char *encoded_string = NULL; | ||
| 95 | gen_state_string_type result = {.errorcode = OK, .state_string = NULL}; | ||
| 96 | |||
| 97 | if (verbose > 1) { | ||
| 98 | printf("%s:\n", __FUNCTION__); | ||
| 99 | for (size_t i = 0; i < num_of_entries; i++) { | ||
| 100 | printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp)); | ||
| 101 | switch (entries[i].type) { | ||
| 102 | case ASN_GAUGE: | ||
| 103 | printf("Type GAUGE\n"); | ||
| 104 | break; | ||
| 105 | case ASN_TIMETICKS: | ||
| 106 | printf("Type TIMETICKS\n"); | ||
| 107 | break; | ||
| 108 | case ASN_COUNTER: | ||
| 109 | printf("Type COUNTER\n"); | ||
| 110 | break; | ||
| 111 | case ASN_UINTEGER: | ||
| 112 | printf("Type UINTEGER\n"); | ||
| 113 | break; | ||
| 114 | case ASN_COUNTER64: | ||
| 115 | printf("Type COUNTER64\n"); | ||
| 116 | break; | ||
| 117 | case ASN_FLOAT: | ||
| 118 | printf("Type FLOAT\n"); | ||
| 119 | case ASN_DOUBLE: | ||
| 120 | printf("Type DOUBLE\n"); | ||
| 121 | break; | ||
| 122 | case ASN_INTEGER: | ||
| 123 | printf("Type INTEGER\n"); | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | |||
| 127 | switch (entries[i].type) { | ||
| 128 | case ASN_GAUGE: | ||
| 129 | case ASN_TIMETICKS: | ||
| 130 | case ASN_COUNTER: | ||
| 131 | case ASN_UINTEGER: | ||
| 132 | case ASN_COUNTER64: | ||
| 133 | printf("Value %llu\n", entries[i].value.uIntVal); | ||
| 134 | break; | ||
| 135 | case ASN_FLOAT: | ||
| 136 | case ASN_DOUBLE: | ||
| 137 | printf("Value %f\n", entries[i].value.doubleVal); | ||
| 138 | break; | ||
| 139 | case ASN_INTEGER: | ||
| 140 | printf("Value %lld\n", entries[i].value.intVal); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | idx_t encoded = base64_encode_alloc((const char *)entries, | ||
| 147 | (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)), | ||
| 148 | &encoded_string); | ||
| 149 | |||
| 150 | if (encoded > 0 && encoded_string != NULL) { | ||
| 151 | // success | ||
| 152 | if (verbose > 1) { | ||
| 153 | printf("encoded string: %s\n", encoded_string); | ||
| 154 | printf("encoded string length: %lu\n", strlen(encoded_string)); | ||
| 155 | } | ||
| 156 | result.state_string = encoded_string; | ||
| 157 | return result; | ||
| 73 | } | 158 | } |
| 74 | /* Given c, keep track of backslashes (bk) and double-quotes (dq) | 159 | result.errorcode = ERROR; |
| 75 | * from c[0] */ | 160 | return result; |
| 76 | #define COUNT_SEQ(c, bk, dq) \ | 161 | } |
| 77 | switch (c[0]) { \ | 162 | |
| 78 | case '\\': \ | 163 | typedef struct { |
| 79 | if (bk) \ | 164 | int errorcode; |
| 80 | bk--; \ | 165 | check_snmp_state_entry *state; |
| 81 | else \ | 166 | } recover_state_data_type; |
| 82 | bk++; \ | 167 | recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) { |
| 83 | break; \ | 168 | recover_state_data_type result = {.errorcode = OK, .state = NULL}; |
| 84 | case '"': \ | 169 | |
| 85 | if (!dq) { \ | 170 | if (verbose > 1) { |
| 86 | dq++; \ | 171 | printf("%s:\n", __FUNCTION__); |
| 87 | } else if (!bk) { \ | 172 | printf("State string: %s\n", state_string); |
| 88 | dq--; \ | 173 | printf("State string length: %lu\n", state_string_length); |
| 89 | } else { \ | ||
| 90 | bk--; \ | ||
| 91 | } \ | ||
| 92 | break; \ | ||
| 93 | } | 174 | } |
| 94 | 175 | ||
| 95 | static int process_arguments(int, char **); | 176 | idx_t outlen = 0; |
| 96 | static int validate_arguments(void); | 177 | bool decoded = |
| 97 | static char *thisarg(char *str); | 178 | base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen); |
| 98 | static char *nextarg(char *str); | 179 | |
| 99 | void print_usage(void); | 180 | if (!decoded) { |
| 100 | static void print_help(void); | 181 | if (verbose) { |
| 101 | static char *multiply(char *str); | 182 | printf("Failed to decode state string\n"); |
| 102 | 183 | } | |
| 103 | #include "regex.h" | 184 | // failure to decode |
| 104 | static char regex_expect[MAX_INPUT_BUFFER] = ""; | 185 | result.errorcode = ERROR; |
| 105 | static regex_t preg; | 186 | return result; |
| 106 | static regmatch_t pmatch[10]; | 187 | } |
| 107 | static char errbuf[MAX_INPUT_BUFFER] = ""; | 188 | |
| 108 | static char perfstr[MAX_INPUT_BUFFER] = "| "; | 189 | if (result.state == NULL) { |
| 109 | static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; | 190 | // Memory Error? |
| 110 | static int eflags = 0; | 191 | result.errorcode = ERROR; |
| 111 | static int errcode, excode; | 192 | return result; |
| 112 | 193 | } | |
| 113 | static char *server_address = NULL; | 194 | |
| 114 | static char *community = NULL; | 195 | if (verbose > 1) { |
| 115 | static char **contextargs = NULL; | 196 | printf("Recovered %lu entries of size %lu\n", |
| 116 | static char *context = NULL; | 197 | (size_t)outlen / sizeof(check_snmp_state_entry), outlen); |
| 117 | static char **authpriv = NULL; | 198 | |
| 118 | static char *proto = NULL; | 199 | for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) { |
| 119 | static char *seclevel = NULL; | 200 | printf("Entry timestamp %lu: %s", result.state[i].timestamp, |
| 120 | static char *secname = NULL; | 201 | ctime(&result.state[i].timestamp)); |
| 121 | static char *authproto = NULL; | 202 | switch (result.state[i].type) { |
| 122 | static char *privproto = NULL; | 203 | case ASN_GAUGE: |
| 123 | static char *authpasswd = NULL; | 204 | printf("Type GAUGE\n"); |
| 124 | static char *privpasswd = NULL; | 205 | break; |
| 125 | static int nulloid = STATE_UNKNOWN; | 206 | case ASN_TIMETICKS: |
| 126 | static char **oids = NULL; | 207 | printf("Type TIMETICKS\n"); |
| 127 | static size_t oids_size = 0; | 208 | break; |
| 128 | static char *label; | 209 | case ASN_COUNTER: |
| 129 | static char *units; | 210 | printf("Type COUNTER\n"); |
| 130 | static char *port; | 211 | break; |
| 131 | static char *snmpcmd; | 212 | case ASN_UINTEGER: |
| 132 | static char string_value[MAX_INPUT_BUFFER] = ""; | 213 | printf("Type UINTEGER\n"); |
| 133 | static int invert_search = 0; | 214 | break; |
| 134 | static char **labels = NULL; | 215 | case ASN_COUNTER64: |
| 135 | static char **unitv = NULL; | 216 | printf("Type COUNTER64\n"); |
| 136 | static size_t nlabels = 0; | 217 | break; |
| 137 | static size_t labels_size = OID_COUNT_STEP; | 218 | case ASN_FLOAT: |
| 138 | static size_t nunits = 0; | 219 | printf("Type FLOAT\n"); |
| 139 | static size_t unitv_size = OID_COUNT_STEP; | 220 | case ASN_DOUBLE: |
| 140 | static size_t numoids = 0; | 221 | printf("Type DOUBLE\n"); |
| 141 | static int numauthpriv = 0; | 222 | break; |
| 142 | static int numcontext = 0; | 223 | case ASN_INTEGER: |
| 143 | static int verbose = 0; | 224 | printf("Type INTEGER\n"); |
| 144 | static bool usesnmpgetnext = false; | 225 | break; |
| 145 | static char *warning_thresholds = NULL; | 226 | } |
| 146 | static char *critical_thresholds = NULL; | 227 | |
| 147 | static thresholds **thlds; | 228 | switch (result.state[i].type) { |
| 148 | static size_t thlds_size = OID_COUNT_STEP; | 229 | case ASN_GAUGE: |
| 149 | static double *response_value; | 230 | case ASN_TIMETICKS: |
| 150 | static size_t response_size = OID_COUNT_STEP; | 231 | case ASN_COUNTER: |
| 151 | static int retries = 0; | 232 | case ASN_UINTEGER: |
| 152 | static int *eval_method; | 233 | case ASN_COUNTER64: |
| 153 | static size_t eval_size = OID_COUNT_STEP; | 234 | printf("Value %llu\n", result.state[i].value.uIntVal); |
| 154 | static char *delimiter; | 235 | break; |
| 155 | static char *output_delim; | 236 | case ASN_FLOAT: |
| 156 | static char *miblist = NULL; | 237 | case ASN_DOUBLE: |
| 157 | static bool needmibs = false; | 238 | printf("Value %f\n", result.state[i].value.doubleVal); |
| 158 | static int calculate_rate = 0; | 239 | break; |
| 159 | static double offset = 0.0; | 240 | case ASN_INTEGER: |
| 160 | static int rate_multiplier = 1; | 241 | printf("Value %lld\n", result.state[i].value.intVal); |
| 161 | static state_data *previous_state; | 242 | break; |
| 162 | static double *previous_value; | 243 | } |
| 163 | static size_t previous_size = OID_COUNT_STEP; | 244 | } |
| 164 | static int perf_labels = 1; | 245 | } |
| 165 | static char *ip_version = ""; | 246 | |
| 166 | static double multiplier = 1.0; | 247 | return result; |
| 167 | static char *fmtstr = ""; | ||
| 168 | static bool fmtstr_set = false; | ||
| 169 | static char buffer[DEFAULT_BUFFER_SIZE]; | ||
| 170 | static bool ignore_mib_parsing_errors = false; | ||
| 171 | |||
| 172 | static 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 | } | 248 | } |
| 193 | 249 | ||
| 194 | int main(int argc, char **argv) { | 250 | int 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, ""); | 251 | setlocale(LC_ALL, ""); |
| 232 | bindtextdomain(PACKAGE, LOCALEDIR); | 252 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 233 | textdomain(PACKAGE); | 253 | textdomain(PACKAGE); |
| 234 | 254 | ||
| 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; | 255 | timeout_interval = DEFAULT_SOCKET_TIMEOUT; |
| 250 | retries = DEFAULT_RETRIES; | ||
| 251 | 256 | ||
| 252 | np_init((char *)progname, argc, argv); | 257 | np_init((char *)progname, argc, argv); |
| 253 | 258 | ||
| 259 | state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv); | ||
| 260 | |||
| 254 | /* Parse extra opts if any */ | 261 | /* Parse extra opts if any */ |
| 255 | argv = np_extra_opts(&argc, argv, progname); | 262 | argv = np_extra_opts(&argc, argv, progname); |
| 256 | 263 | ||
| 257 | np_set_args(argc, argv); | 264 | np_set_args(argc, argv); |
| 258 | 265 | ||
| 259 | time(¤t_time); | 266 | // Initialize net-snmp before touching the session we are going to use |
| 267 | init_snmp("check_snmp"); | ||
| 260 | 268 | ||
| 261 | if (process_arguments(argc, argv) == ERROR) | 269 | process_arguments_wrapper paw_tmp = process_arguments(argc, argv); |
| 270 | if (paw_tmp.errorcode == ERROR) { | ||
| 262 | usage4(_("Could not parse arguments")); | 271 | 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 | } | 272 | } |
| 285 | 273 | ||
| 286 | /* Populate the thresholds */ | 274 | 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 | 275 | ||
| 317 | /* Create the command array to execute */ | 276 | if (config.output_format_is_set) { |
| 318 | if (usesnmpgetnext) { | 277 | mp_set_format(config.output_format); |
| 319 | snmpcmd = strdup(PATH_TO_SNMPGETNEXT); | ||
| 320 | } else { | ||
| 321 | snmpcmd = strdup(PATH_TO_SNMPGET); | ||
| 322 | } | 278 | } |
| 323 | 279 | ||
| 324 | /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ | 280 | /* Set signal handling and alarm */ |
| 325 | 281 | if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { | |
| 326 | unsigned index = 0; | 282 | 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 | } | 283 | } |
| 347 | 284 | ||
| 348 | for (int i = 0; i < numcontext; i++) { | 285 | time_t current_time; |
| 349 | command_line[index++] = contextargs[i]; | 286 | time(¤t_time); |
| 350 | } | ||
| 351 | 287 | ||
| 352 | for (int i = 0; i < numauthpriv; i++) { | 288 | if (verbose > 2) { |
| 353 | command_line[index++] = authpriv[i]; | 289 | printf("current time: %s (timestamp: %lu)\n", ctime(¤t_time), current_time); |
| 354 | } | 290 | } |
| 355 | 291 | ||
| 356 | xasprintf(&command_line[index++], "%s:%s", server_address, port); | 292 | snmp_responces response = do_snmp_query(config.snmp_params); |
| 357 | 293 | ||
| 358 | xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", cl_hidden_auth, server_address, port); | 294 | mp_check overall = mp_check_init(); |
| 359 | 295 | ||
| 360 | for (size_t i = 0; i < numoids; i++) { | 296 | if (response.errorcode == OK) { |
| 361 | command_line[index++] = oids[i]; | 297 | mp_subcheck sc_successfull_query = mp_subcheck_init(); |
| 362 | xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); | 298 | xasprintf(&sc_successfull_query.output, "SNMP query was successful"); |
| 299 | sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); | ||
| 300 | mp_add_subcheck_to_check(&overall, sc_successfull_query); | ||
| 301 | } else { | ||
| 302 | // Error treatment here, either partial or whole | ||
| 303 | mp_subcheck sc_failed_query = mp_subcheck_init(); | ||
| 304 | xasprintf(&sc_failed_query.output, "SNMP query failed"); | ||
| 305 | sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK); | ||
| 306 | mp_add_subcheck_to_check(&overall, sc_failed_query); | ||
| 307 | mp_exit(overall); | ||
| 363 | } | 308 | } |
| 364 | 309 | ||
| 365 | command_line[index++] = NULL; | 310 | check_snmp_state_entry *prev_state = NULL; |
| 311 | bool have_previous_state = false; | ||
| 366 | 312 | ||
| 367 | if (verbose) { | 313 | if (config.evaluation_params.calculate_rate) { |
| 368 | printf("%s\n", cl_hidden_auth); | 314 | state_data *previous_state = np_state_read(stateKey); |
| 369 | } | 315 | if (previous_state == NULL) { |
| 316 | // failed to recover state | ||
| 317 | // or no previous state | ||
| 318 | have_previous_state = false; | ||
| 319 | } else { | ||
| 320 | // sanity check | ||
| 321 | recover_state_data_type prev_state_wrapper = | ||
| 322 | recover_state_data(previous_state->data, (idx_t)previous_state->length); | ||
| 370 | 323 | ||
| 371 | /* Set signal handling and alarm */ | 324 | if (prev_state_wrapper.errorcode == OK) { |
| 372 | if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { | 325 | have_previous_state = true; |
| 373 | usage4(_("Cannot catch SIGALRM")); | 326 | prev_state = prev_state_wrapper.state; |
| 374 | } | 327 | } else { |
| 375 | alarm(timeout_interval * retries + 5); | 328 | have_previous_state = false; |
| 376 | 329 | 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 | } | 330 | } |
| 397 | } else { | ||
| 398 | printf(_("External command error with no output (return code: %d)\n"), return_code); | ||
| 399 | } | 331 | } |
| 400 | exit(STATE_UNKNOWN); | ||
| 401 | } | 332 | } |
| 402 | 333 | ||
| 403 | if (verbose) { | 334 | check_snmp_state_entry *new_state = NULL; |
| 404 | for (size_t i = 0; i < chld_out.lines; i++) { | 335 | if (config.evaluation_params.calculate_rate) { |
| 405 | printf("%s\n", chld_out.line[i]); | 336 | new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry)); |
| 337 | if (new_state == NULL) { | ||
| 338 | die(STATE_UNKNOWN, "memory allocation failed"); | ||
| 406 | } | 339 | } |
| 407 | } | 340 | } |
| 408 | 341 | ||
| 409 | line = 0; | 342 | // We got the the query results, now process them |
| 410 | total_oids = 0; | 343 | 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++) { | 344 | if (verbose > 0) { |
| 412 | if (calculate_rate) | 345 | 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 | } | 346 | } |
| 426 | 347 | ||
| 427 | /* Clean up type array - Sol10 does not necessarily zero it out */ | 348 | check_snmp_state_entry previous_unit_state = {}; |
| 428 | bzero(type, sizeof(type)); | 349 | if (config.evaluation_params.calculate_rate && have_previous_state) { |
| 429 | 350 | previous_unit_state = prev_state[loop_index]; | |
| 430 | is_counter = 0; | 351 | } |
| 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 | 352 | ||
| 498 | iresult = STATE_DEPENDENT; | 353 | check_snmp_evaluation single_eval = |
| 354 | evaluate_single_unit(response.response_values[loop_index], config.evaluation_params, | ||
| 355 | config.snmp_params.test_units[loop_index], current_time, | ||
| 356 | previous_unit_state, have_previous_state); | ||
| 499 | 357 | ||
| 500 | /* Process this block for numeric comparisons */ | 358 | if (config.evaluation_params.calculate_rate && |
| 501 | /* Make some special values,like Timeticks numeric only if a threshold is defined */ | 359 | mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) { |
| 502 | if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { | 360 | 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 | } | 361 | } |
| 547 | 362 | ||
| 548 | /* Process this block for string matching */ | 363 | mp_add_subcheck_to_check(&overall, single_eval.sc); |
| 549 | else if (eval_size > i && eval_method[i] & CRIT_STRING) { | 364 | } |
| 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 | 365 | ||
| 556 | /* Process this block for regex matching */ | 366 | if (config.evaluation_params.calculate_rate) { |
| 557 | else if (eval_size > i && eval_method[i] & CRIT_REGEX) { | 367 | // store state |
| 558 | excode = regexec(&preg, response, 10, pmatch, eflags); | 368 | gen_state_string_type current_state_wrapper = |
| 559 | if (excode == 0) { | 369 | 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 | 370 | ||
| 570 | /* Process this block for existence-nonexistence checks */ | 371 | if (current_state_wrapper.errorcode == OK) { |
| 571 | /* TV: Should this be outside of this else block? */ | 372 | np_state_write_string(stateKey, current_time, current_state_wrapper.state_string); |
| 572 | else { | 373 | } else { |
| 573 | if (eval_size > i && eval_method[i] & CRIT_PRESENT) | 374 | 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 | } | 375 | } |
| 376 | } | ||
| 377 | mp_exit(overall); | ||
| 378 | } | ||
| 580 | 379 | ||
| 581 | /* Result is the worst outcome of all the OIDs tested */ | 380 | /* process command-line arguments */ |
| 582 | result = max_state(result, iresult); | 381 | static process_arguments_wrapper process_arguments(int argc, char **argv) { |
| 583 | 382 | enum { | |
| 584 | /* Prepend a label for this OID if there is one */ | 383 | /* Longopts only arguments */ |
| 585 | if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) | 384 | 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)); | 385 | offset_index, |
| 587 | else | 386 | ignore_mib_parsing_errors_index, |
| 588 | xasprintf(&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim, mark(iresult), show, mark(iresult)); | 387 | connection_prefix_index, |
| 589 | 388 | output_format_index, | |
| 590 | /* Append a unit string for this OID if there is one */ | 389 | calculate_rate, |
| 591 | if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) | 390 | rate_multiplier |
| 592 | xasprintf(&outbuff, "%s %s", outbuff, unitv[i]); | 391 | }; |
| 593 | 392 | ||
| 594 | /* Write perfdata with whatever can be parsed by strtod, if possible */ | 393 | static struct option longopts[] = { |
| 595 | ptr = NULL; | 394 | STD_LONG_OPTS, |
| 596 | strtod(show, &ptr); | 395 | {"community", required_argument, 0, 'C'}, |
| 597 | if (ptr > show) { | 396 | {"oid", required_argument, 0, 'o'}, |
| 598 | if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) | 397 | {"object", required_argument, 0, 'o'}, |
| 599 | temp_string = labels[i]; | 398 | {"delimiter", required_argument, 0, 'd'}, |
| 600 | else | 399 | {"nulloid", required_argument, 0, 'z'}, |
| 601 | temp_string = oidname; | 400 | {"output-delimiter", required_argument, 0, 'D'}, |
| 602 | if (strpbrk(temp_string, " ='\"") == NULL) { | 401 | {"string", required_argument, 0, 's'}, |
| 603 | strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); | 402 | {"timeout", required_argument, 0, 't'}, |
| 604 | } else { | 403 | {"regex", required_argument, 0, 'r'}, |
| 605 | if (strpbrk(temp_string, "'") == NULL) { | 404 | {"ereg", required_argument, 0, 'r'}, |
| 606 | quote_string = "'"; | 405 | {"eregi", required_argument, 0, 'R'}, |
| 607 | } else { | 406 | {"label", required_argument, 0, 'l'}, |
| 608 | quote_string = "\""; | 407 | {"units", required_argument, 0, 'u'}, |
| 609 | } | 408 | {"port", required_argument, 0, 'p'}, |
| 610 | strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); | 409 | {"retries", required_argument, 0, 'e'}, |
| 611 | strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); | 410 | {"miblist", required_argument, 0, 'm'}, |
| 612 | strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); | 411 | {"protocol", required_argument, 0, 'P'}, |
| 613 | } | 412 | {"context", required_argument, 0, 'N'}, |
| 614 | strncat(perfstr, "=", sizeof(perfstr) - strlen(perfstr) - 1); | 413 | {"seclevel", required_argument, 0, 'L'}, |
| 615 | len = sizeof(perfstr) - strlen(perfstr) - 1; | 414 | {"secname", required_argument, 0, 'U'}, |
| 616 | strncat(perfstr, show, len > ptr - show ? ptr - show : len); | 415 | {"authproto", required_argument, 0, 'a'}, |
| 416 | {"privproto", required_argument, 0, 'x'}, | ||
| 417 | {"authpasswd", required_argument, 0, 'A'}, | ||
| 418 | {"privpasswd", required_argument, 0, 'X'}, | ||
| 419 | {"next", no_argument, 0, 'n'}, | ||
| 420 | {"offset", required_argument, 0, offset_index}, | ||
| 421 | {"invert-search", no_argument, 0, invert_search_index}, | ||
| 422 | {"perf-oids", no_argument, 0, 'O'}, | ||
| 423 | {"ipv4", no_argument, 0, '4'}, | ||
| 424 | {"ipv6", no_argument, 0, '6'}, | ||
| 425 | {"multiplier", required_argument, 0, 'M'}, | ||
| 426 | {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index}, | ||
| 427 | {"connection-prefix", required_argument, 0, connection_prefix_index}, | ||
| 428 | {"output-format", required_argument, 0, output_format_index}, | ||
| 429 | {"rate", no_argument, 0, calculate_rate}, | ||
| 430 | {"rate-multiplier", required_argument, 0, rate_multiplier}, | ||
| 431 | {0, 0, 0, 0}}; | ||
| 432 | |||
| 433 | if (argc < 2) { | ||
| 434 | process_arguments_wrapper result = { | ||
| 435 | .errorcode = ERROR, | ||
| 436 | }; | ||
| 437 | return result; | ||
| 438 | } | ||
| 617 | 439 | ||
| 618 | if (strcmp(type, "") != 0) { | 440 | // Count number of OIDs here first |
| 619 | strncat(perfstr, type, sizeof(perfstr) - strlen(perfstr) - 1); | 441 | int option = 0; |
| 620 | } | 442 | size_t oid_counter = 0; |
| 443 | while (true) { | ||
| 444 | int option_char = getopt_long( | ||
| 445 | argc, argv, | ||
| 446 | "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 | 447 | ||
| 622 | if (warning_thresholds) { | 448 | if (option_char == -1 || option_char == EOF) { |
| 623 | strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); | 449 | break; |
| 624 | if (thlds[i]->warning && thlds[i]->warning->text) | 450 | } |
| 625 | strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr) - strlen(perfstr) - 1); | ||
| 626 | } | ||
| 627 | 451 | ||
| 628 | if (critical_thresholds) { | 452 | switch (option_char) { |
| 629 | if (!warning_thresholds) | 453 | case 'o': { |
| 630 | strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); | 454 | // we are going to parse this again, so we work on a copy of that string |
| 631 | strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); | 455 | char *tmp_oids = strdup(optarg); |
| 632 | if (thlds[i]->critical && thlds[i]->critical->text) | 456 | if (tmp_oids == NULL) { |
| 633 | strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr) - strlen(perfstr) - 1); | 457 | die(STATE_UNKNOWN, "strdup failed"); |
| 634 | } | 458 | } |
| 635 | 459 | ||
| 636 | strncat(perfstr, " ", sizeof(perfstr) - strlen(perfstr) - 1); | 460 | for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL; |
| 637 | } | 461 | 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 | } | 462 | } |
| 659 | strcpy(&state_string[current_length], temp_string); | 463 | break; |
| 660 | current_length = current_length + response_length; | ||
| 661 | state_string[current_length] = ':'; | ||
| 662 | current_length++; | ||
| 663 | free(temp_string); | ||
| 664 | } | 464 | } |
| 665 | state_string[--current_length] = '\0'; | 465 | case '?': /* usage */ |
| 666 | if (verbose > 2) | 466 | usage5(); |
| 667 | printf("State string=%s\n", state_string); | 467 | // fallthrough |
| 468 | case 'h': /* help */ | ||
| 469 | print_help(); | ||
| 470 | exit(STATE_UNKNOWN); | ||
| 471 | case 'V': /* version */ | ||
| 472 | print_revision(progname, NP_VERSION); | ||
| 473 | exit(STATE_UNKNOWN); | ||
| 668 | 474 | ||
| 669 | /* This is not strictly the same as time now, but any subtle variations will cancel out */ | 475 | default: |
| 670 | np_state_write_string(current_time, state_string); | 476 | 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 | } | 477 | } |
| 675 | } | 478 | } |
| 676 | 479 | ||
| 677 | printf("%s %s -%s %s\n", label, state_text(result), outbuff, perfstr); | 480 | /* Check whether at least one OID was given */ |
| 678 | if (mult_resp) | 481 | if (oid_counter == 0) { |
| 679 | printf("%s", mult_resp); | 482 | die(STATE_UNKNOWN, _("No OIDs specified\n")); |
| 483 | } | ||
| 680 | 484 | ||
| 681 | return result; | 485 | // Allocate space for test units |
| 682 | } | 486 | check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit)); |
| 487 | if (tmp == NULL) { | ||
| 488 | die(STATE_UNKNOWN, "Failed to calloc"); | ||
| 489 | } | ||
| 683 | 490 | ||
| 684 | /* process command-line arguments */ | 491 | for (size_t i = 0; i < oid_counter; i++) { |
| 685 | int process_arguments(int argc, char **argv) { | 492 | 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 | } | 493 | } |
| 736 | 494 | ||
| 737 | size_t j = 0; | 495 | check_snmp_config config = check_snmp_config_init(); |
| 738 | size_t jj = 0; | 496 | config.snmp_params.test_units = tmp; |
| 497 | config.snmp_params.num_of_test_units = oid_counter; | ||
| 498 | |||
| 499 | option = 0; | ||
| 500 | optind = 1; // Reset argument scanner | ||
| 501 | size_t tmp_oid_counter = 0; | ||
| 502 | size_t eval_counter = 0; | ||
| 503 | size_t unitv_counter = 0; | ||
| 504 | size_t labels_counter = 0; | ||
| 505 | unsigned char *authpasswd = NULL; | ||
| 506 | unsigned char *privpasswd = NULL; | ||
| 507 | int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; | ||
| 508 | char *port = NULL; | ||
| 509 | char *miblist = NULL; | ||
| 510 | char *connection_prefix = NULL; | ||
| 511 | bool snmp_version_set_explicitely = false; | ||
| 512 | // TODO error checking | ||
| 739 | while (true) { | 513 | while (true) { |
| 740 | int option = 0; | 514 | 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); | 515 | argc, argv, |
| 516 | "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 | 517 | ||
| 743 | if (option_char == -1 || option_char == EOF) | 518 | if (option_char == -1 || option_char == EOF) { |
| 744 | break; | 519 | break; |
| 520 | } | ||
| 745 | 521 | ||
| 746 | switch (option_char) { | 522 | switch (option_char) { |
| 747 | case '?': /* usage */ | 523 | case '?': /* usage */ |
| @@ -758,64 +534,155 @@ int process_arguments(int argc, char **argv) { | |||
| 758 | 534 | ||
| 759 | /* Connection info */ | 535 | /* Connection info */ |
| 760 | case 'C': /* group or community */ | 536 | case 'C': /* group or community */ |
| 761 | community = optarg; | 537 | config.snmp_params.snmp_session.community = (unsigned char *)optarg; |
| 538 | config.snmp_params.snmp_session.community_len = strlen(optarg); | ||
| 762 | break; | 539 | break; |
| 763 | case 'H': /* Host or server */ | 540 | case 'H': /* Host or server */ |
| 764 | server_address = optarg; | 541 | config.snmp_params.snmp_session.peername = optarg; |
| 765 | break; | 542 | break; |
| 766 | case 'p': /* TCP port number */ | 543 | case 'p': /*port number */ |
| 544 | // Add port to "peername" below to not rely on argument order | ||
| 767 | port = optarg; | 545 | port = optarg; |
| 768 | break; | 546 | break; |
| 769 | case 'm': /* List of MIBS */ | 547 | case 'm': /* List of MIBS */ |
| 770 | miblist = optarg; | 548 | miblist = optarg; |
| 771 | break; | 549 | break; |
| 772 | case 'n': /* usesnmpgetnext */ | 550 | case 'n': /* use_getnext instead of get */ |
| 773 | usesnmpgetnext = true; | 551 | config.snmp_params.use_getnext = true; |
| 774 | break; | 552 | break; |
| 775 | case 'P': /* SNMP protocol version */ | 553 | case 'P': /* SNMP protocol version */ |
| 776 | proto = optarg; | 554 | if (strcasecmp("1", optarg) == 0) { |
| 555 | config.snmp_params.snmp_session.version = SNMP_VERSION_1; | ||
| 556 | } else if (strcasecmp("2c", optarg) == 0) { | ||
| 557 | config.snmp_params.snmp_session.version = SNMP_VERSION_2c; | ||
| 558 | } else if (strcasecmp("3", optarg) == 0) { | ||
| 559 | config.snmp_params.snmp_session.version = SNMP_VERSION_3; | ||
| 560 | } else { | ||
| 561 | die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); | ||
| 562 | } | ||
| 563 | snmp_version_set_explicitely = true; | ||
| 564 | |||
| 777 | break; | 565 | break; |
| 778 | case 'N': /* SNMPv3 context */ | 566 | case 'N': /* SNMPv3 context name */ |
| 779 | context = optarg; | 567 | config.snmp_params.snmp_session.contextName = optarg; |
| 568 | config.snmp_params.snmp_session.contextNameLen = strlen(optarg); | ||
| 780 | break; | 569 | break; |
| 781 | case 'L': /* security level */ | 570 | case 'L': /* security level */ |
| 782 | seclevel = optarg; | 571 | if (strcasecmp("noAuthNoPriv", optarg) == 0) { |
| 572 | config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; | ||
| 573 | } else if (strcasecmp("authNoPriv", optarg) == 0) { | ||
| 574 | config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; | ||
| 575 | } else if (strcasecmp("authPriv", optarg) == 0) { | ||
| 576 | config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; | ||
| 577 | } else { | ||
| 578 | die(STATE_UNKNOWN, "invalid security level: %s", optarg); | ||
| 579 | } | ||
| 783 | break; | 580 | break; |
| 784 | case 'U': /* security username */ | 581 | case 'U': /* security username */ |
| 785 | secname = optarg; | 582 | config.snmp_params.snmp_session.securityName = optarg; |
| 583 | config.snmp_params.snmp_session.securityNameLen = strlen(optarg); | ||
| 786 | break; | 584 | break; |
| 787 | case 'a': /* auth protocol */ | 585 | case 'a': /* auth protocol */ |
| 788 | authproto = optarg; | 586 | // SNMPv3: SHA or MD5 |
| 587 | // TODO Test for availability of individual protocols | ||
| 588 | if (strcasecmp("MD5", optarg) == 0) { | ||
| 589 | config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; | ||
| 590 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 591 | OID_LENGTH(usmHMACMD5AuthProtocol); | ||
| 592 | } else if (strcasecmp("SHA", optarg) == 0) { | ||
| 593 | config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; | ||
| 594 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 595 | OID_LENGTH(usmHMACSHA1AuthProtocol); | ||
| 596 | } else if (strcasecmp("SHA224", optarg) == 0) { | ||
| 597 | config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; | ||
| 598 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 599 | OID_LENGTH(usmHMAC128SHA224AuthProtocol); | ||
| 600 | } else if (strcasecmp("SHA256", optarg) == 0) { | ||
| 601 | config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; | ||
| 602 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 603 | OID_LENGTH(usmHMAC192SHA256AuthProtocol); | ||
| 604 | } else if (strcasecmp("SHA384", optarg) == 0) { | ||
| 605 | config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; | ||
| 606 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 607 | OID_LENGTH(usmHMAC256SHA384AuthProtocol); | ||
| 608 | } else if (strcasecmp("SHA512", optarg) == 0) { | ||
| 609 | config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; | ||
| 610 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 611 | OID_LENGTH(usmHMAC384SHA512AuthProtocol); | ||
| 612 | } else { | ||
| 613 | die(STATE_UNKNOWN, "Unknown authentication protocol"); | ||
| 614 | } | ||
| 789 | break; | 615 | break; |
| 790 | case 'x': /* priv protocol */ | 616 | case 'x': /* priv protocol */ |
| 791 | privproto = optarg; | 617 | if (strcasecmp("DES", optarg) == 0) { |
| 618 | #ifdef HAVE_USM_DES_PRIV_PROTOCOL | ||
| 619 | config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol; | ||
| 620 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 621 | OID_LENGTH(usmDESPrivProtocol); | ||
| 622 | #else | ||
| 623 | die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform"); | ||
| 624 | #endif | ||
| 625 | } else if (strcasecmp("AES", optarg) == 0) { | ||
| 626 | config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol; | ||
| 627 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 628 | OID_LENGTH(usmAESPrivProtocol); | ||
| 629 | // } else if (strcasecmp("AES128", optarg)) { | ||
| 630 | // config.snmp_session.securityAuthProto = usmAES128PrivProtocol; | ||
| 631 | // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol) | ||
| 632 | // / OID_LENGTH(oid); | ||
| 633 | } else if (strcasecmp("AES192", optarg) == 0) { | ||
| 634 | config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol; | ||
| 635 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 636 | OID_LENGTH(usmAES192PrivProtocol); | ||
| 637 | } else if (strcasecmp("AES256", optarg) == 0) { | ||
| 638 | config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol; | ||
| 639 | config.snmp_params.snmp_session.securityAuthProtoLen = | ||
| 640 | OID_LENGTH(usmAES256PrivProtocol); | ||
| 641 | // } else if (strcasecmp("AES192Cisco", optarg)) { | ||
| 642 | // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol; | ||
| 643 | // config.snmp_session.securityAuthProtoLen = | ||
| 644 | // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if | ||
| 645 | // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto = | ||
| 646 | // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen = | ||
| 647 | // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if | ||
| 648 | // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto | ||
| 649 | // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = | ||
| 650 | // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if | ||
| 651 | // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto | ||
| 652 | // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = | ||
| 653 | // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid); | ||
| 654 | } else { | ||
| 655 | die(STATE_UNKNOWN, "Unknown privacy protocol"); | ||
| 656 | } | ||
| 792 | break; | 657 | break; |
| 793 | case 'A': /* auth passwd */ | 658 | case 'A': /* auth passwd */ |
| 794 | authpasswd = optarg; | 659 | authpasswd = (unsigned char *)optarg; |
| 795 | break; | 660 | break; |
| 796 | case 'X': /* priv passwd */ | 661 | case 'X': /* priv passwd */ |
| 797 | privpasswd = optarg; | 662 | privpasswd = (unsigned char *)optarg; |
| 663 | break; | ||
| 664 | case 'e': | ||
| 665 | case 'E': | ||
| 666 | if (!is_integer(optarg)) { | ||
| 667 | usage2(_("Retries interval must be a positive integer"), optarg); | ||
| 668 | } else { | ||
| 669 | config.snmp_params.snmp_session.retries = atoi(optarg); | ||
| 670 | } | ||
| 798 | break; | 671 | break; |
| 799 | case 't': /* timeout period */ | 672 | case 't': /* timeout period */ |
| 800 | if (!is_integer(optarg)) | 673 | if (!is_integer(optarg)) { |
| 801 | usage2(_("Timeout interval must be a positive integer"), optarg); | 674 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 802 | else | 675 | } else { |
| 803 | timeout_interval = atoi(optarg); | 676 | timeout_interval = (unsigned int)atoi(optarg); |
| 677 | } | ||
| 804 | break; | 678 | break; |
| 805 | 679 | ||
| 806 | /* Test parameters */ | 680 | /* Test parameters */ |
| 807 | case 'c': /* critical threshold */ | 681 | case 'c': /* critical threshold */ |
| 808 | critical_thresholds = optarg; | 682 | check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true); |
| 809 | break; | 683 | break; |
| 810 | case 'w': /* warning threshold */ | 684 | case 'w': /* warning threshold */ |
| 811 | warning_thresholds = optarg; | 685 | 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; | 686 | break; |
| 820 | case 'o': /* object identifier */ | 687 | case 'o': /* object identifier */ |
| 821 | if (strspn(optarg, "0123456789.,") != strlen(optarg)) { | 688 | if (strspn(optarg, "0123456789.,") != strlen(optarg)) { |
| @@ -824,306 +691,292 @@ int process_arguments(int argc, char **argv) { | |||
| 824 | * so we have a mib variable, rather than just an SNMP OID, | 691 | * so we have a mib variable, rather than just an SNMP OID, |
| 825 | * so we have to actually read the mib files | 692 | * so we have to actually read the mib files |
| 826 | */ | 693 | */ |
| 827 | needmibs = true; | 694 | 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 | } | 695 | } |
| 836 | numoids = j; | 696 | |
| 837 | if (option_char == 'E' || option_char == 'e') { | 697 | for (char *ptr = strtok(optarg, ", "); ptr != NULL; |
| 838 | jj++; | 698 | ptr = strtok(NULL, ", "), tmp_oid_counter++) { |
| 839 | while (j + 1 >= eval_size) { | 699 | 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 | } | 700 | } |
| 849 | break; | 701 | break; |
| 850 | case 'z': /* Null OID Return Check */ | 702 | case 'z': /* Null OID Return Check */ |
| 851 | if (!is_integer(optarg)) | 703 | if (!is_integer(optarg)) { |
| 852 | usage2(_("Exit status must be a positive integer"), optarg); | 704 | usage2(_("Exit status must be a positive integer"), optarg); |
| 853 | else | 705 | } else { |
| 854 | nulloid = atoi(optarg); | 706 | config.evaluation_params.nulloid_result = atoi(optarg); |
| 707 | } | ||
| 855 | break; | 708 | break; |
| 856 | case 's': /* string or substring */ | 709 | case 's': /* string or substring */ |
| 857 | strncpy(string_value, optarg, sizeof(string_value) - 1); | 710 | strncpy(config.evaluation_params.string_cmp_value, optarg, |
| 858 | string_value[sizeof(string_value) - 1] = 0; | 711 | sizeof(config.evaluation_params.string_cmp_value) - 1); |
| 859 | while (jj >= eval_size) { | 712 | config.evaluation_params |
| 860 | eval_size += OID_COUNT_STEP; | 713 | .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0; |
| 861 | eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); | 714 | 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; | 715 | break; |
| 866 | case 'R': /* regex */ | 716 | case 'R': /* regex */ |
| 867 | cflags = REG_ICASE; | 717 | cflags = REG_ICASE; |
| 868 | // fall through | 718 | // fall through |
| 869 | case 'r': /* regex */ | 719 | case 'r': /* regex */ |
| 720 | { | ||
| 721 | char regex_expect[MAX_INPUT_BUFFER] = ""; | ||
| 870 | cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; | 722 | cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; |
| 871 | strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); | 723 | strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); |
| 872 | regex_expect[sizeof(regex_expect) - 1] = 0; | 724 | regex_expect[sizeof(regex_expect) - 1] = 0; |
| 873 | errcode = regcomp(&preg, regex_expect, cflags); | 725 | int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags); |
| 874 | if (errcode != 0) { | 726 | if (errcode != 0) { |
| 875 | regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); | 727 | char errbuf[MAX_INPUT_BUFFER] = ""; |
| 876 | printf(_("Could Not Compile Regular Expression")); | 728 | regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf, |
| 877 | return ERROR; | 729 | MAX_INPUT_BUFFER); |
| 878 | } | 730 | printf("Could Not Compile Regular Expression: %s", errbuf); |
| 879 | while (jj >= eval_size) { | 731 | process_arguments_wrapper result = { |
| 880 | eval_size += OID_COUNT_STEP; | 732 | .errorcode = ERROR, |
| 881 | eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); | 733 | }; |
| 882 | memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); | 734 | return result; |
| 883 | } | 735 | } |
| 884 | eval_method[jj++] = CRIT_REGEX; | 736 | config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true; |
| 885 | break; | 737 | } 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 */ | 738 | case 'l': /* label */ |
| 895 | nlabels++; | 739 | { |
| 896 | if (nlabels > labels_size) { | 740 | if (labels_counter >= config.snmp_params.num_of_test_units) { |
| 897 | labels_size += 8; | 741 | break; |
| 898 | labels = realloc(labels, labels_size * sizeof(*labels)); | 742 | } |
| 899 | if (labels == NULL) | 743 | char *ptr = trim_whitespaces_and_check_quoting(optarg); |
| 900 | die(STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels); | 744 | if (ptr[0] == '\'') { |
| 745 | config.snmp_params.test_units[labels_counter].label = ptr + 1; | ||
| 746 | } else { | ||
| 747 | config.snmp_params.test_units[labels_counter].label = ptr; | ||
| 901 | } | 748 | } |
| 902 | labels[nlabels - 1] = optarg; | 749 | |
| 903 | char *ptr = thisarg(optarg); | 750 | while (ptr && (ptr = get_next_argument(ptr))) { |
| 904 | labels[nlabels - 1] = ptr; | 751 | labels_counter++; |
| 905 | if (ptr[0] == '\'') | 752 | ptr = trim_whitespaces_and_check_quoting(ptr); |
| 906 | labels[nlabels - 1] = ptr + 1; | 753 | if (ptr[0] == '\'') { |
| 907 | while (ptr && (ptr = nextarg(ptr))) { | 754 | config.snmp_params.test_units[labels_counter].label = ptr + 1; |
| 908 | nlabels++; | 755 | } else { |
| 909 | if (nlabels > labels_size) { | 756 | 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 | } | 757 | } |
| 915 | ptr = thisarg(ptr); | ||
| 916 | if (ptr[0] == '\'') | ||
| 917 | labels[nlabels - 1] = ptr + 1; | ||
| 918 | else | ||
| 919 | labels[nlabels - 1] = ptr; | ||
| 920 | } | 758 | } |
| 921 | break; | 759 | labels_counter++; |
| 760 | } break; | ||
| 922 | case 'u': /* units */ | 761 | case 'u': /* units */ |
| 923 | units = optarg; | 762 | { |
| 924 | nunits++; | 763 | if (unitv_counter >= config.snmp_params.num_of_test_units) { |
| 925 | if (nunits > unitv_size) { | 764 | 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 | } | 765 | } |
| 931 | unitv[nunits - 1] = optarg; | 766 | char *ptr = trim_whitespaces_and_check_quoting(optarg); |
| 932 | ptr = thisarg(optarg); | 767 | if (ptr[0] == '\'') { |
| 933 | unitv[nunits - 1] = ptr; | 768 | config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; |
| 934 | if (ptr[0] == '\'') | 769 | } else { |
| 935 | unitv[nunits - 1] = ptr + 1; | 770 | config.snmp_params.test_units[unitv_counter].unit_value = ptr; |
| 936 | while (ptr && (ptr = nextarg(ptr))) { | 771 | } |
| 937 | if (nunits > unitv_size) { | 772 | while (ptr && (ptr = get_next_argument(ptr))) { |
| 938 | unitv_size += 8; | 773 | unitv_counter++; |
| 939 | unitv = realloc(unitv, unitv_size * sizeof(*unitv)); | 774 | ptr = trim_whitespaces_and_check_quoting(ptr); |
| 940 | if (units == NULL) | 775 | if (ptr[0] == '\'') { |
| 941 | die(STATE_UNKNOWN, _("Could not realloc() units\n")); | 776 | config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; |
| 777 | } else { | ||
| 778 | config.snmp_params.test_units[unitv_counter].unit_value = ptr; | ||
| 942 | } | 779 | } |
| 943 | nunits++; | ||
| 944 | ptr = thisarg(ptr); | ||
| 945 | if (ptr[0] == '\'') | ||
| 946 | unitv[nunits - 1] = ptr + 1; | ||
| 947 | else | ||
| 948 | unitv[nunits - 1] = ptr; | ||
| 949 | } | 780 | } |
| 781 | unitv_counter++; | ||
| 782 | } break; | ||
| 783 | case offset_index: | ||
| 784 | config.evaluation_params.offset = strtod(optarg, NULL); | ||
| 785 | config.evaluation_params.offset_set = true; | ||
| 950 | break; | 786 | break; |
| 951 | case L_CALCULATE_RATE: | 787 | case invert_search_index: |
| 952 | if (calculate_rate == 0) | 788 | 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; | 789 | break; |
| 966 | case 'O': | 790 | case 'O': |
| 967 | perf_labels = 0; | 791 | config.evaluation_params.use_oid_as_perf_data_label = true; |
| 968 | break; | 792 | break; |
| 969 | case '4': | 793 | case '4': |
| 794 | // The default, do something here to be exclusive to -6 instead of doing nothing? | ||
| 795 | connection_prefix = "udp"; | ||
| 970 | break; | 796 | break; |
| 971 | case '6': | 797 | case '6': |
| 972 | xasprintf(&ip_version, "udp6:"); | 798 | connection_prefix = "udp6"; |
| 973 | if (verbose > 2) | 799 | break; |
| 974 | printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); | 800 | case connection_prefix_index: |
| 801 | connection_prefix = optarg; | ||
| 975 | break; | 802 | break; |
| 976 | case 'M': | 803 | case 'M': |
| 977 | if (strspn(optarg, "0123456789.,") == strlen(optarg)) { | 804 | if (strspn(optarg, "0123456789.,") == strlen(optarg)) { |
| 978 | multiplier = strtod(optarg, NULL); | 805 | config.evaluation_params.multiplier = strtod(optarg, NULL); |
| 806 | config.evaluation_params.multiplier_set = true; | ||
| 979 | } | 807 | } |
| 980 | break; | 808 | break; |
| 981 | case 'f': | 809 | case ignore_mib_parsing_errors_index: |
| 982 | if (multiplier != 1.0) { | 810 | config.snmp_params.ignore_mib_parsing_errors = true; |
| 983 | fmtstr = optarg; | 811 | break; |
| 984 | fmtstr_set = true; | 812 | case 'f': // Deprecated format option for floating point values |
| 813 | break; | ||
| 814 | case output_format_index: { | ||
| 815 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 816 | if (!parser.parsing_success) { | ||
| 817 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 818 | printf("Invalid output format: %s\n", optarg); | ||
| 819 | exit(STATE_UNKNOWN); | ||
| 820 | } | ||
| 821 | |||
| 822 | config.output_format_is_set = true; | ||
| 823 | config.output_format = parser.output_format; | ||
| 824 | break; | ||
| 825 | } | ||
| 826 | case calculate_rate: | ||
| 827 | config.evaluation_params.calculate_rate = true; | ||
| 828 | break; | ||
| 829 | case rate_multiplier: | ||
| 830 | if (!is_integer(optarg) || | ||
| 831 | ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) { | ||
| 832 | usage2(_("Rate multiplier must be a positive integer"), optarg); | ||
| 985 | } | 833 | } |
| 986 | break; | 834 | break; |
| 987 | case L_IGNORE_MIB_PARSING_ERRORS: | 835 | default: |
| 988 | ignore_mib_parsing_errors = true; | 836 | die(STATE_UNKNOWN, "Unknown option"); |
| 989 | } | 837 | } |
| 990 | } | 838 | } |
| 991 | 839 | ||
| 992 | if (server_address == NULL) | 840 | if (config.snmp_params.snmp_session.peername == NULL) { |
| 993 | server_address = argv[optind]; | 841 | config.snmp_params.snmp_session.peername = argv[optind]; |
| 994 | 842 | } | |
| 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 | ||
| 1010 | the authentication and authorization combinations based on protocol version | ||
| 1011 | selected.</para> | ||
| 1012 | |||
| 1013 | <para></para> | ||
| 1014 | |||
| 1015 | </sect3> | ||
| 1016 | -@@ | ||
| 1017 | ******************************************************************************/ | ||
| 1018 | 843 | ||
| 1019 | static int validate_arguments() { | 844 | // Build true peername here if necessary |
| 1020 | /* check whether to load locally installed MIBS (CPU/disk intensive) */ | 845 | if (connection_prefix != NULL) { |
| 1021 | if (miblist == NULL) { | 846 | // We got something in the connection prefix |
| 1022 | if (needmibs) { | 847 | if (strcasecmp(connection_prefix, "udp") == 0) { |
| 1023 | miblist = strdup(DEFAULT_MIBLIST); | 848 | // The default, do nothing |
| 849 | } else if (strcasecmp(connection_prefix, "tcp") == 0) { | ||
| 850 | // use tcp/ipv4 | ||
| 851 | xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s", | ||
| 852 | config.snmp_params.snmp_session.peername); | ||
| 853 | } else if (strcasecmp(connection_prefix, "tcp6") == 0 || | ||
| 854 | strcasecmp(connection_prefix, "tcpv6") == 0 || | ||
| 855 | strcasecmp(connection_prefix, "tcpipv6") == 0 || | ||
| 856 | strcasecmp(connection_prefix, "udp6") == 0 || | ||
| 857 | strcasecmp(connection_prefix, "udpipv6") == 0 || | ||
| 858 | strcasecmp(connection_prefix, "udpv6") == 0) { | ||
| 859 | // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it | ||
| 860 | // works anyway therefore do nothing here | ||
| 861 | xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix, | ||
| 862 | config.snmp_params.snmp_session.peername); | ||
| 863 | } else if (strcmp(connection_prefix, "tls") == 0) { | ||
| 864 | // TODO: Anything else to do here? | ||
| 865 | xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s", | ||
| 866 | config.snmp_params.snmp_session.peername); | ||
| 867 | } else if (strcmp(connection_prefix, "dtls") == 0) { | ||
| 868 | // TODO: Anything else to do here? | ||
| 869 | xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s", | ||
| 870 | config.snmp_params.snmp_session.peername); | ||
| 871 | } else if (strcmp(connection_prefix, "unix") == 0) { | ||
| 872 | // TODO: Check whether this is a valid path? | ||
| 873 | xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s", | ||
| 874 | config.snmp_params.snmp_session.peername); | ||
| 875 | } else if (strcmp(connection_prefix, "ipx") == 0) { | ||
| 876 | xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s", | ||
| 877 | config.snmp_params.snmp_session.peername); | ||
| 1024 | } else { | 878 | } else { |
| 1025 | miblist = ""; /* don't read any mib files for numeric oids */ | 879 | // Don't know that prefix, die here |
| 880 | die(STATE_UNKNOWN, "Unknown connection prefix"); | ||
| 1026 | } | 881 | } |
| 1027 | } | 882 | } |
| 1028 | 883 | ||
| 1029 | /* Check server_address is given */ | 884 | /* Check server_address is given */ |
| 1030 | if (server_address == NULL) | 885 | if (config.snmp_params.snmp_session.peername == NULL) { |
| 1031 | die(STATE_UNKNOWN, _("No host specified\n")); | 886 | die(STATE_UNKNOWN, _("No host specified\n")); |
| 887 | } | ||
| 1032 | 888 | ||
| 1033 | /* Check oid is given */ | 889 | if (port != NULL) { |
| 1034 | if (numoids == 0) | 890 | xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", |
| 1035 | die(STATE_UNKNOWN, _("No OIDs specified\n")); | 891 | config.snmp_params.snmp_session.peername, port); |
| 892 | } | ||
| 1036 | 893 | ||
| 1037 | if (proto == NULL) | 894 | /* check whether to load locally installed MIBS (CPU/disk intensive) */ |
| 1038 | xasprintf(&proto, DEFAULT_PROTOCOL); | 895 | if (miblist == NULL) { |
| 1039 | 896 | if (config.snmp_params.need_mibs) { | |
| 1040 | if ((strcmp(proto, "1") == 0) || (strcmp(proto, "2c") == 0)) { /* snmpv1 or snmpv2c */ | 897 | setenv("MIBLS", DEFAULT_MIBLIST, 1); |
| 1041 | numauthpriv = 2; | 898 | } else { |
| 1042 | authpriv = calloc(numauthpriv, sizeof(char *)); | 899 | setenv("MIBLS", "NONE", 1); |
| 1043 | authpriv[0] = strdup("-c"); | 900 | 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 | } | 901 | } |
| 902 | } else { | ||
| 903 | // Blatantly stolen from snmplib/snmp_parse_args | ||
| 904 | setenv("MIBS", miblist, 1); | ||
| 905 | } | ||
| 1052 | 906 | ||
| 1053 | if (seclevel == NULL) | 907 | // Historical default is SNMP v2c |
| 1054 | xasprintf(&seclevel, "noAuthNoPriv"); | 908 | if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) { |
| 909 | config.snmp_params.snmp_session.version = SNMP_VERSION_2c; | ||
| 910 | } | ||
| 1055 | 911 | ||
| 1056 | if (secname == NULL) | 912 | if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) || |
| 913 | (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ | ||
| 914 | /* | ||
| 915 | config.numauthpriv = 2; | ||
| 916 | config.authpriv = calloc(config.numauthpriv, sizeof(char *)); | ||
| 917 | config.authpriv[0] = strdup("-c"); | ||
| 918 | config.authpriv[1] = strdup(community); | ||
| 919 | */ | ||
| 920 | } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ | ||
| 921 | // generate keys for priv and auth here (if demanded) | ||
| 922 | |||
| 923 | if (config.snmp_params.snmp_session.securityName == NULL) { | ||
| 1057 | die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); | 924 | die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); |
| 925 | } | ||
| 1058 | 926 | ||
| 1059 | if (strcmp(seclevel, "noAuthNoPriv") == 0) { | 927 | switch (config.snmp_params.snmp_session.securityLevel) { |
| 1060 | numauthpriv = 4; | 928 | case SNMP_SEC_LEVEL_AUTHPRIV: { |
| 1061 | authpriv = calloc(numauthpriv, sizeof(char *)); | 929 | if (authpasswd == NULL) { |
| 1062 | authpriv[0] = strdup("-l"); | 930 | die(STATE_UNKNOWN, |
| 1063 | authpriv[1] = strdup("noAuthNoPriv"); | 931 | "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 | } | 932 | } |
| 1070 | 933 | // auth and priv | |
| 1071 | if (authproto == NULL) | 934 | int priv_key_generated = generate_Ku( |
| 1072 | xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); | 935 | config.snmp_params.snmp_session.securityPrivProto, |
| 1073 | 936 | (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd, | |
| 1074 | if (authpasswd == NULL) | 937 | strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey, |
| 1075 | die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); | 938 | &config.snmp_params.snmp_session.securityPrivKeyLen); |
| 1076 | 939 | ||
| 1077 | if (strcmp(seclevel, "authNoPriv") == 0) { | 940 | if (priv_key_generated != SNMPERR_SUCCESS) { |
| 1078 | numauthpriv = 8; | 941 | 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 | } | 942 | } |
| 1110 | } | 943 | } |
| 1111 | 944 | // fall through | |
| 1112 | } else { | 945 | case SNMP_SEC_LEVEL_AUTHNOPRIV: { |
| 1113 | usage2(_("Invalid SNMP version"), proto); | 946 | if (privpasswd == NULL) { |
| 947 | die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); | ||
| 948 | } | ||
| 949 | int auth_key_generated = generate_Ku( | ||
| 950 | config.snmp_params.snmp_session.securityAuthProto, | ||
| 951 | (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd, | ||
| 952 | strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey, | ||
| 953 | &config.snmp_params.snmp_session.securityAuthKeyLen); | ||
| 954 | |||
| 955 | if (auth_key_generated != SNMPERR_SUCCESS) { | ||
| 956 | die(STATE_UNKNOWN, "Failed to generate privacy key"); | ||
| 957 | } | ||
| 958 | } break; | ||
| 959 | case SNMP_SEC_LEVEL_NOAUTH: | ||
| 960 | // No auth, no priv, not much todo | ||
| 961 | break; | ||
| 962 | } | ||
| 1114 | } | 963 | } |
| 1115 | 964 | ||
| 1116 | return OK; | 965 | process_arguments_wrapper result = { |
| 966 | .config = config, | ||
| 967 | .errorcode = OK, | ||
| 968 | }; | ||
| 969 | return result; | ||
| 1117 | } | 970 | } |
| 1118 | 971 | ||
| 1119 | /* trim leading whitespace | 972 | /* trim leading whitespace |
| 1120 | if there is a leading quote, make sure it balances */ | 973 | if there is a leading quote, make sure it balances */ |
| 1121 | 974 | char *trim_whitespaces_and_check_quoting(char *str) { | |
| 1122 | static char *thisarg(char *str) { | ||
| 1123 | str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ | 975 | str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ |
| 1124 | if (str[0] == '\'') { /* handle SIMPLE quoted strings */ | 976 | if (str[0] == '\'') { /* handle SIMPLE quoted strings */ |
| 1125 | if (strlen(str) == 1 || !strstr(str + 1, "'")) | 977 | if (strlen(str) == 1 || !strstr(str + 1, "'")) { |
| 1126 | die(STATE_UNKNOWN, _("Unbalanced quotes\n")); | 978 | die(STATE_UNKNOWN, _("Unbalanced quotes\n")); |
| 979 | } | ||
| 1127 | } | 980 | } |
| 1128 | return str; | 981 | return str; |
| 1129 | } | 982 | } |
| @@ -1132,23 +985,21 @@ static char *thisarg(char *str) { | |||
| 1132 | set the trailing quote to '\x0' | 985 | set the trailing quote to '\x0' |
| 1133 | if the string continues, advance beyond the comma */ | 986 | if the string continues, advance beyond the comma */ |
| 1134 | 987 | ||
| 1135 | static char *nextarg(char *str) { | 988 | char *get_next_argument(char *str) { |
| 1136 | if (str[0] == '\'') { | 989 | if (str[0] == '\'') { |
| 1137 | str[0] = 0; | 990 | str[0] = 0; |
| 1138 | if (strlen(str) > 1) { | 991 | if (strlen(str) > 1) { |
| 1139 | str = strstr(str + 1, "'"); | 992 | str = strstr(str + 1, "'"); |
| 1140 | return (++str); | 993 | return (++str); |
| 1141 | } else { | ||
| 1142 | return NULL; | ||
| 1143 | } | 994 | } |
| 995 | return NULL; | ||
| 1144 | } | 996 | } |
| 1145 | if (str[0] == ',') { | 997 | if (str[0] == ',') { |
| 1146 | str[0] = 0; | 998 | str[0] = 0; |
| 1147 | if (strlen(str) > 1) { | 999 | if (strlen(str) > 1) { |
| 1148 | return (++str); | 1000 | return (++str); |
| 1149 | } else { | ||
| 1150 | return NULL; | ||
| 1151 | } | 1001 | } |
| 1002 | return NULL; | ||
| 1152 | } | 1003 | } |
| 1153 | if ((str = strstr(str, ",")) && strlen(str) > 1) { | 1004 | if ((str = strstr(str, ",")) && strlen(str) > 1) { |
| 1154 | str[0] = 0; | 1005 | str[0] = 0; |
| @@ -1157,41 +1008,7 @@ static char *nextarg(char *str) { | |||
| 1157 | return NULL; | 1008 | return NULL; |
| 1158 | } | 1009 | } |
| 1159 | 1010 | ||
| 1160 | /* multiply result (values 0 < n < 1 work as divider) */ | 1011 | void print_help(void) { |
| 1161 | static 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 | |||
| 1194 | static void print_help(void) { | ||
| 1195 | print_revision(progname, NP_VERSION); | 1012 | print_revision(progname, NP_VERSION); |
| 1196 | 1013 | ||
| 1197 | printf(COPYRIGHT, copyright, email); | 1014 | printf(COPYRIGHT, copyright, email); |
| @@ -1204,8 +1021,6 @@ static void print_help(void) { | |||
| 1204 | 1021 | ||
| 1205 | printf(UT_HELP_VRSN); | 1022 | printf(UT_HELP_VRSN); |
| 1206 | printf(UT_EXTRA_OPTS); | 1023 | printf(UT_EXTRA_OPTS); |
| 1207 | printf(UT_IPv46); | ||
| 1208 | |||
| 1209 | printf(UT_HOST_PORT, 'p', DEFAULT_PORT); | 1024 | printf(UT_HOST_PORT, 'p', DEFAULT_PORT); |
| 1210 | 1025 | ||
| 1211 | /* SNMP and Authentication Protocol */ | 1026 | /* SNMP and Authentication Protocol */ |
| @@ -1217,13 +1032,15 @@ static void print_help(void) { | |||
| 1217 | printf(" %s\n", _("SNMPv3 context")); | 1032 | printf(" %s\n", _("SNMPv3 context")); |
| 1218 | printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); | 1033 | printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); |
| 1219 | printf(" %s\n", _("SNMPv3 securityLevel")); | 1034 | printf(" %s\n", _("SNMPv3 securityLevel")); |
| 1220 | printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); | 1035 | printf(" %s\n", "-a, --authproto=[MD5|SHA]"); |
| 1221 | printf(" %s\n", | 1036 | printf(" %s\n", _("SNMPv3 auth proto")); |
| 1222 | _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); | 1037 | #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")); | 1038 | printf(" %s\n", "-x, --privproto=[DES|AES]"); |
| 1224 | printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); | 1039 | 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")); | 1040 | #else |
| 1226 | printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); | 1041 | printf(" %s\n", "-x, --privproto=[AES]"); |
| 1042 | printf(" %s\n", _("SNMPv3 priv proto (default AES)")); | ||
| 1043 | #endif | ||
| 1227 | 1044 | ||
| 1228 | /* Authentication Tokens*/ | 1045 | /* Authentication Tokens*/ |
| 1229 | printf(" %s\n", "-C, --community=STRING"); | 1046 | printf(" %s\n", "-C, --community=STRING"); |
| @@ -1235,15 +1052,18 @@ static void print_help(void) { | |||
| 1235 | printf(" %s\n", _("SNMPv3 authentication password")); | 1052 | printf(" %s\n", _("SNMPv3 authentication password")); |
| 1236 | printf(" %s\n", "-X, --privpasswd=PASSWORD"); | 1053 | printf(" %s\n", "-X, --privpasswd=PASSWORD"); |
| 1237 | printf(" %s\n", _("SNMPv3 privacy password")); | 1054 | printf(" %s\n", _("SNMPv3 privacy password")); |
| 1055 | printf(" %s\n", "--connection-prefix"); | ||
| 1056 | printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, " | ||
| 1057 | "tcp6, tcpv6, tcpipv6, tls, dtls - " | ||
| 1058 | "default is \"udp\"\n"); | ||
| 1238 | 1059 | ||
| 1239 | /* OID Stuff */ | 1060 | /* OID Stuff */ |
| 1240 | printf(" %s\n", "-o, --oid=OID(s)"); | 1061 | printf(" %s\n", "-o, --oid=OID(s)"); |
| 1241 | printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); | 1062 | printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); |
| 1242 | printf(" %s\n", "-m, --miblist=STRING"); | 1063 | printf(" %s\n", "-m, --miblist=STRING"); |
| 1243 | printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); | 1064 | printf(" %s\n", |
| 1065 | _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); | ||
| 1244 | printf(" %s\n", _("for symbolic OIDs.)")); | 1066 | 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")); | 1067 | 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.")); | 1068 | printf(" %s\n", _("to be the data that should be used in the evaluation.")); |
| 1249 | printf(" %s\n", "-z, --nulloid=#"); | 1069 | printf(" %s\n", "-z, --nulloid=#"); |
| @@ -1260,10 +1080,6 @@ static void print_help(void) { | |||
| 1260 | printf(" %s\n", _("Warning threshold range(s)")); | 1080 | printf(" %s\n", _("Warning threshold range(s)")); |
| 1261 | printf(" %s\n", "-c, --critical=THRESHOLD(s)"); | 1081 | printf(" %s\n", "-c, --critical=THRESHOLD(s)"); |
| 1262 | printf(" %s\n", _("Critical threshold range(s)")); | 1082 | 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"); | 1083 | printf(" %s\n", "--offset=OFFSET"); |
| 1268 | printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); | 1084 | printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); |
| 1269 | 1085 | ||
| @@ -1271,9 +1087,11 @@ static void print_help(void) { | |||
| 1271 | printf(" %s\n", "-s, --string=STRING"); | 1087 | printf(" %s\n", "-s, --string=STRING"); |
| 1272 | printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); | 1088 | printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); |
| 1273 | printf(" %s\n", "-r, --ereg=REGEX"); | 1089 | printf(" %s\n", "-r, --ereg=REGEX"); |
| 1274 | printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); | 1090 | printf(" %s\n", |
| 1091 | _("Return OK state (for that OID) if extended regular expression REGEX matches")); | ||
| 1275 | printf(" %s\n", "-R, --eregi=REGEX"); | 1092 | printf(" %s\n", "-R, --eregi=REGEX"); |
| 1276 | printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); | 1093 | printf(" %s\n", |
| 1094 | _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); | ||
| 1277 | printf(" %s\n", "--invert-search"); | 1095 | printf(" %s\n", "--invert-search"); |
| 1278 | printf(" %s\n", _("Invert search result (CRITICAL if found)")); | 1096 | printf(" %s\n", _("Invert search result (CRITICAL if found)")); |
| 1279 | 1097 | ||
| @@ -1282,53 +1100,46 @@ static void print_help(void) { | |||
| 1282 | printf(" %s\n", _("Prefix label for output from plugin")); | 1100 | printf(" %s\n", _("Prefix label for output from plugin")); |
| 1283 | printf(" %s\n", "-u, --units=STRING"); | 1101 | printf(" %s\n", "-u, --units=STRING"); |
| 1284 | printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); | 1102 | 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"); | 1103 | printf(" %s\n", "-M, --multiplier=FLOAT"); |
| 1288 | printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); | 1104 | printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); |
| 1289 | printf(" %s\n", "-f, --fmtstr=STRING"); | 1105 | printf(UT_OUTPUT_FORMAT); |
| 1290 | printf(" %s\n", _("C-style format string for float values (see option -M)")); | ||
| 1291 | 1106 | ||
| 1292 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 1107 | 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")); | 1108 | printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: " |
| 1109 | "timeout_interval * retries + 5")); | ||
| 1294 | printf(" %s\n", "-e, --retries=INTEGER"); | 1110 | printf(" %s\n", "-e, --retries=INTEGER"); |
| 1295 | printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES); | 1111 | printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), |
| 1112 | DEFAULT_RETRIES); | ||
| 1296 | 1113 | ||
| 1297 | printf(" %s\n", "-O, --perf-oids"); | 1114 | printf(" %s\n", "-O, --perf-oids"); |
| 1298 | printf(" %s\n", _("Label performance data with OIDs instead of --label's")); | 1115 | printf(" %s\n", _("Label performance data with OIDs instead of --label's")); |
| 1299 | 1116 | ||
| 1300 | printf(" %s\n", "--ignore-mib-parsing-errors"); | 1117 | printf(" %s\n", "--ignore-mib-parsing-errors"); |
| 1301 | printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files")); | 1118 | printf(" %s\n", _("Do to not print errors encountered when parsing MIB files")); |
| 1302 | 1119 | ||
| 1303 | printf(UT_VERBOSE); | 1120 | printf(UT_VERBOSE); |
| 1304 | 1121 | ||
| 1305 | printf("\n"); | 1122 | printf("\n"); |
| 1306 | printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package.")); | 1123 | 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")); | 1124 | printf("%s\n", |
| 1125 | _("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.")); | 1126 | printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin.")); |
| 1309 | 1127 | ||
| 1310 | printf("\n"); | 1128 | printf("\n"); |
| 1311 | printf("%s\n", _("Notes:")); | 1129 | printf("%s\n", _("Notes:")); |
| 1312 | printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); | 1130 | printf(" %s\n", |
| 1131 | _("- 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).")); | 1132 | printf(" %s\n", _("list (lists with internal spaces must be quoted).")); |
| 1314 | 1133 | ||
| 1315 | printf(" -%s", UT_THRESHOLDS_NOTES); | 1134 | printf(" -%s", UT_THRESHOLDS_NOTES); |
| 1316 | 1135 | ||
| 1317 | printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); | 1136 | printf(" %s\n", |
| 1137 | _("- 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")); | 1138 | 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")); | 1139 | printf(" %s\n", |
| 1140 | _("- 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.")); | 1141 | printf(" %s\n", _("returned from the SNMP query is an unsigned integer.")); |
| 1321 | 1142 | ||
| 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); | 1143 | printf(UT_SUPPORT); |
| 1333 | } | 1144 | } |
| 1334 | 1145 | ||
| @@ -1339,5 +1150,5 @@ void print_usage(void) { | |||
| 1339 | printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); | 1150 | 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"); | 1151 | 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"); | 1152 | printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n"); |
| 1342 | printf("[-M multiplier [-f format]]\n"); | 1153 | printf("[-M multiplier]\n"); |
| 1343 | } | 1154 | } |
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..f506537a --- /dev/null +++ b/plugins/check_snmp.d/check_snmp_helpers.c | |||
| @@ -0,0 +1,936 @@ | |||
| 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 | |||
| 12 | extern int verbose; | ||
| 13 | |||
| 14 | check_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 | |||
| 21 | int 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 | |||
| 87 | const int DEFAULT_PROTOCOL = SNMP_VERSION_1; | ||
| 88 | const char DEFAULT_OUTPUT_DELIMITER[] = " "; | ||
| 89 | |||
| 90 | const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192; | ||
| 91 | |||
| 92 | check_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 | |||
| 135 | snmp_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(¶meters.snmp_session); | ||
| 173 | if (active_session == NULL) { | ||
| 174 | int pcliberr = 0; | ||
| 175 | int psnmperr = 0; | ||
| 176 | char *pperrstring = NULL; | ||
| 177 | snmp_error(¶meters.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 | |||
| 297 | check_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 = (double)response.value.uIntVal; | ||
| 450 | |||
| 451 | if (eval_params.offset_set) { | ||
| 452 | processed += eval_params.offset; | ||
| 453 | } | ||
| 454 | |||
| 455 | if (eval_params.multiplier_set) { | ||
| 456 | processed = processed * eval_params.multiplier; | ||
| 457 | } | ||
| 458 | |||
| 459 | treated_value = lround(processed); | ||
| 460 | } | ||
| 461 | |||
| 462 | result_state.value.intVal = treated_value; | ||
| 463 | |||
| 464 | if (eval_params.calculate_rate && have_previous_state) { | ||
| 465 | if (verbose > 2) { | ||
| 466 | printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__, | ||
| 467 | prev_state.value.intVal); | ||
| 468 | printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__, | ||
| 469 | treated_value); | ||
| 470 | } | ||
| 471 | double rate = (treated_value - prev_state.value.intVal) / timeDiff; | ||
| 472 | pd_result_val = mp_create_pd_value(rate); | ||
| 473 | } else { | ||
| 474 | pd_result_val = mp_create_pd_value(treated_value); | ||
| 475 | |||
| 476 | if (response.type == ASN_COUNTER) { | ||
| 477 | pd_num_val.uom = "c"; | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | } break; | ||
| 482 | case ASN_INTEGER: { | ||
| 483 | if (eval_params.multiplier_set || eval_params.offset_set) { | ||
| 484 | double processed = (double)response.value.intVal; | ||
| 485 | |||
| 486 | if (eval_params.offset_set) { | ||
| 487 | processed += eval_params.offset; | ||
| 488 | } | ||
| 489 | |||
| 490 | if (eval_params.multiplier_set) { | ||
| 491 | processed *= eval_params.multiplier; | ||
| 492 | } | ||
| 493 | |||
| 494 | result_state.value.doubleVal = processed; | ||
| 495 | |||
| 496 | if (eval_params.calculate_rate && have_previous_state) { | ||
| 497 | pd_result_val = | ||
| 498 | mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff); | ||
| 499 | } else { | ||
| 500 | pd_result_val = mp_create_pd_value(processed); | ||
| 501 | } | ||
| 502 | } else { | ||
| 503 | result_state.value.intVal = response.value.intVal; | ||
| 504 | |||
| 505 | if (eval_params.calculate_rate && have_previous_state) { | ||
| 506 | pd_result_val = mp_create_pd_value( | ||
| 507 | (response.value.intVal - prev_state.value.intVal) / timeDiff); | ||
| 508 | } else { | ||
| 509 | pd_result_val = mp_create_pd_value(response.value.intVal); | ||
| 510 | } | ||
| 511 | } | ||
| 512 | |||
| 513 | got_a_numerical_value = true; | ||
| 514 | } break; | ||
| 515 | case ASN_FLOAT: // fallthrough | ||
| 516 | case ASN_DOUBLE: { | ||
| 517 | got_a_numerical_value = true; | ||
| 518 | double tmp = response.value.doubleVal; | ||
| 519 | if (eval_params.offset_set) { | ||
| 520 | tmp += eval_params.offset; | ||
| 521 | } | ||
| 522 | |||
| 523 | if (eval_params.multiplier_set) { | ||
| 524 | tmp *= eval_params.multiplier; | ||
| 525 | } | ||
| 526 | |||
| 527 | if (eval_params.calculate_rate && have_previous_state) { | ||
| 528 | pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff); | ||
| 529 | } else { | ||
| 530 | pd_result_val = mp_create_pd_value(tmp); | ||
| 531 | } | ||
| 532 | got_a_numerical_value = true; | ||
| 533 | |||
| 534 | result_state.value.doubleVal = tmp; | ||
| 535 | } break; | ||
| 536 | case ASN_IPADDRESS: | ||
| 537 | // TODO | ||
| 538 | break; | ||
| 539 | } | ||
| 540 | |||
| 541 | if (got_a_numerical_value) { | ||
| 542 | if (eval_params.use_oid_as_perf_data_label) { | ||
| 543 | // Use oid for perdata label | ||
| 544 | pd_num_val.label = strdup(oid_string); | ||
| 545 | // TODO strdup error checking | ||
| 546 | } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) { | ||
| 547 | pd_num_val.label = strdup(test_unit.label); | ||
| 548 | } else { | ||
| 549 | pd_num_val.label = strdup(test_unit.oid); | ||
| 550 | } | ||
| 551 | |||
| 552 | if (!(eval_params.calculate_rate && !have_previous_state)) { | ||
| 553 | // some kind of numerical value | ||
| 554 | if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { | ||
| 555 | pd_num_val.uom = test_unit.unit_value; | ||
| 556 | } | ||
| 557 | |||
| 558 | pd_num_val.value = pd_result_val; | ||
| 559 | |||
| 560 | xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, | ||
| 561 | pd_value_to_string(pd_result_val)); | ||
| 562 | |||
| 563 | if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { | ||
| 564 | xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value); | ||
| 565 | } | ||
| 566 | |||
| 567 | if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) { | ||
| 568 | pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold); | ||
| 569 | mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); | ||
| 570 | |||
| 571 | if (tmp_state == STATE_WARNING) { | ||
| 572 | sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); | ||
| 573 | xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", | ||
| 574 | sc_oid_test.output); | ||
| 575 | } else if (tmp_state == STATE_CRITICAL) { | ||
| 576 | sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); | ||
| 577 | xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", | ||
| 578 | sc_oid_test.output); | ||
| 579 | } | ||
| 580 | } | ||
| 581 | |||
| 582 | mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); | ||
| 583 | } else { | ||
| 584 | // should calculate rate, but there is no previous state, so first run | ||
| 585 | // exit with ok now | ||
| 586 | sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK); | ||
| 587 | xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay", | ||
| 588 | sc_oid_test.output); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | |||
| 592 | check_snmp_evaluation result = { | ||
| 593 | .sc = sc_oid_test, | ||
| 594 | .state = result_state, | ||
| 595 | }; | ||
| 596 | |||
| 597 | return result; | ||
| 598 | } | ||
| 599 | |||
| 600 | char *_np_state_generate_key(int argc, char **argv); | ||
| 601 | |||
| 602 | /* | ||
| 603 | * If time=NULL, use current time. Create state file, with state format | ||
| 604 | * version, default text. Writes version, time, and data. Avoid locking | ||
| 605 | * problems - use mv to write and then swap. Possible loss of state data if | ||
| 606 | * two things writing to same key at same time. | ||
| 607 | * Will die with UNKNOWN if errors | ||
| 608 | */ | ||
| 609 | void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) { | ||
| 610 | time_t current_time; | ||
| 611 | if (timestamp == 0) { | ||
| 612 | time(¤t_time); | ||
| 613 | } else { | ||
| 614 | current_time = timestamp; | ||
| 615 | } | ||
| 616 | |||
| 617 | int result = 0; | ||
| 618 | |||
| 619 | /* If file doesn't currently exist, create directories */ | ||
| 620 | if (access(stateKey._filename, F_OK) != 0) { | ||
| 621 | char *directories = NULL; | ||
| 622 | result = asprintf(&directories, "%s", stateKey._filename); | ||
| 623 | if (result < 0) { | ||
| 624 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 625 | } | ||
| 626 | |||
| 627 | for (char *p = directories + 1; *p; p++) { | ||
| 628 | if (*p == '/') { | ||
| 629 | *p = '\0'; | ||
| 630 | if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { | ||
| 631 | /* Can't free this! Otherwise error message is wrong! */ | ||
| 632 | /* np_free(directories); */ | ||
| 633 | die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); | ||
| 634 | } | ||
| 635 | *p = '/'; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | if (directories) { | ||
| 640 | free(directories); | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | char *temp_file = NULL; | ||
| 645 | result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename); | ||
| 646 | if (result < 0) { | ||
| 647 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 648 | } | ||
| 649 | |||
| 650 | int temp_file_desc = 0; | ||
| 651 | if ((temp_file_desc = mkstemp(temp_file)) == -1) { | ||
| 652 | if (temp_file) { | ||
| 653 | free(temp_file); | ||
| 654 | } | ||
| 655 | die(STATE_UNKNOWN, _("Cannot create temporary filename")); | ||
| 656 | } | ||
| 657 | |||
| 658 | FILE *temp_file_pointer = fdopen(temp_file_desc, "w"); | ||
| 659 | if (temp_file_pointer == NULL) { | ||
| 660 | close(temp_file_desc); | ||
| 661 | unlink(temp_file); | ||
| 662 | if (temp_file) { | ||
| 663 | free(temp_file); | ||
| 664 | } | ||
| 665 | die(STATE_UNKNOWN, _("Unable to open temporary state file")); | ||
| 666 | } | ||
| 667 | |||
| 668 | fprintf(temp_file_pointer, "# NP State file\n"); | ||
| 669 | fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); | ||
| 670 | fprintf(temp_file_pointer, "%d\n", stateKey.data_version); | ||
| 671 | fprintf(temp_file_pointer, "%lu\n", current_time); | ||
| 672 | fprintf(temp_file_pointer, "%s\n", stringToStore); | ||
| 673 | |||
| 674 | fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); | ||
| 675 | |||
| 676 | fflush(temp_file_pointer); | ||
| 677 | |||
| 678 | result = fclose(temp_file_pointer); | ||
| 679 | |||
| 680 | fsync(temp_file_desc); | ||
| 681 | |||
| 682 | if (result != 0) { | ||
| 683 | unlink(temp_file); | ||
| 684 | if (temp_file) { | ||
| 685 | free(temp_file); | ||
| 686 | } | ||
| 687 | die(STATE_UNKNOWN, _("Error writing temp file")); | ||
| 688 | } | ||
| 689 | |||
| 690 | if (rename(temp_file, stateKey._filename) != 0) { | ||
| 691 | unlink(temp_file); | ||
| 692 | if (temp_file) { | ||
| 693 | free(temp_file); | ||
| 694 | } | ||
| 695 | die(STATE_UNKNOWN, _("Cannot rename state temp file")); | ||
| 696 | } | ||
| 697 | |||
| 698 | if (temp_file) { | ||
| 699 | free(temp_file); | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | /* | ||
| 704 | * Read the state file | ||
| 705 | */ | ||
| 706 | bool _np_state_read_file(FILE *state_file, state_key stateKey) { | ||
| 707 | time_t current_time; | ||
| 708 | time(¤t_time); | ||
| 709 | |||
| 710 | /* Note: This introduces a limit of 8192 bytes in the string data */ | ||
| 711 | char *line = (char *)calloc(1, 8192); | ||
| 712 | if (line == NULL) { | ||
| 713 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 714 | } | ||
| 715 | |||
| 716 | bool status = false; | ||
| 717 | enum { | ||
| 718 | STATE_FILE_VERSION, | ||
| 719 | STATE_DATA_VERSION, | ||
| 720 | STATE_DATA_TIME, | ||
| 721 | STATE_DATA_TEXT, | ||
| 722 | STATE_DATA_END | ||
| 723 | } expected = STATE_FILE_VERSION; | ||
| 724 | |||
| 725 | int failure = 0; | ||
| 726 | while (!failure && (fgets(line, 8192, state_file)) != NULL) { | ||
| 727 | size_t pos = strlen(line); | ||
| 728 | if (line[pos - 1] == '\n') { | ||
| 729 | line[pos - 1] = '\0'; | ||
| 730 | } | ||
| 731 | |||
| 732 | if (line[0] == '#') { | ||
| 733 | continue; | ||
| 734 | } | ||
| 735 | |||
| 736 | switch (expected) { | ||
| 737 | case STATE_FILE_VERSION: { | ||
| 738 | int i = atoi(line); | ||
| 739 | if (i != NP_STATE_FORMAT_VERSION) { | ||
| 740 | failure++; | ||
| 741 | } else { | ||
| 742 | expected = STATE_DATA_VERSION; | ||
| 743 | } | ||
| 744 | } break; | ||
| 745 | case STATE_DATA_VERSION: { | ||
| 746 | int i = atoi(line); | ||
| 747 | if (i != stateKey.data_version) { | ||
| 748 | failure++; | ||
| 749 | } else { | ||
| 750 | expected = STATE_DATA_TIME; | ||
| 751 | } | ||
| 752 | } break; | ||
| 753 | case STATE_DATA_TIME: { | ||
| 754 | /* If time > now, error */ | ||
| 755 | time_t data_time = strtoul(line, NULL, 10); | ||
| 756 | if (data_time > current_time) { | ||
| 757 | failure++; | ||
| 758 | } else { | ||
| 759 | stateKey.state_data->time = data_time; | ||
| 760 | expected = STATE_DATA_TEXT; | ||
| 761 | } | ||
| 762 | } break; | ||
| 763 | case STATE_DATA_TEXT: | ||
| 764 | stateKey.state_data->data = strdup(line); | ||
| 765 | if (stateKey.state_data->data == NULL) { | ||
| 766 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); | ||
| 767 | } | ||
| 768 | stateKey.state_data->length = strlen(line); | ||
| 769 | expected = STATE_DATA_END; | ||
| 770 | status = true; | ||
| 771 | break; | ||
| 772 | case STATE_DATA_END:; | ||
| 773 | } | ||
| 774 | } | ||
| 775 | |||
| 776 | if (line) { | ||
| 777 | free(line); | ||
| 778 | } | ||
| 779 | return status; | ||
| 780 | } | ||
| 781 | /* | ||
| 782 | * Will return NULL if no data is available (first run). If key currently | ||
| 783 | * exists, read data. If state file format version is not expected, return | ||
| 784 | * as if no data. Get state data version number and compares to expected. | ||
| 785 | * If numerically lower, then return as no previous state. die with UNKNOWN | ||
| 786 | * if exceptional error. | ||
| 787 | */ | ||
| 788 | state_data *np_state_read(state_key stateKey) { | ||
| 789 | /* Open file. If this fails, no previous state found */ | ||
| 790 | FILE *statefile = fopen(stateKey._filename, "r"); | ||
| 791 | state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); | ||
| 792 | if (statefile != NULL) { | ||
| 793 | |||
| 794 | if (this_state_data == NULL) { | ||
| 795 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 796 | } | ||
| 797 | |||
| 798 | this_state_data->data = NULL; | ||
| 799 | stateKey.state_data = this_state_data; | ||
| 800 | |||
| 801 | if (_np_state_read_file(statefile, stateKey)) { | ||
| 802 | this_state_data->errorcode = OK; | ||
| 803 | } else { | ||
| 804 | this_state_data->errorcode = ERROR; | ||
| 805 | } | ||
| 806 | |||
| 807 | fclose(statefile); | ||
| 808 | } else { | ||
| 809 | // Failed to open state file | ||
| 810 | this_state_data->errorcode = ERROR; | ||
| 811 | } | ||
| 812 | |||
| 813 | return stateKey.state_data; | ||
| 814 | } | ||
| 815 | |||
| 816 | /* | ||
| 817 | * Internal function. Returns either: | ||
| 818 | * envvar NAGIOS_PLUGIN_STATE_DIRECTORY | ||
| 819 | * statically compiled shared state directory | ||
| 820 | */ | ||
| 821 | char *_np_state_calculate_location_prefix(void) { | ||
| 822 | char *env_dir; | ||
| 823 | |||
| 824 | /* Do not allow passing MP_STATE_PATH in setuid plugins | ||
| 825 | * for security reasons */ | ||
| 826 | if (!mp_suid()) { | ||
| 827 | env_dir = getenv("MP_STATE_PATH"); | ||
| 828 | if (env_dir && env_dir[0] != '\0') { | ||
| 829 | return env_dir; | ||
| 830 | } | ||
| 831 | /* This is the former ENV, for backward-compatibility */ | ||
| 832 | env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); | ||
| 833 | if (env_dir && env_dir[0] != '\0') { | ||
| 834 | return env_dir; | ||
| 835 | } | ||
| 836 | } | ||
| 837 | |||
| 838 | return NP_STATE_DIR_PREFIX; | ||
| 839 | } | ||
| 840 | |||
| 841 | /* | ||
| 842 | * Initiatializer for state routines. | ||
| 843 | * Sets variables. Generates filename. Returns np_state_key. die with | ||
| 844 | * UNKNOWN if exception | ||
| 845 | */ | ||
| 846 | state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, | ||
| 847 | char **argv) { | ||
| 848 | state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); | ||
| 849 | if (this_state == NULL) { | ||
| 850 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 851 | } | ||
| 852 | |||
| 853 | char *temp_keyname = NULL; | ||
| 854 | if (keyname == NULL) { | ||
| 855 | temp_keyname = _np_state_generate_key(argc, argv); | ||
| 856 | } else { | ||
| 857 | temp_keyname = strdup(keyname); | ||
| 858 | if (temp_keyname == NULL) { | ||
| 859 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); | ||
| 860 | } | ||
| 861 | } | ||
| 862 | |||
| 863 | /* Die if invalid characters used for keyname */ | ||
| 864 | char *tmp_char = temp_keyname; | ||
| 865 | while (*tmp_char != '\0') { | ||
| 866 | if (!(isalnum(*tmp_char) || *tmp_char == '_')) { | ||
| 867 | die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); | ||
| 868 | } | ||
| 869 | tmp_char++; | ||
| 870 | } | ||
| 871 | this_state->name = temp_keyname; | ||
| 872 | this_state->plugin_name = plugin_name; | ||
| 873 | this_state->data_version = expected_data_version; | ||
| 874 | this_state->state_data = NULL; | ||
| 875 | |||
| 876 | /* Calculate filename */ | ||
| 877 | char *temp_filename = NULL; | ||
| 878 | int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), | ||
| 879 | (unsigned long)geteuid(), plugin_name, this_state->name); | ||
| 880 | if (error < 0) { | ||
| 881 | die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); | ||
| 882 | } | ||
| 883 | |||
| 884 | this_state->_filename = temp_filename; | ||
| 885 | |||
| 886 | return *this_state; | ||
| 887 | } | ||
| 888 | |||
| 889 | /* | ||
| 890 | * Returns a string to use as a keyname, based on an md5 hash of argv, thus | ||
| 891 | * hopefully a unique key per service/plugin invocation. Use the extra-opts | ||
| 892 | * parse of argv, so that uniqueness in parameters are reflected there. | ||
| 893 | */ | ||
| 894 | char *_np_state_generate_key(int argc, char **argv) { | ||
| 895 | unsigned char result[256]; | ||
| 896 | |||
| 897 | #ifdef USE_OPENSSL | ||
| 898 | /* | ||
| 899 | * This code path is chosen if openssl is available (which should be the most common | ||
| 900 | * scenario). Alternatively, the gnulib implementation/ | ||
| 901 | * | ||
| 902 | */ | ||
| 903 | EVP_MD_CTX *ctx = EVP_MD_CTX_new(); | ||
| 904 | |||
| 905 | EVP_DigestInit(ctx, EVP_sha256()); | ||
| 906 | |||
| 907 | for (int i = 0; i < argc; i++) { | ||
| 908 | EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); | ||
| 909 | } | ||
| 910 | |||
| 911 | EVP_DigestFinal(ctx, result, NULL); | ||
| 912 | #else | ||
| 913 | |||
| 914 | struct sha256_ctx ctx; | ||
| 915 | |||
| 916 | for (int i = 0; i < this_monitoring_plugin->argc; i++) { | ||
| 917 | sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); | ||
| 918 | } | ||
| 919 | |||
| 920 | sha256_finish_ctx(&ctx, result); | ||
| 921 | #endif // FOUNDOPENSSL | ||
| 922 | |||
| 923 | char keyname[41]; | ||
| 924 | for (int i = 0; i < 20; ++i) { | ||
| 925 | sprintf(&keyname[2 * i], "%02x", result[i]); | ||
| 926 | } | ||
| 927 | |||
| 928 | keyname[40] = '\0'; | ||
| 929 | |||
| 930 | char *keyname_copy = strdup(keyname); | ||
| 931 | if (keyname_copy == NULL) { | ||
| 932 | die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); | ||
| 933 | } | ||
| 934 | |||
| 935 | return keyname_copy; | ||
| 936 | } | ||
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 | |||
| 6 | check_snmp_test_unit check_snmp_test_unit_init(); | ||
| 7 | int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool); | ||
| 8 | check_snmp_config check_snmp_config_init(); | ||
| 9 | |||
| 10 | typedef 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 | |||
| 22 | typedef struct { | ||
| 23 | int errorcode; | ||
| 24 | response_value *response_values; | ||
| 25 | } snmp_responces; | ||
| 26 | snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters); | ||
| 27 | |||
| 28 | // state is similar to response, but only numerics and a timestamp | ||
| 29 | typedef 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 | |||
| 41 | typedef struct { | ||
| 42 | check_snmp_state_entry state; | ||
| 43 | mp_subcheck sc; | ||
| 44 | } check_snmp_evaluation; | ||
| 45 | check_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 | |||
| 53 | typedef 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 | |||
| 60 | typedef 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 | |||
| 68 | state_data *np_state_read(state_key stateKey); | ||
| 69 | state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, | ||
| 70 | char **argv); | ||
| 71 | void 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 | |||
| 24 | typedef struct eval_method { | ||
| 25 | bool crit_string; | ||
| 26 | bool crit_regex; | ||
| 27 | } eval_method; | ||
| 28 | |||
| 29 | typedef 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 | |||
| 37 | typedef 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 | |||
| 50 | typedef 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 | |||
| 73 | typedef struct check_snmp_config { | ||
| 74 | // SNMP session to use | ||
| 75 | check_snmp_config_snmp_parameters snmp_params; | ||
| 76 | |||
| 77 | check_snmp_evaluation_parameters evaluation_params; | ||
| 78 | |||
| 79 | mp_output_format output_format; | ||
| 80 | bool output_format_is_set; | ||
| 81 | } check_snmp_config; | ||
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index 42a88cf9..f6c8d551 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c | |||
| @@ -28,6 +28,9 @@ | |||
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | #include "output.h" | ||
| 32 | #include "perfdata.h" | ||
| 33 | #include "states.h" | ||
| 31 | const char *progname = "check_ssh"; | 34 | const char *progname = "check_ssh"; |
| 32 | const char *copyright = "2000-2024"; | 35 | const char *copyright = "2000-2024"; |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 36 | const char *email = "devel@monitoring-plugins.org"; |
| @@ -35,26 +38,27 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 35 | #include "./common.h" | 38 | #include "./common.h" |
| 36 | #include "./netutils.h" | 39 | #include "./netutils.h" |
| 37 | #include "utils.h" | 40 | #include "utils.h" |
| 41 | #include "./check_ssh.d/config.h" | ||
| 38 | 42 | ||
| 39 | #ifndef MSG_DONTWAIT | 43 | #ifndef MSG_DONTWAIT |
| 40 | # define MSG_DONTWAIT 0 | 44 | # define MSG_DONTWAIT 0 |
| 41 | #endif | 45 | #endif |
| 42 | 46 | ||
| 43 | #define SSH_DFL_PORT 22 | 47 | #define BUFF_SZ 256 |
| 44 | #define BUFF_SZ 256 | ||
| 45 | 48 | ||
| 46 | static int port = -1; | ||
| 47 | static char *server_name = NULL; | ||
| 48 | static char *remote_version = NULL; | ||
| 49 | static char *remote_protocol = NULL; | ||
| 50 | static bool verbose = false; | 49 | static bool verbose = false; |
| 51 | 50 | ||
| 52 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 51 | typedef struct process_arguments_wrapper { |
| 53 | static int validate_arguments(void); | 52 | int errorcode; |
| 53 | check_ssh_config config; | ||
| 54 | } process_arguments_wrapper; | ||
| 55 | |||
| 56 | static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 54 | static void print_help(void); | 57 | static void print_help(void); |
| 55 | void print_usage(void); | 58 | void print_usage(void); |
| 56 | 59 | ||
| 57 | static int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_protocol); | 60 | static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, |
| 61 | char *remote_protocol); | ||
| 58 | 62 | ||
| 59 | int main(int argc, char **argv) { | 63 | int main(int argc, char **argv) { |
| 60 | setlocale(LC_ALL, ""); | 64 | setlocale(LC_ALL, ""); |
| @@ -64,51 +68,75 @@ int main(int argc, char **argv) { | |||
| 64 | /* Parse extra opts if any */ | 68 | /* Parse extra opts if any */ |
| 65 | argv = np_extra_opts(&argc, argv, progname); | 69 | argv = np_extra_opts(&argc, argv, progname); |
| 66 | 70 | ||
| 67 | if (process_arguments(argc, argv) == ERROR) | 71 | process_arguments_wrapper tmp_config = process_arguments(argc, argv); |
| 72 | |||
| 73 | if (tmp_config.errorcode == ERROR) { | ||
| 68 | usage4(_("Could not parse arguments")); | 74 | usage4(_("Could not parse arguments")); |
| 75 | } | ||
| 76 | |||
| 77 | check_ssh_config config = tmp_config.config; | ||
| 78 | |||
| 79 | mp_check overall = mp_check_init(); | ||
| 80 | if (config.output_format_is_set) { | ||
| 81 | mp_set_format(config.output_format); | ||
| 82 | } | ||
| 69 | 83 | ||
| 70 | /* initialize alarm signal handling */ | 84 | /* initialize alarm signal handling */ |
| 71 | signal(SIGALRM, socket_timeout_alarm_handler); | 85 | signal(SIGALRM, socket_timeout_alarm_handler); |
| 72 | |||
| 73 | alarm(socket_timeout); | 86 | alarm(socket_timeout); |
| 74 | 87 | ||
| 75 | /* ssh_connect exits if error is found */ | 88 | /* ssh_connect exits if error is found */ |
| 76 | int result = ssh_connect(server_name, port, remote_version, remote_protocol); | 89 | ssh_connect(&overall, config.server_name, config.port, config.remote_version, |
| 90 | config.remote_protocol); | ||
| 77 | 91 | ||
| 78 | alarm(0); | 92 | alarm(0); |
| 79 | 93 | ||
| 80 | return (result); | 94 | mp_exit(overall); |
| 81 | } | 95 | } |
| 82 | 96 | ||
| 97 | #define output_format_index CHAR_MAX + 1 | ||
| 98 | |||
| 83 | /* process command-line arguments */ | 99 | /* process command-line arguments */ |
| 84 | int process_arguments(int argc, char **argv) { | 100 | process_arguments_wrapper process_arguments(int argc, char **argv) { |
| 85 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, | 101 | static struct option longopts[] = { |
| 86 | {"version", no_argument, 0, 'V'}, | 102 | {"help", no_argument, 0, 'h'}, |
| 87 | {"host", required_argument, 0, 'H'}, /* backward compatibility */ | 103 | {"version", no_argument, 0, 'V'}, |
| 88 | {"hostname", required_argument, 0, 'H'}, | 104 | {"host", required_argument, 0, 'H'}, /* backward compatibility */ |
| 89 | {"port", required_argument, 0, 'p'}, | 105 | {"hostname", required_argument, 0, 'H'}, |
| 90 | {"use-ipv4", no_argument, 0, '4'}, | 106 | {"port", required_argument, 0, 'p'}, |
| 91 | {"use-ipv6", no_argument, 0, '6'}, | 107 | {"use-ipv4", no_argument, 0, '4'}, |
| 92 | {"timeout", required_argument, 0, 't'}, | 108 | {"use-ipv6", no_argument, 0, '6'}, |
| 93 | {"verbose", no_argument, 0, 'v'}, | 109 | {"timeout", required_argument, 0, 't'}, |
| 94 | {"remote-version", required_argument, 0, 'r'}, | 110 | {"verbose", no_argument, 0, 'v'}, |
| 95 | {"remote-protocol", required_argument, 0, 'P'}, | 111 | {"remote-version", required_argument, 0, 'r'}, |
| 96 | {0, 0, 0, 0}}; | 112 | {"remote-protocol", required_argument, 0, 'P'}, |
| 97 | 113 | {"output-format", required_argument, 0, output_format_index}, | |
| 98 | if (argc < 2) | 114 | {0, 0, 0, 0}}; |
| 99 | return ERROR; | 115 | |
| 100 | 116 | process_arguments_wrapper result = { | |
| 101 | for (int i = 1; i < argc; i++) | 117 | .config = check_ssh_config_init(), |
| 102 | if (strcmp("-to", argv[i]) == 0) | 118 | .errorcode = OK, |
| 119 | }; | ||
| 120 | |||
| 121 | if (argc < 2) { | ||
| 122 | result.errorcode = ERROR; | ||
| 123 | return result; | ||
| 124 | } | ||
| 125 | |||
| 126 | for (int i = 1; i < argc; i++) { | ||
| 127 | if (strcmp("-to", argv[i]) == 0) { | ||
| 103 | strcpy(argv[i], "-t"); | 128 | strcpy(argv[i], "-t"); |
| 129 | } | ||
| 130 | } | ||
| 104 | 131 | ||
| 105 | int option_char; | 132 | int option_char; |
| 106 | while (true) { | 133 | while (true) { |
| 107 | int option = 0; | 134 | int option = 0; |
| 108 | option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option); | 135 | option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option); |
| 109 | 136 | ||
| 110 | if (option_char == -1 || option_char == EOF) | 137 | if (option_char == -1 || option_char == EOF) { |
| 111 | break; | 138 | break; |
| 139 | } | ||
| 112 | 140 | ||
| 113 | switch (option_char) { | 141 | switch (option_char) { |
| 114 | case '?': /* help */ | 142 | case '?': /* help */ |
| @@ -123,10 +151,11 @@ int process_arguments(int argc, char **argv) { | |||
| 123 | verbose = true; | 151 | verbose = true; |
| 124 | break; | 152 | break; |
| 125 | case 't': /* timeout period */ | 153 | case 't': /* timeout period */ |
| 126 | if (!is_integer(optarg)) | 154 | if (!is_intpos(optarg)) { |
| 127 | usage2(_("Timeout interval must be a positive integer"), optarg); | 155 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 128 | else | 156 | } else { |
| 129 | socket_timeout = atoi(optarg); | 157 | socket_timeout = (unsigned int)atoi(optarg); |
| 158 | } | ||
| 130 | break; | 159 | break; |
| 131 | case '4': | 160 | case '4': |
| 132 | address_family = AF_INET; | 161 | address_family = AF_INET; |
| @@ -139,50 +168,61 @@ int process_arguments(int argc, char **argv) { | |||
| 139 | #endif | 168 | #endif |
| 140 | break; | 169 | break; |
| 141 | case 'r': /* remote version */ | 170 | case 'r': /* remote version */ |
| 142 | remote_version = optarg; | 171 | result.config.remote_version = optarg; |
| 143 | break; | 172 | break; |
| 144 | case 'P': /* remote version */ | 173 | case 'P': /* remote version */ |
| 145 | remote_protocol = optarg; | 174 | result.config.remote_protocol = optarg; |
| 146 | break; | 175 | break; |
| 147 | case 'H': /* host */ | 176 | case 'H': /* host */ |
| 148 | if (!is_host(optarg)) | 177 | if (!is_host(optarg)) { |
| 149 | usage2(_("Invalid hostname/address"), optarg); | 178 | usage2(_("Invalid hostname/address"), optarg); |
| 150 | server_name = optarg; | 179 | } |
| 180 | result.config.server_name = optarg; | ||
| 151 | break; | 181 | break; |
| 152 | case 'p': /* port */ | 182 | case 'p': /* port */ |
| 153 | if (is_intpos(optarg)) { | 183 | if (is_intpos(optarg)) { |
| 154 | port = atoi(optarg); | 184 | result.config.port = atoi(optarg); |
| 155 | } else { | 185 | } else { |
| 156 | usage2(_("Port number must be a positive integer"), optarg); | 186 | usage2(_("Port number must be a positive integer"), optarg); |
| 157 | } | 187 | } |
| 188 | break; | ||
| 189 | case output_format_index: { | ||
| 190 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 191 | if (!parser.parsing_success) { | ||
| 192 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 193 | printf("Invalid output format: %s\n", optarg); | ||
| 194 | exit(STATE_UNKNOWN); | ||
| 195 | } | ||
| 196 | |||
| 197 | result.config.output_format_is_set = true; | ||
| 198 | result.config.output_format = parser.output_format; | ||
| 199 | break; | ||
| 200 | } | ||
| 158 | } | 201 | } |
| 159 | } | 202 | } |
| 160 | 203 | ||
| 161 | option_char = optind; | 204 | option_char = optind; |
| 162 | if (server_name == NULL && option_char < argc) { | 205 | if (result.config.server_name == NULL && option_char < argc) { |
| 163 | if (is_host(argv[option_char])) { | 206 | if (is_host(argv[option_char])) { |
| 164 | server_name = argv[option_char++]; | 207 | result.config.server_name = argv[option_char++]; |
| 165 | } | 208 | } |
| 166 | } | 209 | } |
| 167 | 210 | ||
| 168 | if (port == -1 && option_char < argc) { | 211 | if (result.config.port == -1 && option_char < argc) { |
| 169 | if (is_intpos(argv[option_char])) { | 212 | if (is_intpos(argv[option_char])) { |
| 170 | port = atoi(argv[option_char++]); | 213 | result.config.port = atoi(argv[option_char++]); |
| 171 | } else { | 214 | } else { |
| 172 | print_usage(); | 215 | print_usage(); |
| 173 | exit(STATE_UNKNOWN); | 216 | exit(STATE_UNKNOWN); |
| 174 | } | 217 | } |
| 175 | } | 218 | } |
| 176 | 219 | ||
| 177 | return validate_arguments(); | 220 | if (result.config.server_name == NULL) { |
| 178 | } | 221 | result.errorcode = ERROR; |
| 222 | return result; | ||
| 223 | } | ||
| 179 | 224 | ||
| 180 | int validate_arguments(void) { | 225 | return result; |
| 181 | if (server_name == NULL) | ||
| 182 | return ERROR; | ||
| 183 | if (port == -1) /* funky, but allows -p to override stray integer in args */ | ||
| 184 | port = SSH_DFL_PORT; | ||
| 185 | return OK; | ||
| 186 | } | 226 | } |
| 187 | 227 | ||
| 188 | /************************************************************************ | 228 | /************************************************************************ |
| @@ -191,36 +231,45 @@ int validate_arguments(void) { | |||
| 191 | * | 231 | * |
| 192 | *-----------------------------------------------------------------------*/ | 232 | *-----------------------------------------------------------------------*/ |
| 193 | 233 | ||
| 194 | int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_protocol) { | 234 | int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, |
| 235 | char *desired_remote_protocol) { | ||
| 195 | struct timeval tv; | 236 | struct timeval tv; |
| 196 | gettimeofday(&tv, NULL); | 237 | gettimeofday(&tv, NULL); |
| 197 | 238 | ||
| 198 | int socket; | 239 | int socket; |
| 199 | int result = my_tcp_connect(haddr, hport, &socket); | 240 | int result = my_tcp_connect(haddr, hport, &socket); |
| 200 | 241 | ||
| 201 | if (result != STATE_OK) | 242 | mp_subcheck connection_sc = mp_subcheck_init(); |
| 243 | if (result != STATE_OK) { | ||
| 244 | connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); | ||
| 245 | xasprintf(&connection_sc.output, | ||
| 246 | "Failed to establish TCP connection to Host %s and Port %d", haddr, hport); | ||
| 247 | mp_add_subcheck_to_check(overall, connection_sc); | ||
| 202 | return result; | 248 | return result; |
| 249 | } | ||
| 203 | 250 | ||
| 204 | char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); | 251 | char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); |
| 205 | char *buffer = NULL; | 252 | char *buffer = NULL; |
| 206 | ssize_t recv_ret = 0; | 253 | ssize_t recv_ret = 0; |
| 207 | char *version_control_string = NULL; | 254 | char *version_control_string = NULL; |
| 208 | ssize_t byte_offset = 0; | 255 | size_t byte_offset = 0; |
| 209 | while ((version_control_string == NULL) && (recv_ret = recv(socket, output + byte_offset, BUFF_SZ - byte_offset, 0) > 0)) { | 256 | while ((version_control_string == NULL) && |
| 257 | (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), | ||
| 258 | 0) > 0)) { | ||
| 210 | 259 | ||
| 211 | if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ | 260 | if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ |
| 212 | byte_offset = 0; | 261 | byte_offset = 0; |
| 213 | 262 | ||
| 214 | char *index = NULL; | 263 | char *index = NULL; |
| 215 | int len = 0; | ||
| 216 | while ((index = strchr(output + byte_offset, '\n')) != NULL) { | 264 | while ((index = strchr(output + byte_offset, '\n')) != NULL) { |
| 217 | /*Partition the buffer so that this line is a separate string, | 265 | /*Partition the buffer so that this line is a separate string, |
| 218 | * by replacing the newline with NUL*/ | 266 | * by replacing the newline with NUL*/ |
| 219 | output[(index - output)] = '\0'; | 267 | output[(index - output)] = '\0'; |
| 220 | len = strlen(output + byte_offset); | 268 | size_t len = strlen(output + byte_offset); |
| 221 | 269 | ||
| 222 | if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { | 270 | if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { |
| 223 | /*if the string starts with SSH-, this _should_ be a valid version control string*/ | 271 | /*if the string starts with SSH-, this _should_ be a valid version control |
| 272 | * string*/ | ||
| 224 | version_control_string = output + byte_offset; | 273 | version_control_string = output + byte_offset; |
| 225 | break; | 274 | break; |
| 226 | } | 275 | } |
| @@ -230,27 +279,38 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto | |||
| 230 | } | 279 | } |
| 231 | 280 | ||
| 232 | if (version_control_string == NULL) { | 281 | if (version_control_string == NULL) { |
| 233 | /* move unconsumed data to beginning of buffer, null rest */ | 282 | /* move unconsumed data to beginning of buffer */ |
| 234 | memmove((void *)output, (void *)(output + byte_offset + 1), BUFF_SZ - len + 1); | 283 | memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset); |
| 235 | memset(output + byte_offset + 1, 0, BUFF_SZ - byte_offset + 1); | ||
| 236 | 284 | ||
| 237 | /*start reading from end of current line chunk on next recv*/ | 285 | /*start reading from end of current line chunk on next recv*/ |
| 238 | byte_offset = strlen(output); | 286 | byte_offset = strlen(output); |
| 287 | |||
| 288 | /* NUL the rest of the buffer */ | ||
| 289 | memset(output + byte_offset, 0, BUFF_SZ - byte_offset); | ||
| 239 | } | 290 | } |
| 240 | } else { | 291 | } else { |
| 241 | byte_offset += recv_ret; | 292 | byte_offset += (size_t)recv_ret; |
| 242 | } | 293 | } |
| 243 | } | 294 | } |
| 244 | 295 | ||
| 245 | if (recv_ret < 0) { | 296 | if (recv_ret < 0) { |
| 246 | printf("SSH CRITICAL - %s", strerror(errno)); | 297 | connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); |
| 247 | exit(STATE_CRITICAL); | 298 | xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno)); |
| 299 | mp_add_subcheck_to_check(overall, connection_sc); | ||
| 300 | return OK; | ||
| 248 | } | 301 | } |
| 249 | 302 | ||
| 250 | if (version_control_string == NULL) { | 303 | if (version_control_string == NULL) { |
| 251 | printf("SSH CRITICAL - No version control string received"); | 304 | connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); |
| 252 | exit(STATE_CRITICAL); | 305 | xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - No version control string received"); |
| 306 | mp_add_subcheck_to_check(overall, connection_sc); | ||
| 307 | return OK; | ||
| 253 | } | 308 | } |
| 309 | |||
| 310 | connection_sc = mp_set_subcheck_state(connection_sc, STATE_OK); | ||
| 311 | xasprintf(&connection_sc.output, "%s", "Initial connection succeeded"); | ||
| 312 | mp_add_subcheck_to_check(overall, connection_sc); | ||
| 313 | |||
| 254 | /* | 314 | /* |
| 255 | * "When the connection has been established, both sides MUST send an | 315 | * "When the connection has been established, both sides MUST send an |
| 256 | * identification string. This identification string MUST be | 316 | * identification string. This identification string MUST be |
| @@ -259,8 +319,9 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto | |||
| 259 | * - RFC 4253:4.2 | 319 | * - RFC 4253:4.2 |
| 260 | */ | 320 | */ |
| 261 | strip(version_control_string); | 321 | strip(version_control_string); |
| 262 | if (verbose) | 322 | if (verbose) { |
| 263 | printf("%s\n", version_control_string); | 323 | printf("%s\n", version_control_string); |
| 324 | } | ||
| 264 | 325 | ||
| 265 | char *ssh_proto = version_control_string + 4; | 326 | char *ssh_proto = version_control_string + 4; |
| 266 | 327 | ||
| @@ -280,7 +341,8 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto | |||
| 280 | * "1.x" (e.g., "1.5" or "1.3")." | 341 | * "1.x" (e.g., "1.5" or "1.3")." |
| 281 | * - RFC 4253:5 | 342 | * - RFC 4253:5 |
| 282 | */ | 343 | */ |
| 283 | char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ | 344 | char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + |
| 345 | 1; /* (+1 for the '-' separating protoversion from softwareversion) */ | ||
| 284 | 346 | ||
| 285 | /* If there's a space in the version string, whatever's after the space is a comment | 347 | /* If there's a space in the version string, whatever's after the space is a comment |
| 286 | * (which is NOT part of the server name/version)*/ | 348 | * (which is NOT part of the server name/version)*/ |
| @@ -288,41 +350,69 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto | |||
| 288 | if (tmp) { | 350 | if (tmp) { |
| 289 | ssh_server[tmp - ssh_server] = '\0'; | 351 | ssh_server[tmp - ssh_server] = '\0'; |
| 290 | } | 352 | } |
| 353 | |||
| 354 | mp_subcheck protocol_validity_sc = mp_subcheck_init(); | ||
| 291 | if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { | 355 | if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { |
| 292 | printf(_("SSH CRITICAL - Invalid protocol version control string %s\n"), version_control_string); | 356 | protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL); |
| 293 | exit(STATE_CRITICAL); | 357 | xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", |
| 358 | version_control_string); | ||
| 359 | mp_add_subcheck_to_check(overall, protocol_validity_sc); | ||
| 360 | return OK; | ||
| 294 | } | 361 | } |
| 362 | |||
| 363 | protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK); | ||
| 364 | xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", | ||
| 365 | version_control_string); | ||
| 366 | mp_add_subcheck_to_check(overall, protocol_validity_sc); | ||
| 367 | |||
| 295 | ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; | 368 | ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; |
| 296 | 369 | ||
| 297 | static char *rev_no = VERSION; | 370 | static char *rev_no = VERSION; |
| 298 | xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no); | 371 | xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no); |
| 299 | send(socket, buffer, strlen(buffer), MSG_DONTWAIT); | 372 | send(socket, buffer, strlen(buffer), MSG_DONTWAIT); |
| 300 | if (verbose) | 373 | if (verbose) { |
| 301 | printf("%s\n", buffer); | 374 | printf("%s\n", buffer); |
| 375 | } | ||
| 302 | 376 | ||
| 303 | if (remote_version && strcmp(remote_version, ssh_server)) { | 377 | if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) { |
| 304 | printf(_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"), ssh_server, ssh_proto, remote_version); | 378 | mp_subcheck remote_version_sc = mp_subcheck_init(); |
| 379 | remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL); | ||
| 380 | xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), | ||
| 381 | ssh_server, ssh_proto, desired_remote_version); | ||
| 305 | close(socket); | 382 | close(socket); |
| 306 | exit(STATE_CRITICAL); | 383 | mp_add_subcheck_to_check(overall, remote_version_sc); |
| 384 | return OK; | ||
| 307 | } | 385 | } |
| 308 | 386 | ||
| 309 | double elapsed_time = (double)deltime(tv) / 1.0e6; | 387 | double elapsed_time = (double)deltime(tv) / 1.0e6; |
| 310 | if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { | 388 | mp_perfdata time_pd = perfdata_init(); |
| 311 | printf(_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s' | %s\n"), ssh_server, ssh_proto, remote_protocol, | 389 | time_pd.value = mp_create_pd_value(elapsed_time); |
| 312 | fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, true, (int)socket_timeout)); | 390 | time_pd.label = "time"; |
| 313 | close(socket); | 391 | time_pd.max_present = true; |
| 314 | exit(STATE_CRITICAL); | 392 | time_pd.max = mp_create_pd_value(socket_timeout); |
| 393 | |||
| 394 | mp_subcheck protocol_version_sc = mp_subcheck_init(); | ||
| 395 | mp_add_perfdata_to_subcheck(&protocol_version_sc, time_pd); | ||
| 396 | |||
| 397 | if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) { | ||
| 398 | protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL); | ||
| 399 | xasprintf(&protocol_version_sc.output, | ||
| 400 | _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, | ||
| 401 | ssh_proto, desired_remote_protocol); | ||
| 402 | } else { | ||
| 403 | protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK); | ||
| 404 | xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", | ||
| 405 | ssh_server, ssh_proto); | ||
| 315 | } | 406 | } |
| 316 | 407 | ||
| 317 | printf(_("SSH OK - %s (protocol %s) | %s\n"), ssh_server, ssh_proto, | 408 | mp_add_subcheck_to_check(overall, protocol_version_sc); |
| 318 | fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, true, (int)socket_timeout)); | ||
| 319 | close(socket); | 409 | close(socket); |
| 320 | exit(STATE_OK); | 410 | return OK; |
| 321 | } | 411 | } |
| 322 | 412 | ||
| 323 | void print_help(void) { | 413 | void print_help(void) { |
| 324 | char *myport; | 414 | char *myport; |
| 325 | xasprintf(&myport, "%d", SSH_DFL_PORT); | 415 | xasprintf(&myport, "%d", default_ssh_port); |
| 326 | 416 | ||
| 327 | print_revision(progname, NP_VERSION); | 417 | print_revision(progname, NP_VERSION); |
| 328 | 418 | ||
| @@ -345,10 +435,12 @@ void print_help(void) { | |||
| 345 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 435 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 346 | 436 | ||
| 347 | printf(" %s\n", "-r, --remote-version=STRING"); | 437 | printf(" %s\n", "-r, --remote-version=STRING"); |
| 348 | printf(" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); | 438 | printf(" %s\n", |
| 439 | _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); | ||
| 349 | 440 | ||
| 350 | printf(" %s\n", "-P, --remote-protocol=STRING"); | 441 | printf(" %s\n", "-P, --remote-protocol=STRING"); |
| 351 | printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); | 442 | printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); |
| 443 | printf(UT_OUTPUT_FORMAT); | ||
| 352 | 444 | ||
| 353 | printf(UT_VERBOSE); | 445 | printf(UT_VERBOSE); |
| 354 | 446 | ||
| @@ -357,5 +449,6 @@ void print_help(void) { | |||
| 357 | 449 | ||
| 358 | void print_usage(void) { | 450 | void print_usage(void) { |
| 359 | printf("%s\n", _("Usage:")); | 451 | printf("%s\n", _("Usage:")); |
| 360 | printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname); | 452 | printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n", |
| 453 | progname); | ||
| 361 | } | 454 | } |
diff --git a/plugins/check_ssh.d/config.h b/plugins/check_ssh.d/config.h new file mode 100644 index 00000000..c150fd30 --- /dev/null +++ b/plugins/check_ssh.d/config.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <stddef.h> | ||
| 4 | #include "../../lib/monitoringplug.h" | ||
| 5 | |||
| 6 | const int default_ssh_port = 22; | ||
| 7 | |||
| 8 | typedef struct check_ssh_config { | ||
| 9 | int port; | ||
| 10 | char *server_name; | ||
| 11 | char *remote_version; | ||
| 12 | char *remote_protocol; | ||
| 13 | |||
| 14 | bool output_format_is_set; | ||
| 15 | mp_output_format output_format; | ||
| 16 | } check_ssh_config; | ||
| 17 | |||
| 18 | check_ssh_config check_ssh_config_init(void) { | ||
| 19 | check_ssh_config tmp = { | ||
| 20 | .port = default_ssh_port, | ||
| 21 | .server_name = NULL, | ||
| 22 | .remote_version = NULL, | ||
| 23 | .remote_protocol = NULL, | ||
| 24 | |||
| 25 | .output_format_is_set = false, | ||
| 26 | }; | ||
| 27 | |||
| 28 | return tmp; | ||
| 29 | } | ||
diff --git a/plugins/check_swap.c b/plugins/check_swap.c index bc90a90b..dbf53a00 100644 --- a/plugins/check_swap.c +++ b/plugins/check_swap.c | |||
| @@ -28,6 +28,9 @@ | |||
| 28 | *****************************************************************************/ | 28 | *****************************************************************************/ |
| 29 | 29 | ||
| 30 | #include "common.h" | 30 | #include "common.h" |
| 31 | #include "output.h" | ||
| 32 | #include "states.h" | ||
| 33 | #include <limits.h> | ||
| 31 | #ifdef HAVE_DECL_SWAPCTL | 34 | #ifdef HAVE_DECL_SWAPCTL |
| 32 | # ifdef HAVE_SYS_PARAM_H | 35 | # ifdef HAVE_SYS_PARAM_H |
| 33 | # include <sys/param.h> | 36 | # include <sys/param.h> |
| @@ -69,8 +72,6 @@ int main(int argc, char **argv) { | |||
| 69 | bindtextdomain(PACKAGE, LOCALEDIR); | 72 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 70 | textdomain(PACKAGE); | 73 | textdomain(PACKAGE); |
| 71 | 74 | ||
| 72 | char *status = strdup(""); | ||
| 73 | |||
| 74 | /* Parse extra opts if any */ | 75 | /* Parse extra opts if any */ |
| 75 | argv = np_extra_opts(&argc, argv, progname); | 76 | argv = np_extra_opts(&argc, argv, progname); |
| 76 | 77 | ||
| @@ -89,60 +90,112 @@ int main(int argc, char **argv) { | |||
| 89 | exit(STATE_UNKNOWN); | 90 | exit(STATE_UNKNOWN); |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 93 | if (verbose) { | ||
| 94 | printf("Swap retrieval result:\n" | ||
| 95 | "\tFree: %llu\n" | ||
| 96 | "\tUsed: %llu\n" | ||
| 97 | "\tTotal: %llu\n", | ||
| 98 | data.metrics.free, data.metrics.used, data.metrics.total); | ||
| 99 | } | ||
| 100 | |||
| 92 | double percent_used; | 101 | double percent_used; |
| 102 | mp_check overall = mp_check_init(); | ||
| 103 | if (config.output_format_is_set) { | ||
| 104 | mp_set_format(config.output_format); | ||
| 105 | } | ||
| 106 | mp_subcheck sc1 = mp_subcheck_init(); | ||
| 107 | sc1 = mp_set_subcheck_default_state(sc1, STATE_OK); | ||
| 108 | |||
| 93 | /* if total_swap_mb == 0, let's not divide by 0 */ | 109 | /* if total_swap_mb == 0, let's not divide by 0 */ |
| 94 | if (data.metrics.total != 0) { | 110 | if (data.metrics.total != 0) { |
| 95 | percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total); | 111 | percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total); |
| 96 | } else { | 112 | } else { |
| 97 | printf(_("SWAP %s - Swap is either disabled, not present, or of zero " | 113 | sc1 = mp_set_subcheck_state(sc1, config.no_swap_state); |
| 98 | "size."), | 114 | sc1.output = (char *)_("Swap is either disabled, not present, or of zero size."); |
| 99 | state_text(data.statusCode)); | 115 | |
| 100 | exit(config.no_swap_state); | 116 | mp_add_subcheck_to_check(&overall, sc1); |
| 117 | mp_exit(overall); | ||
| 101 | } | 118 | } |
| 102 | 119 | ||
| 103 | if (verbose) { | 120 | if (verbose) { |
| 104 | printf("Computed usage percentage: %g\n", percent_used); | 121 | printf("Computed usage percentage: %g\n", percent_used); |
| 105 | } | 122 | } |
| 106 | 123 | ||
| 107 | uint64_t warn_print = config.warn.value; | 124 | mp_perfdata pd = perfdata_init(); |
| 108 | if (config.warn.is_percentage) { | 125 | pd.label = "swap"; |
| 109 | warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT); | 126 | pd = mp_set_pd_value(pd, data.metrics.free); |
| 127 | pd.uom = "B"; | ||
| 128 | |||
| 129 | if (config.warn_is_set) { | ||
| 130 | uint64_t warn_print = config.warn.value; | ||
| 131 | if (config.warn.is_percentage) { | ||
| 132 | warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT); | ||
| 133 | } | ||
| 134 | |||
| 135 | mp_perfdata_value warn_pd = mp_create_pd_value(warn_print); | ||
| 136 | |||
| 137 | mp_range warn_range = mp_range_init(); | ||
| 138 | warn_range.end_infinity = false; | ||
| 139 | warn_range.end = warn_pd; | ||
| 140 | |||
| 141 | pd.warn = warn_range; | ||
| 142 | pd.warn_present = true; | ||
| 110 | } | 143 | } |
| 111 | 144 | ||
| 112 | uint64_t crit_print = config.crit.value; | 145 | if (config.crit_is_set) { |
| 113 | if (config.crit.is_percentage) { | 146 | uint64_t crit_print = config.crit.value; |
| 114 | crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT); | 147 | if (config.crit.is_percentage) { |
| 148 | crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT); | ||
| 149 | } | ||
| 150 | |||
| 151 | mp_perfdata_value crit_pd = mp_create_pd_value(crit_print); | ||
| 152 | |||
| 153 | mp_range crit_range = mp_range_init(); | ||
| 154 | crit_range.end_infinity = false; | ||
| 155 | crit_range.end = crit_pd; | ||
| 156 | |||
| 157 | pd.crit = crit_range; | ||
| 158 | pd.crit_present = true; | ||
| 115 | } | 159 | } |
| 116 | 160 | ||
| 117 | char *perfdata = perfdata_uint64("swap", data.metrics.free, "B", config.warn_is_set, warn_print, config.crit_is_set, crit_print, true, | 161 | mp_perfdata_value max = mp_create_pd_value(data.metrics.total); |
| 118 | 0, true, data.metrics.total); | 162 | pd.max = max; |
| 163 | pd.max_present = true; | ||
| 119 | 164 | ||
| 120 | if (config.warn_is_set) { | 165 | mp_perfdata_value min = mp_create_pd_value(0); |
| 121 | if (verbose > 1) { | 166 | pd.min = min; |
| 122 | printf("Warn threshold value: %" PRIu64 "\n", config.warn.value); | 167 | pd.min_present = true; |
| 123 | } | 168 | |
| 169 | mp_add_perfdata_to_subcheck(&sc1, pd); | ||
| 170 | if (verbose > 1) { | ||
| 171 | printf("Warn threshold value: %" PRIu64 "\n", config.warn.value); | ||
| 172 | } | ||
| 124 | 173 | ||
| 125 | if ((config.warn.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.warn.value))) || | 174 | if (config.warn_is_set) { |
| 175 | if ((config.warn.is_percentage && (percent_used >= (100 - (double)config.warn.value))) || | ||
| 126 | config.warn.value >= data.metrics.free) { | 176 | config.warn.value >= data.metrics.free) { |
| 127 | data.statusCode = max_state(data.statusCode, STATE_WARNING); | 177 | sc1 = mp_set_subcheck_state(sc1, STATE_WARNING); |
| 128 | } | 178 | } |
| 129 | } | 179 | } |
| 130 | 180 | ||
| 131 | if (config.crit_is_set) { | 181 | if (verbose > 1) { |
| 132 | if (verbose > 1) { | 182 | printf("Crit threshold value: %" PRIu64 "\n", config.crit.value); |
| 133 | printf("Crit threshold value: %" PRIu64 "\n", config.crit.value); | 183 | } |
| 134 | } | ||
| 135 | 184 | ||
| 136 | if ((config.crit.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.crit.value))) || | 185 | if (config.crit_is_set) { |
| 186 | if ((config.crit.is_percentage && (percent_used >= (100 - (double)config.crit.value))) || | ||
| 137 | config.crit.value >= data.metrics.free) { | 187 | config.crit.value >= data.metrics.free) { |
| 138 | data.statusCode = max_state(data.statusCode, STATE_CRITICAL); | 188 | sc1 = mp_set_subcheck_state(sc1, STATE_CRITICAL); |
| 139 | } | 189 | } |
| 140 | } | 190 | } |
| 141 | 191 | ||
| 142 | printf(_("SWAP %s - %g%% free (%lluMiB out of %lluMiB) %s|%s\n"), state_text(data.statusCode), (HUNDRED_PERCENT - percent_used), | 192 | xasprintf(&sc1.output, _("%g%% free (%lluMiB out of %lluMiB)"), (100 - percent_used), |
| 143 | BYTES_TO_MiB(data.metrics.free), BYTES_TO_MiB(data.metrics.total), status, perfdata); | 193 | data.metrics.free >> 20, data.metrics.total >> 20); |
| 144 | 194 | ||
| 145 | exit(data.statusCode); | 195 | overall.summary = "Swap"; |
| 196 | mp_add_subcheck_to_check(&overall, sc1); | ||
| 197 | |||
| 198 | mp_exit(overall); | ||
| 146 | } | 199 | } |
| 147 | 200 | ||
| 148 | int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) { | 201 | int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) { |
| @@ -150,7 +203,9 @@ int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) { | |||
| 150 | return config.no_swap_state; | 203 | return config.no_swap_state; |
| 151 | } | 204 | } |
| 152 | 205 | ||
| 153 | uint64_t free_swap = (uint64_t)(free_swap_mb * (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */ | 206 | uint64_t free_swap = |
| 207 | (uint64_t)(free_swap_mb * | ||
| 208 | (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */ | ||
| 154 | 209 | ||
| 155 | if (!config.crit.is_percentage && config.crit.value >= free_swap) { | 210 | if (!config.crit.is_percentage && config.crit.value >= free_swap) { |
| 156 | return STATE_CRITICAL; | 211 | return STATE_CRITICAL; |
| @@ -159,28 +214,38 @@ int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) { | |||
| 159 | return STATE_WARNING; | 214 | return STATE_WARNING; |
| 160 | } | 215 | } |
| 161 | 216 | ||
| 162 | uint64_t usage_percentage = (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT; | 217 | uint64_t usage_percentage = |
| 218 | (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT; | ||
| 163 | 219 | ||
| 164 | if (config.crit.is_percentage && config.crit.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) { | 220 | if (config.crit.is_percentage && config.crit.value != 0 && |
| 221 | usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) { | ||
| 165 | return STATE_CRITICAL; | 222 | return STATE_CRITICAL; |
| 166 | } | 223 | } |
| 167 | 224 | ||
| 168 | if (config.warn.is_percentage && config.warn.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) { | 225 | if (config.warn.is_percentage && config.warn.value != 0 && |
| 226 | usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) { | ||
| 169 | return STATE_WARNING; | 227 | return STATE_WARNING; |
| 170 | } | 228 | } |
| 171 | 229 | ||
| 172 | return STATE_OK; | 230 | return STATE_OK; |
| 173 | } | 231 | } |
| 174 | 232 | ||
| 233 | #define output_format_index CHAR_MAX + 1 | ||
| 234 | |||
| 175 | /* process command-line arguments */ | 235 | /* process command-line arguments */ |
| 176 | swap_config_wrapper process_arguments(int argc, char **argv) { | 236 | swap_config_wrapper process_arguments(int argc, char **argv) { |
| 177 | swap_config_wrapper conf_wrapper = {.errorcode = OK}; | 237 | swap_config_wrapper conf_wrapper = {.errorcode = OK}; |
| 178 | conf_wrapper.config = swap_config_init(); | 238 | conf_wrapper.config = swap_config_init(); |
| 179 | 239 | ||
| 180 | static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, | 240 | static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, |
| 181 | {"allswaps", no_argument, 0, 'a'}, {"no-swap", required_argument, 0, 'n'}, | 241 | {"critical", required_argument, 0, 'c'}, |
| 182 | {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, | 242 | {"allswaps", no_argument, 0, 'a'}, |
| 183 | {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; | 243 | {"no-swap", required_argument, 0, 'n'}, |
| 244 | {"verbose", no_argument, 0, 'v'}, | ||
| 245 | {"version", no_argument, 0, 'V'}, | ||
| 246 | {"help", no_argument, 0, 'h'}, | ||
| 247 | {"output-format", required_argument, 0, output_format_index}, | ||
| 248 | {0, 0, 0, 0}}; | ||
| 184 | 249 | ||
| 185 | while (true) { | 250 | while (true) { |
| 186 | int option = 0; | 251 | int option = 0; |
| @@ -263,6 +328,18 @@ swap_config_wrapper process_arguments(int argc, char **argv) { | |||
| 263 | case 'v': /* verbose */ | 328 | case 'v': /* verbose */ |
| 264 | verbose++; | 329 | verbose++; |
| 265 | break; | 330 | break; |
| 331 | case output_format_index: { | ||
| 332 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 333 | if (!parser.parsing_success) { | ||
| 334 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 335 | printf("Invalid output format: %s\n", optarg); | ||
| 336 | exit(STATE_UNKNOWN); | ||
| 337 | } | ||
| 338 | |||
| 339 | conf_wrapper.config.output_format_is_set = true; | ||
| 340 | conf_wrapper.config.output_format = parser.output_format; | ||
| 341 | break; | ||
| 342 | } | ||
| 266 | case 'V': /* version */ | 343 | case 'V': /* version */ |
| 267 | print_revision(progname, NP_VERSION); | 344 | print_revision(progname, NP_VERSION); |
| 268 | exit(STATE_UNKNOWN); | 345 | exit(STATE_UNKNOWN); |
| @@ -319,6 +396,7 @@ void print_help(swap_config config) { | |||
| 319 | _("Resulting state when there is no swap regardless of thresholds. " | 396 | _("Resulting state when there is no swap regardless of thresholds. " |
| 320 | "Default:"), | 397 | "Default:"), |
| 321 | state_text(config.no_swap_state)); | 398 | state_text(config.no_swap_state)); |
| 399 | printf(UT_OUTPUT_FORMAT); | ||
| 322 | printf(UT_VERBOSE); | 400 | printf(UT_VERBOSE); |
| 323 | 401 | ||
| 324 | printf("\n"); | 402 | printf("\n"); |
diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h index 99039b21..8d3c7fcf 100644 --- a/plugins/check_swap.d/check_swap.h +++ b/plugins/check_swap.d/check_swap.h | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include "../common.h" | 3 | #include "../common.h" |
| 4 | #include "../../lib/output.h" | ||
| 5 | #include "../../lib/states.h" | ||
| 4 | 6 | ||
| 5 | #ifndef SWAP_CONVERSION | 7 | #ifndef SWAP_CONVERSION |
| 6 | # define SWAP_CONVERSION 1 | 8 | # define SWAP_CONVERSION 1 |
| @@ -25,19 +27,23 @@ typedef struct { | |||
| 25 | 27 | ||
| 26 | typedef struct { | 28 | typedef struct { |
| 27 | bool allswaps; | 29 | bool allswaps; |
| 28 | int no_swap_state; | 30 | mp_state_enum no_swap_state; |
| 29 | bool warn_is_set; | 31 | bool warn_is_set; |
| 30 | check_swap_threshold warn; | 32 | check_swap_threshold warn; |
| 31 | bool crit_is_set; | 33 | bool crit_is_set; |
| 32 | check_swap_threshold crit; | 34 | check_swap_threshold crit; |
| 33 | bool on_aix; | 35 | bool on_aix; |
| 34 | int conversion_factor; | 36 | int conversion_factor; |
| 37 | |||
| 38 | bool output_format_is_set; | ||
| 39 | mp_output_format output_format; | ||
| 35 | } swap_config; | 40 | } swap_config; |
| 36 | 41 | ||
| 37 | swap_config swap_config_init(void); | 42 | swap_config swap_config_init(void); |
| 38 | 43 | ||
| 39 | swap_result get_swap_data(swap_config config); | 44 | swap_result get_swap_data(swap_config config); |
| 40 | swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]); | 45 | swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]); |
| 41 | swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]); | 46 | swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], |
| 47 | const char swap_format[]); | ||
| 42 | swap_result getSwapFromSwapctl_BSD(swap_config config); | 48 | swap_result getSwapFromSwapctl_BSD(swap_config config); |
| 43 | swap_result getSwapFromSwap_SRV4(swap_config config); | 49 | swap_result getSwapFromSwap_SRV4(swap_config config); |
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c index 2fe4544f..58213a3c 100644 --- a/plugins/check_swap.d/swap.c +++ b/plugins/check_swap.d/swap.c | |||
| @@ -14,6 +14,8 @@ swap_config swap_config_init(void) { | |||
| 14 | tmp.warn_is_set = false; | 14 | tmp.warn_is_set = false; |
| 15 | tmp.crit_is_set = false; | 15 | tmp.crit_is_set = false; |
| 16 | 16 | ||
| 17 | tmp.output_format_is_set = false; | ||
| 18 | |||
| 17 | #ifdef _AIX | 19 | #ifdef _AIX |
| 18 | tmp.on_aix = true; | 20 | tmp.on_aix = true; |
| 19 | #else | 21 | #else |
| @@ -50,10 +52,10 @@ swap_result get_swap_data(swap_config config) { | |||
| 50 | } | 52 | } |
| 51 | # else // HAVE_SWAP | 53 | # else // HAVE_SWAP |
| 52 | # ifdef CHECK_SWAP_SWAPCTL_SVR4 | 54 | # ifdef CHECK_SWAP_SWAPCTL_SVR4 |
| 53 | return getSwapFromSwapctl_SRV4(); | 55 | return getSwapFromSwapctl_SRV4(config); |
| 54 | # else // CHECK_SWAP_SWAPCTL_SVR4 | 56 | # else // CHECK_SWAP_SWAPCTL_SVR4 |
| 55 | # ifdef CHECK_SWAP_SWAPCTL_BSD | 57 | # ifdef CHECK_SWAP_SWAPCTL_BSD |
| 56 | return getSwapFromSwapctl_BSD(); | 58 | return getSwapFromSwapctl_BSD(config); |
| 57 | # else // CHECK_SWAP_SWAPCTL_BSD | 59 | # else // CHECK_SWAP_SWAPCTL_BSD |
| 58 | # error No way found to retrieve swap | 60 | # error No way found to retrieve swap |
| 59 | # endif /* CHECK_SWAP_SWAPCTL_BSD */ | 61 | # endif /* CHECK_SWAP_SWAPCTL_BSD */ |
| @@ -66,7 +68,7 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) { | |||
| 66 | FILE *meminfo_file_ptr; | 68 | FILE *meminfo_file_ptr; |
| 67 | meminfo_file_ptr = fopen(proc_meminfo, "r"); | 69 | meminfo_file_ptr = fopen(proc_meminfo, "r"); |
| 68 | 70 | ||
| 69 | swap_result result = {0}; | 71 | swap_result result = {}; |
| 70 | result.errorcode = STATE_UNKNOWN; | 72 | result.errorcode = STATE_UNKNOWN; |
| 71 | 73 | ||
| 72 | if (meminfo_file_ptr == NULL) { | 74 | if (meminfo_file_ptr == NULL) { |
| @@ -76,90 +78,81 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) { | |||
| 76 | return result; | 78 | return result; |
| 77 | } | 79 | } |
| 78 | 80 | ||
| 79 | uint64_t swap_total = 0; | 81 | unsigned long swap_total = 0; |
| 80 | uint64_t swap_used = 0; | 82 | unsigned long swap_used = 0; |
| 81 | uint64_t swap_free = 0; | 83 | unsigned long swap_free = 0; |
| 82 | 84 | ||
| 83 | bool found_total = false; | 85 | bool found_total = false; |
| 84 | bool found_used = false; | ||
| 85 | bool found_free = false; | 86 | bool found_free = false; |
| 86 | 87 | ||
| 87 | char input_buffer[MAX_INPUT_BUFFER]; | 88 | char input_buffer[MAX_INPUT_BUFFER]; |
| 88 | char str[32]; | 89 | char str[32]; |
| 89 | 90 | ||
| 90 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) { | 91 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) { |
| 91 | uint64_t tmp_KB = 0; | ||
| 92 | 92 | ||
| 93 | /* | 93 | /* |
| 94 | * The following sscanf call looks for a line looking like: "Swap: 123 | 94 | * The following sscanf call looks for a line looking like: "Swap: 123 |
| 95 | * 123 123" On which kind of system this format exists, I can not say, | 95 | * 123 123" which exists on NetBSD (at least), |
| 96 | * but I wanted to document this for people who are not adapt with | 96 | * The unit should be Bytes |
| 97 | * sscanf anymore, like me | ||
| 98 | * Also the units used here are unclear and probably wrong | ||
| 99 | */ | 97 | */ |
| 100 | if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) { | 98 | if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, |
| 101 | 99 | &swap_free) == 3) { | |
| 102 | result.metrics.total += swap_total; | ||
| 103 | result.metrics.used += swap_used; | ||
| 104 | result.metrics.free += swap_free; | ||
| 105 | |||
| 106 | found_total = true; | 100 | found_total = true; |
| 107 | found_free = true; | 101 | found_free = true; |
| 108 | found_used = true; | ||
| 109 | |||
| 110 | // Set error | 102 | // Set error |
| 111 | result.errorcode = STATE_OK; | 103 | result.errorcode = STATE_OK; |
| 104 | // Break out of fgets here, since both scanf expressions might match (NetBSD for | ||
| 105 | // example) | ||
| 106 | break; | ||
| 107 | } | ||
| 112 | 108 | ||
| 113 | /* | 109 | /* |
| 114 | * The following sscanf call looks for lines looking like: | 110 | * The following sscanf call looks for lines looking like: |
| 115 | * "SwapTotal: 123" and "SwapFree: 123" This format exists at least | 111 | * "SwapTotal: 123" and "SwapFree: 123" This format exists at least |
| 116 | * on Debian Linux with a 5.* kernel | 112 | * on Debian Linux with a 5.* kernel |
| 117 | */ | 113 | */ |
| 118 | } else { | 114 | unsigned long tmp_KB = 0; |
| 119 | int sscanf_result = sscanf(input_buffer, | 115 | int sscanf_result = sscanf(input_buffer, |
| 120 | "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu " | 116 | "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu " |
| 121 | "%*[k]%*[B]", | 117 | "%*[k]%*[B]", |
| 122 | str, &tmp_KB); | 118 | str, &tmp_KB); |
| 123 | 119 | ||
| 124 | if (sscanf_result == 2) { | 120 | if (sscanf_result == 2) { |
| 125 | 121 | ||
| 126 | if (verbose >= 3) { | 122 | if (verbose >= 3) { |
| 127 | printf("Got %s with %lu\n", str, tmp_KB); | 123 | printf("Got %s with %lu\n", str, tmp_KB); |
| 128 | } | ||
| 129 | |||
| 130 | /* I think this part is always in Kb, so convert to bytes */ | ||
| 131 | if (strcmp("Total", str) == 0) { | ||
| 132 | swap_total = tmp_KB * 1000; | ||
| 133 | found_total = true; | ||
| 134 | } else if (strcmp("Free", str) == 0) { | ||
| 135 | swap_free = swap_free + tmp_KB * 1000; | ||
| 136 | found_free = true; | ||
| 137 | found_used = true; // No explicit used metric available | ||
| 138 | } else if (strcmp("Cached", str) == 0) { | ||
| 139 | swap_free = swap_free + tmp_KB * 1000; | ||
| 140 | found_free = true; | ||
| 141 | found_used = true; // No explicit used metric available | ||
| 142 | } | ||
| 143 | |||
| 144 | result.errorcode = STATE_OK; | ||
| 145 | } | 124 | } |
| 125 | |||
| 126 | /* I think this part is always in Kb, so convert to bytes */ | ||
| 127 | if (strcmp("Total", str) == 0) { | ||
| 128 | swap_total = tmp_KB * 1000; | ||
| 129 | found_total = true; | ||
| 130 | } else if (strcmp("Free", str) == 0) { | ||
| 131 | swap_free += tmp_KB * 1000; | ||
| 132 | found_free = true; | ||
| 133 | } else if (strcmp("Cached", str) == 0) { | ||
| 134 | swap_free += tmp_KB * 1000; | ||
| 135 | } | ||
| 136 | |||
| 137 | result.errorcode = STATE_OK; | ||
| 146 | } | 138 | } |
| 147 | } | 139 | } |
| 148 | 140 | ||
| 149 | fclose(meminfo_file_ptr); | 141 | fclose(meminfo_file_ptr); |
| 150 | 142 | ||
| 151 | result.metrics.total = swap_total; | 143 | result.metrics.total = swap_total; |
| 152 | result.metrics.used = swap_total - swap_free; | ||
| 153 | result.metrics.free = swap_free; | 144 | result.metrics.free = swap_free; |
| 145 | result.metrics.used = swap_total - swap_free; | ||
| 154 | 146 | ||
| 155 | if (!found_free || !found_total || !found_used) { | 147 | if (!found_free || !found_total) { |
| 156 | result.errorcode = STATE_UNKNOWN; | 148 | result.errorcode = STATE_UNKNOWN; |
| 157 | } | 149 | } |
| 158 | 150 | ||
| 159 | return result; | 151 | return result; |
| 160 | } | 152 | } |
| 161 | 153 | ||
| 162 | swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]) { | 154 | swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], |
| 155 | const char swap_format[]) { | ||
| 163 | swap_result result = {0}; | 156 | swap_result result = {0}; |
| 164 | 157 | ||
| 165 | char *temp_buffer; | 158 | char *temp_buffer; |
| @@ -222,7 +215,8 @@ swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[] | |||
| 222 | used_swap_mb = total_swap_mb - free_swap_mb; | 215 | used_swap_mb = total_swap_mb - free_swap_mb; |
| 223 | 216 | ||
| 224 | if (verbose >= 3) { | 217 | if (verbose >= 3) { |
| 225 | printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb); | 218 | printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, |
| 219 | free_swap_mb); | ||
| 226 | } | 220 | } |
| 227 | } else { | 221 | } else { |
| 228 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { | 222 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { |
| @@ -295,8 +289,14 @@ struct swapent { | |||
| 295 | }; | 289 | }; |
| 296 | 290 | ||
| 297 | #else | 291 | #else |
| 292 | |||
| 293 | // Includes for NetBSD | ||
| 294 | # include <unistd.h> | ||
| 295 | # include <sys/swap.h> | ||
| 296 | |||
| 298 | # define bsd_swapctl swapctl | 297 | # define bsd_swapctl swapctl |
| 299 | #endif | 298 | |
| 299 | #endif // CHECK_SWAP_SWAPCTL_BSD | ||
| 300 | 300 | ||
| 301 | swap_result getSwapFromSwapctl_BSD(swap_config config) { | 301 | swap_result getSwapFromSwapctl_BSD(swap_config config) { |
| 302 | /* get the number of active swap devices */ | 302 | /* get the number of active swap devices */ |
| @@ -320,8 +320,8 @@ swap_result getSwapFromSwapctl_BSD(swap_config config) { | |||
| 320 | unsigned long long used_swap_mb = 0; | 320 | unsigned long long used_swap_mb = 0; |
| 321 | 321 | ||
| 322 | for (int i = 0; i < nswaps; i++) { | 322 | for (int i = 0; i < nswaps; i++) { |
| 323 | dsktotal_mb = (float)ent[i].se_nblks / (float)config.conversion_factor; | 323 | dsktotal_mb = (double)ent[i].se_nblks / (double)config.conversion_factor; |
| 324 | dskused_mb = (float)ent[i].se_inuse / (float)config.conversion_factor; | 324 | dskused_mb = (double)ent[i].se_inuse / (double)config.conversion_factor; |
| 325 | dskfree_mb = (dsktotal_mb - dskused_mb); | 325 | dskfree_mb = (dsktotal_mb - dskused_mb); |
| 326 | 326 | ||
| 327 | if (config.allswaps && dsktotal_mb > 0) { | 327 | if (config.allswaps && dsktotal_mb > 0) { |
| @@ -402,7 +402,8 @@ swap_result getSwapFromSwap_SRV4(swap_config config) { | |||
| 402 | } | 402 | } |
| 403 | 403 | ||
| 404 | /* initialize swap table + entries */ | 404 | /* initialize swap table + entries */ |
| 405 | swaptbl_t *tbl = (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps)); | 405 | swaptbl_t *tbl = |
| 406 | (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps)); | ||
| 406 | 407 | ||
| 407 | if (tbl == NULL) { | 408 | if (tbl == NULL) { |
| 408 | die(STATE_UNKNOWN, _("malloc() failed!\n")); | 409 | die(STATE_UNKNOWN, _("malloc() failed!\n")); |
| @@ -437,7 +438,8 @@ swap_result getSwapFromSwap_SRV4(swap_config config) { | |||
| 437 | dskused_mb = (dsktotal_mb - dskfree_mb); | 438 | dskused_mb = (dsktotal_mb - dskfree_mb); |
| 438 | 439 | ||
| 439 | if (verbose >= 3) { | 440 | if (verbose >= 3) { |
| 440 | printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb); | 441 | printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, |
| 442 | dskused_mb); | ||
| 441 | } | 443 | } |
| 442 | 444 | ||
| 443 | if (config.allswaps && dsktotal_mb > 0) { | 445 | if (config.allswaps && dsktotal_mb > 0) { |
diff --git a/plugins/check_tcp.c b/plugins/check_tcp.c index 49ad096c..09806373 100644 --- a/plugins/check_tcp.c +++ b/plugins/check_tcp.c | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * Monitoring check_tcp plugin | 3 | * Monitoring check_tcp plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 1999-2024 Monitoring Plugins Development Team | 6 | * Copyright (c) 1999-2025 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| @@ -29,74 +29,64 @@ | |||
| 29 | 29 | ||
| 30 | /* progname "check_tcp" changes depending on symlink called */ | 30 | /* progname "check_tcp" changes depending on symlink called */ |
| 31 | char *progname; | 31 | char *progname; |
| 32 | const char *copyright = "1999-2024"; | 32 | const char *copyright = "1999-2025"; |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 33 | const char *email = "devel@monitoring-plugins.org"; |
| 34 | 34 | ||
| 35 | #include "common.h" | 35 | #include "./common.h" |
| 36 | #include "netutils.h" | 36 | #include "./netutils.h" |
| 37 | #include "utils.h" | 37 | #include "./utils.h" |
| 38 | #include "utils_tcp.h" | 38 | #include "./check_tcp.d/config.h" |
| 39 | #include "output.h" | ||
| 40 | #include "states.h" | ||
| 39 | 41 | ||
| 42 | #include <sys/types.h> | ||
| 40 | #include <ctype.h> | 43 | #include <ctype.h> |
| 41 | #include <sys/select.h> | 44 | #include <sys/select.h> |
| 42 | 45 | ||
| 46 | ssize_t my_recv(int socket_descriptor, char *buf, size_t len, bool use_tls) { | ||
| 43 | #ifdef HAVE_SSL | 47 | #ifdef HAVE_SSL |
| 44 | static bool check_cert = false; | 48 | if (use_tls) { |
| 45 | static int days_till_exp_warn, days_till_exp_crit; | 49 | return np_net_ssl_read(buf, (int)len); |
| 46 | # define my_recv(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) | 50 | } |
| 47 | # define my_send(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) | 51 | #endif |
| 48 | #else | 52 | return read(socket_descriptor, buf, len); |
| 49 | # define my_recv(buf, len) read(sd, buf, len) | 53 | } |
| 50 | # define my_send(buf, len) send(sd, buf, len, 0) | 54 | |
| 55 | ssize_t my_send(int socket_descriptor, char *buf, size_t len, bool use_tls) { | ||
| 56 | #ifdef HAVE_SSL | ||
| 57 | if (use_tls) { | ||
| 58 | return np_net_ssl_write(buf, (int)len); | ||
| 59 | } | ||
| 51 | #endif | 60 | #endif |
| 61 | return write(socket_descriptor, buf, len); | ||
| 62 | } | ||
| 52 | 63 | ||
| 53 | /* int my_recv(char *, size_t); */ | 64 | typedef struct { |
| 54 | static int process_arguments(int /*argc*/, char ** /*argv*/); | 65 | int errorcode; |
| 55 | static void print_help(void); | 66 | check_tcp_config config; |
| 67 | } check_tcp_config_wrapper; | ||
| 68 | static check_tcp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/, | ||
| 69 | check_tcp_config /*config*/); | ||
| 70 | void print_help(const char *service); | ||
| 56 | void print_usage(void); | 71 | void print_usage(void); |
| 57 | 72 | ||
| 58 | #define EXPECT server_expect[0] | 73 | int verbosity = 0; |
| 59 | static char *SERVICE = "TCP"; | ||
| 60 | static char *SEND = NULL; | ||
| 61 | static char *QUIT = NULL; | ||
| 62 | static int PROTOCOL = IPPROTO_TCP; /* most common is default */ | ||
| 63 | static int PORT = 0; | ||
| 64 | static int READ_TIMEOUT = 2; | ||
| 65 | |||
| 66 | static int server_port = 0; | ||
| 67 | static char *server_address = NULL; | ||
| 68 | static bool host_specified = false; | ||
| 69 | static char *server_send = NULL; | ||
| 70 | static char *server_quit = NULL; | ||
| 71 | static char **server_expect; | ||
| 72 | static size_t server_expect_count = 0; | ||
| 73 | static ssize_t maxbytes = 0; | ||
| 74 | static char **warn_codes = NULL; | ||
| 75 | static size_t warn_codes_count = 0; | ||
| 76 | static char **crit_codes = NULL; | ||
| 77 | static size_t crit_codes_count = 0; | ||
| 78 | static unsigned int delay = 0; | ||
| 79 | static double warning_time = 0; | ||
| 80 | static double critical_time = 0; | ||
| 81 | static double elapsed_time = 0; | ||
| 82 | static long microsec; | ||
| 83 | static int sd = 0; | ||
| 84 | #define MAXBUF 1024 | ||
| 85 | static char buffer[MAXBUF]; | ||
| 86 | static int expect_mismatch_state = STATE_WARNING; | ||
| 87 | static int match_flags = NP_MATCH_EXACT; | ||
| 88 | 74 | ||
| 89 | #ifdef HAVE_SSL | 75 | static const int READ_TIMEOUT = 2; |
| 90 | static char *sni = NULL; | 76 | |
| 91 | static bool sni_specified = false; | 77 | const int MAXBUF = 1024; |
| 92 | #endif | ||
| 93 | 78 | ||
| 94 | #define FLAG_SSL 0x01 | 79 | const int DEFAULT_FTP_PORT = 21; |
| 95 | #define FLAG_VERBOSE 0x02 | 80 | const int DEFAULT_POP_PORT = 110; |
| 96 | #define FLAG_TIME_WARN 0x04 | 81 | const int DEFAULT_SPOP_PORT = 995; |
| 97 | #define FLAG_TIME_CRIT 0x08 | 82 | const int DEFAULT_SMTP_PORT = 25; |
| 98 | #define FLAG_HIDE_OUTPUT 0x10 | 83 | const int DEFAULT_SSMTP_PORT = 465; |
| 99 | static size_t flags; | 84 | const int DEFAULT_IMAP_PORT = 143; |
| 85 | const int DEFAULT_SIMAP_PORT = 993; | ||
| 86 | const int DEFAULT_XMPP_C2S_PORT = 5222; | ||
| 87 | const int DEFAULT_NNTP_PORT = 119; | ||
| 88 | const int DEFAULT_NNTPS_PORT = 563; | ||
| 89 | const int DEFAULT_CLAMD_PORT = 3310; | ||
| 100 | 90 | ||
| 101 | int main(int argc, char **argv) { | 91 | int main(int argc, char **argv) { |
| 102 | setlocale(LC_ALL, ""); | 92 | setlocale(LC_ALL, ""); |
| @@ -105,353 +95,482 @@ int main(int argc, char **argv) { | |||
| 105 | 95 | ||
| 106 | /* determine program- and service-name quickly */ | 96 | /* determine program- and service-name quickly */ |
| 107 | progname = strrchr(argv[0], '/'); | 97 | progname = strrchr(argv[0], '/'); |
| 108 | if (progname != NULL) | 98 | if (progname != NULL) { |
| 109 | progname++; | 99 | progname++; |
| 110 | else | 100 | } else { |
| 111 | progname = argv[0]; | 101 | progname = argv[0]; |
| 102 | } | ||
| 103 | |||
| 104 | // Initialize config here with values from above, | ||
| 105 | // might be changed by on disk config or cli commands | ||
| 106 | check_tcp_config config = check_tcp_config_init(); | ||
| 112 | 107 | ||
| 113 | size_t prog_name_len = strlen(progname); | 108 | size_t prog_name_len = strlen(progname); |
| 114 | if (prog_name_len > 6 && !memcmp(progname, "check_", 6)) { | 109 | const size_t prefix_length = strlen("check_"); |
| 115 | SERVICE = strdup(progname + 6); | 110 | |
| 116 | for (size_t i = 0; i < prog_name_len - 6; i++) | 111 | if (prog_name_len <= prefix_length) { |
| 117 | SERVICE[i] = toupper(SERVICE[i]); | 112 | die(STATE_UNKNOWN, _("Weird progname")); |
| 113 | } | ||
| 114 | |||
| 115 | if (!memcmp(progname, "check_", prefix_length)) { | ||
| 116 | config.service = strdup(progname + prefix_length); | ||
| 117 | if (config.service == NULL) { | ||
| 118 | die(STATE_UNKNOWN, _("Allocation failed")); | ||
| 119 | } | ||
| 120 | |||
| 121 | for (size_t i = 0; i < prog_name_len - prefix_length; i++) { | ||
| 122 | config.service[i] = toupper(config.service[i]); | ||
| 123 | } | ||
| 118 | } | 124 | } |
| 119 | 125 | ||
| 120 | /* set up a reasonable buffer at first (will be realloc()'ed if | 126 | /* set up a reasonable buffer at first (will be realloc()'ed if |
| 121 | * user specifies other options) */ | 127 | * user specifies other options) */ |
| 122 | server_expect = calloc(2, sizeof(char *)); | 128 | config.server_expect = calloc(2, sizeof(char *)); |
| 129 | |||
| 130 | if (config.server_expect == NULL) { | ||
| 131 | die(STATE_UNKNOWN, _("Allocation failed")); | ||
| 132 | } | ||
| 123 | 133 | ||
| 124 | /* determine defaults for this service's protocol */ | 134 | /* determine defaults for this service's protocol */ |
| 125 | if (!strncmp(SERVICE, "UDP", 3)) { | 135 | if (!strncmp(config.service, "UDP", strlen("UDP"))) { |
| 126 | PROTOCOL = IPPROTO_UDP; | 136 | config.protocol = IPPROTO_UDP; |
| 127 | } else if (!strncmp(SERVICE, "FTP", 3)) { | 137 | } else if (!strncmp(config.service, "FTP", strlen("FTP"))) { |
| 128 | EXPECT = "220"; | 138 | config.server_expect[0] = "220"; |
| 129 | QUIT = "QUIT\r\n"; | 139 | config.quit = "QUIT\r\n"; |
| 130 | PORT = 21; | 140 | config.server_port = DEFAULT_FTP_PORT; |
| 131 | } else if (!strncmp(SERVICE, "POP", 3) || !strncmp(SERVICE, "POP3", 4)) { | 141 | } else if (!strncmp(config.service, "POP", strlen("POP")) || |
| 132 | EXPECT = "+OK"; | 142 | !strncmp(config.service, "POP3", strlen("POP3"))) { |
| 133 | QUIT = "QUIT\r\n"; | 143 | config.server_expect[0] = "+OK"; |
| 134 | PORT = 110; | 144 | config.quit = "QUIT\r\n"; |
| 135 | } else if (!strncmp(SERVICE, "SMTP", 4)) { | 145 | config.server_port = DEFAULT_POP_PORT; |
| 136 | EXPECT = "220"; | 146 | } else if (!strncmp(config.service, "SMTP", strlen("SMTP"))) { |
| 137 | QUIT = "QUIT\r\n"; | 147 | config.server_expect[0] = "220"; |
| 138 | PORT = 25; | 148 | config.quit = "QUIT\r\n"; |
| 139 | } else if (!strncmp(SERVICE, "IMAP", 4)) { | 149 | config.server_port = DEFAULT_SMTP_PORT; |
| 140 | EXPECT = "* OK"; | 150 | } else if (!strncmp(config.service, "IMAP", strlen("IMAP"))) { |
| 141 | QUIT = "a1 LOGOUT\r\n"; | 151 | config.server_expect[0] = "* OK"; |
| 142 | PORT = 143; | 152 | config.quit = "a1 LOGOUT\r\n"; |
| 153 | config.server_port = DEFAULT_IMAP_PORT; | ||
| 143 | } | 154 | } |
| 144 | #ifdef HAVE_SSL | 155 | #ifdef HAVE_SSL |
| 145 | else if (!strncmp(SERVICE, "SIMAP", 5)) { | 156 | else if (!strncmp(config.service, "SIMAP", strlen("SIMAP"))) { |
| 146 | EXPECT = "* OK"; | 157 | config.server_expect[0] = "* OK"; |
| 147 | QUIT = "a1 LOGOUT\r\n"; | 158 | config.quit = "a1 LOGOUT\r\n"; |
| 148 | flags |= FLAG_SSL; | 159 | config.use_tls = true; |
| 149 | PORT = 993; | 160 | config.server_port = DEFAULT_SIMAP_PORT; |
| 150 | } else if (!strncmp(SERVICE, "SPOP", 4)) { | 161 | } else if (!strncmp(config.service, "SPOP", strlen("SPOP"))) { |
| 151 | EXPECT = "+OK"; | 162 | config.server_expect[0] = "+OK"; |
| 152 | QUIT = "QUIT\r\n"; | 163 | config.quit = "QUIT\r\n"; |
| 153 | flags |= FLAG_SSL; | 164 | config.use_tls = true; |
| 154 | PORT = 995; | 165 | config.server_port = DEFAULT_SPOP_PORT; |
| 155 | } else if (!strncmp(SERVICE, "SSMTP", 5)) { | 166 | } else if (!strncmp(config.service, "SSMTP", strlen("SSMTP"))) { |
| 156 | EXPECT = "220"; | 167 | config.server_expect[0] = "220"; |
| 157 | QUIT = "QUIT\r\n"; | 168 | config.quit = "QUIT\r\n"; |
| 158 | flags |= FLAG_SSL; | 169 | config.use_tls = true; |
| 159 | PORT = 465; | 170 | config.server_port = DEFAULT_SSMTP_PORT; |
| 160 | } else if (!strncmp(SERVICE, "JABBER", 6)) { | 171 | } else if (!strncmp(config.service, "JABBER", strlen("JABBER"))) { |
| 161 | SEND = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n"; | 172 | config.send = "<stream:stream to=\'host\' xmlns=\'jabber:client\' " |
| 162 | EXPECT = "<?xml version=\'1.0\'"; | 173 | "xmlns:stream=\'http://etherx.jabber.org/streams\'>\n"; |
| 163 | QUIT = "</stream:stream>\n"; | 174 | config.server_expect[0] = "<?xml version=\'1.0\'"; |
| 164 | flags |= FLAG_HIDE_OUTPUT; | 175 | config.quit = "</stream:stream>\n"; |
| 165 | PORT = 5222; | 176 | config.hide_output = true; |
| 166 | } else if (!strncmp(SERVICE, "NNTPS", 5)) { | 177 | config.server_port = DEFAULT_XMPP_C2S_PORT; |
| 167 | server_expect_count = 2; | 178 | } else if (!strncmp(config.service, "NNTPS", strlen("NNTPS"))) { |
| 168 | server_expect[0] = "200"; | 179 | config.server_expect_count = 2; |
| 169 | server_expect[1] = "201"; | 180 | config.server_expect[0] = "200"; |
| 170 | QUIT = "QUIT\r\n"; | 181 | config.server_expect[1] = "201"; |
| 171 | flags |= FLAG_SSL; | 182 | config.quit = "QUIT\r\n"; |
| 172 | PORT = 563; | 183 | config.use_tls = true; |
| 184 | config.server_port = DEFAULT_NNTPS_PORT; | ||
| 173 | } | 185 | } |
| 174 | #endif | 186 | #endif |
| 175 | else if (!strncmp(SERVICE, "NNTP", 4)) { | 187 | else if (!strncmp(config.service, "NNTP", strlen("NNTP"))) { |
| 176 | server_expect_count = 2; | 188 | config.server_expect_count = 2; |
| 177 | server_expect = malloc(sizeof(char *) * server_expect_count); | 189 | char **tmp = realloc(config.server_expect, config.server_expect_count * sizeof(char *)); |
| 178 | server_expect[0] = strdup("200"); | 190 | if (tmp == NULL) { |
| 179 | server_expect[1] = strdup("201"); | 191 | free(config.server_expect); |
| 180 | QUIT = "QUIT\r\n"; | 192 | die(STATE_UNKNOWN, _("Allocation failed")); |
| 181 | PORT = 119; | 193 | } |
| 182 | } else if (!strncmp(SERVICE, "CLAMD", 5)) { | 194 | config.server_expect = tmp; |
| 183 | SEND = "PING"; | 195 | |
| 184 | EXPECT = "PONG"; | 196 | config.server_expect[0] = strdup("200"); |
| 185 | QUIT = NULL; | 197 | config.server_expect[1] = strdup("201"); |
| 186 | PORT = 3310; | 198 | config.quit = "QUIT\r\n"; |
| 199 | config.server_port = DEFAULT_NNTP_PORT; | ||
| 200 | } else if (!strncmp(config.service, "CLAMD", strlen("CLAMD"))) { | ||
| 201 | config.send = "PING"; | ||
| 202 | config.server_expect[0] = "PONG"; | ||
| 203 | config.quit = NULL; | ||
| 204 | config.server_port = DEFAULT_CLAMD_PORT; | ||
| 187 | } | 205 | } |
| 188 | /* fallthrough check, so it's supposed to use reverse matching */ | 206 | /* fallthrough check, so it's supposed to use reverse matching */ |
| 189 | else if (strcmp(SERVICE, "TCP")) | 207 | else if (strcmp(config.service, "TCP")) { |
| 190 | usage(_("CRITICAL - Generic check_tcp called with unknown service\n")); | 208 | usage(_("CRITICAL - Generic check_tcp called with unknown service\n")); |
| 191 | 209 | } | |
| 192 | server_address = "127.0.0.1"; | ||
| 193 | server_port = PORT; | ||
| 194 | server_send = SEND; | ||
| 195 | server_quit = QUIT; | ||
| 196 | char *status = NULL; | ||
| 197 | 210 | ||
| 198 | /* Parse extra opts if any */ | 211 | /* Parse extra opts if any */ |
| 199 | argv = np_extra_opts(&argc, argv, progname); | 212 | argv = np_extra_opts(&argc, argv, progname); |
| 200 | 213 | ||
| 201 | if (process_arguments(argc, argv) == ERROR) | 214 | check_tcp_config_wrapper paw = process_arguments(argc, argv, config); |
| 215 | if (paw.errorcode == ERROR) { | ||
| 202 | usage4(_("Could not parse arguments")); | 216 | usage4(_("Could not parse arguments")); |
| 217 | } | ||
| 218 | |||
| 219 | config = paw.config; | ||
| 203 | 220 | ||
| 204 | if (flags & FLAG_VERBOSE) { | 221 | if (verbosity > 0) { |
| 205 | printf("Using service %s\n", SERVICE); | 222 | printf("Using service %s\n", config.service); |
| 206 | printf("Port: %d\n", server_port); | 223 | printf("Port: %d\n", config.server_port); |
| 207 | printf("flags: 0x%x\n", (int)flags); | ||
| 208 | } | 224 | } |
| 209 | 225 | ||
| 210 | if (EXPECT && !server_expect_count) | 226 | if ((config.server_expect_count == 0) && config.server_expect[0]) { |
| 211 | server_expect_count++; | 227 | config.server_expect_count++; |
| 228 | } | ||
| 212 | 229 | ||
| 213 | if (PROTOCOL == IPPROTO_UDP && !(server_expect_count && server_send)) { | 230 | if (config.protocol == IPPROTO_UDP && !(config.server_expect_count && config.send)) { |
| 214 | usage(_("With UDP checks, a send/expect string must be specified.")); | 231 | usage(_("With UDP checks, a send/expect string must be specified.")); |
| 215 | } | 232 | } |
| 216 | 233 | ||
| 234 | // Initialize check stuff before setting timers | ||
| 235 | mp_check overall = mp_check_init(); | ||
| 236 | if (config.output_format_set) { | ||
| 237 | mp_set_format(config.output_format); | ||
| 238 | } | ||
| 239 | |||
| 217 | /* set up the timer */ | 240 | /* set up the timer */ |
| 218 | signal(SIGALRM, socket_timeout_alarm_handler); | 241 | signal(SIGALRM, socket_timeout_alarm_handler); |
| 219 | alarm(socket_timeout); | 242 | alarm(socket_timeout); |
| 220 | 243 | ||
| 221 | /* try to connect to the host at the given port number */ | 244 | /* try to connect to the host at the given port number */ |
| 222 | struct timeval tv; | 245 | struct timeval start_time; |
| 223 | gettimeofday(&tv, NULL); | 246 | gettimeofday(&start_time, NULL); |
| 224 | 247 | ||
| 225 | int result = STATE_UNKNOWN; | 248 | int socket_descriptor = 0; |
| 226 | result = np_net_connect(server_address, server_port, &sd, PROTOCOL); | 249 | mp_subcheck inital_connect_result = mp_subcheck_init(); |
| 227 | if (result == STATE_CRITICAL) | 250 | |
| 228 | return econn_refuse_state; | 251 | // Try initial connection |
| 252 | if (np_net_connect(config.server_address, config.server_port, &socket_descriptor, | ||
| 253 | config.protocol) == STATE_CRITICAL) { | ||
| 254 | // Early exit here, we got connection refused | ||
| 255 | inital_connect_result = | ||
| 256 | mp_set_subcheck_state(inital_connect_result, config.econn_refuse_state); | ||
| 257 | xasprintf(&inital_connect_result.output, "Connection to %s on port %i was REFUSED", | ||
| 258 | config.server_address, config.server_port); | ||
| 259 | mp_add_subcheck_to_check(&overall, inital_connect_result); | ||
| 260 | mp_exit(overall); | ||
| 261 | } else { | ||
| 262 | inital_connect_result = mp_set_subcheck_state(inital_connect_result, STATE_OK); | ||
| 263 | xasprintf(&inital_connect_result.output, "Connection to %s on port %i was a SUCCESS", | ||
| 264 | config.server_address, config.server_port); | ||
| 265 | mp_add_subcheck_to_check(&overall, inital_connect_result); | ||
| 266 | } | ||
| 229 | 267 | ||
| 230 | #ifdef HAVE_SSL | 268 | #ifdef HAVE_SSL |
| 231 | if (flags & FLAG_SSL) { | 269 | if (config.use_tls) { |
| 232 | result = np_net_ssl_init_with_hostname(sd, (sni_specified ? sni : NULL)); | 270 | mp_subcheck tls_connection_result = mp_subcheck_init(); |
| 233 | if (result == STATE_OK && check_cert) { | 271 | mp_state_enum result = np_net_ssl_init_with_hostname( |
| 234 | result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); | 272 | socket_descriptor, (config.sni_specified ? config.sni : NULL)); |
| 273 | tls_connection_result = mp_set_subcheck_default_state(tls_connection_result, result); | ||
| 274 | |||
| 275 | if (result == STATE_OK) { | ||
| 276 | xasprintf(&tls_connection_result.output, "TLS connection succeeded"); | ||
| 277 | |||
| 278 | if (config.check_cert) { | ||
| 279 | result = | ||
| 280 | np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit); | ||
| 281 | |||
| 282 | mp_subcheck tls_certificate_lifetime_result = mp_subcheck_init(); | ||
| 283 | tls_certificate_lifetime_result = | ||
| 284 | mp_set_subcheck_state(tls_certificate_lifetime_result, result); | ||
| 285 | |||
| 286 | if (result == STATE_OK) { | ||
| 287 | xasprintf(&tls_certificate_lifetime_result.output, | ||
| 288 | "Certificate lifetime is within thresholds"); | ||
| 289 | } else if (result == STATE_WARNING) { | ||
| 290 | xasprintf(&tls_certificate_lifetime_result.output, | ||
| 291 | "Certificate lifetime is violating warning threshold (%i)", | ||
| 292 | config.days_till_exp_warn); | ||
| 293 | } else if (result == STATE_CRITICAL) { | ||
| 294 | xasprintf(&tls_certificate_lifetime_result.output, | ||
| 295 | "Certificate lifetime is violating critical threshold (%i)", | ||
| 296 | config.days_till_exp_crit); | ||
| 297 | } else { | ||
| 298 | xasprintf(&tls_certificate_lifetime_result.output, | ||
| 299 | "Certificate lifetime is somehow unknown"); | ||
| 300 | } | ||
| 301 | |||
| 302 | mp_add_subcheck_to_subcheck(&tls_connection_result, | ||
| 303 | tls_certificate_lifetime_result); | ||
| 304 | } | ||
| 305 | |||
| 306 | mp_add_subcheck_to_check(&overall, tls_connection_result); | ||
| 307 | } else { | ||
| 308 | xasprintf(&tls_connection_result.output, "TLS connection failed"); | ||
| 309 | mp_add_subcheck_to_check(&overall, tls_connection_result); | ||
| 310 | |||
| 311 | if (socket_descriptor) { | ||
| 312 | close(socket_descriptor); | ||
| 313 | } | ||
| 314 | np_net_ssl_cleanup(); | ||
| 315 | |||
| 316 | mp_exit(overall); | ||
| 235 | } | 317 | } |
| 236 | } | 318 | } |
| 237 | if (result != STATE_OK) { | ||
| 238 | if (sd) | ||
| 239 | close(sd); | ||
| 240 | np_net_ssl_cleanup(); | ||
| 241 | return result; | ||
| 242 | } | ||
| 243 | #endif /* HAVE_SSL */ | 319 | #endif /* HAVE_SSL */ |
| 244 | 320 | ||
| 245 | if (server_send != NULL) { /* Something to send? */ | 321 | if (config.send != NULL) { /* Something to send? */ |
| 246 | my_send(server_send, strlen(server_send)); | 322 | my_send(socket_descriptor, config.send, strlen(config.send), config.use_tls); |
| 247 | } | 323 | } |
| 248 | 324 | ||
| 249 | if (delay > 0) { | 325 | if (config.delay > 0) { |
| 250 | tv.tv_sec += delay; | 326 | start_time.tv_sec += config.delay; |
| 251 | sleep(delay); | 327 | sleep(config.delay); |
| 252 | } | 328 | } |
| 253 | 329 | ||
| 254 | if (flags & FLAG_VERBOSE) { | 330 | if (verbosity > 0) { |
| 255 | if (server_send) { | 331 | if (config.send) { |
| 256 | printf("Send string: %s\n", server_send); | 332 | printf("Send string: %s\n", config.send); |
| 333 | } | ||
| 334 | if (config.quit) { | ||
| 335 | printf("Quit string: %s\n", config.quit); | ||
| 257 | } | 336 | } |
| 258 | if (server_quit) { | 337 | printf("server_expect_count: %d\n", (int)config.server_expect_count); |
| 259 | printf("Quit string: %s\n", server_quit); | 338 | for (size_t i = 0; i < config.server_expect_count; i++) { |
| 339 | printf("\t%zd: %s\n", i, config.server_expect[i]); | ||
| 260 | } | 340 | } |
| 261 | printf("server_expect_count: %d\n", (int)server_expect_count); | ||
| 262 | for (size_t i = 0; i < server_expect_count; i++) | ||
| 263 | printf("\t%zd: %s\n", i, server_expect[i]); | ||
| 264 | } | 341 | } |
| 265 | 342 | ||
| 266 | /* if(len) later on, we know we have a non-NULL response */ | 343 | /* if(len) later on, we know we have a non-NULL response */ |
| 267 | ssize_t len = 0; | 344 | ssize_t len = 0; |
| 345 | char *received_buffer = NULL; | ||
| 346 | enum np_match_result match = NP_MATCH_NONE; | ||
| 347 | mp_subcheck expected_data_result = mp_subcheck_init(); | ||
| 268 | 348 | ||
| 269 | int match = -1; | 349 | if (config.server_expect_count) { |
| 270 | struct timeval timeout; | ||
| 271 | fd_set rfds; | ||
| 272 | FD_ZERO(&rfds); | ||
| 273 | if (server_expect_count) { | ||
| 274 | ssize_t received = 0; | 350 | ssize_t received = 0; |
| 351 | char buffer[MAXBUF]; | ||
| 275 | 352 | ||
| 276 | /* watch for the expect string */ | 353 | /* watch for the expect string */ |
| 277 | while ((received = my_recv(buffer, sizeof(buffer))) > 0) { | 354 | while ((received = my_recv(socket_descriptor, buffer, sizeof(buffer), config.use_tls)) > |
| 278 | status = realloc(status, len + received + 1); | 355 | 0) { |
| 279 | memcpy(&status[len], buffer, received); | 356 | received_buffer = realloc(received_buffer, len + received + 1); |
| 357 | |||
| 358 | if (received_buffer == NULL) { | ||
| 359 | die(STATE_UNKNOWN, _("Allocation failed")); | ||
| 360 | } | ||
| 361 | |||
| 362 | memcpy(&received_buffer[len], buffer, received); | ||
| 280 | len += received; | 363 | len += received; |
| 281 | status[len] = '\0'; | 364 | received_buffer[len] = '\0'; |
| 282 | 365 | ||
| 283 | /* stop reading if user-forced */ | 366 | /* stop reading if user-forced */ |
| 284 | if (maxbytes && len >= maxbytes) | 367 | if (config.maxbytes && len >= config.maxbytes) { |
| 285 | break; | 368 | break; |
| 369 | } | ||
| 286 | 370 | ||
| 287 | if ((match = np_expect_match(status, server_expect, server_expect_count, match_flags)) != NP_MATCH_RETRY) | 371 | if ((match = np_expect_match(received_buffer, config.server_expect, |
| 372 | config.server_expect_count, config.match_flags)) != | ||
| 373 | NP_MATCH_RETRY) { | ||
| 288 | break; | 374 | break; |
| 375 | } | ||
| 376 | |||
| 377 | fd_set rfds; | ||
| 378 | FD_ZERO(&rfds); | ||
| 379 | FD_SET(socket_descriptor, &rfds); | ||
| 289 | 380 | ||
| 290 | /* some protocols wait for further input, so make sure we don't wait forever */ | 381 | /* some protocols wait for further input, so make sure we don't wait forever */ |
| 291 | FD_SET(sd, &rfds); | 382 | struct timeval timeout; |
| 292 | timeout.tv_sec = READ_TIMEOUT; | 383 | timeout.tv_sec = READ_TIMEOUT; |
| 293 | timeout.tv_usec = 0; | 384 | timeout.tv_usec = 0; |
| 294 | if (select(sd + 1, &rfds, NULL, NULL, &timeout) <= 0) | 385 | |
| 386 | if (select(socket_descriptor + 1, &rfds, NULL, NULL, &timeout) <= 0) { | ||
| 295 | break; | 387 | break; |
| 388 | } | ||
| 296 | } | 389 | } |
| 297 | 390 | ||
| 298 | if (match == NP_MATCH_RETRY) | 391 | if (match == NP_MATCH_RETRY) { |
| 299 | match = NP_MATCH_FAILURE; | 392 | match = NP_MATCH_FAILURE; |
| 393 | } | ||
| 300 | 394 | ||
| 301 | /* no data when expected, so return critical */ | 395 | /* no data when expected, so return critical */ |
| 302 | if (len == 0) | 396 | if (len == 0) { |
| 303 | die(STATE_CRITICAL, _("No data received from host\n")); | 397 | xasprintf(&expected_data_result.output, "Received no data when some was expected"); |
| 398 | expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_CRITICAL); | ||
| 399 | mp_add_subcheck_to_check(&overall, expected_data_result); | ||
| 400 | mp_exit(overall); | ||
| 401 | } | ||
| 304 | 402 | ||
| 305 | /* print raw output if we're debugging */ | 403 | /* print raw output if we're debugging */ |
| 306 | if (flags & FLAG_VERBOSE) | 404 | if (verbosity > 0) { |
| 307 | printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", (int)len + 1, status); | 405 | printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", |
| 406 | (int)len + 1, received_buffer); | ||
| 407 | } | ||
| 308 | /* strip whitespace from end of output */ | 408 | /* strip whitespace from end of output */ |
| 309 | while (--len > 0 && isspace(status[len])) | 409 | while (--len > 0 && isspace(received_buffer[len])) { |
| 310 | status[len] = '\0'; | 410 | received_buffer[len] = '\0'; |
| 411 | } | ||
| 311 | } | 412 | } |
| 312 | 413 | ||
| 313 | if (server_quit != NULL) { | 414 | if (config.quit != NULL) { |
| 314 | my_send(server_quit, strlen(server_quit)); | 415 | my_send(socket_descriptor, config.quit, strlen(config.quit), config.use_tls); |
| 416 | } | ||
| 417 | |||
| 418 | if (socket_descriptor) { | ||
| 419 | close(socket_descriptor); | ||
| 315 | } | 420 | } |
| 316 | if (sd) | ||
| 317 | close(sd); | ||
| 318 | #ifdef HAVE_SSL | 421 | #ifdef HAVE_SSL |
| 319 | np_net_ssl_cleanup(); | 422 | np_net_ssl_cleanup(); |
| 320 | #endif | 423 | #endif |
| 321 | 424 | ||
| 322 | microsec = deltime(tv); | 425 | long microsec = deltime(start_time); |
| 323 | elapsed_time = (double)microsec / 1.0e6; | 426 | double elapsed_time = (double)microsec / 1.0e6; |
| 427 | |||
| 428 | mp_subcheck elapsed_time_result = mp_subcheck_init(); | ||
| 429 | |||
| 430 | mp_perfdata time_pd = perfdata_init(); | ||
| 431 | time_pd = mp_set_pd_value(time_pd, elapsed_time); | ||
| 432 | time_pd.label = "time"; | ||
| 433 | time_pd.uom = "s"; | ||
| 434 | |||
| 435 | if (config.critical_time_set && elapsed_time > config.critical_time) { | ||
| 436 | xasprintf(&elapsed_time_result.output, | ||
| 437 | "Connection time %fs exceeded critical threshold (%f)", elapsed_time, | ||
| 438 | config.critical_time); | ||
| 439 | |||
| 440 | elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_CRITICAL); | ||
| 441 | time_pd.crit_present = true; | ||
| 442 | mp_range crit_val = mp_range_init(); | ||
| 443 | |||
| 444 | crit_val.end = mp_create_pd_value(config.critical_time); | ||
| 445 | crit_val.end_infinity = false; | ||
| 446 | |||
| 447 | time_pd.crit = crit_val; | ||
| 448 | } else if (config.warning_time_set && elapsed_time > config.warning_time) { | ||
| 449 | xasprintf(&elapsed_time_result.output, | ||
| 450 | "Connection time %fs exceeded warning threshold (%f)", elapsed_time, | ||
| 451 | config.critical_time); | ||
| 452 | |||
| 453 | elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_WARNING); | ||
| 454 | time_pd.warn_present = true; | ||
| 455 | mp_range warn_val = mp_range_init(); | ||
| 456 | warn_val.end = mp_create_pd_value(config.critical_time); | ||
| 457 | warn_val.end_infinity = false; | ||
| 458 | |||
| 459 | time_pd.warn = warn_val; | ||
| 460 | } else { | ||
| 461 | elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_OK); | ||
| 462 | xasprintf(&elapsed_time_result.output, "Connection time %fs is within thresholds", | ||
| 463 | elapsed_time); | ||
| 464 | } | ||
| 324 | 465 | ||
| 325 | if (flags & FLAG_TIME_CRIT && elapsed_time > critical_time) | 466 | mp_add_perfdata_to_subcheck(&elapsed_time_result, time_pd); |
| 326 | result = STATE_CRITICAL; | 467 | mp_add_subcheck_to_check(&overall, elapsed_time_result); |
| 327 | else if (flags & FLAG_TIME_WARN && elapsed_time > warning_time) | ||
| 328 | result = STATE_WARNING; | ||
| 329 | 468 | ||
| 330 | /* did we get the response we hoped? */ | 469 | /* did we get the response we hoped? */ |
| 331 | if (match == NP_MATCH_FAILURE && result != STATE_CRITICAL) | 470 | if (match == NP_MATCH_FAILURE) { |
| 332 | result = expect_mismatch_state; | 471 | expected_data_result = |
| 472 | mp_set_subcheck_state(expected_data_result, config.expect_mismatch_state); | ||
| 473 | xasprintf(&expected_data_result.output, "Answer failed to match expectation"); | ||
| 474 | mp_add_subcheck_to_check(&overall, expected_data_result); | ||
| 475 | } else if (match == NP_MATCH_SUCCESS) { | ||
| 476 | expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_OK); | ||
| 477 | xasprintf(&expected_data_result.output, "The answer of the server matched the expectation"); | ||
| 478 | mp_add_subcheck_to_check(&overall, expected_data_result); | ||
| 479 | } | ||
| 333 | 480 | ||
| 334 | /* reset the alarm */ | 481 | /* reset the alarm */ |
| 335 | alarm(0); | 482 | alarm(0); |
| 336 | 483 | ||
| 337 | /* this is a bit stupid, because we don't want to print the | 484 | mp_exit(overall); |
| 338 | * response time (which can look ok to the user) if we didn't get | ||
| 339 | * the response we were looking for. if-else */ | ||
| 340 | printf("%s %s - ", SERVICE, state_text(result)); | ||
| 341 | |||
| 342 | if (match == NP_MATCH_FAILURE && len && !(flags & FLAG_HIDE_OUTPUT)) | ||
| 343 | printf("Unexpected response from host/socket: %s", status); | ||
| 344 | else { | ||
| 345 | if (match == NP_MATCH_FAILURE) | ||
| 346 | printf("Unexpected response from host/socket on "); | ||
| 347 | else | ||
| 348 | printf("%.3f second response time on ", elapsed_time); | ||
| 349 | if (server_address[0] != '/') { | ||
| 350 | if (host_specified) | ||
| 351 | printf("%s port %d", server_address, server_port); | ||
| 352 | else | ||
| 353 | printf("port %d", server_port); | ||
| 354 | } else | ||
| 355 | printf("socket %s", server_address); | ||
| 356 | } | ||
| 357 | |||
| 358 | if (match != NP_MATCH_FAILURE && !(flags & FLAG_HIDE_OUTPUT) && len) | ||
| 359 | printf(" [%s]", status); | ||
| 360 | |||
| 361 | /* perf-data doesn't apply when server doesn't talk properly, | ||
| 362 | * so print all zeroes on warn and crit. Use fperfdata since | ||
| 363 | * localisation settings can make different outputs */ | ||
| 364 | if (match == NP_MATCH_FAILURE) | ||
| 365 | printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), 0, | ||
| 366 | (flags & FLAG_TIME_CRIT ? true : false), 0, true, 0, true, socket_timeout)); | ||
| 367 | else | ||
| 368 | printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), warning_time, | ||
| 369 | (flags & FLAG_TIME_CRIT ? true : false), critical_time, true, 0, true, socket_timeout)); | ||
| 370 | |||
| 371 | putchar('\n'); | ||
| 372 | return result; | ||
| 373 | } | 485 | } |
| 374 | 486 | ||
| 375 | /* process command-line arguments */ | 487 | /* process command-line arguments */ |
| 376 | static int process_arguments(int argc, char **argv) { | 488 | static check_tcp_config_wrapper process_arguments(int argc, char **argv, check_tcp_config config) { |
| 377 | enum { | 489 | enum { |
| 378 | SNI_OPTION = CHAR_MAX + 1 | 490 | SNI_OPTION = CHAR_MAX + 1, |
| 491 | output_format_index, | ||
| 379 | }; | 492 | }; |
| 380 | 493 | ||
| 381 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, | 494 | static struct option longopts[] = { |
| 382 | {"critical", required_argument, 0, 'c'}, | 495 | {"hostname", required_argument, 0, 'H'}, |
| 383 | {"warning", required_argument, 0, 'w'}, | 496 | {"critical", required_argument, 0, 'c'}, |
| 384 | {"critical-codes", required_argument, 0, 'C'}, | 497 | {"warning", required_argument, 0, 'w'}, |
| 385 | {"warning-codes", required_argument, 0, 'W'}, | 498 | {"critical-codes", required_argument, 0, 'C'}, |
| 386 | {"timeout", required_argument, 0, 't'}, | 499 | {"warning-codes", required_argument, 0, 'W'}, |
| 387 | {"protocol", required_argument, 0, 'P'}, /* FIXME: Unhandled */ | 500 | {"timeout", required_argument, 0, 't'}, |
| 388 | {"port", required_argument, 0, 'p'}, | 501 | {"protocol", required_argument, 0, 'P'}, /* FIXME: Unhandled */ |
| 389 | {"escape", no_argument, 0, 'E'}, | 502 | {"port", required_argument, 0, 'p'}, |
| 390 | {"all", no_argument, 0, 'A'}, | 503 | {"escape", no_argument, 0, 'E'}, |
| 391 | {"send", required_argument, 0, 's'}, | 504 | {"all", no_argument, 0, 'A'}, |
| 392 | {"expect", required_argument, 0, 'e'}, | 505 | {"send", required_argument, 0, 's'}, |
| 393 | {"maxbytes", required_argument, 0, 'm'}, | 506 | {"expect", required_argument, 0, 'e'}, |
| 394 | {"quit", required_argument, 0, 'q'}, | 507 | {"maxbytes", required_argument, 0, 'm'}, |
| 395 | {"jail", no_argument, 0, 'j'}, | 508 | {"quit", required_argument, 0, 'q'}, |
| 396 | {"delay", required_argument, 0, 'd'}, | 509 | {"jail", no_argument, 0, 'j'}, |
| 397 | {"refuse", required_argument, 0, 'r'}, | 510 | {"delay", required_argument, 0, 'd'}, |
| 398 | {"mismatch", required_argument, 0, 'M'}, | 511 | {"refuse", required_argument, 0, 'r'}, |
| 399 | {"use-ipv4", no_argument, 0, '4'}, | 512 | {"mismatch", required_argument, 0, 'M'}, |
| 400 | {"use-ipv6", no_argument, 0, '6'}, | 513 | {"use-ipv4", no_argument, 0, '4'}, |
| 401 | {"verbose", no_argument, 0, 'v'}, | 514 | {"use-ipv6", no_argument, 0, '6'}, |
| 402 | {"version", no_argument, 0, 'V'}, | 515 | {"verbose", no_argument, 0, 'v'}, |
| 403 | {"help", no_argument, 0, 'h'}, | 516 | {"version", no_argument, 0, 'V'}, |
| 404 | {"ssl", no_argument, 0, 'S'}, | 517 | {"help", no_argument, 0, 'h'}, |
| 405 | {"sni", required_argument, 0, SNI_OPTION}, | 518 | {"ssl", no_argument, 0, 'S'}, |
| 406 | {"certificate", required_argument, 0, 'D'}, | 519 | {"sni", required_argument, 0, SNI_OPTION}, |
| 407 | {0, 0, 0, 0}}; | 520 | {"certificate", required_argument, 0, 'D'}, |
| 408 | 521 | {"output-format", required_argument, 0, output_format_index}, | |
| 409 | if (argc < 2) | 522 | {0, 0, 0, 0}}; |
| 523 | |||
| 524 | if (argc < 2) { | ||
| 410 | usage4(_("No arguments found")); | 525 | usage4(_("No arguments found")); |
| 526 | } | ||
| 411 | 527 | ||
| 412 | /* backwards compatibility */ | 528 | /* backwards compatibility */ |
| 413 | for (int i = 1; i < argc; i++) { | 529 | for (int i = 1; i < argc; i++) { |
| 414 | if (strcmp("-to", argv[i]) == 0) | 530 | if (strcmp("-to", argv[i]) == 0) { |
| 415 | strcpy(argv[i], "-t"); | 531 | strcpy(argv[i], "-t"); |
| 416 | else if (strcmp("-wt", argv[i]) == 0) | 532 | } else if (strcmp("-wt", argv[i]) == 0) { |
| 417 | strcpy(argv[i], "-w"); | 533 | strcpy(argv[i], "-w"); |
| 418 | else if (strcmp("-ct", argv[i]) == 0) | 534 | } else if (strcmp("-ct", argv[i]) == 0) { |
| 419 | strcpy(argv[i], "-c"); | 535 | strcpy(argv[i], "-c"); |
| 536 | } | ||
| 420 | } | 537 | } |
| 421 | 538 | ||
| 422 | if (!is_option(argv[1])) { | 539 | if (!is_option(argv[1])) { |
| 423 | server_address = argv[1]; | 540 | config.server_address = argv[1]; |
| 424 | argv[1] = argv[0]; | 541 | argv[1] = argv[0]; |
| 425 | argv = &argv[1]; | 542 | argv = &argv[1]; |
| 426 | argc--; | 543 | argc--; |
| 427 | } | 544 | } |
| 428 | 545 | ||
| 429 | int option_char; | ||
| 430 | bool escape = false; | 546 | bool escape = false; |
| 547 | |||
| 431 | while (true) { | 548 | while (true) { |
| 432 | int option = 0; | 549 | int option = 0; |
| 433 | option_char = getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option); | 550 | int option_index = |
| 551 | getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option); | ||
| 434 | 552 | ||
| 435 | if (option_char == -1 || option_char == EOF || option_char == 1) | 553 | if (option_index == -1 || option_index == EOF || option_index == 1) { |
| 436 | break; | 554 | break; |
| 555 | } | ||
| 437 | 556 | ||
| 438 | switch (option_char) { | 557 | switch (option_index) { |
| 439 | case '?': /* print short usage statement if args not parsable */ | 558 | case '?': /* print short usage statement if args not parsable */ |
| 440 | usage5(); | 559 | usage5(); |
| 441 | case 'h': /* help */ | 560 | case 'h': /* help */ |
| 442 | print_help(); | 561 | print_help(config.service); |
| 443 | exit(STATE_UNKNOWN); | 562 | exit(STATE_UNKNOWN); |
| 444 | case 'V': /* version */ | 563 | case 'V': /* version */ |
| 445 | print_revision(progname, NP_VERSION); | 564 | print_revision(progname, NP_VERSION); |
| 446 | exit(STATE_UNKNOWN); | 565 | exit(STATE_UNKNOWN); |
| 447 | case 'v': /* verbose mode */ | 566 | case 'v': /* verbose mode */ |
| 448 | flags |= FLAG_VERBOSE; | 567 | verbosity++; |
| 449 | match_flags |= NP_MATCH_VERBOSE; | 568 | config.match_flags |= NP_MATCH_VERBOSE; |
| 450 | break; | 569 | break; |
| 451 | case '4': | 570 | case '4': // Apparently unused TODO |
| 452 | address_family = AF_INET; | 571 | address_family = AF_INET; |
| 453 | break; | 572 | break; |
| 454 | case '6': | 573 | case '6': // Apparently unused TODO |
| 455 | #ifdef USE_IPV6 | 574 | #ifdef USE_IPV6 |
| 456 | address_family = AF_INET6; | 575 | address_family = AF_INET6; |
| 457 | #else | 576 | #else |
| @@ -459,163 +578,192 @@ static int process_arguments(int argc, char **argv) { | |||
| 459 | #endif | 578 | #endif |
| 460 | break; | 579 | break; |
| 461 | case 'H': /* hostname */ | 580 | case 'H': /* hostname */ |
| 462 | host_specified = true; | 581 | config.host_specified = true; |
| 463 | server_address = optarg; | 582 | config.server_address = optarg; |
| 464 | break; | 583 | break; |
| 465 | case 'c': /* critical */ | 584 | case 'c': /* critical */ |
| 466 | critical_time = strtod(optarg, NULL); | 585 | config.critical_time = strtod(optarg, NULL); |
| 467 | flags |= FLAG_TIME_CRIT; | 586 | config.critical_time_set = true; |
| 468 | break; | 587 | break; |
| 469 | case 'j': /* hide output */ | 588 | case 'j': /* hide output */ |
| 470 | flags |= FLAG_HIDE_OUTPUT; | 589 | config.hide_output = true; |
| 471 | break; | 590 | break; |
| 472 | case 'w': /* warning */ | 591 | case 'w': /* warning */ |
| 473 | warning_time = strtod(optarg, NULL); | 592 | config.warning_time = strtod(optarg, NULL); |
| 474 | flags |= FLAG_TIME_WARN; | 593 | config.warning_time_set = true; |
| 475 | break; | ||
| 476 | case 'C': | ||
| 477 | crit_codes = realloc(crit_codes, ++crit_codes_count); | ||
| 478 | crit_codes[crit_codes_count - 1] = optarg; | ||
| 479 | break; | ||
| 480 | case 'W': | ||
| 481 | warn_codes = realloc(warn_codes, ++warn_codes_count); | ||
| 482 | warn_codes[warn_codes_count - 1] = optarg; | ||
| 483 | break; | 594 | break; |
| 484 | case 't': /* timeout */ | 595 | case 't': /* timeout */ |
| 485 | if (!is_intpos(optarg)) | 596 | if (!is_intpos(optarg)) { |
| 486 | usage4(_("Timeout interval must be a positive integer")); | 597 | usage4(_("Timeout interval must be a positive integer")); |
| 487 | else | 598 | } else { |
| 488 | socket_timeout = atoi(optarg); | 599 | socket_timeout = atoi(optarg); |
| 600 | } | ||
| 489 | break; | 601 | break; |
| 490 | case 'p': /* port */ | 602 | case 'p': /* port */ |
| 491 | if (!is_intpos(optarg)) | 603 | if (!is_intpos(optarg)) { |
| 492 | usage4(_("Port must be a positive integer")); | 604 | usage4(_("Port must be a positive integer")); |
| 493 | else | 605 | } else { |
| 494 | server_port = atoi(optarg); | 606 | config.server_port = atoi(optarg); |
| 607 | } | ||
| 495 | break; | 608 | break; |
| 496 | case 'E': | 609 | case 'E': |
| 497 | escape = true; | 610 | escape = true; |
| 498 | break; | 611 | break; |
| 499 | case 's': | 612 | case 's': |
| 500 | if (escape) | 613 | if (escape) { |
| 501 | server_send = np_escaped_string(optarg); | 614 | config.send = np_escaped_string(optarg); |
| 502 | else | 615 | } else { |
| 503 | xasprintf(&server_send, "%s", optarg); | 616 | xasprintf(&config.send, "%s", optarg); |
| 617 | } | ||
| 504 | break; | 618 | break; |
| 505 | case 'e': /* expect string (may be repeated) */ | 619 | case 'e': /* expect string (may be repeated) */ |
| 506 | match_flags &= ~NP_MATCH_EXACT; | 620 | config.match_flags &= ~NP_MATCH_EXACT; |
| 507 | if (server_expect_count == 0) | 621 | if (config.server_expect_count == 0) { |
| 508 | server_expect = malloc(sizeof(char *) * (++server_expect_count)); | 622 | config.server_expect = malloc(sizeof(char *) * (++config.server_expect_count)); |
| 509 | else | 623 | } else { |
| 510 | server_expect = realloc(server_expect, sizeof(char *) * (++server_expect_count)); | 624 | config.server_expect = |
| 511 | server_expect[server_expect_count - 1] = optarg; | 625 | realloc(config.server_expect, sizeof(char *) * (++config.server_expect_count)); |
| 626 | } | ||
| 627 | |||
| 628 | if (config.server_expect == NULL) { | ||
| 629 | die(STATE_UNKNOWN, _("Allocation failed")); | ||
| 630 | } | ||
| 631 | config.server_expect[config.server_expect_count - 1] = optarg; | ||
| 512 | break; | 632 | break; |
| 513 | case 'm': | 633 | case 'm': |
| 514 | if (!is_intpos(optarg)) | 634 | if (!is_intpos(optarg)) { |
| 515 | usage4(_("Maxbytes must be a positive integer")); | 635 | usage4(_("Maxbytes must be a positive integer")); |
| 516 | else | 636 | } else { |
| 517 | maxbytes = strtol(optarg, NULL, 0); | 637 | config.maxbytes = strtol(optarg, NULL, 0); |
| 638 | } | ||
| 518 | break; | 639 | break; |
| 519 | case 'q': | 640 | case 'q': |
| 520 | if (escape) | 641 | if (escape) { |
| 521 | server_quit = np_escaped_string(optarg); | 642 | config.quit = np_escaped_string(optarg); |
| 522 | else | 643 | } else { |
| 523 | xasprintf(&server_quit, "%s\r\n", optarg); | 644 | xasprintf(&config.quit, "%s\r\n", optarg); |
| 645 | } | ||
| 524 | break; | 646 | break; |
| 525 | case 'r': | 647 | case 'r': |
| 526 | if (!strncmp(optarg, "ok", 2)) | 648 | if (!strncmp(optarg, "ok", 2)) { |
| 527 | econn_refuse_state = STATE_OK; | 649 | config.econn_refuse_state = STATE_OK; |
| 528 | else if (!strncmp(optarg, "warn", 4)) | 650 | } else if (!strncmp(optarg, "warn", 4)) { |
| 529 | econn_refuse_state = STATE_WARNING; | 651 | config.econn_refuse_state = STATE_WARNING; |
| 530 | else if (!strncmp(optarg, "crit", 4)) | 652 | } else if (!strncmp(optarg, "crit", 4)) { |
| 531 | econn_refuse_state = STATE_CRITICAL; | 653 | config.econn_refuse_state = STATE_CRITICAL; |
| 532 | else | 654 | } else { |
| 533 | usage4(_("Refuse must be one of ok, warn, crit")); | 655 | usage4(_("Refuse must be one of ok, warn, crit")); |
| 656 | } | ||
| 534 | break; | 657 | break; |
| 535 | case 'M': | 658 | case 'M': |
| 536 | if (!strncmp(optarg, "ok", 2)) | 659 | if (!strncmp(optarg, "ok", 2)) { |
| 537 | expect_mismatch_state = STATE_OK; | 660 | config.expect_mismatch_state = STATE_OK; |
| 538 | else if (!strncmp(optarg, "warn", 4)) | 661 | } else if (!strncmp(optarg, "warn", 4)) { |
| 539 | expect_mismatch_state = STATE_WARNING; | 662 | config.expect_mismatch_state = STATE_WARNING; |
| 540 | else if (!strncmp(optarg, "crit", 4)) | 663 | } else if (!strncmp(optarg, "crit", 4)) { |
| 541 | expect_mismatch_state = STATE_CRITICAL; | 664 | config.expect_mismatch_state = STATE_CRITICAL; |
| 542 | else | 665 | } else { |
| 543 | usage4(_("Mismatch must be one of ok, warn, crit")); | 666 | usage4(_("Mismatch must be one of ok, warn, crit")); |
| 667 | } | ||
| 544 | break; | 668 | break; |
| 545 | case 'd': | 669 | case 'd': |
| 546 | if (is_intpos(optarg)) | 670 | if (is_intpos(optarg)) { |
| 547 | delay = atoi(optarg); | 671 | config.delay = atoi(optarg); |
| 548 | else | 672 | } else { |
| 549 | usage4(_("Delay must be a positive integer")); | 673 | usage4(_("Delay must be a positive integer")); |
| 674 | } | ||
| 550 | break; | 675 | break; |
| 551 | case 'D': { /* Check SSL cert validity - days 'til certificate expiration */ | 676 | case 'D': /* Check SSL cert validity - days 'til certificate expiration */ |
| 552 | #ifdef HAVE_SSL | 677 | #ifdef HAVE_SSL |
| 553 | # ifdef USE_OPENSSL /* XXX */ | 678 | # ifdef USE_OPENSSL /* XXX */ |
| 679 | { | ||
| 554 | char *temp; | 680 | char *temp; |
| 555 | if ((temp = strchr(optarg, ',')) != NULL) { | 681 | if ((temp = strchr(optarg, ',')) != NULL) { |
| 556 | *temp = '\0'; | 682 | *temp = '\0'; |
| 557 | if (!is_intnonneg(optarg)) | 683 | if (!is_intnonneg(optarg)) { |
| 558 | usage2(_("Invalid certificate expiration period"), optarg); | 684 | usage2(_("Invalid certificate expiration period"), optarg); |
| 559 | days_till_exp_warn = atoi(optarg); | 685 | } |
| 686 | config.days_till_exp_warn = atoi(optarg); | ||
| 560 | *temp = ','; | 687 | *temp = ','; |
| 561 | temp++; | 688 | temp++; |
| 562 | if (!is_intnonneg(temp)) | 689 | if (!is_intnonneg(temp)) { |
| 563 | usage2(_("Invalid certificate expiration period"), temp); | 690 | usage2(_("Invalid certificate expiration period"), temp); |
| 564 | days_till_exp_crit = atoi(temp); | 691 | } |
| 692 | config.days_till_exp_crit = atoi(temp); | ||
| 565 | } else { | 693 | } else { |
| 566 | days_till_exp_crit = 0; | 694 | config.days_till_exp_crit = 0; |
| 567 | if (!is_intnonneg(optarg)) | 695 | if (!is_intnonneg(optarg)) { |
| 568 | usage2(_("Invalid certificate expiration period"), optarg); | 696 | usage2(_("Invalid certificate expiration period"), optarg); |
| 569 | days_till_exp_warn = atoi(optarg); | 697 | } |
| 698 | config.days_till_exp_warn = atoi(optarg); | ||
| 570 | } | 699 | } |
| 571 | check_cert = true; | 700 | config.check_cert = true; |
| 572 | flags |= FLAG_SSL; | 701 | config.use_tls = true; |
| 573 | } break; | 702 | } break; |
| 574 | # endif /* USE_OPENSSL */ | 703 | # endif /* USE_OPENSSL */ |
| 575 | #endif | 704 | #endif |
| 576 | /* fallthrough if we don't have ssl */ | 705 | /* fallthrough if we don't have ssl */ |
| 577 | case 'S': | 706 | case 'S': |
| 578 | #ifdef HAVE_SSL | 707 | #ifdef HAVE_SSL |
| 579 | flags |= FLAG_SSL; | 708 | config.use_tls = true; |
| 580 | #else | 709 | #else |
| 581 | die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); | 710 | die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); |
| 582 | #endif | 711 | #endif |
| 583 | break; | 712 | break; |
| 584 | case SNI_OPTION: | 713 | case SNI_OPTION: |
| 585 | #ifdef HAVE_SSL | 714 | #ifdef HAVE_SSL |
| 586 | flags |= FLAG_SSL; | 715 | config.use_tls = true; |
| 587 | sni_specified = true; | 716 | config.sni_specified = true; |
| 588 | sni = optarg; | 717 | config.sni = optarg; |
| 589 | #else | 718 | #else |
| 590 | die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); | 719 | die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); |
| 591 | #endif | 720 | #endif |
| 592 | break; | 721 | break; |
| 593 | case 'A': | 722 | case 'A': |
| 594 | match_flags |= NP_MATCH_ALL; | 723 | config.match_flags |= NP_MATCH_ALL; |
| 724 | break; | ||
| 725 | case output_format_index: { | ||
| 726 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 727 | if (!parser.parsing_success) { | ||
| 728 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 729 | printf("Invalid output format: %s\n", optarg); | ||
| 730 | exit(STATE_UNKNOWN); | ||
| 731 | } | ||
| 732 | |||
| 733 | config.output_format_set = true; | ||
| 734 | config.output_format = parser.output_format; | ||
| 595 | break; | 735 | break; |
| 596 | } | 736 | } |
| 737 | } | ||
| 597 | } | 738 | } |
| 598 | 739 | ||
| 599 | option_char = optind; | 740 | int index = optind; |
| 600 | if (!host_specified && option_char < argc) | 741 | if (!config.host_specified && index < argc) { |
| 601 | server_address = strdup(argv[option_char++]); | 742 | config.server_address = strdup(argv[index++]); |
| 743 | } | ||
| 602 | 744 | ||
| 603 | if (server_address == NULL) | 745 | if (config.server_address == NULL) { |
| 604 | usage4(_("You must provide a server address")); | 746 | usage4(_("You must provide a server address")); |
| 605 | else if (server_address[0] != '/' && !is_host(server_address)) | 747 | } else if (config.server_address[0] != '/' && !is_host(config.server_address)) { |
| 606 | die(STATE_CRITICAL, "%s %s - %s: %s\n", SERVICE, state_text(STATE_CRITICAL), _("Invalid hostname, address or socket"), | 748 | die(STATE_CRITICAL, "%s %s - %s: %s\n", config.service, state_text(STATE_CRITICAL), |
| 607 | server_address); | 749 | _("Invalid hostname, address or socket"), config.server_address); |
| 750 | } | ||
| 608 | 751 | ||
| 609 | return OK; | 752 | check_tcp_config_wrapper result = { |
| 753 | .config = config, | ||
| 754 | .errorcode = OK, | ||
| 755 | }; | ||
| 756 | return result; | ||
| 610 | } | 757 | } |
| 611 | 758 | ||
| 612 | void print_help(void) { | 759 | void print_help(const char *service) { |
| 613 | print_revision(progname, NP_VERSION); | 760 | print_revision(progname, NP_VERSION); |
| 614 | 761 | ||
| 615 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); | 762 | printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); |
| 616 | printf(COPYRIGHT, copyright, email); | 763 | printf(COPYRIGHT, copyright, email); |
| 617 | 764 | ||
| 618 | printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), SERVICE); | 765 | printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), |
| 766 | service); | ||
| 619 | 767 | ||
| 620 | print_usage(); | 768 | print_usage(); |
| 621 | 769 | ||
| @@ -627,7 +775,8 @@ void print_help(void) { | |||
| 627 | printf(UT_IPv46); | 775 | printf(UT_IPv46); |
| 628 | 776 | ||
| 629 | printf(" %s\n", "-E, --escape"); | 777 | printf(" %s\n", "-E, --escape"); |
| 630 | printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before send or quit option")); | 778 | printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before " |
| 779 | "send or quit option")); | ||
| 631 | printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit")); | 780 | printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit")); |
| 632 | printf(" %s\n", "-s, --send=STRING"); | 781 | printf(" %s\n", "-s, --send=STRING"); |
| 633 | printf(" %s\n", _("String to send to the server")); | 782 | printf(" %s\n", _("String to send to the server")); |
| @@ -640,7 +789,8 @@ void print_help(void) { | |||
| 640 | printf(" %s\n", "-r, --refuse=ok|warn|crit"); | 789 | printf(" %s\n", "-r, --refuse=ok|warn|crit"); |
| 641 | printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)")); | 790 | printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)")); |
| 642 | printf(" %s\n", "-M, --mismatch=ok|warn|crit"); | 791 | printf(" %s\n", "-M, --mismatch=ok|warn|crit"); |
| 643 | printf(" %s\n", _("Accept expected string mismatches with states ok, warn, crit (default: warn)")); | 792 | printf(" %s\n", |
| 793 | _("Accept expected string mismatches with states ok, warn, crit (default: warn)")); | ||
| 644 | printf(" %s\n", "-j, --jail"); | 794 | printf(" %s\n", "-j, --jail"); |
| 645 | printf(" %s\n", _("Hide output from TCP socket")); | 795 | printf(" %s\n", _("Hide output from TCP socket")); |
| 646 | printf(" %s\n", "-m, --maxbytes=INTEGER"); | 796 | printf(" %s\n", "-m, --maxbytes=INTEGER"); |
| @@ -662,6 +812,7 @@ void print_help(void) { | |||
| 662 | 812 | ||
| 663 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 813 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
| 664 | 814 | ||
| 815 | printf(UT_OUTPUT_FORMAT); | ||
| 665 | printf(UT_VERBOSE); | 816 | printf(UT_VERBOSE); |
| 666 | 817 | ||
| 667 | printf(UT_SUPPORT); | 818 | printf(UT_SUPPORT); |
| @@ -669,7 +820,8 @@ void print_help(void) { | |||
| 669 | 820 | ||
| 670 | void print_usage(void) { | 821 | void print_usage(void) { |
| 671 | printf("%s\n", _("Usage:")); | 822 | printf("%s\n", _("Usage:")); |
| 672 | printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n", progname); | 823 | printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n", |
| 824 | progname); | ||
| 673 | printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n"); | 825 | printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n"); |
| 674 | printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n"); | 826 | printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n"); |
| 675 | printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n"); | 827 | printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n"); |
diff --git a/plugins/check_tcp.d/config.h b/plugins/check_tcp.d/config.h new file mode 100644 index 00000000..dc25d79e --- /dev/null +++ b/plugins/check_tcp.d/config.h | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../lib/utils_tcp.h" | ||
| 4 | #include "output.h" | ||
| 5 | #include "states.h" | ||
| 6 | #include <netinet/in.h> | ||
| 7 | |||
| 8 | typedef struct { | ||
| 9 | char *server_address; | ||
| 10 | bool host_specified; | ||
| 11 | int server_port; // TODO can this be a uint16? | ||
| 12 | |||
| 13 | int protocol; /* most common is default */ | ||
| 14 | char *service; | ||
| 15 | char *send; | ||
| 16 | char *quit; | ||
| 17 | char **server_expect; | ||
| 18 | size_t server_expect_count; | ||
| 19 | bool use_tls; | ||
| 20 | #ifdef HAVE_SSL | ||
| 21 | char *sni; | ||
| 22 | bool sni_specified; | ||
| 23 | bool check_cert; | ||
| 24 | int days_till_exp_warn; | ||
| 25 | int days_till_exp_crit; | ||
| 26 | #endif // HAVE_SSL | ||
| 27 | int match_flags; | ||
| 28 | mp_state_enum expect_mismatch_state; | ||
| 29 | unsigned int delay; | ||
| 30 | |||
| 31 | bool warning_time_set; | ||
| 32 | double warning_time; | ||
| 33 | bool critical_time_set; | ||
| 34 | double critical_time; | ||
| 35 | |||
| 36 | mp_state_enum econn_refuse_state; | ||
| 37 | |||
| 38 | ssize_t maxbytes; | ||
| 39 | |||
| 40 | bool hide_output; | ||
| 41 | |||
| 42 | bool output_format_set; | ||
| 43 | mp_output_format output_format; | ||
| 44 | } check_tcp_config; | ||
| 45 | |||
| 46 | check_tcp_config check_tcp_config_init() { | ||
| 47 | check_tcp_config result = { | ||
| 48 | .server_address = "127.0.0.1", | ||
| 49 | .host_specified = false, | ||
| 50 | .server_port = 0, | ||
| 51 | |||
| 52 | .protocol = IPPROTO_TCP, | ||
| 53 | .service = "TCP", | ||
| 54 | .send = NULL, | ||
| 55 | .quit = NULL, | ||
| 56 | .server_expect = NULL, | ||
| 57 | .server_expect_count = 0, | ||
| 58 | .use_tls = false, | ||
| 59 | #ifdef HAVE_SSL | ||
| 60 | .sni = NULL, | ||
| 61 | .sni_specified = false, | ||
| 62 | .check_cert = false, | ||
| 63 | .days_till_exp_warn = 0, | ||
| 64 | .days_till_exp_crit = 0, | ||
| 65 | #endif // HAVE_SSL | ||
| 66 | .match_flags = NP_MATCH_EXACT, | ||
| 67 | .expect_mismatch_state = STATE_WARNING, | ||
| 68 | .delay = 0, | ||
| 69 | |||
| 70 | .warning_time_set = false, | ||
| 71 | .warning_time = 0, | ||
| 72 | .critical_time_set = false, | ||
| 73 | .critical_time = 0, | ||
| 74 | |||
| 75 | .econn_refuse_state = STATE_CRITICAL, | ||
| 76 | |||
| 77 | .maxbytes = 0, | ||
| 78 | |||
| 79 | .hide_output = false, | ||
| 80 | |||
| 81 | .output_format_set = false, | ||
| 82 | }; | ||
| 83 | return result; | ||
| 84 | } | ||
diff --git a/plugins/check_time.c b/plugins/check_time.c index d1f50683..fc9ba3f9 100644 --- a/plugins/check_time.c +++ b/plugins/check_time.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | #include "states.h" | ||
| 31 | const char *progname = "check_time"; | 32 | const char *progname = "check_time"; |
| 32 | const char *copyright = "1999-2024"; | 33 | const char *copyright = "1999-2024"; |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 34 | const char *email = "devel@monitoring-plugins.org"; |
| @@ -35,28 +36,15 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 35 | #include "common.h" | 36 | #include "common.h" |
| 36 | #include "netutils.h" | 37 | #include "netutils.h" |
| 37 | #include "utils.h" | 38 | #include "utils.h" |
| 38 | 39 | #include "check_time.d/config.h" | |
| 39 | enum { | ||
| 40 | TIME_PORT = 37 | ||
| 41 | }; | ||
| 42 | 40 | ||
| 43 | #define UNIX_EPOCH 2208988800UL | 41 | #define UNIX_EPOCH 2208988800UL |
| 44 | 42 | ||
| 45 | static uint32_t raw_server_time; | 43 | typedef struct { |
| 46 | static unsigned long server_time, diff_time; | 44 | int errorcode; |
| 47 | static int warning_time = 0; | 45 | check_time_config config; |
| 48 | static bool check_warning_time = false; | 46 | } check_time_config_wrapper; |
| 49 | static int critical_time = 0; | 47 | static check_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 50 | static bool check_critical_time = false; | ||
| 51 | static unsigned long warning_diff = 0; | ||
| 52 | static bool check_warning_diff = false; | ||
| 53 | static unsigned long critical_diff = 0; | ||
| 54 | static bool check_critical_diff = false; | ||
| 55 | static int server_port = TIME_PORT; | ||
| 56 | static char *server_address = NULL; | ||
| 57 | static bool use_udp = false; | ||
| 58 | |||
| 59 | static int process_arguments(int, char **); | ||
| 60 | static void print_help(void); | 48 | static void print_help(void); |
| 61 | void print_usage(void); | 49 | void print_usage(void); |
| 62 | 50 | ||
| @@ -68,8 +56,12 @@ int main(int argc, char **argv) { | |||
| 68 | /* Parse extra opts if any */ | 56 | /* Parse extra opts if any */ |
| 69 | argv = np_extra_opts(&argc, argv, progname); | 57 | argv = np_extra_opts(&argc, argv, progname); |
| 70 | 58 | ||
| 71 | if (process_arguments(argc, argv) == ERROR) | 59 | check_time_config_wrapper tmp_config = process_arguments(argc, argv); |
| 60 | if (tmp_config.errorcode == ERROR) { | ||
| 72 | usage4(_("Could not parse arguments")); | 61 | usage4(_("Could not parse arguments")); |
| 62 | } | ||
| 63 | |||
| 64 | const check_time_config config = tmp_config.config; | ||
| 73 | 65 | ||
| 74 | /* initialize alarm signal handling */ | 66 | /* initialize alarm signal handling */ |
| 75 | signal(SIGALRM, socket_timeout_alarm_handler); | 67 | signal(SIGALRM, socket_timeout_alarm_handler); |
| @@ -79,37 +71,42 @@ int main(int argc, char **argv) { | |||
| 79 | time(&start_time); | 71 | time(&start_time); |
| 80 | 72 | ||
| 81 | int socket; | 73 | int socket; |
| 82 | int result = STATE_UNKNOWN; | 74 | mp_state_enum result = STATE_UNKNOWN; |
| 83 | /* try to connect to the host at the given port number */ | 75 | /* try to connect to the host at the given port number */ |
| 84 | if (use_udp) { | 76 | if (config.use_udp) { |
| 85 | result = my_udp_connect(server_address, server_port, &socket); | 77 | result = my_udp_connect(config.server_address, config.server_port, &socket); |
| 86 | } else { | 78 | } else { |
| 87 | result = my_tcp_connect(server_address, server_port, &socket); | 79 | result = my_tcp_connect(config.server_address, config.server_port, &socket); |
| 88 | } | 80 | } |
| 89 | 81 | ||
| 90 | if (result != STATE_OK) { | 82 | if (result != STATE_OK) { |
| 91 | if (check_critical_time) | 83 | if (config.check_critical_time) { |
| 92 | result = STATE_CRITICAL; | 84 | result = STATE_CRITICAL; |
| 93 | else if (check_warning_time) | 85 | } else if (config.check_warning_time) { |
| 94 | result = STATE_WARNING; | 86 | result = STATE_WARNING; |
| 95 | else | 87 | } else { |
| 96 | result = STATE_UNKNOWN; | 88 | result = STATE_UNKNOWN; |
| 97 | die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"), server_address, server_port); | 89 | } |
| 90 | die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"), | ||
| 91 | config.server_address, config.server_port); | ||
| 98 | } | 92 | } |
| 99 | 93 | ||
| 100 | if (use_udp) { | 94 | if (config.use_udp) { |
| 101 | if (send(socket, "", 0, 0) < 0) { | 95 | if (send(socket, "", 0, 0) < 0) { |
| 102 | if (check_critical_time) | 96 | if (config.check_critical_time) { |
| 103 | result = STATE_CRITICAL; | 97 | result = STATE_CRITICAL; |
| 104 | else if (check_warning_time) | 98 | } else if (config.check_warning_time) { |
| 105 | result = STATE_WARNING; | 99 | result = STATE_WARNING; |
| 106 | else | 100 | } else { |
| 107 | result = STATE_UNKNOWN; | 101 | result = STATE_UNKNOWN; |
| 108 | die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), server_address, server_port); | 102 | } |
| 103 | die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), | ||
| 104 | config.server_address, config.server_port); | ||
| 109 | } | 105 | } |
| 110 | } | 106 | } |
| 111 | 107 | ||
| 112 | /* watch for the connection string */ | 108 | /* watch for the connection string */ |
| 109 | uint32_t raw_server_time; | ||
| 113 | result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0); | 110 | result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0); |
| 114 | 111 | ||
| 115 | /* close the connection */ | 112 | /* close the connection */ |
| @@ -121,48 +118,59 @@ int main(int argc, char **argv) { | |||
| 121 | 118 | ||
| 122 | /* return a WARNING status if we couldn't read any data */ | 119 | /* return a WARNING status if we couldn't read any data */ |
| 123 | if (result <= 0) { | 120 | if (result <= 0) { |
| 124 | if (check_critical_time) | 121 | if (config.check_critical_time) { |
| 125 | result = STATE_CRITICAL; | 122 | result = STATE_CRITICAL; |
| 126 | else if (check_warning_time) | 123 | } else if (config.check_warning_time) { |
| 127 | result = STATE_WARNING; | 124 | result = STATE_WARNING; |
| 128 | else | 125 | } else { |
| 129 | result = STATE_UNKNOWN; | 126 | result = STATE_UNKNOWN; |
| 130 | die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"), server_address, server_port); | 127 | } |
| 128 | die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"), | ||
| 129 | config.server_address, config.server_port); | ||
| 131 | } | 130 | } |
| 132 | 131 | ||
| 133 | result = STATE_OK; | 132 | result = STATE_OK; |
| 134 | 133 | ||
| 135 | time_t conntime = (end_time - start_time); | 134 | time_t conntime = (end_time - start_time); |
| 136 | if (check_critical_time && conntime > critical_time) | 135 | if (config.check_critical_time && conntime > config.critical_time) { |
| 137 | result = STATE_CRITICAL; | 136 | result = STATE_CRITICAL; |
| 138 | else if (check_warning_time && conntime > warning_time) | 137 | } else if (config.check_warning_time && conntime > config.warning_time) { |
| 139 | result = STATE_WARNING; | 138 | result = STATE_WARNING; |
| 139 | } | ||
| 140 | 140 | ||
| 141 | if (result != STATE_OK) | 141 | if (result != STATE_OK) { |
| 142 | die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime, | 142 | die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime, |
| 143 | perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, | 143 | perfdata("time", (long)conntime, "s", config.check_warning_time, |
| 144 | false, 0)); | 144 | (long)config.warning_time, config.check_critical_time, |
| 145 | (long)config.critical_time, true, 0, false, 0)); | ||
| 146 | } | ||
| 145 | 147 | ||
| 148 | unsigned long server_time; | ||
| 149 | unsigned long diff_time; | ||
| 146 | server_time = ntohl(raw_server_time) - UNIX_EPOCH; | 150 | server_time = ntohl(raw_server_time) - UNIX_EPOCH; |
| 147 | if (server_time > (unsigned long)end_time) | 151 | if (server_time > (unsigned long)end_time) { |
| 148 | diff_time = server_time - (unsigned long)end_time; | 152 | diff_time = server_time - (unsigned long)end_time; |
| 149 | else | 153 | } else { |
| 150 | diff_time = (unsigned long)end_time - server_time; | 154 | diff_time = (unsigned long)end_time - server_time; |
| 155 | } | ||
| 151 | 156 | ||
| 152 | if (check_critical_diff && diff_time > critical_diff) | 157 | if (config.check_critical_diff && diff_time > config.critical_diff) { |
| 153 | result = STATE_CRITICAL; | 158 | result = STATE_CRITICAL; |
| 154 | else if (check_warning_diff && diff_time > warning_diff) | 159 | } else if (config.check_warning_diff && diff_time > config.warning_diff) { |
| 155 | result = STATE_WARNING; | 160 | result = STATE_WARNING; |
| 161 | } | ||
| 156 | 162 | ||
| 157 | printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time, | 163 | printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time, |
| 158 | perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, | 164 | perfdata("time", (long)conntime, "s", config.check_warning_time, |
| 159 | false, 0), | 165 | (long)config.warning_time, config.check_critical_time, |
| 160 | perfdata("offset", diff_time, "s", check_warning_diff, warning_diff, check_critical_diff, critical_diff, true, 0, false, 0)); | 166 | (long)config.critical_time, true, 0, false, 0), |
| 167 | perfdata("offset", diff_time, "s", config.check_warning_diff, config.warning_diff, | ||
| 168 | config.check_critical_diff, config.critical_diff, true, 0, false, 0)); | ||
| 161 | return result; | 169 | return result; |
| 162 | } | 170 | } |
| 163 | 171 | ||
| 164 | /* process command-line arguments */ | 172 | /* process command-line arguments */ |
| 165 | int process_arguments(int argc, char **argv) { | 173 | check_time_config_wrapper process_arguments(int argc, char **argv) { |
| 166 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, | 174 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 167 | {"warning-variance", required_argument, 0, 'w'}, | 175 | {"warning-variance", required_argument, 0, 'w'}, |
| 168 | {"critical-variance", required_argument, 0, 'c'}, | 176 | {"critical-variance", required_argument, 0, 'c'}, |
| @@ -175,29 +183,37 @@ int process_arguments(int argc, char **argv) { | |||
| 175 | {"help", no_argument, 0, 'h'}, | 183 | {"help", no_argument, 0, 'h'}, |
| 176 | {0, 0, 0, 0}}; | 184 | {0, 0, 0, 0}}; |
| 177 | 185 | ||
| 178 | if (argc < 2) | 186 | if (argc < 2) { |
| 179 | usage("\n"); | 187 | usage("\n"); |
| 188 | } | ||
| 180 | 189 | ||
| 181 | for (int i = 1; i < argc; i++) { | 190 | for (int i = 1; i < argc; i++) { |
| 182 | if (strcmp("-to", argv[i]) == 0) | 191 | if (strcmp("-to", argv[i]) == 0) { |
| 183 | strcpy(argv[i], "-t"); | 192 | strcpy(argv[i], "-t"); |
| 184 | else if (strcmp("-wd", argv[i]) == 0) | 193 | } else if (strcmp("-wd", argv[i]) == 0) { |
| 185 | strcpy(argv[i], "-w"); | 194 | strcpy(argv[i], "-w"); |
| 186 | else if (strcmp("-cd", argv[i]) == 0) | 195 | } else if (strcmp("-cd", argv[i]) == 0) { |
| 187 | strcpy(argv[i], "-c"); | 196 | strcpy(argv[i], "-c"); |
| 188 | else if (strcmp("-wt", argv[i]) == 0) | 197 | } else if (strcmp("-wt", argv[i]) == 0) { |
| 189 | strcpy(argv[i], "-W"); | 198 | strcpy(argv[i], "-W"); |
| 190 | else if (strcmp("-ct", argv[i]) == 0) | 199 | } else if (strcmp("-ct", argv[i]) == 0) { |
| 191 | strcpy(argv[i], "-C"); | 200 | strcpy(argv[i], "-C"); |
| 201 | } | ||
| 192 | } | 202 | } |
| 193 | 203 | ||
| 204 | check_time_config_wrapper result = { | ||
| 205 | .errorcode = OK, | ||
| 206 | .config = check_time_config_init(), | ||
| 207 | }; | ||
| 208 | |||
| 194 | int option_char; | 209 | int option_char; |
| 195 | while (true) { | 210 | while (true) { |
| 196 | int option = 0; | 211 | int option = 0; |
| 197 | option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option); | 212 | option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option); |
| 198 | 213 | ||
| 199 | if (option_char == -1 || option_char == EOF) | 214 | if (option_char == -1 || option_char == EOF) { |
| 200 | break; | 215 | break; |
| 216 | } | ||
| 201 | 217 | ||
| 202 | switch (option_char) { | 218 | switch (option_char) { |
| 203 | case '?': /* print short usage statement if args not parsable */ | 219 | case '?': /* print short usage statement if args not parsable */ |
| @@ -209,18 +225,20 @@ int process_arguments(int argc, char **argv) { | |||
| 209 | print_revision(progname, NP_VERSION); | 225 | print_revision(progname, NP_VERSION); |
| 210 | exit(STATE_UNKNOWN); | 226 | exit(STATE_UNKNOWN); |
| 211 | case 'H': /* hostname */ | 227 | case 'H': /* hostname */ |
| 212 | if (!is_host(optarg)) | 228 | if (!is_host(optarg)) { |
| 213 | usage2(_("Invalid hostname/address"), optarg); | 229 | usage2(_("Invalid hostname/address"), optarg); |
| 214 | server_address = optarg; | 230 | } |
| 231 | result.config.server_address = optarg; | ||
| 215 | break; | 232 | break; |
| 216 | case 'w': /* warning-variance */ | 233 | case 'w': /* warning-variance */ |
| 217 | if (is_intnonneg(optarg)) { | 234 | if (is_intnonneg(optarg)) { |
| 218 | warning_diff = strtoul(optarg, NULL, 10); | 235 | result.config.warning_diff = strtoul(optarg, NULL, 10); |
| 219 | check_warning_diff = true; | 236 | result.config.check_warning_diff = true; |
| 220 | } else if (strspn(optarg, "0123456789:,") > 0) { | 237 | } else if (strspn(optarg, "0123456789:,") > 0) { |
| 221 | if (sscanf(optarg, "%lu%*[:,]%d", &warning_diff, &warning_time) == 2) { | 238 | if (sscanf(optarg, "%lu%*[:,]%d", &result.config.warning_diff, |
| 222 | check_warning_diff = true; | 239 | &result.config.warning_time) == 2) { |
| 223 | check_warning_time = true; | 240 | result.config.check_warning_diff = true; |
| 241 | result.config.check_warning_time = true; | ||
| 224 | } else { | 242 | } else { |
| 225 | usage4(_("Warning thresholds must be a positive integer")); | 243 | usage4(_("Warning thresholds must be a positive integer")); |
| 226 | } | 244 | } |
| @@ -230,12 +248,13 @@ int process_arguments(int argc, char **argv) { | |||
| 230 | break; | 248 | break; |
| 231 | case 'c': /* critical-variance */ | 249 | case 'c': /* critical-variance */ |
| 232 | if (is_intnonneg(optarg)) { | 250 | if (is_intnonneg(optarg)) { |
| 233 | critical_diff = strtoul(optarg, NULL, 10); | 251 | result.config.critical_diff = strtoul(optarg, NULL, 10); |
| 234 | check_critical_diff = true; | 252 | result.config.check_critical_diff = true; |
| 235 | } else if (strspn(optarg, "0123456789:,") > 0) { | 253 | } else if (strspn(optarg, "0123456789:,") > 0) { |
| 236 | if (sscanf(optarg, "%lu%*[:,]%d", &critical_diff, &critical_time) == 2) { | 254 | if (sscanf(optarg, "%lu%*[:,]%d", &result.config.critical_diff, |
| 237 | check_critical_diff = true; | 255 | &result.config.critical_time) == 2) { |
| 238 | check_critical_time = true; | 256 | result.config.check_critical_diff = true; |
| 257 | result.config.check_critical_time = true; | ||
| 239 | } else { | 258 | } else { |
| 240 | usage4(_("Critical thresholds must be a positive integer")); | 259 | usage4(_("Critical thresholds must be a positive integer")); |
| 241 | } | 260 | } |
| @@ -244,48 +263,53 @@ int process_arguments(int argc, char **argv) { | |||
| 244 | } | 263 | } |
| 245 | break; | 264 | break; |
| 246 | case 'W': /* warning-connect */ | 265 | case 'W': /* warning-connect */ |
| 247 | if (!is_intnonneg(optarg)) | 266 | if (!is_intnonneg(optarg)) { |
| 248 | usage4(_("Warning threshold must be a positive integer")); | 267 | usage4(_("Warning threshold must be a positive integer")); |
| 249 | else | 268 | } else { |
| 250 | warning_time = atoi(optarg); | 269 | result.config.warning_time = atoi(optarg); |
| 251 | check_warning_time = true; | 270 | } |
| 271 | result.config.check_warning_time = true; | ||
| 252 | break; | 272 | break; |
| 253 | case 'C': /* critical-connect */ | 273 | case 'C': /* critical-connect */ |
| 254 | if (!is_intnonneg(optarg)) | 274 | if (!is_intnonneg(optarg)) { |
| 255 | usage4(_("Critical threshold must be a positive integer")); | 275 | usage4(_("Critical threshold must be a positive integer")); |
| 256 | else | 276 | } else { |
| 257 | critical_time = atoi(optarg); | 277 | result.config.critical_time = atoi(optarg); |
| 258 | check_critical_time = true; | 278 | } |
| 279 | result.config.check_critical_time = true; | ||
| 259 | break; | 280 | break; |
| 260 | case 'p': /* port */ | 281 | case 'p': /* port */ |
| 261 | if (!is_intnonneg(optarg)) | 282 | if (!is_intnonneg(optarg)) { |
| 262 | usage4(_("Port must be a positive integer")); | 283 | usage4(_("Port must be a positive integer")); |
| 263 | else | 284 | } else { |
| 264 | server_port = atoi(optarg); | 285 | result.config.server_port = atoi(optarg); |
| 286 | } | ||
| 265 | break; | 287 | break; |
| 266 | case 't': /* timeout */ | 288 | case 't': /* timeout */ |
| 267 | if (!is_intnonneg(optarg)) | 289 | if (!is_intnonneg(optarg)) { |
| 268 | usage2(_("Timeout interval must be a positive integer"), optarg); | 290 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 269 | else | 291 | } else { |
| 270 | socket_timeout = atoi(optarg); | 292 | socket_timeout = atoi(optarg); |
| 293 | } | ||
| 271 | break; | 294 | break; |
| 272 | case 'u': /* udp */ | 295 | case 'u': /* udp */ |
| 273 | use_udp = true; | 296 | result.config.use_udp = true; |
| 274 | } | 297 | } |
| 275 | } | 298 | } |
| 276 | 299 | ||
| 277 | option_char = optind; | 300 | option_char = optind; |
| 278 | if (server_address == NULL) { | 301 | if (result.config.server_address == NULL) { |
| 279 | if (argc > option_char) { | 302 | if (argc > option_char) { |
| 280 | if (!is_host(argv[option_char])) | 303 | if (!is_host(argv[option_char])) { |
| 281 | usage2(_("Invalid hostname/address"), optarg); | 304 | usage2(_("Invalid hostname/address"), optarg); |
| 282 | server_address = argv[option_char]; | 305 | } |
| 306 | result.config.server_address = argv[option_char]; | ||
| 283 | } else { | 307 | } else { |
| 284 | usage4(_("Hostname was not supplied")); | 308 | usage4(_("Hostname was not supplied")); |
| 285 | } | 309 | } |
| 286 | } | 310 | } |
| 287 | 311 | ||
| 288 | return OK; | 312 | return result; |
| 289 | } | 313 | } |
| 290 | 314 | ||
| 291 | void print_help(void) { | 315 | void print_help(void) { |
diff --git a/plugins/check_time.d/config.h b/plugins/check_time.d/config.h new file mode 100644 index 00000000..09bd7c45 --- /dev/null +++ b/plugins/check_time.d/config.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | enum { | ||
| 7 | TIME_PORT = 37 | ||
| 8 | }; | ||
| 9 | |||
| 10 | typedef struct { | ||
| 11 | char *server_address; | ||
| 12 | int server_port; | ||
| 13 | bool use_udp; | ||
| 14 | |||
| 15 | int warning_time; | ||
| 16 | bool check_warning_time; | ||
| 17 | int critical_time; | ||
| 18 | bool check_critical_time; | ||
| 19 | unsigned long warning_diff; | ||
| 20 | bool check_warning_diff; | ||
| 21 | unsigned long critical_diff; | ||
| 22 | bool check_critical_diff; | ||
| 23 | } check_time_config; | ||
| 24 | |||
| 25 | check_time_config check_time_config_init() { | ||
| 26 | check_time_config tmp = { | ||
| 27 | .server_address = NULL, | ||
| 28 | .server_port = TIME_PORT, | ||
| 29 | .use_udp = false, | ||
| 30 | |||
| 31 | .warning_time = 0, | ||
| 32 | .check_warning_time = false, | ||
| 33 | .critical_time = 0, | ||
| 34 | .check_critical_time = false, | ||
| 35 | |||
| 36 | .warning_diff = 0, | ||
| 37 | .check_warning_diff = false, | ||
| 38 | .critical_diff = 0, | ||
| 39 | .check_critical_diff = false, | ||
| 40 | }; | ||
| 41 | return tmp; | ||
| 42 | } | ||
diff --git a/plugins/check_ups.c b/plugins/check_ups.c index 526a29df..54decce3 100644 --- a/plugins/check_ups.c +++ b/plugins/check_ups.c | |||
| @@ -39,69 +39,29 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 39 | #include "common.h" | 39 | #include "common.h" |
| 40 | #include "netutils.h" | 40 | #include "netutils.h" |
| 41 | #include "utils.h" | 41 | #include "utils.h" |
| 42 | 42 | #include "check_ups.d/config.h" | |
| 43 | enum { | 43 | #include "states.h" |
| 44 | PORT = 3493 | ||
| 45 | }; | ||
| 46 | |||
| 47 | #define UPS_NONE 0 /* no supported options */ | ||
| 48 | #define UPS_UTILITY 1 /* supports utility line */ | ||
| 49 | #define UPS_BATTPCT 2 /* supports percent battery remaining */ | ||
| 50 | #define UPS_STATUS 4 /* supports UPS status */ | ||
| 51 | #define UPS_TEMP 8 /* supports UPS temperature */ | ||
| 52 | #define UPS_LOADPCT 16 /* supports load percent */ | ||
| 53 | #define UPS_REALPOWER 32 /* supports real power */ | ||
| 54 | |||
| 55 | #define UPSSTATUS_NONE 0 | ||
| 56 | #define UPSSTATUS_OFF 1 | ||
| 57 | #define UPSSTATUS_OL 2 | ||
| 58 | #define UPSSTATUS_OB 4 | ||
| 59 | #define UPSSTATUS_LB 8 | ||
| 60 | #define UPSSTATUS_CAL 16 | ||
| 61 | #define UPSSTATUS_RB 32 /*Replace Battery */ | ||
| 62 | #define UPSSTATUS_BYPASS 64 | ||
| 63 | #define UPSSTATUS_OVER 128 | ||
| 64 | #define UPSSTATUS_TRIM 256 | ||
| 65 | #define UPSSTATUS_BOOST 512 | ||
| 66 | #define UPSSTATUS_CHRG 1024 | ||
| 67 | #define UPSSTATUS_DISCHRG 2048 | ||
| 68 | #define UPSSTATUS_UNKNOWN 4096 | ||
| 69 | #define UPSSTATUS_ALARM 8192 | ||
| 70 | 44 | ||
| 71 | enum { | 45 | enum { |
| 72 | NOSUCHVAR = ERROR - 1 | 46 | NOSUCHVAR = ERROR - 1 |
| 73 | }; | 47 | }; |
| 74 | 48 | ||
| 75 | typedef struct ups_config { | ||
| 76 | unsigned int server_port; | ||
| 77 | char *server_address; | ||
| 78 | char *ups_name; | ||
| 79 | double warning_value; | ||
| 80 | double critical_value; | ||
| 81 | bool check_warn; | ||
| 82 | bool check_crit; | ||
| 83 | int check_variable; | ||
| 84 | int status; | ||
| 85 | bool temp_output_c; | ||
| 86 | } ups_config; | ||
| 87 | |||
| 88 | ups_config ups_config_init(void) { | ||
| 89 | ups_config tmp = {0}; | ||
| 90 | tmp.server_port = PORT; | ||
| 91 | tmp.server_address = NULL; | ||
| 92 | tmp.ups_name = NULL; | ||
| 93 | tmp.check_variable = UPS_NONE; | ||
| 94 | tmp.status = UPSSTATUS_NONE; | ||
| 95 | |||
| 96 | return tmp; | ||
| 97 | } | ||
| 98 | |||
| 99 | // Forward declarations | 49 | // Forward declarations |
| 100 | static int determine_status(ups_config * /*config*/, int *supported_options); | 50 | typedef struct { |
| 101 | static int get_ups_variable(const char * /*varname*/, char * /*buf*/, ups_config config); | 51 | int errorcode; |
| 52 | int ups_status; | ||
| 53 | int supported_options; | ||
| 54 | } determine_status_result; | ||
| 55 | static determine_status_result determine_status(check_ups_config /*config*/); | ||
| 56 | static int get_ups_variable(const char * /*varname*/, char * /*buf*/, check_ups_config config); | ||
| 57 | |||
| 58 | typedef struct { | ||
| 59 | int errorcode; | ||
| 60 | check_ups_config config; | ||
| 61 | } check_ups_config_wrapper; | ||
| 62 | static check_ups_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 63 | static check_ups_config_wrapper validate_arguments(check_ups_config_wrapper /*config_wrapper*/); | ||
| 102 | 64 | ||
| 103 | static int process_arguments(int /*argc*/, char ** /*argv*/, ups_config * /*config*/); | ||
| 104 | static int validate_arguments(ups_config /*config*/); | ||
| 105 | static void print_help(void); | 65 | static void print_help(void); |
| 106 | void print_usage(void); | 66 | void print_usage(void); |
| 107 | 67 | ||
| @@ -109,28 +69,16 @@ int main(int argc, char **argv) { | |||
| 109 | setlocale(LC_ALL, ""); | 69 | setlocale(LC_ALL, ""); |
| 110 | bindtextdomain(PACKAGE, LOCALEDIR); | 70 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 111 | textdomain(PACKAGE); | 71 | textdomain(PACKAGE); |
| 112 | |||
| 113 | char *ups_status; | ||
| 114 | ups_status = strdup("N/A"); | ||
| 115 | |||
| 116 | char *data; | ||
| 117 | data = strdup(""); | ||
| 118 | |||
| 119 | char *message; | ||
| 120 | message = strdup(""); | ||
| 121 | |||
| 122 | // Exit result | ||
| 123 | int result = STATE_UNKNOWN; | ||
| 124 | |||
| 125 | /* Parse extra opts if any */ | 72 | /* Parse extra opts if any */ |
| 126 | argv = np_extra_opts(&argc, argv, progname); | 73 | argv = np_extra_opts(&argc, argv, progname); |
| 127 | 74 | ||
| 128 | // Config from commandline | 75 | check_ups_config_wrapper tmp_config = process_arguments(argc, argv); |
| 129 | ups_config config = ups_config_init(); | ||
| 130 | 76 | ||
| 131 | if (process_arguments(argc, argv, &config) == ERROR) { | 77 | if (tmp_config.errorcode == ERROR) { |
| 132 | usage4(_("Could not parse arguments")); | 78 | usage4(_("Could not parse arguments")); |
| 133 | } | 79 | } |
| 80 | // Config from commandline | ||
| 81 | check_ups_config config = tmp_config.config; | ||
| 134 | 82 | ||
| 135 | /* initialize alarm signal handling */ | 83 | /* initialize alarm signal handling */ |
| 136 | signal(SIGALRM, socket_timeout_alarm_handler); | 84 | signal(SIGALRM, socket_timeout_alarm_handler); |
| @@ -138,71 +86,76 @@ int main(int argc, char **argv) { | |||
| 138 | /* set socket timeout */ | 86 | /* set socket timeout */ |
| 139 | alarm(socket_timeout); | 87 | alarm(socket_timeout); |
| 140 | 88 | ||
| 141 | int supported_options = UPS_NONE; | ||
| 142 | |||
| 143 | /* get the ups status if possible */ | 89 | /* get the ups status if possible */ |
| 144 | if (determine_status(&config, &supported_options) != OK) { | 90 | determine_status_result query_result = determine_status(config); |
| 91 | if (query_result.errorcode != OK) { | ||
| 145 | return STATE_CRITICAL; | 92 | return STATE_CRITICAL; |
| 146 | } | 93 | } |
| 147 | 94 | ||
| 148 | if (supported_options & UPS_STATUS) { | 95 | int ups_status_flags = query_result.ups_status; |
| 96 | int supported_options = query_result.supported_options; | ||
| 149 | 97 | ||
| 150 | ups_status = strdup(""); | 98 | // Exit result |
| 99 | mp_state_enum result = STATE_UNKNOWN; | ||
| 100 | char *message = NULL; | ||
| 151 | 101 | ||
| 102 | if (supported_options & UPS_STATUS) { | ||
| 103 | char *ups_status = strdup(""); | ||
| 152 | result = STATE_OK; | 104 | result = STATE_OK; |
| 153 | 105 | ||
| 154 | if (config.status & UPSSTATUS_OFF) { | 106 | if (ups_status_flags & UPSSTATUS_OFF) { |
| 155 | xasprintf(&ups_status, "Off"); | 107 | xasprintf(&ups_status, "Off"); |
| 156 | result = STATE_CRITICAL; | 108 | result = STATE_CRITICAL; |
| 157 | } else if ((config.status & (UPSSTATUS_OB | UPSSTATUS_LB)) == (UPSSTATUS_OB | UPSSTATUS_LB)) { | 109 | } else if ((ups_status_flags & (UPSSTATUS_OB | UPSSTATUS_LB)) == |
| 110 | (UPSSTATUS_OB | UPSSTATUS_LB)) { | ||
| 158 | xasprintf(&ups_status, _("On Battery, Low Battery")); | 111 | xasprintf(&ups_status, _("On Battery, Low Battery")); |
| 159 | result = STATE_CRITICAL; | 112 | result = STATE_CRITICAL; |
| 160 | } else { | 113 | } else { |
| 161 | if (config.status & UPSSTATUS_OL) { | 114 | if (ups_status_flags & UPSSTATUS_OL) { |
| 162 | xasprintf(&ups_status, "%s%s", ups_status, _("Online")); | 115 | xasprintf(&ups_status, "%s%s", ups_status, _("Online")); |
| 163 | } | 116 | } |
| 164 | if (config.status & UPSSTATUS_OB) { | 117 | if (ups_status_flags & UPSSTATUS_OB) { |
| 165 | xasprintf(&ups_status, "%s%s", ups_status, _("On Battery")); | 118 | xasprintf(&ups_status, "%s%s", ups_status, _("On Battery")); |
| 166 | result = max_state(result, STATE_WARNING); | 119 | result = max_state(result, STATE_WARNING); |
| 167 | } | 120 | } |
| 168 | if (config.status & UPSSTATUS_LB) { | 121 | if (ups_status_flags & UPSSTATUS_LB) { |
| 169 | xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery")); | 122 | xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery")); |
| 170 | result = max_state(result, STATE_WARNING); | 123 | result = max_state(result, STATE_WARNING); |
| 171 | } | 124 | } |
| 172 | if (config.status & UPSSTATUS_CAL) { | 125 | if (ups_status_flags & UPSSTATUS_CAL) { |
| 173 | xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating")); | 126 | xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating")); |
| 174 | } | 127 | } |
| 175 | if (config.status & UPSSTATUS_RB) { | 128 | if (ups_status_flags & UPSSTATUS_RB) { |
| 176 | xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery")); | 129 | xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery")); |
| 177 | result = max_state(result, STATE_WARNING); | 130 | result = max_state(result, STATE_WARNING); |
| 178 | } | 131 | } |
| 179 | if (config.status & UPSSTATUS_BYPASS) { | 132 | if (ups_status_flags & UPSSTATUS_BYPASS) { |
| 180 | xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass")); | 133 | xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass")); |
| 181 | // Bypassing the battery is likely a bad thing | 134 | // Bypassing the battery is likely a bad thing |
| 182 | result = STATE_CRITICAL; | 135 | result = STATE_CRITICAL; |
| 183 | } | 136 | } |
| 184 | if (config.status & UPSSTATUS_OVER) { | 137 | if (ups_status_flags & UPSSTATUS_OVER) { |
| 185 | xasprintf(&ups_status, "%s%s", ups_status, _(", Overload")); | 138 | xasprintf(&ups_status, "%s%s", ups_status, _(", Overload")); |
| 186 | result = max_state(result, STATE_WARNING); | 139 | result = max_state(result, STATE_WARNING); |
| 187 | } | 140 | } |
| 188 | if (config.status & UPSSTATUS_TRIM) { | 141 | if (ups_status_flags & UPSSTATUS_TRIM) { |
| 189 | xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming")); | 142 | xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming")); |
| 190 | } | 143 | } |
| 191 | if (config.status & UPSSTATUS_BOOST) { | 144 | if (ups_status_flags & UPSSTATUS_BOOST) { |
| 192 | xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting")); | 145 | xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting")); |
| 193 | } | 146 | } |
| 194 | if (config.status & UPSSTATUS_CHRG) { | 147 | if (ups_status_flags & UPSSTATUS_CHRG) { |
| 195 | xasprintf(&ups_status, "%s%s", ups_status, _(", Charging")); | 148 | xasprintf(&ups_status, "%s%s", ups_status, _(", Charging")); |
| 196 | } | 149 | } |
| 197 | if (config.status & UPSSTATUS_DISCHRG) { | 150 | if (ups_status_flags & UPSSTATUS_DISCHRG) { |
| 198 | xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging")); | 151 | xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging")); |
| 199 | result = max_state(result, STATE_WARNING); | 152 | result = max_state(result, STATE_WARNING); |
| 200 | } | 153 | } |
| 201 | if (config.status & UPSSTATUS_ALARM) { | 154 | if (ups_status_flags & UPSSTATUS_ALARM) { |
| 202 | xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM")); | 155 | xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM")); |
| 203 | result = STATE_CRITICAL; | 156 | result = STATE_CRITICAL; |
| 204 | } | 157 | } |
| 205 | if (config.status & UPSSTATUS_UNKNOWN) { | 158 | if (ups_status_flags & UPSSTATUS_UNKNOWN) { |
| 206 | xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown")); | 159 | xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown")); |
| 207 | } | 160 | } |
| 208 | } | 161 | } |
| @@ -211,7 +164,7 @@ int main(int argc, char **argv) { | |||
| 211 | 164 | ||
| 212 | int res; | 165 | int res; |
| 213 | char temp_buffer[MAX_INPUT_BUFFER]; | 166 | char temp_buffer[MAX_INPUT_BUFFER]; |
| 214 | 167 | char *performance_data = strdup(""); | |
| 215 | /* get the ups utility voltage if possible */ | 168 | /* get the ups utility voltage if possible */ |
| 216 | res = get_ups_variable("input.voltage", temp_buffer, config); | 169 | res = get_ups_variable("input.voltage", temp_buffer, config); |
| 217 | if (res == NOSUCHVAR) { | 170 | if (res == NOSUCHVAR) { |
| @@ -239,11 +192,15 @@ int main(int argc, char **argv) { | |||
| 239 | } else if (config.check_warn && ups_utility_deviation >= config.warning_value) { | 192 | } else if (config.check_warn && ups_utility_deviation >= config.warning_value) { |
| 240 | result = max_state(result, STATE_WARNING); | 193 | result = max_state(result, STATE_WARNING); |
| 241 | } | 194 | } |
| 242 | xasprintf(&data, "%s", | 195 | xasprintf(&performance_data, "%s", |
| 243 | perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", config.check_warn, (long)(1000 * config.warning_value), | 196 | perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", |
| 244 | config.check_crit, (long)(1000 * config.critical_value), true, 0, false, 0)); | 197 | config.check_warn, (long)(1000 * config.warning_value), |
| 198 | config.check_crit, (long)(1000 * config.critical_value), true, 0, | ||
| 199 | false, 0)); | ||
| 245 | } else { | 200 | } else { |
| 246 | xasprintf(&data, "%s", perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false, 0, true, 0, false, 0)); | 201 | xasprintf(&performance_data, "%s", |
| 202 | perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false, | ||
| 203 | 0, true, 0, false, 0)); | ||
| 247 | } | 204 | } |
| 248 | } | 205 | } |
| 249 | 206 | ||
| @@ -266,11 +223,14 @@ int main(int argc, char **argv) { | |||
| 266 | } else if (config.check_warn && ups_battery_percent <= config.warning_value) { | 223 | } else if (config.check_warn && ups_battery_percent <= config.warning_value) { |
| 267 | result = max_state(result, STATE_WARNING); | 224 | result = max_state(result, STATE_WARNING); |
| 268 | } | 225 | } |
| 269 | xasprintf(&data, "%s %s", data, | 226 | xasprintf(&performance_data, "%s %s", performance_data, |
| 270 | perfdata("battery", (long)ups_battery_percent, "%", config.check_warn, (long)(config.warning_value), | 227 | perfdata("battery", (long)ups_battery_percent, "%", config.check_warn, |
| 271 | config.check_crit, (long)(config.critical_value), true, 0, true, 100)); | 228 | (long)(config.warning_value), config.check_crit, |
| 229 | (long)(config.critical_value), true, 0, true, 100)); | ||
| 272 | } else { | 230 | } else { |
| 273 | xasprintf(&data, "%s %s", data, perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true, 0, true, 100)); | 231 | xasprintf(&performance_data, "%s %s", performance_data, |
| 232 | perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true, | ||
| 233 | 0, true, 100)); | ||
| 274 | } | 234 | } |
| 275 | } | 235 | } |
| 276 | 236 | ||
| @@ -293,11 +253,14 @@ int main(int argc, char **argv) { | |||
| 293 | } else if (config.check_warn && ups_load_percent >= config.warning_value) { | 253 | } else if (config.check_warn && ups_load_percent >= config.warning_value) { |
| 294 | result = max_state(result, STATE_WARNING); | 254 | result = max_state(result, STATE_WARNING); |
| 295 | } | 255 | } |
| 296 | xasprintf(&data, "%s %s", data, | 256 | xasprintf(&performance_data, "%s %s", performance_data, |
| 297 | perfdata("load", (long)ups_load_percent, "%", config.check_warn, (long)(config.warning_value), config.check_crit, | 257 | perfdata("load", (long)ups_load_percent, "%", config.check_warn, |
| 258 | (long)(config.warning_value), config.check_crit, | ||
| 298 | (long)(config.critical_value), true, 0, true, 100)); | 259 | (long)(config.critical_value), true, 0, true, 100)); |
| 299 | } else { | 260 | } else { |
| 300 | xasprintf(&data, "%s %s", data, perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0, true, 100)); | 261 | xasprintf(&performance_data, "%s %s", performance_data, |
| 262 | perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0, | ||
| 263 | true, 100)); | ||
| 301 | } | 264 | } |
| 302 | } | 265 | } |
| 303 | 266 | ||
| @@ -329,11 +292,14 @@ int main(int argc, char **argv) { | |||
| 329 | } else if (config.check_warn && ups_temperature >= config.warning_value) { | 292 | } else if (config.check_warn && ups_temperature >= config.warning_value) { |
| 330 | result = max_state(result, STATE_WARNING); | 293 | result = max_state(result, STATE_WARNING); |
| 331 | } | 294 | } |
| 332 | xasprintf(&data, "%s %s", data, | 295 | xasprintf(&performance_data, "%s %s", performance_data, |
| 333 | perfdata("temp", (long)ups_temperature, tunits, config.check_warn, (long)(config.warning_value), config.check_crit, | 296 | perfdata("temp", (long)ups_temperature, tunits, config.check_warn, |
| 297 | (long)(config.warning_value), config.check_crit, | ||
| 334 | (long)(config.critical_value), true, 0, false, 0)); | 298 | (long)(config.critical_value), true, 0, false, 0)); |
| 335 | } else { | 299 | } else { |
| 336 | xasprintf(&data, "%s %s", data, perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0, false, 0)); | 300 | xasprintf(&performance_data, "%s %s", performance_data, |
| 301 | perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0, | ||
| 302 | false, 0)); | ||
| 337 | } | 303 | } |
| 338 | } | 304 | } |
| 339 | 305 | ||
| @@ -355,11 +321,14 @@ int main(int argc, char **argv) { | |||
| 355 | } else if (config.check_warn && ups_realpower >= config.warning_value) { | 321 | } else if (config.check_warn && ups_realpower >= config.warning_value) { |
| 356 | result = max_state(result, STATE_WARNING); | 322 | result = max_state(result, STATE_WARNING); |
| 357 | } | 323 | } |
| 358 | xasprintf(&data, "%s %s", data, | 324 | xasprintf(&performance_data, "%s %s", performance_data, |
| 359 | perfdata("realpower", (long)ups_realpower, "W", config.check_warn, (long)(config.warning_value), config.check_crit, | 325 | perfdata("realpower", (long)ups_realpower, "W", config.check_warn, |
| 326 | (long)(config.warning_value), config.check_crit, | ||
| 360 | (long)(config.critical_value), true, 0, false, 0)); | 327 | (long)(config.critical_value), true, 0, false, 0)); |
| 361 | } else { | 328 | } else { |
| 362 | xasprintf(&data, "%s %s", data, perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0, false, 0)); | 329 | xasprintf(&performance_data, "%s %s", performance_data, |
| 330 | perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0, | ||
| 331 | false, 0)); | ||
| 363 | } | 332 | } |
| 364 | } | 333 | } |
| 365 | 334 | ||
| @@ -373,71 +342,79 @@ int main(int argc, char **argv) { | |||
| 373 | /* reset timeout */ | 342 | /* reset timeout */ |
| 374 | alarm(0); | 343 | alarm(0); |
| 375 | 344 | ||
| 376 | printf("UPS %s - %s|%s\n", state_text(result), message, data); | 345 | printf("UPS %s - %s|%s\n", state_text(result), message, performance_data); |
| 377 | return result; | 346 | exit(result); |
| 378 | } | 347 | } |
| 379 | 348 | ||
| 380 | /* determines what options are supported by the UPS */ | 349 | /* determines what options are supported by the UPS */ |
| 381 | int determine_status(ups_config *config, int *supported_options) { | 350 | determine_status_result determine_status(const check_ups_config config) { |
| 382 | char recv_buffer[MAX_INPUT_BUFFER]; | ||
| 383 | 351 | ||
| 384 | int res = get_ups_variable("ups.status", recv_buffer, *config); | 352 | determine_status_result result = { |
| 353 | .errorcode = OK, | ||
| 354 | .ups_status = UPSSTATUS_NONE, | ||
| 355 | .supported_options = 0, | ||
| 356 | }; | ||
| 357 | |||
| 358 | char recv_buffer[MAX_INPUT_BUFFER]; | ||
| 359 | int res = get_ups_variable("ups.status", recv_buffer, config); | ||
| 385 | if (res == NOSUCHVAR) { | 360 | if (res == NOSUCHVAR) { |
| 386 | return OK; | 361 | return result; |
| 387 | } | 362 | } |
| 388 | 363 | ||
| 389 | if (res != STATE_OK) { | 364 | if (res != STATE_OK) { |
| 390 | printf("%s\n", _("Invalid response received from host")); | 365 | printf("%s\n", _("Invalid response received from host")); |
| 391 | return ERROR; | 366 | result.errorcode = ERROR; |
| 367 | return result; | ||
| 392 | } | 368 | } |
| 393 | 369 | ||
| 394 | *supported_options |= UPS_STATUS; | 370 | result.supported_options |= UPS_STATUS; |
| 395 | 371 | ||
| 396 | char temp_buffer[MAX_INPUT_BUFFER]; | 372 | char temp_buffer[MAX_INPUT_BUFFER]; |
| 397 | 373 | ||
| 398 | strcpy(temp_buffer, recv_buffer); | 374 | strcpy(temp_buffer, recv_buffer); |
| 399 | for (char *ptr = (char *)strtok(temp_buffer, " "); ptr != NULL; ptr = (char *)strtok(NULL, " ")) { | 375 | for (char *ptr = strtok(temp_buffer, " "); ptr != NULL; ptr = strtok(NULL, " ")) { |
| 400 | if (!strcmp(ptr, "OFF")) { | 376 | if (!strcmp(ptr, "OFF")) { |
| 401 | config->status |= UPSSTATUS_OFF; | 377 | result.ups_status |= UPSSTATUS_OFF; |
| 402 | } else if (!strcmp(ptr, "OL")) { | 378 | } else if (!strcmp(ptr, "OL")) { |
| 403 | config->status |= UPSSTATUS_OL; | 379 | result.ups_status |= UPSSTATUS_OL; |
| 404 | } else if (!strcmp(ptr, "OB")) { | 380 | } else if (!strcmp(ptr, "OB")) { |
| 405 | config->status |= UPSSTATUS_OB; | 381 | result.ups_status |= UPSSTATUS_OB; |
| 406 | } else if (!strcmp(ptr, "LB")) { | 382 | } else if (!strcmp(ptr, "LB")) { |
| 407 | config->status |= UPSSTATUS_LB; | 383 | result.ups_status |= UPSSTATUS_LB; |
| 408 | } else if (!strcmp(ptr, "CAL")) { | 384 | } else if (!strcmp(ptr, "CAL")) { |
| 409 | config->status |= UPSSTATUS_CAL; | 385 | result.ups_status |= UPSSTATUS_CAL; |
| 410 | } else if (!strcmp(ptr, "RB")) { | 386 | } else if (!strcmp(ptr, "RB")) { |
| 411 | config->status |= UPSSTATUS_RB; | 387 | result.ups_status |= UPSSTATUS_RB; |
| 412 | } else if (!strcmp(ptr, "BYPASS")) { | 388 | } else if (!strcmp(ptr, "BYPASS")) { |
| 413 | config->status |= UPSSTATUS_BYPASS; | 389 | result.ups_status |= UPSSTATUS_BYPASS; |
| 414 | } else if (!strcmp(ptr, "OVER")) { | 390 | } else if (!strcmp(ptr, "OVER")) { |
| 415 | config->status |= UPSSTATUS_OVER; | 391 | result.ups_status |= UPSSTATUS_OVER; |
| 416 | } else if (!strcmp(ptr, "TRIM")) { | 392 | } else if (!strcmp(ptr, "TRIM")) { |
| 417 | config->status |= UPSSTATUS_TRIM; | 393 | result.ups_status |= UPSSTATUS_TRIM; |
| 418 | } else if (!strcmp(ptr, "BOOST")) { | 394 | } else if (!strcmp(ptr, "BOOST")) { |
| 419 | config->status |= UPSSTATUS_BOOST; | 395 | result.ups_status |= UPSSTATUS_BOOST; |
| 420 | } else if (!strcmp(ptr, "CHRG")) { | 396 | } else if (!strcmp(ptr, "CHRG")) { |
| 421 | config->status |= UPSSTATUS_CHRG; | 397 | result.ups_status |= UPSSTATUS_CHRG; |
| 422 | } else if (!strcmp(ptr, "DISCHRG")) { | 398 | } else if (!strcmp(ptr, "DISCHRG")) { |
| 423 | config->status |= UPSSTATUS_DISCHRG; | 399 | result.ups_status |= UPSSTATUS_DISCHRG; |
| 424 | } else if (!strcmp(ptr, "ALARM")) { | 400 | } else if (!strcmp(ptr, "ALARM")) { |
| 425 | config->status |= UPSSTATUS_ALARM; | 401 | result.ups_status |= UPSSTATUS_ALARM; |
| 426 | } else { | 402 | } else { |
| 427 | config->status |= UPSSTATUS_UNKNOWN; | 403 | result.ups_status |= UPSSTATUS_UNKNOWN; |
| 428 | } | 404 | } |
| 429 | } | 405 | } |
| 430 | 406 | ||
| 431 | return OK; | 407 | return result; |
| 432 | } | 408 | } |
| 433 | 409 | ||
| 434 | /* gets a variable value for a specific UPS */ | 410 | /* gets a variable value for a specific UPS */ |
| 435 | int get_ups_variable(const char *varname, char *buf, const ups_config config) { | 411 | int get_ups_variable(const char *varname, char *buf, const check_ups_config config) { |
| 436 | char send_buffer[MAX_INPUT_BUFFER]; | 412 | char send_buffer[MAX_INPUT_BUFFER]; |
| 437 | 413 | ||
| 438 | /* create the command string to send to the UPS daemon */ | 414 | /* create the command string to send to the UPS daemon */ |
| 439 | /* Add LOGOUT to avoid read failure logs */ | 415 | /* Add LOGOUT to avoid read failure logs */ |
| 440 | int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name, varname); | 416 | int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name, |
| 417 | varname); | ||
| 441 | if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) { | 418 | if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) { |
| 442 | printf("%s\n", _("UPS name to long for buffer")); | 419 | printf("%s\n", _("UPS name to long for buffer")); |
| 443 | return ERROR; | 420 | return ERROR; |
| @@ -446,7 +423,8 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) { | |||
| 446 | char temp_buffer[MAX_INPUT_BUFFER]; | 423 | char temp_buffer[MAX_INPUT_BUFFER]; |
| 447 | 424 | ||
| 448 | /* send the command to the daemon and get a response back */ | 425 | /* send the command to the daemon and get a response back */ |
| 449 | if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer, sizeof(temp_buffer)) != STATE_OK) { | 426 | if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer, |
| 427 | sizeof(temp_buffer)) != STATE_OK) { | ||
| 450 | printf("%s\n", _("Invalid response received from host")); | 428 | printf("%s\n", _("Invalid response received from host")); |
| 451 | return ERROR; | 429 | return ERROR; |
| 452 | } | 430 | } |
| @@ -500,7 +478,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) { | |||
| 500 | [-wv warn_value] [-cv crit_value] [-to to_sec] */ | 478 | [-wv warn_value] [-cv crit_value] [-to to_sec] */ |
| 501 | 479 | ||
| 502 | /* process command-line arguments */ | 480 | /* process command-line arguments */ |
| 503 | int process_arguments(int argc, char **argv, ups_config *config) { | 481 | check_ups_config_wrapper process_arguments(int argc, char **argv) { |
| 504 | 482 | ||
| 505 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, | 483 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 506 | {"ups", required_argument, 0, 'u'}, | 484 | {"ups", required_argument, 0, 'u'}, |
| @@ -514,8 +492,14 @@ int process_arguments(int argc, char **argv, ups_config *config) { | |||
| 514 | {"help", no_argument, 0, 'h'}, | 492 | {"help", no_argument, 0, 'h'}, |
| 515 | {0, 0, 0, 0}}; | 493 | {0, 0, 0, 0}}; |
| 516 | 494 | ||
| 495 | check_ups_config_wrapper result = { | ||
| 496 | .errorcode = OK, | ||
| 497 | .config = check_ups_config_init(), | ||
| 498 | }; | ||
| 499 | |||
| 517 | if (argc < 2) { | 500 | if (argc < 2) { |
| 518 | return ERROR; | 501 | result.errorcode = ERROR; |
| 502 | return result; | ||
| 519 | } | 503 | } |
| 520 | 504 | ||
| 521 | int c; | 505 | int c; |
| @@ -542,52 +526,52 @@ int process_arguments(int argc, char **argv, ups_config *config) { | |||
| 542 | usage5(); | 526 | usage5(); |
| 543 | case 'H': /* hostname */ | 527 | case 'H': /* hostname */ |
| 544 | if (is_host(optarg)) { | 528 | if (is_host(optarg)) { |
| 545 | config->server_address = optarg; | 529 | result.config.server_address = optarg; |
| 546 | } else { | 530 | } else { |
| 547 | usage2(_("Invalid hostname/address"), optarg); | 531 | usage2(_("Invalid hostname/address"), optarg); |
| 548 | } | 532 | } |
| 549 | break; | 533 | break; |
| 550 | case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for | 534 | case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for |
| 551 | Fahrenheit) */ | 535 | Fahrenheit) */ |
| 552 | config->temp_output_c = true; | 536 | result.config.temp_output_c = true; |
| 553 | break; | 537 | break; |
| 554 | case 'u': /* ups name */ | 538 | case 'u': /* ups name */ |
| 555 | config->ups_name = optarg; | 539 | result.config.ups_name = optarg; |
| 556 | break; | 540 | break; |
| 557 | case 'p': /* port */ | 541 | case 'p': /* port */ |
| 558 | if (is_intpos(optarg)) { | 542 | if (is_intpos(optarg)) { |
| 559 | config->server_port = atoi(optarg); | 543 | result.config.server_port = atoi(optarg); |
| 560 | } else { | 544 | } else { |
| 561 | usage2(_("Port must be a positive integer"), optarg); | 545 | usage2(_("Port must be a positive integer"), optarg); |
| 562 | } | 546 | } |
| 563 | break; | 547 | break; |
| 564 | case 'c': /* critical time threshold */ | 548 | case 'c': /* critical time threshold */ |
| 565 | if (is_intnonneg(optarg)) { | 549 | if (is_intnonneg(optarg)) { |
| 566 | config->critical_value = atoi(optarg); | 550 | result.config.critical_value = atoi(optarg); |
| 567 | config->check_crit = true; | 551 | result.config.check_crit = true; |
| 568 | } else { | 552 | } else { |
| 569 | usage2(_("Critical time must be a positive integer"), optarg); | 553 | usage2(_("Critical time must be a positive integer"), optarg); |
| 570 | } | 554 | } |
| 571 | break; | 555 | break; |
| 572 | case 'w': /* warning time threshold */ | 556 | case 'w': /* warning time threshold */ |
| 573 | if (is_intnonneg(optarg)) { | 557 | if (is_intnonneg(optarg)) { |
| 574 | config->warning_value = atoi(optarg); | 558 | result.config.warning_value = atoi(optarg); |
| 575 | config->check_warn = true; | 559 | result.config.check_warn = true; |
| 576 | } else { | 560 | } else { |
| 577 | usage2(_("Warning time must be a positive integer"), optarg); | 561 | usage2(_("Warning time must be a positive integer"), optarg); |
| 578 | } | 562 | } |
| 579 | break; | 563 | break; |
| 580 | case 'v': /* variable */ | 564 | case 'v': /* variable */ |
| 581 | if (!strcmp(optarg, "LINE")) { | 565 | if (!strcmp(optarg, "LINE")) { |
| 582 | config->check_variable = UPS_UTILITY; | 566 | result.config.check_variable = UPS_UTILITY; |
| 583 | } else if (!strcmp(optarg, "TEMP")) { | 567 | } else if (!strcmp(optarg, "TEMP")) { |
| 584 | config->check_variable = UPS_TEMP; | 568 | result.config.check_variable = UPS_TEMP; |
| 585 | } else if (!strcmp(optarg, "BATTPCT")) { | 569 | } else if (!strcmp(optarg, "BATTPCT")) { |
| 586 | config->check_variable = UPS_BATTPCT; | 570 | result.config.check_variable = UPS_BATTPCT; |
| 587 | } else if (!strcmp(optarg, "LOADPCT")) { | 571 | } else if (!strcmp(optarg, "LOADPCT")) { |
| 588 | config->check_variable = UPS_LOADPCT; | 572 | result.config.check_variable = UPS_LOADPCT; |
| 589 | } else if (!strcmp(optarg, "REALPOWER")) { | 573 | } else if (!strcmp(optarg, "REALPOWER")) { |
| 590 | config->check_variable = UPS_REALPOWER; | 574 | result.config.check_variable = UPS_REALPOWER; |
| 591 | } else { | 575 | } else { |
| 592 | usage2(_("Unrecognized UPS variable"), optarg); | 576 | usage2(_("Unrecognized UPS variable"), optarg); |
| 593 | } | 577 | } |
| @@ -608,27 +592,27 @@ int process_arguments(int argc, char **argv, ups_config *config) { | |||
| 608 | } | 592 | } |
| 609 | } | 593 | } |
| 610 | 594 | ||
| 611 | if (config->server_address == NULL && argc > optind) { | 595 | if (result.config.server_address == NULL && argc > optind) { |
| 612 | if (is_host(argv[optind])) { | 596 | if (is_host(argv[optind])) { |
| 613 | config->server_address = argv[optind++]; | 597 | result.config.server_address = argv[optind++]; |
| 614 | } else { | 598 | } else { |
| 615 | usage2(_("Invalid hostname/address"), optarg); | 599 | usage2(_("Invalid hostname/address"), optarg); |
| 616 | } | 600 | } |
| 617 | } | 601 | } |
| 618 | 602 | ||
| 619 | if (config->server_address == NULL) { | 603 | if (result.config.server_address == NULL) { |
| 620 | config->server_address = strdup("127.0.0.1"); | 604 | result.config.server_address = strdup("127.0.0.1"); |
| 621 | } | 605 | } |
| 622 | 606 | ||
| 623 | return validate_arguments(*config); | 607 | return validate_arguments(result); |
| 624 | } | 608 | } |
| 625 | 609 | ||
| 626 | int validate_arguments(ups_config config) { | 610 | check_ups_config_wrapper validate_arguments(check_ups_config_wrapper config_wrapper) { |
| 627 | if (!config.ups_name) { | 611 | if (config_wrapper.config.ups_name) { |
| 628 | printf("%s\n", _("Error : no UPS indicated")); | 612 | printf("%s\n", _("Error : no UPS indicated")); |
| 629 | return ERROR; | 613 | config_wrapper.errorcode = ERROR; |
| 630 | } | 614 | } |
| 631 | return OK; | 615 | return config_wrapper; |
| 632 | } | 616 | } |
| 633 | 617 | ||
| 634 | void print_help(void) { | 618 | void print_help(void) { |
| @@ -660,7 +644,8 @@ void print_help(void) { | |||
| 660 | printf(" %s\n", "-T, --temperature"); | 644 | printf(" %s\n", "-T, --temperature"); |
| 661 | printf(" %s\n", _("Output of temperatures in Celsius")); | 645 | printf(" %s\n", _("Output of temperatures in Celsius")); |
| 662 | printf(" %s\n", "-v, --variable=STRING"); | 646 | printf(" %s\n", "-v, --variable=STRING"); |
| 663 | printf(" %s %s\n", _("Valid values for STRING are"), "LINE, TEMP, BATTPCT, LOADPCT or REALPOWER"); | 647 | printf(" %s %s\n", _("Valid values for STRING are"), |
| 648 | "LINE, TEMP, BATTPCT, LOADPCT or REALPOWER"); | ||
| 664 | 649 | ||
| 665 | printf(UT_WARN_CRIT); | 650 | printf(UT_WARN_CRIT); |
| 666 | 651 | ||
diff --git a/plugins/check_ups.d/config.h b/plugins/check_ups.d/config.h new file mode 100644 index 00000000..e05edceb --- /dev/null +++ b/plugins/check_ups.d/config.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "../../config.h" | ||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | #define UPS_NONE 0 /* no supported options */ | ||
| 7 | #define UPS_UTILITY 1 /* supports utility line */ | ||
| 8 | #define UPS_BATTPCT 2 /* supports percent battery remaining */ | ||
| 9 | #define UPS_STATUS 4 /* supports UPS status */ | ||
| 10 | #define UPS_TEMP 8 /* supports UPS temperature */ | ||
| 11 | #define UPS_LOADPCT 16 /* supports load percent */ | ||
| 12 | #define UPS_REALPOWER 32 /* supports real power */ | ||
| 13 | |||
| 14 | #define UPSSTATUS_NONE 0 | ||
| 15 | #define UPSSTATUS_OFF 1 | ||
| 16 | #define UPSSTATUS_OL 2 | ||
| 17 | #define UPSSTATUS_OB 4 | ||
| 18 | #define UPSSTATUS_LB 8 | ||
| 19 | #define UPSSTATUS_CAL 16 | ||
| 20 | #define UPSSTATUS_RB 32 /*Replace Battery */ | ||
| 21 | #define UPSSTATUS_BYPASS 64 | ||
| 22 | #define UPSSTATUS_OVER 128 | ||
| 23 | #define UPSSTATUS_TRIM 256 | ||
| 24 | #define UPSSTATUS_BOOST 512 | ||
| 25 | #define UPSSTATUS_CHRG 1024 | ||
| 26 | #define UPSSTATUS_DISCHRG 2048 | ||
| 27 | #define UPSSTATUS_UNKNOWN 4096 | ||
| 28 | #define UPSSTATUS_ALARM 8192 | ||
| 29 | |||
| 30 | enum { | ||
| 31 | PORT = 3493 | ||
| 32 | }; | ||
| 33 | |||
| 34 | typedef struct ups_config { | ||
| 35 | unsigned int server_port; | ||
| 36 | char *server_address; | ||
| 37 | char *ups_name; | ||
| 38 | double warning_value; | ||
| 39 | double critical_value; | ||
| 40 | bool check_warn; | ||
| 41 | bool check_crit; | ||
| 42 | int check_variable; | ||
| 43 | bool temp_output_c; | ||
| 44 | } check_ups_config; | ||
| 45 | |||
| 46 | check_ups_config check_ups_config_init(void) { | ||
| 47 | check_ups_config tmp = {0}; | ||
| 48 | tmp.server_port = PORT; | ||
| 49 | tmp.server_address = NULL; | ||
| 50 | tmp.ups_name = NULL; | ||
| 51 | tmp.check_variable = UPS_NONE; | ||
| 52 | |||
| 53 | return tmp; | ||
| 54 | } | ||
diff --git a/plugins/check_users.c b/plugins/check_users.c index f1e1c39d..3b2e265e 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c | |||
| @@ -34,8 +34,15 @@ const char *progname = "check_users"; | |||
| 34 | const char *copyright = "2000-2024"; | 34 | const char *copyright = "2000-2024"; |
| 35 | const char *email = "devel@monitoring-plugins.org"; | 35 | const 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> |
| @@ -44,8 +51,6 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 44 | # define ERROR -1 | 51 | # define ERROR -1 |
| 45 | #elif HAVE_UTMPX_H | 52 | #elif HAVE_UTMPX_H |
| 46 | # include <utmpx.h> | 53 | # include <utmpx.h> |
| 47 | #else | ||
| 48 | # include "popen.h" | ||
| 49 | #endif | 54 | #endif |
| 50 | 55 | ||
| 51 | #ifdef HAVE_LIBSYSTEMD | 56 | #ifdef HAVE_LIBSYSTEMD |
| @@ -53,29 +58,16 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 53 | # include <systemd/sd-login.h> | 58 | # include <systemd/sd-login.h> |
| 54 | #endif | 59 | #endif |
| 55 | 60 | ||
| 56 | #define possibly_set(a, b) ((a) == 0 ? (b) : 0) | 61 | typedef struct process_argument_wrapper { |
| 62 | int errorcode; | ||
| 63 | check_users_config config; | ||
| 64 | } check_users_config_wrapper; | ||
| 65 | check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 57 | 66 | ||
| 58 | static int process_arguments(int, char **); | 67 | void print_help(void); |
| 59 | static void print_help(void); | ||
| 60 | void print_usage(void); | 68 | void print_usage(void); |
| 61 | 69 | ||
| 62 | static char *warning_range = NULL; | ||
| 63 | static char *critical_range = NULL; | ||
| 64 | static thresholds *thlds = NULL; | ||
| 65 | |||
| 66 | int main(int argc, char **argv) { | 70 | int 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, ""); | 71 | setlocale(LC_ALL, ""); |
| 80 | bindtextdomain(PACKAGE, LOCALEDIR); | 72 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 81 | textdomain(PACKAGE); | 73 | textdomain(PACKAGE); |
| @@ -83,121 +75,104 @@ int main(int argc, char **argv) { | |||
| 83 | /* Parse extra opts if any */ | 75 | /* Parse extra opts if any */ |
| 84 | argv = np_extra_opts(&argc, argv, progname); | 76 | argv = np_extra_opts(&argc, argv, progname); |
| 85 | 77 | ||
| 86 | if (process_arguments(argc, argv) == ERROR) | 78 | 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 | |||
| 117 | if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) | ||
| 118 | users++; | ||
| 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 | 79 | ||
| 130 | endutxent(); | 80 | if (tmp_config.errorcode == ERROR) { |
| 131 | #else | 81 | usage4(_("Could not parse arguments")); |
| 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 | } | 82 | } |
| 138 | 83 | ||
| 139 | child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); | 84 | 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 | 85 | ||
| 150 | /* get total logged in users */ | 86 | #ifdef _WIN32 |
| 151 | if (sscanf(input_buffer, _("# users=%d"), &users) == 1) | 87 | # if HAVE_WTSAPI32_H |
| 152 | break; | 88 | get_num_of_users_wrapper user_wrapper = get_num_of_users_windows(); |
| 89 | # else | ||
| 90 | # error Did not find WTSAPI32 | ||
| 91 | # endif // HAVE_WTSAPI32_H | ||
| 92 | #else | ||
| 93 | # ifdef HAVE_LIBSYSTEMD | ||
| 94 | get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd(); | ||
| 95 | # elif HAVE_UTMPX_H | ||
| 96 | get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp(); | ||
| 97 | # else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H | ||
| 98 | get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command(); | ||
| 99 | # endif // HAVE_LIBSYSTEMD | ||
| 100 | #endif // _WIN32 | ||
| 101 | |||
| 102 | mp_check overall = mp_check_init(); | ||
| 103 | if (config.output_format_is_set) { | ||
| 104 | mp_set_format(config.output_format); | ||
| 153 | } | 105 | } |
| 106 | mp_subcheck sc_users = mp_subcheck_init(); | ||
| 154 | 107 | ||
| 155 | /* check STDERR */ | 108 | if (user_wrapper.errorcode != 0) { |
| 156 | if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) | 109 | sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN); |
| 157 | result = possibly_set(result, STATE_UNKNOWN); | 110 | sc_users.output = "Failed to retrieve number of users"; |
| 158 | (void)fclose(child_stderr); | 111 | mp_add_subcheck_to_check(&overall, sc_users); |
| 159 | 112 | 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 | } | 113 | } |
| 166 | #endif | ||
| 167 | |||
| 168 | /* check the user count against warning and critical thresholds */ | 114 | /* check the user count against warning and critical thresholds */ |
| 169 | result = get_status((double)users, thlds); | ||
| 170 | 115 | ||
| 171 | if (result == STATE_UNKNOWN) | 116 | mp_perfdata users_pd = { |
| 172 | printf("%s\n", _("Unable to read output")); | 117 | .label = "users", |
| 173 | else { | 118 | .value = mp_create_pd_value(user_wrapper.users), |
| 174 | printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, | 119 | }; |
| 175 | sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); | 120 | |
| 121 | users_pd = mp_pd_set_thresholds(users_pd, config.thresholds); | ||
| 122 | mp_add_perfdata_to_subcheck(&sc_users, users_pd); | ||
| 123 | |||
| 124 | int tmp_status = mp_get_pd_status(users_pd); | ||
| 125 | sc_users = mp_set_subcheck_state(sc_users, tmp_status); | ||
| 126 | |||
| 127 | switch (tmp_status) { | ||
| 128 | case STATE_WARNING: | ||
| 129 | xasprintf(&sc_users.output, | ||
| 130 | "%d users currently logged in. This violates the warning threshold", | ||
| 131 | user_wrapper.users); | ||
| 132 | break; | ||
| 133 | case STATE_CRITICAL: | ||
| 134 | xasprintf(&sc_users.output, | ||
| 135 | "%d users currently logged in. This violates the critical threshold", | ||
| 136 | user_wrapper.users); | ||
| 137 | break; | ||
| 138 | default: | ||
| 139 | xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users); | ||
| 176 | } | 140 | } |
| 177 | 141 | ||
| 178 | return result; | 142 | mp_add_subcheck_to_check(&overall, sc_users); |
| 143 | mp_exit(overall); | ||
| 179 | } | 144 | } |
| 180 | 145 | ||
| 146 | #define output_format_index CHAR_MAX + 1 | ||
| 147 | |||
| 181 | /* process command-line arguments */ | 148 | /* process command-line arguments */ |
| 182 | int process_arguments(int argc, char **argv) { | 149 | check_users_config_wrapper process_arguments(int argc, char **argv) { |
| 183 | static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, | 150 | static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, |
| 184 | {"warning", required_argument, 0, 'w'}, | 151 | {"warning", required_argument, 0, 'w'}, |
| 185 | {"version", no_argument, 0, 'V'}, | 152 | {"version", no_argument, 0, 'V'}, |
| 186 | {"help", no_argument, 0, 'h'}, | 153 | {"help", no_argument, 0, 'h'}, |
| 154 | {"output-format", required_argument, 0, output_format_index}, | ||
| 187 | {0, 0, 0, 0}}; | 155 | {0, 0, 0, 0}}; |
| 188 | 156 | ||
| 189 | if (argc < 2) | 157 | if (argc < 2) { |
| 190 | usage("\n"); | 158 | usage(progname); |
| 159 | } | ||
| 160 | |||
| 161 | char *warning_range = NULL; | ||
| 162 | char *critical_range = NULL; | ||
| 163 | check_users_config_wrapper result = { | ||
| 164 | .config = check_users_config_init(), | ||
| 165 | .errorcode = OK, | ||
| 166 | }; | ||
| 191 | 167 | ||
| 192 | int option_char; | ||
| 193 | while (true) { | 168 | while (true) { |
| 194 | int option = 0; | 169 | int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL); |
| 195 | option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option); | ||
| 196 | 170 | ||
| 197 | if (option_char == -1 || option_char == EOF || option_char == 1) | 171 | if (counter == -1 || counter == EOF || counter == 1) { |
| 198 | break; | 172 | break; |
| 173 | } | ||
| 199 | 174 | ||
| 200 | switch (option_char) { | 175 | switch (counter) { |
| 201 | case '?': /* print short usage statement if args not parsable */ | 176 | case '?': /* print short usage statement if args not parsable */ |
| 202 | usage5(); | 177 | usage5(); |
| 203 | case 'h': /* help */ | 178 | case 'h': /* help */ |
| @@ -212,29 +187,66 @@ int process_arguments(int argc, char **argv) { | |||
| 212 | case 'w': /* warning */ | 187 | case 'w': /* warning */ |
| 213 | warning_range = optarg; | 188 | warning_range = optarg; |
| 214 | break; | 189 | break; |
| 190 | case output_format_index: { | ||
| 191 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 192 | if (!parser.parsing_success) { | ||
| 193 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 194 | printf("Invalid output format: %s\n", optarg); | ||
| 195 | exit(STATE_UNKNOWN); | ||
| 196 | } | ||
| 197 | |||
| 198 | result.config.output_format_is_set = true; | ||
| 199 | result.config.output_format = parser.output_format; | ||
| 200 | break; | ||
| 201 | } | ||
| 215 | } | 202 | } |
| 216 | } | 203 | } |
| 217 | 204 | ||
| 218 | option_char = optind; | 205 | int option_char = optind; |
| 219 | 206 | ||
| 220 | if (warning_range == NULL && argc > option_char) | 207 | if (warning_range == NULL && argc > option_char) { |
| 221 | warning_range = argv[option_char++]; | 208 | warning_range = argv[option_char++]; |
| 209 | } | ||
| 222 | 210 | ||
| 223 | if (critical_range == NULL && argc > option_char) | 211 | if (critical_range == NULL && argc > option_char) { |
| 224 | critical_range = argv[option_char++]; | 212 | critical_range = argv[option_char++]; |
| 213 | } | ||
| 214 | |||
| 215 | // TODO add proper verification for ranges here! | ||
| 216 | mp_range_parsed tmp; | ||
| 217 | if (warning_range) { | ||
| 218 | tmp = mp_parse_range_string(warning_range); | ||
| 219 | } else { | ||
| 220 | printf("Warning threshold missing\n"); | ||
| 221 | print_usage(); | ||
| 222 | exit(STATE_UNKNOWN); | ||
| 223 | } | ||
| 225 | 224 | ||
| 226 | /* this will abort in case of invalid ranges */ | 225 | if (tmp.error == MP_PARSING_SUCCES) { |
| 227 | set_thresholds(&thlds, warning_range, critical_range); | 226 | result.config.thresholds.warning = tmp.range; |
| 227 | result.config.thresholds.warning_is_set = true; | ||
| 228 | } else { | ||
| 229 | printf("Failed to parse warning range: %s", warning_range); | ||
| 230 | exit(STATE_UNKNOWN); | ||
| 231 | } | ||
| 228 | 232 | ||
| 229 | if (!thlds->warning) { | 233 | if (critical_range) { |
| 230 | usage4(_("Warning threshold must be a valid range expression")); | 234 | tmp = mp_parse_range_string(critical_range); |
| 235 | } else { | ||
| 236 | printf("Critical threshold missing\n"); | ||
| 237 | print_usage(); | ||
| 238 | exit(STATE_UNKNOWN); | ||
| 231 | } | 239 | } |
| 232 | 240 | ||
| 233 | if (!thlds->critical) { | 241 | if (tmp.error == MP_PARSING_SUCCES) { |
| 234 | usage4(_("Critical threshold must be a valid range expression")); | 242 | result.config.thresholds.critical = tmp.range; |
| 243 | result.config.thresholds.critical_is_set = true; | ||
| 244 | } else { | ||
| 245 | printf("Failed to parse critical range: %s", critical_range); | ||
| 246 | exit(STATE_UNKNOWN); | ||
| 235 | } | 247 | } |
| 236 | 248 | ||
| 237 | return OK; | 249 | return result; |
| 238 | } | 250 | } |
| 239 | 251 | ||
| 240 | void print_help(void) { | 252 | void print_help(void) { |
| @@ -244,7 +256,8 @@ void print_help(void) { | |||
| 244 | printf(COPYRIGHT, copyright, email); | 256 | printf(COPYRIGHT, copyright, email); |
| 245 | 257 | ||
| 246 | printf("%s\n", _("This plugin checks the number of users currently logged in on the local")); | 258 | printf("%s\n", _("This plugin checks the number of users currently logged in on the local")); |
| 247 | printf("%s\n", _("system and generates an error if the number exceeds the thresholds specified.")); | 259 | printf("%s\n", |
| 260 | _("system and generates an error if the number exceeds the thresholds specified.")); | ||
| 248 | 261 | ||
| 249 | printf("\n\n"); | 262 | printf("\n\n"); |
| 250 | 263 | ||
| @@ -254,9 +267,12 @@ void print_help(void) { | |||
| 254 | printf(UT_EXTRA_OPTS); | 267 | printf(UT_EXTRA_OPTS); |
| 255 | 268 | ||
| 256 | printf(" %s\n", "-w, --warning=RANGE_EXPRESSION"); | 269 | printf(" %s\n", "-w, --warning=RANGE_EXPRESSION"); |
| 257 | printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); | 270 | printf(" %s\n", |
| 271 | _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); | ||
| 258 | printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); | 272 | printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); |
| 259 | printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); | 273 | printf(" %s\n", |
| 274 | _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); | ||
| 275 | printf(UT_OUTPUT_FORMAT); | ||
| 260 | 276 | ||
| 261 | printf(UT_SUPPORT); | 277 | printf(UT_SUPPORT); |
| 262 | } | 278 | } |
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 | |||
| 6 | typedef 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 | |||
| 13 | check_users_config check_users_config_init() { | ||
| 14 | check_users_config tmp = { | ||
| 15 | .thresholds = mp_thresholds_init(), | ||
| 16 | |||
| 17 | .output_format_is_set = false, | ||
| 18 | }; | ||
| 19 | return tmp; | ||
| 20 | } | ||
diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c new file mode 100644 index 00000000..a08f79c5 --- /dev/null +++ b/plugins/check_users.d/users.c | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | #include "./users.h" | ||
| 2 | |||
| 3 | #ifdef _WIN32 | ||
| 4 | # ifdef HAVE_WTSAPI32_H | ||
| 5 | # include <windows.h> | ||
| 6 | # include <wtsapi32.h> | ||
| 7 | # undef ERROR | ||
| 8 | # define ERROR -1 | ||
| 9 | |||
| 10 | get_num_of_users_wrapper get_num_of_users_windows() { | ||
| 11 | WTS_SESSION_INFO *wtsinfo; | ||
| 12 | DWORD wtscount; | ||
| 13 | |||
| 14 | get_num_of_users_wrapper result = {}; | ||
| 15 | |||
| 16 | if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { | ||
| 17 | // printf(_("Could not enumerate RD sessions: %d\n"), GetLastError()); | ||
| 18 | result.error = WINDOWS_COULD_NOT_ENUMERATE_SESSIONS; | ||
| 19 | return result; | ||
| 20 | } | ||
| 21 | |||
| 22 | for (DWORD index = 0; index < wtscount; index++) { | ||
| 23 | LPTSTR username; | ||
| 24 | DWORD size; | ||
| 25 | |||
| 26 | if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, | ||
| 27 | WTSUserName, &username, &size)) { | ||
| 28 | continue; | ||
| 29 | } | ||
| 30 | |||
| 31 | int len = lstrlen(username); | ||
| 32 | |||
| 33 | WTSFreeMemory(username); | ||
| 34 | |||
| 35 | if (len == 0) { | ||
| 36 | continue; | ||
| 37 | } | ||
| 38 | |||
| 39 | if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { | ||
| 40 | result.users++; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | WTSFreeMemory(wtsinfo); | ||
| 45 | return result; | ||
| 46 | } | ||
| 47 | # else // HAVE_WTSAPI32_H | ||
| 48 | # error On windows but without the WTSAPI32 lib | ||
| 49 | # endif // HAVE_WTSAPI32_H | ||
| 50 | |||
| 51 | #else // _WIN32 | ||
| 52 | |||
| 53 | # include "../../config.h" | ||
| 54 | # include <stddef.h> | ||
| 55 | |||
| 56 | # ifdef HAVE_LIBSYSTEMD | ||
| 57 | # include <systemd/sd-daemon.h> | ||
| 58 | # include <systemd/sd-login.h> | ||
| 59 | |||
| 60 | get_num_of_users_wrapper get_num_of_users_systemd() { | ||
| 61 | get_num_of_users_wrapper result = {}; | ||
| 62 | |||
| 63 | // Test whether we booted with systemd | ||
| 64 | if (sd_booted() > 0) { | ||
| 65 | int users = sd_get_uids(NULL); | ||
| 66 | if (users >= 0) { | ||
| 67 | // Success | ||
| 68 | result.users = users; | ||
| 69 | return result; | ||
| 70 | } | ||
| 71 | |||
| 72 | // Failure! return the error code | ||
| 73 | result.errorcode = users; | ||
| 74 | return result; | ||
| 75 | } | ||
| 76 | |||
| 77 | // Looks like we are not running systemd, | ||
| 78 | // return with error here | ||
| 79 | result.errorcode = NO_SYSTEMD_ERROR; | ||
| 80 | return result; | ||
| 81 | } | ||
| 82 | # endif | ||
| 83 | |||
| 84 | # ifdef HAVE_UTMPX_H | ||
| 85 | # include <utmpx.h> | ||
| 86 | |||
| 87 | get_num_of_users_wrapper get_num_of_users_utmp() { | ||
| 88 | int users = 0; | ||
| 89 | |||
| 90 | /* get currently logged users from utmpx */ | ||
| 91 | setutxent(); | ||
| 92 | |||
| 93 | struct utmpx *putmpx; | ||
| 94 | while ((putmpx = getutxent()) != NULL) { | ||
| 95 | if (putmpx->ut_type == USER_PROCESS) { | ||
| 96 | users++; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | endutxent(); | ||
| 101 | |||
| 102 | get_num_of_users_wrapper result = { | ||
| 103 | .errorcode = 0, | ||
| 104 | .users = users, | ||
| 105 | }; | ||
| 106 | |||
| 107 | return result; | ||
| 108 | } | ||
| 109 | # endif | ||
| 110 | |||
| 111 | # ifndef HAVE_WTSAPI32_H | ||
| 112 | # ifndef HAVE_LIBSYSTEMD | ||
| 113 | # ifndef HAVE_UTMPX_H | ||
| 114 | // Fall back option here for the others (probably still not on windows) | ||
| 115 | |||
| 116 | # include "../common.h" | ||
| 117 | # include "../popen.h" | ||
| 118 | # include "../utils.h" | ||
| 119 | |||
| 120 | get_num_of_users_wrapper get_num_of_users_who_command() { | ||
| 121 | /* run the command */ | ||
| 122 | child_process = spopen(WHO_COMMAND); | ||
| 123 | if (child_process == NULL) { | ||
| 124 | // printf(_("Could not open pipe: %s\n"), WHO_COMMAND); | ||
| 125 | get_num_of_users_wrapper result = { | ||
| 126 | .errorcode = COULD_NOT_OPEN_PIPE, | ||
| 127 | }; | ||
| 128 | return result; | ||
| 129 | } | ||
| 130 | |||
| 131 | child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); | ||
| 132 | if (child_stderr == NULL) { | ||
| 133 | // printf(_("Could not open stderr for %s\n"), WHO_COMMAND); | ||
| 134 | // TODO this error should probably be reported | ||
| 135 | } | ||
| 136 | |||
| 137 | get_num_of_users_wrapper result = {}; | ||
| 138 | char input_buffer[MAX_INPUT_BUFFER]; | ||
| 139 | while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { | ||
| 140 | /* increment 'users' on all lines except total user count */ | ||
| 141 | if (input_buffer[0] != '#') { | ||
| 142 | result.users++; | ||
| 143 | continue; | ||
| 144 | } | ||
| 145 | |||
| 146 | /* get total logged in users */ | ||
| 147 | if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) { | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | /* check STDERR */ | ||
| 153 | if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { | ||
| 154 | // if this fails, something broke and the result can not be relied upon or so is the theorie | ||
| 155 | // here | ||
| 156 | result.errorcode = STDERR_COULD_NOT_BE_READ; | ||
| 157 | } | ||
| 158 | (void)fclose(child_stderr); | ||
| 159 | |||
| 160 | /* close the pipe */ | ||
| 161 | spclose(child_process); | ||
| 162 | |||
| 163 | return result; | ||
| 164 | } | ||
| 165 | |||
| 166 | # endif | ||
| 167 | # endif | ||
| 168 | # endif | ||
| 169 | #endif | ||
diff --git a/plugins/check_users.d/users.h b/plugins/check_users.d/users.h new file mode 100644 index 00000000..aacba775 --- /dev/null +++ b/plugins/check_users.d/users.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | typedef struct get_num_of_users_wrapper { | ||
| 4 | int errorcode; | ||
| 5 | int users; | ||
| 6 | } get_num_of_users_wrapper; | ||
| 7 | |||
| 8 | enum { | ||
| 9 | NO_SYSTEMD_ERROR = 64, | ||
| 10 | WINDOWS_COULD_NOT_ENUMERATE_SESSIONS, | ||
| 11 | COULD_NOT_OPEN_PIPE, | ||
| 12 | STDERR_COULD_NOT_BE_READ, | ||
| 13 | }; | ||
| 14 | |||
| 15 | get_num_of_users_wrapper get_num_of_users_systemd(); | ||
| 16 | get_num_of_users_wrapper get_num_of_users_utmp(); | ||
| 17 | get_num_of_users_wrapper get_num_of_users_windows(); | ||
| 18 | get_num_of_users_wrapper get_num_of_users_who_command(); | ||
diff --git a/plugins/common.h b/plugins/common.h index b7a7d59b..ef888d08 100644 --- a/plugins/common.h +++ b/plugins/common.h | |||
| @@ -1,120 +1,122 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring Plugins common include file | 3 | * Monitoring Plugins common include file |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) | 6 | * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) |
| 7 | * Copyright (c) 2003-2007 Monitoring Plugins Development Team | 7 | * Copyright (c) 2003-2007 Monitoring Plugins Development Team |
| 8 | * | 8 | * |
| 9 | * Description: | 9 | * Description: |
| 10 | * | 10 | * |
| 11 | * This file contains common include files and defines used in many of | 11 | * This file contains common include files and defines used in many of |
| 12 | * the plugins. | 12 | * the plugins. |
| 13 | * | 13 | * |
| 14 | * | 14 | * |
| 15 | * This program is free software: you can redistribute it and/or modify | 15 | * This program is free software: you can redistribute it and/or modify |
| 16 | * it under the terms of the GNU General Public License as published by | 16 | * it under the terms of the GNU General Public License as published by |
| 17 | * the Free Software Foundation, either version 3 of the License, or | 17 | * the Free Software Foundation, either version 3 of the License, or |
| 18 | * (at your option) any later version. | 18 | * (at your option) any later version. |
| 19 | * | 19 | * |
| 20 | * This program is distributed in the hope that it will be useful, | 20 | * This program is distributed in the hope that it will be useful, |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | * GNU General Public License for more details. | 23 | * GNU General Public License for more details. |
| 24 | * | 24 | * |
| 25 | * You should have received a copy of the GNU General Public License | 25 | * You should have received a copy of the GNU General Public License |
| 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | * | 27 | * |
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | #ifndef _COMMON_H_ | 31 | #ifndef _COMMON_H_ |
| 32 | #define _COMMON_H_ | 32 | #define _COMMON_H_ |
| 33 | 33 | ||
| 34 | #include "config.h" | 34 | #include "../config.h" |
| 35 | #include "../lib/monitoringplug.h" | ||
| 35 | 36 | ||
| 36 | #ifdef HAVE_FEATURES_H | 37 | #ifdef HAVE_FEATURES_H |
| 37 | #include <features.h> | 38 | # include <features.h> |
| 38 | #endif | 39 | #endif |
| 39 | 40 | ||
| 40 | #include <stdio.h> /* obligatory includes */ | 41 | #include <stdio.h> /* obligatory includes */ |
| 41 | #include <stdlib.h> | 42 | #include <stdlib.h> |
| 42 | #include <errno.h> | 43 | #include <errno.h> |
| 43 | 44 | ||
| 44 | /* This block provides uintmax_t - should be reported to coreutils that this should be added to fsuage.h */ | 45 | /* This block provides uintmax_t - should be reported to coreutils that this should be added to |
| 46 | * fsuage.h */ | ||
| 45 | #if HAVE_INTTYPES_H | 47 | #if HAVE_INTTYPES_H |
| 46 | # include <inttypes.h> | 48 | # include <inttypes.h> |
| 47 | #endif | 49 | #endif |
| 48 | #if HAVE_STDINT_H | 50 | #if HAVE_STDINT_H |
| 49 | # include <stdint.h> | 51 | # include <stdint.h> |
| 50 | #endif | 52 | #endif |
| 51 | #include <unistd.h> | 53 | #include <unistd.h> |
| 52 | #ifndef UINTMAX_MAX | 54 | #ifndef UINTMAX_MAX |
| 53 | # define UINTMAX_MAX ((uintmax_t) -1) | 55 | # define UINTMAX_MAX ((uintmax_t) - 1) |
| 54 | #endif | 56 | #endif |
| 55 | 57 | ||
| 56 | #include <limits.h> /* This is assumed true, because coreutils assume it too */ | 58 | #include <limits.h> /* This is assumed true, because coreutils assume it too */ |
| 57 | 59 | ||
| 58 | #ifdef HAVE_MATH_H | 60 | #ifdef HAVE_MATH_H |
| 59 | #include <math.h> | 61 | # include <math.h> |
| 60 | #endif | 62 | #endif |
| 61 | 63 | ||
| 62 | #ifdef _AIX | 64 | #ifdef _AIX |
| 63 | #ifdef HAVE_MP_H | 65 | # ifdef HAVE_MP_H |
| 64 | #include <mp.h> | 66 | # include <mp.h> |
| 65 | #endif | 67 | # endif |
| 66 | #endif | 68 | #endif |
| 67 | 69 | ||
| 68 | #ifdef HAVE_STRINGS_H | 70 | #ifdef HAVE_STRINGS_H |
| 69 | #include <strings.h> | 71 | # include <strings.h> |
| 70 | #endif | 72 | #endif |
| 71 | #ifdef HAVE_STRING_H | 73 | #ifdef HAVE_STRING_H |
| 72 | #include <string.h> | 74 | # include <string.h> |
| 73 | #endif | 75 | #endif |
| 74 | 76 | ||
| 75 | #ifdef HAVE_UNISTD_H | 77 | #ifdef HAVE_UNISTD_H |
| 76 | #include <unistd.h> | 78 | # include <unistd.h> |
| 77 | #endif | 79 | #endif |
| 78 | 80 | ||
| 79 | /* GET_NUMBER_OF_CPUS is a macro to return | 81 | /* GET_NUMBER_OF_CPUS is a macro to return |
| 80 | number of CPUs, if we can get that data. | 82 | number of CPUs, if we can get that data. |
| 81 | Use configure.in to test for various OS ways of | 83 | Use configure.in to test for various OS ways of |
| 82 | getting that data | 84 | getting that data |
| 83 | Will return -1 if cannot get data | 85 | Will return -1 if cannot get data |
| 84 | */ | 86 | */ |
| 85 | #if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN) | 87 | #if defined(HAVE_SYSCONF__SC_NPROCESSORS_ONLN) |
| 86 | # define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN) | 88 | # define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_ONLN) |
| 87 | #elif defined (HAVE_SYSCONF__SC_NPROCESSORS_CONF) | 89 | #elif defined(HAVE_SYSCONF__SC_NPROCESSORS_CONF) |
| 88 | # define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF) | 90 | # define GET_NUMBER_OF_CPUS() sysconf(_SC_NPROCESSORS_CONF) |
| 89 | #else | 91 | #else |
| 90 | # define GET_NUMBER_OF_CPUS() -1 | 92 | # define GET_NUMBER_OF_CPUS() -1 |
| 91 | #endif | 93 | #endif |
| 92 | 94 | ||
| 93 | #ifdef HAVE_SYS_TIME_H | 95 | #ifdef HAVE_SYS_TIME_H |
| 94 | # include <sys/time.h> | 96 | # include <sys/time.h> |
| 95 | #endif | 97 | #endif |
| 96 | #include <time.h> | 98 | #include <time.h> |
| 97 | 99 | ||
| 98 | #ifdef HAVE_SYS_TYPES_H | 100 | #ifdef HAVE_SYS_TYPES_H |
| 99 | #include <sys/types.h> | 101 | # include <sys/types.h> |
| 100 | #endif | 102 | #endif |
| 101 | 103 | ||
| 102 | #ifdef HAVE_SYS_SOCKET_H | 104 | #ifdef HAVE_SYS_SOCKET_H |
| 103 | #include <sys/socket.h> | 105 | # include <sys/socket.h> |
| 104 | #endif | 106 | #endif |
| 105 | 107 | ||
| 106 | #ifdef HAVE_SIGNAL_H | 108 | #ifdef HAVE_SIGNAL_H |
| 107 | #include <signal.h> | 109 | # include <signal.h> |
| 108 | #endif | 110 | #endif |
| 109 | 111 | ||
| 110 | /* GNU Libraries */ | 112 | /* GNU Libraries */ |
| 111 | #include <getopt.h> | 113 | #include <getopt.h> |
| 112 | #include "dirname.h" | 114 | #include "../gl/dirname.h" |
| 113 | 115 | ||
| 114 | #include <locale.h> | 116 | #include <locale.h> |
| 115 | 117 | ||
| 116 | #ifdef HAVE_SYS_POLL_H | 118 | #ifdef HAVE_SYS_POLL_H |
| 117 | # include "sys/poll.h" | 119 | # include "sys/poll.h" |
| 118 | #endif | 120 | #endif |
| 119 | 121 | ||
| 120 | /* | 122 | /* |
| @@ -124,42 +126,42 @@ | |||
| 124 | */ | 126 | */ |
| 125 | 127 | ||
| 126 | #ifndef HAVE_STRTOL | 128 | #ifndef HAVE_STRTOL |
| 127 | # define strtol(a,b,c) atol((a)) | 129 | # define strtol(a, b, c) atol((a)) |
| 128 | #endif | 130 | #endif |
| 129 | 131 | ||
| 130 | #ifndef HAVE_STRTOUL | 132 | #ifndef HAVE_STRTOUL |
| 131 | # define strtoul(a,b,c) (unsigned long)atol((a)) | 133 | # define strtoul(a, b, c) (unsigned long)atol((a)) |
| 132 | #endif | 134 | #endif |
| 133 | 135 | ||
| 134 | /* SSL implementations */ | 136 | /* SSL implementations */ |
| 135 | #ifdef HAVE_GNUTLS_OPENSSL_H | 137 | #ifdef HAVE_GNUTLS_OPENSSL_H |
| 136 | # include <gnutls/openssl.h> | 138 | # include <gnutls/openssl.h> |
| 137 | #else | 139 | #else |
| 138 | # define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */ | 140 | # define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */ |
| 139 | # ifdef HAVE_SSL_H | 141 | # ifdef HAVE_SSL_H |
| 140 | # include <rsa.h> | 142 | # include <rsa.h> |
| 141 | # include <crypto.h> | 143 | # include <crypto.h> |
| 142 | # include <x509.h> | 144 | # include <x509.h> |
| 143 | # include <pem.h> | 145 | # include <pem.h> |
| 144 | # include <ssl.h> | 146 | # include <ssl.h> |
| 145 | # include <err.h> | 147 | # include <err.h> |
| 146 | # else | 148 | # else |
| 147 | # ifdef HAVE_OPENSSL_SSL_H | 149 | # ifdef HAVE_OPENSSL_SSL_H |
| 148 | # include <openssl/rsa.h> | 150 | # include <openssl/rsa.h> |
| 149 | # include <openssl/crypto.h> | 151 | # include <openssl/crypto.h> |
| 150 | # include <openssl/x509.h> | 152 | # include <openssl/x509.h> |
| 151 | # include <openssl/pem.h> | 153 | # include <openssl/pem.h> |
| 152 | # include <openssl/ssl.h> | 154 | # include <openssl/ssl.h> |
| 153 | # include <openssl/err.h> | 155 | # include <openssl/err.h> |
| 154 | # endif | 156 | # endif |
| 155 | # endif | 157 | # endif |
| 156 | #endif | 158 | #endif |
| 157 | 159 | ||
| 158 | /* openssl 1.1 does not set OPENSSL_NO_SSL2 by default but ships without ssl2 */ | 160 | /* openssl 1.1 does not set OPENSSL_NO_SSL2 by default but ships without ssl2 */ |
| 159 | #ifdef OPENSSL_VERSION_NUMBER | 161 | #ifdef OPENSSL_VERSION_NUMBER |
| 160 | # if OPENSSL_VERSION_NUMBER >= 0x10100000 | 162 | # if OPENSSL_VERSION_NUMBER >= 0x10100000 |
| 161 | # define OPENSSL_NO_SSL2 | 163 | # define OPENSSL_NO_SSL2 |
| 162 | # endif | 164 | # endif |
| 163 | #endif | 165 | #endif |
| 164 | 166 | ||
| 165 | /* | 167 | /* |
| @@ -170,7 +172,7 @@ | |||
| 170 | 172 | ||
| 171 | /* MariaDB 10.2 client does not set MYSQL_PORT */ | 173 | /* MariaDB 10.2 client does not set MYSQL_PORT */ |
| 172 | #ifndef MYSQL_PORT | 174 | #ifndef MYSQL_PORT |
| 173 | # define MYSQL_PORT 3306 | 175 | # define MYSQL_PORT 3306 |
| 174 | #endif | 176 | #endif |
| 175 | 177 | ||
| 176 | enum { | 178 | enum { |
| @@ -179,17 +181,9 @@ enum { | |||
| 179 | }; | 181 | }; |
| 180 | 182 | ||
| 181 | enum { | 183 | enum { |
| 182 | STATE_OK, | 184 | DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */ |
| 183 | STATE_WARNING, | 185 | MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */ |
| 184 | STATE_CRITICAL, | 186 | MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */ |
| 185 | STATE_UNKNOWN, | ||
| 186 | STATE_DEPENDENT | ||
| 187 | }; | ||
| 188 | |||
| 189 | enum { | ||
| 190 | DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */ | ||
| 191 | MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */ | ||
| 192 | MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */ | ||
| 193 | }; | 187 | }; |
| 194 | 188 | ||
| 195 | /* | 189 | /* |
| @@ -197,18 +191,18 @@ enum { | |||
| 197 | * Internationalization | 191 | * Internationalization |
| 198 | * | 192 | * |
| 199 | */ | 193 | */ |
| 200 | #include "gettext.h" | 194 | #include "../gl/gettext.h" |
| 201 | #define _(String) gettext (String) | 195 | #define _(String) gettext(String) |
| 202 | #if ! ENABLE_NLS | 196 | #if !ENABLE_NLS |
| 203 | # undef textdomain | 197 | # undef textdomain |
| 204 | # define textdomain(Domainname) /* empty */ | 198 | # define textdomain(Domainname) /* empty */ |
| 205 | # undef bindtextdomain | 199 | # undef bindtextdomain |
| 206 | # define bindtextdomain(Domainname, Dirname) /* empty */ | 200 | # define bindtextdomain(Domainname, Dirname) /* empty */ |
| 207 | #endif | 201 | #endif |
| 208 | 202 | ||
| 209 | /* For non-GNU compilers to ignore __attribute__ */ | 203 | /* For non-GNU compilers to ignore __attribute__ */ |
| 210 | #ifndef __GNUC__ | 204 | #ifndef __GNUC__ |
| 211 | # define __attribute__(x) /* do nothing */ | 205 | # define __attribute__(x) /* do nothing */ |
| 212 | #endif | 206 | #endif |
| 213 | 207 | ||
| 214 | #endif /* _COMMON_H_ */ | 208 | #endif /* _COMMON_H_ */ |
diff --git a/plugins/negate.c b/plugins/negate.c index 750c0bfb..a42a6c59 100644 --- a/plugins/negate.c +++ b/plugins/negate.c | |||
| @@ -38,21 +38,18 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 38 | #include "common.h" | 38 | #include "common.h" |
| 39 | #include "utils.h" | 39 | #include "utils.h" |
| 40 | #include "utils_cmd.h" | 40 | #include "utils_cmd.h" |
| 41 | #include "negate.d/config.h" | ||
| 42 | #include "../lib/states.h" | ||
| 41 | 43 | ||
| 42 | #include <ctype.h> | 44 | typedef struct { |
| 45 | int errorcode; | ||
| 46 | negate_config config; | ||
| 47 | } negate_config_wrapper; | ||
| 48 | static negate_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 49 | static negate_config_wrapper validate_arguments(negate_config_wrapper /*config_wrapper*/); | ||
| 43 | 50 | ||
| 44 | static const char **process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 45 | static void validate_arguments(char ** /*command_line*/); | ||
| 46 | static void print_help(void); | 51 | static void print_help(void); |
| 47 | void print_usage(void); | 52 | void print_usage(void); |
| 48 | static bool subst_text = false; | ||
| 49 | |||
| 50 | static int state[4] = { | ||
| 51 | STATE_OK, | ||
| 52 | STATE_WARNING, | ||
| 53 | STATE_CRITICAL, | ||
| 54 | STATE_UNKNOWN, | ||
| 55 | }; | ||
| 56 | 53 | ||
| 57 | int main(int argc, char **argv) { | 54 | int main(int argc, char **argv) { |
| 58 | setlocale(LC_ALL, ""); | 55 | setlocale(LC_ALL, ""); |
| @@ -61,15 +58,24 @@ int main(int argc, char **argv) { | |||
| 61 | 58 | ||
| 62 | timeout_interval = DEFAULT_TIMEOUT; | 59 | timeout_interval = DEFAULT_TIMEOUT; |
| 63 | 60 | ||
| 64 | char **command_line = (char **)process_arguments(argc, argv); | 61 | negate_config_wrapper tmp_config = process_arguments(argc, argv); |
| 62 | |||
| 63 | if (tmp_config.errorcode == ERROR) { | ||
| 64 | die(STATE_UNKNOWN, _("negate: Failed to parse input")); | ||
| 65 | } | ||
| 66 | |||
| 67 | negate_config config = tmp_config.config; | ||
| 68 | |||
| 69 | char **command_line = config.command_line; | ||
| 65 | 70 | ||
| 66 | /* Set signal handling and alarm */ | 71 | /* Set signal handling and alarm */ |
| 67 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) | 72 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| 68 | die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); | 73 | die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); |
| 74 | } | ||
| 69 | 75 | ||
| 70 | (void)alarm((unsigned)timeout_interval); | 76 | (void)alarm(timeout_interval); |
| 71 | 77 | ||
| 72 | int result = STATE_UNKNOWN; | 78 | mp_state_enum result = STATE_UNKNOWN; |
| 73 | output chld_out; | 79 | output chld_out; |
| 74 | output chld_err; | 80 | output chld_err; |
| 75 | 81 | ||
| @@ -86,46 +92,54 @@ int main(int argc, char **argv) { | |||
| 86 | } | 92 | } |
| 87 | 93 | ||
| 88 | /* Return UNKNOWN or worse if no output is returned */ | 94 | /* Return UNKNOWN or worse if no output is returned */ |
| 89 | if (chld_out.lines == 0) | 95 | if (chld_out.lines == 0) { |
| 90 | die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n")); | 96 | die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n")); |
| 97 | } | ||
| 91 | 98 | ||
| 92 | char *sub; | 99 | char *sub; |
| 93 | for (size_t i = 0; i < chld_out.lines; i++) { | 100 | for (size_t i = 0; i < chld_out.lines; i++) { |
| 94 | if (subst_text && result >= 0 && result <= 4 && result != state[result]) { | 101 | if (config.subst_text && result >= 0 && result <= 4 && result != config.state[result]) { |
| 95 | /* Loop over each match found */ | 102 | /* Loop over each match found */ |
| 96 | while ((sub = strstr(chld_out.line[i], state_text(result)))) { | 103 | while ((sub = strstr(chld_out.line[i], state_text(result)))) { |
| 97 | /* Terminate the first part and skip over the string we'll substitute */ | 104 | /* Terminate the first part and skip over the string we'll substitute */ |
| 98 | *sub = '\0'; | 105 | *sub = '\0'; |
| 99 | sub += strlen(state_text(result)); | 106 | sub += strlen(state_text(result)); |
| 100 | /* then put everything back together */ | 107 | /* then put everything back together */ |
| 101 | xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i], state_text(state[result]), sub); | 108 | xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i], |
| 109 | state_text(config.state[result]), sub); | ||
| 102 | } | 110 | } |
| 103 | } | 111 | } |
| 104 | printf("%s\n", chld_out.line[i]); | 112 | printf("%s\n", chld_out.line[i]); |
| 105 | } | 113 | } |
| 106 | 114 | ||
| 107 | if (result >= 0 && result <= 4) { | 115 | if (result >= 0 && result <= 4) { |
| 108 | exit(state[result]); | 116 | exit(config.state[result]); |
| 109 | } else { | 117 | } else { |
| 110 | exit(result); | 118 | exit(result); |
| 111 | } | 119 | } |
| 112 | } | 120 | } |
| 113 | 121 | ||
| 114 | /* process command-line arguments */ | 122 | /* process command-line arguments */ |
| 115 | static const char **process_arguments(int argc, char **argv) { | 123 | static negate_config_wrapper process_arguments(int argc, char **argv) { |
| 116 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, | 124 | static struct option longopts[] = { |
| 117 | {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'}, | 125 | {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, |
| 118 | {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'}, | 126 | {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'}, |
| 119 | {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'}, | 127 | {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'}, |
| 120 | {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}}; | 128 | {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'}, |
| 121 | 129 | {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}}; | |
| 130 | |||
| 131 | negate_config_wrapper result = { | ||
| 132 | .errorcode = OK, | ||
| 133 | .config = negate_config_init(), | ||
| 134 | }; | ||
| 122 | bool permute = true; | 135 | bool permute = true; |
| 123 | while (true) { | 136 | while (true) { |
| 124 | int option = 0; | 137 | int option = 0; |
| 125 | int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option); | 138 | int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option); |
| 126 | 139 | ||
| 127 | if (option_char == -1 || option_char == EOF) | 140 | if (option_char == -1 || option_char == EOF) { |
| 128 | break; | 141 | break; |
| 142 | } | ||
| 129 | 143 | ||
| 130 | switch (option_char) { | 144 | switch (option_char) { |
| 131 | case '?': /* help */ | 145 | case '?': /* help */ |
| @@ -139,58 +153,74 @@ static const char **process_arguments(int argc, char **argv) { | |||
| 139 | print_revision(progname, NP_VERSION); | 153 | print_revision(progname, NP_VERSION); |
| 140 | exit(STATE_UNKNOWN); | 154 | exit(STATE_UNKNOWN); |
| 141 | case 't': /* timeout period */ | 155 | case 't': /* timeout period */ |
| 142 | if (!is_integer(optarg)) | 156 | if (!is_integer(optarg)) { |
| 143 | usage2(_("Timeout interval must be a positive integer"), optarg); | 157 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 144 | else | 158 | } else { |
| 145 | timeout_interval = atoi(optarg); | 159 | timeout_interval = atoi(optarg); |
| 160 | } | ||
| 146 | break; | 161 | break; |
| 147 | case 'T': /* Result to return on timeouts */ | 162 | case 'T': /* Result to return on timeouts */ |
| 148 | if ((timeout_state = mp_translate_state(optarg)) == ERROR) | 163 | if ((timeout_state = mp_translate_state(optarg)) == ERROR) { |
| 149 | usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); | 164 | usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, " |
| 165 | "UNKNOWN) or integer (0-3).")); | ||
| 166 | } | ||
| 150 | break; | 167 | break; |
| 151 | case 'o': /* replacement for OK */ | 168 | case 'o': /* replacement for OK */ |
| 152 | if ((state[STATE_OK] = mp_translate_state(optarg)) == ERROR) | 169 | if ((result.config.state[STATE_OK] = mp_translate_state(optarg)) == ERROR) { |
| 153 | usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); | 170 | usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or " |
| 171 | "integer (0-3).")); | ||
| 172 | } | ||
| 154 | permute = false; | 173 | permute = false; |
| 155 | break; | 174 | break; |
| 156 | 175 | ||
| 157 | case 'w': /* replacement for WARNING */ | 176 | case 'w': /* replacement for WARNING */ |
| 158 | if ((state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) | 177 | if ((result.config.state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) { |
| 159 | usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); | 178 | usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or " |
| 179 | "integer (0-3).")); | ||
| 180 | } | ||
| 160 | permute = false; | 181 | permute = false; |
| 161 | break; | 182 | break; |
| 162 | case 'c': /* replacement for CRITICAL */ | 183 | case 'c': /* replacement for CRITICAL */ |
| 163 | if ((state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) | 184 | if ((result.config.state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) { |
| 164 | usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); | 185 | usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or " |
| 186 | "integer (0-3).")); | ||
| 187 | } | ||
| 165 | permute = false; | 188 | permute = false; |
| 166 | break; | 189 | break; |
| 167 | case 'u': /* replacement for UNKNOWN */ | 190 | case 'u': /* replacement for UNKNOWN */ |
| 168 | if ((state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) | 191 | if ((result.config.state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) { |
| 169 | usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); | 192 | usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or " |
| 193 | "integer (0-3).")); | ||
| 194 | } | ||
| 170 | permute = false; | 195 | permute = false; |
| 171 | break; | 196 | break; |
| 172 | case 's': /* Substitute status text */ | 197 | case 's': /* Substitute status text */ |
| 173 | subst_text = true; | 198 | result.config.subst_text = true; |
| 174 | break; | 199 | break; |
| 175 | } | 200 | } |
| 176 | } | 201 | } |
| 177 | 202 | ||
| 178 | validate_arguments(&argv[optind]); | ||
| 179 | |||
| 180 | if (permute) { /* No [owcu] switch specified, default to this */ | 203 | if (permute) { /* No [owcu] switch specified, default to this */ |
| 181 | state[STATE_OK] = STATE_CRITICAL; | 204 | result.config.state[STATE_OK] = STATE_CRITICAL; |
| 182 | state[STATE_CRITICAL] = STATE_OK; | 205 | result.config.state[STATE_CRITICAL] = STATE_OK; |
| 183 | } | 206 | } |
| 184 | 207 | ||
| 185 | return (const char **)&argv[optind]; | 208 | result.config.command_line = &argv[optind]; |
| 209 | |||
| 210 | return validate_arguments(result); | ||
| 186 | } | 211 | } |
| 187 | 212 | ||
| 188 | void validate_arguments(char **command_line) { | 213 | negate_config_wrapper validate_arguments(negate_config_wrapper config_wrapper) { |
| 189 | if (command_line[0] == NULL) | 214 | if (config_wrapper.config.command_line[0] == NULL) { |
| 190 | usage4(_("Could not parse arguments")); | 215 | usage4(_("Could not parse arguments")); |
| 216 | } | ||
| 191 | 217 | ||
| 192 | if (strncmp(command_line[0], "/", 1) != 0 && strncmp(command_line[0], "./", 2) != 0) | 218 | if (strncmp(config_wrapper.config.command_line[0], "/", 1) != 0 && |
| 219 | strncmp(config_wrapper.config.command_line[0], "./", 2) != 0) { | ||
| 193 | usage4(_("Require path to command")); | 220 | usage4(_("Require path to command")); |
| 221 | } | ||
| 222 | |||
| 223 | return config_wrapper; | ||
| 194 | } | 224 | } |
| 195 | 225 | ||
| 196 | void print_help(void) { | 226 | void print_help(void) { |
| @@ -198,7 +228,8 @@ void print_help(void) { | |||
| 198 | 228 | ||
| 199 | printf(COPYRIGHT, copyright, email); | 229 | printf(COPYRIGHT, copyright, email); |
| 200 | 230 | ||
| 201 | printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and vice-versa) by default.")); | 231 | printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and " |
| 232 | "vice-versa) by default.")); | ||
| 202 | printf("%s\n", _("Additional switches can be used to control:\n")); | 233 | printf("%s\n", _("Additional switches can be used to control:\n")); |
| 203 | printf("\t - which state becomes what\n"); | 234 | printf("\t - which state becomes what\n"); |
| 204 | printf("\t - changing the plugin output text to match the return code"); | 235 | printf("\t - changing the plugin output text to match the return code"); |
| @@ -228,17 +259,20 @@ void print_help(void) { | |||
| 228 | printf("%s\n", _("Examples:")); | 259 | printf("%s\n", _("Examples:")); |
| 229 | printf(" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host"); | 260 | printf(" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host"); |
| 230 | printf(" %s\n", _("Run check_ping and invert result. Must use full path to plugin")); | 261 | printf(" %s\n", _("Run check_ping and invert result. Must use full path to plugin")); |
| 231 | printf(" %s\n", "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'"); | 262 | printf(" %s\n", |
| 263 | "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'"); | ||
| 232 | printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL")); | 264 | printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL")); |
| 233 | printf("\n"); | 265 | printf("\n"); |
| 234 | printf("%s\n", _("Notes:")); | 266 | printf("%s\n", _("Notes:")); |
| 235 | printf(" %s\n", _("This plugin is a wrapper to take the output of another plugin and invert it.")); | 267 | printf(" %s\n", |
| 268 | _("This plugin is a wrapper to take the output of another plugin and invert it.")); | ||
| 236 | printf(" %s\n", _("The full path of the plugin must be provided.")); | 269 | printf(" %s\n", _("The full path of the plugin must be provided.")); |
| 237 | printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL.")); | 270 | printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL.")); |
| 238 | printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK.")); | 271 | printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK.")); |
| 239 | printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged.")); | 272 | printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged.")); |
| 240 | printf("\n"); | 273 | printf("\n"); |
| 241 | printf(" %s\n", _("Using timeout-result, it is possible to override the timeout behaviour or a")); | 274 | printf(" %s\n", |
| 275 | _("Using timeout-result, it is possible to override the timeout behaviour or a")); | ||
| 242 | printf(" %s\n", _("plugin by setting the negate timeout a bit lower.")); | 276 | printf(" %s\n", _("plugin by setting the negate timeout a bit lower.")); |
| 243 | 277 | ||
| 244 | printf(UT_SUPPORT); | 278 | printf(UT_SUPPORT); |
diff --git a/plugins/negate.d/config.h b/plugins/negate.d/config.h new file mode 100644 index 00000000..0cf30cd4 --- /dev/null +++ b/plugins/negate.d/config.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "states.h" | ||
| 4 | |||
| 5 | typedef struct { | ||
| 6 | mp_state_enum state[4]; | ||
| 7 | bool subst_text; | ||
| 8 | char **command_line; | ||
| 9 | } negate_config; | ||
| 10 | |||
| 11 | negate_config negate_config_init() { | ||
| 12 | negate_config tmp = { | ||
| 13 | .state = | ||
| 14 | { | ||
| 15 | STATE_OK, | ||
| 16 | STATE_WARNING, | ||
| 17 | STATE_CRITICAL, | ||
| 18 | STATE_UNKNOWN, | ||
| 19 | }, | ||
| 20 | .subst_text = false, | ||
| 21 | .command_line = NULL, | ||
| 22 | }; | ||
| 23 | return tmp; | ||
| 24 | } | ||
diff --git a/plugins/netutils.c b/plugins/netutils.c index ee81912a..b4c6ff0a 100644 --- a/plugins/netutils.c +++ b/plugins/netutils.c | |||
| @@ -28,13 +28,16 @@ | |||
| 28 | *****************************************************************************/ | 28 | *****************************************************************************/ |
| 29 | 29 | ||
| 30 | #include "common.h" | 30 | #include "common.h" |
| 31 | #include "output.h" | ||
| 32 | #include "states.h" | ||
| 33 | #include <sys/types.h> | ||
| 31 | #include "netutils.h" | 34 | #include "netutils.h" |
| 32 | 35 | ||
| 33 | unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT; | 36 | unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT; |
| 34 | unsigned int socket_timeout_state = STATE_CRITICAL; | 37 | mp_state_enum socket_timeout_state = STATE_CRITICAL; |
| 35 | 38 | mp_state_enum econn_refuse_state = STATE_CRITICAL; | |
| 36 | int econn_refuse_state = STATE_CRITICAL; | ||
| 37 | bool was_refused = false; | 39 | bool was_refused = false; |
| 40 | |||
| 38 | #if USE_IPV6 | 41 | #if USE_IPV6 |
| 39 | int address_family = AF_UNSPEC; | 42 | int address_family = AF_UNSPEC; |
| 40 | #else | 43 | #else |
| @@ -43,119 +46,129 @@ int address_family = AF_INET; | |||
| 43 | 46 | ||
| 44 | /* handles socket timeouts */ | 47 | /* handles socket timeouts */ |
| 45 | void socket_timeout_alarm_handler(int sig) { | 48 | void socket_timeout_alarm_handler(int sig) { |
| 46 | if (sig == SIGALRM) | 49 | mp_subcheck timeout_sc = mp_subcheck_init(); |
| 47 | printf(_("%s - Socket timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); | 50 | timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state); |
| 48 | else | 51 | |
| 49 | printf(_("%s - Abnormal timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); | 52 | if (sig == SIGALRM) { |
| 53 | xasprintf(&timeout_sc.output, _("Socket timeout after %d seconds\n"), socket_timeout); | ||
| 54 | } else { | ||
| 55 | xasprintf(&timeout_sc.output, _("Abnormal timeout after %d seconds\n"), socket_timeout); | ||
| 56 | } | ||
| 50 | 57 | ||
| 51 | exit(socket_timeout_state); | 58 | mp_check overall = mp_check_init(); |
| 59 | mp_add_subcheck_to_check(&overall, timeout_sc); | ||
| 60 | |||
| 61 | mp_exit(overall); | ||
| 52 | } | 62 | } |
| 53 | 63 | ||
| 54 | /* connects to a host on a specified tcp port, sends a string, and gets a | 64 | /* connects to a host on a specified tcp port, sends a string, and gets a |
| 55 | response. loops on select-recv until timeout or eof to get all of a | 65 | response. loops on select-recv until timeout or eof to get all of a |
| 56 | multi-packet answer */ | 66 | multi-packet answer */ |
| 57 | int process_tcp_request2(const char *server_address, int server_port, const char *send_buffer, char *recv_buffer, int recv_size) { | 67 | mp_state_enum process_tcp_request2(const char *server_address, const int server_port, |
| 68 | const char *send_buffer, char *recv_buffer, | ||
| 69 | const int recv_size) { | ||
| 58 | 70 | ||
| 59 | int result; | 71 | int socket; |
| 60 | int send_result; | ||
| 61 | int recv_result; | ||
| 62 | int sd; | ||
| 63 | struct timeval tv; | ||
| 64 | fd_set readfds; | ||
| 65 | int recv_length = 0; | ||
| 66 | 72 | ||
| 67 | result = np_net_connect(server_address, server_port, &sd, IPPROTO_TCP); | 73 | mp_state_enum connect_result = |
| 68 | if (result != STATE_OK) | 74 | np_net_connect(server_address, server_port, &socket, IPPROTO_TCP); |
| 75 | if (connect_result != STATE_OK) { | ||
| 69 | return STATE_CRITICAL; | 76 | return STATE_CRITICAL; |
| 77 | } | ||
| 70 | 78 | ||
| 71 | send_result = send(sd, send_buffer, strlen(send_buffer), 0); | 79 | mp_state_enum result; |
| 80 | ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0); | ||
| 72 | if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { | 81 | if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { |
| 73 | printf("%s\n", _("Send failed")); | 82 | // printf("%s\n", _("Send failed")); |
| 74 | result = STATE_WARNING; | 83 | result = STATE_WARNING; |
| 75 | } | 84 | } |
| 76 | 85 | ||
| 77 | while (1) { | 86 | fd_set readfds; |
| 87 | ssize_t recv_length = 0; | ||
| 88 | while (true) { | ||
| 78 | /* wait up to the number of seconds for socket timeout | 89 | /* wait up to the number of seconds for socket timeout |
| 79 | minus one for data from the host */ | 90 | minus one for data from the host */ |
| 80 | tv.tv_sec = socket_timeout - 1; | 91 | struct timeval timeout = { |
| 81 | tv.tv_usec = 0; | 92 | .tv_sec = socket_timeout - 1, |
| 93 | .tv_usec = 0, | ||
| 94 | }; | ||
| 82 | FD_ZERO(&readfds); | 95 | FD_ZERO(&readfds); |
| 83 | FD_SET(sd, &readfds); | 96 | FD_SET(socket, &readfds); |
| 84 | select(sd + 1, &readfds, NULL, NULL, &tv); | 97 | select(socket + 1, &readfds, NULL, NULL, &timeout); |
| 85 | 98 | ||
| 86 | /* make sure some data has arrived */ | 99 | /* make sure some data has arrived */ |
| 87 | if (!FD_ISSET(sd, &readfds)) { /* it hasn't */ | 100 | if (!FD_ISSET(socket, &readfds)) { /* it hasn't */ |
| 88 | if (!recv_length) { | 101 | if (!recv_length) { |
| 89 | strcpy(recv_buffer, ""); | 102 | strcpy(recv_buffer, ""); |
| 90 | printf("%s\n", _("No data was received from host!")); | 103 | // printf("%s\n", _("No data was received from host!")); |
| 91 | result = STATE_WARNING; | 104 | result = STATE_WARNING; |
| 92 | } else { /* this one failed, but previous ones worked */ | 105 | } else { /* this one failed, but previous ones worked */ |
| 93 | recv_buffer[recv_length] = 0; | 106 | recv_buffer[recv_length] = 0; |
| 94 | } | 107 | } |
| 95 | break; | 108 | break; |
| 96 | } else { /* it has */ | 109 | } /* it has */ |
| 97 | recv_result = recv(sd, recv_buffer + recv_length, (size_t)recv_size - recv_length - 1, 0); | 110 | |
| 98 | if (recv_result == -1) { | 111 | ssize_t recv_result = |
| 99 | /* recv failed, bail out */ | 112 | recv(socket, recv_buffer + recv_length, (size_t)(recv_size - recv_length - 1), 0); |
| 100 | strcpy(recv_buffer + recv_length, ""); | 113 | if (recv_result == -1) { |
| 101 | result = STATE_WARNING; | 114 | /* recv failed, bail out */ |
| 102 | break; | 115 | strcpy(recv_buffer + recv_length, ""); |
| 103 | } else if (recv_result == 0) { | 116 | result = STATE_WARNING; |
| 104 | /* end of file ? */ | 117 | break; |
| 105 | recv_buffer[recv_length] = 0; | 118 | } |
| 106 | break; | 119 | |
| 107 | } else { /* we got data! */ | 120 | if (recv_result == 0) { |
| 108 | recv_length += recv_result; | 121 | /* end of file ? */ |
| 109 | if (recv_length >= recv_size - 1) { | 122 | recv_buffer[recv_length] = 0; |
| 110 | /* buffer full, we're done */ | 123 | break; |
| 111 | recv_buffer[recv_size - 1] = 0; | 124 | } |
| 112 | break; | 125 | |
| 113 | } | 126 | /* we got data! */ |
| 114 | } | 127 | recv_length += recv_result; |
| 128 | if (recv_length >= recv_size - 1) { | ||
| 129 | /* buffer full, we're done */ | ||
| 130 | recv_buffer[recv_size - 1] = 0; | ||
| 131 | break; | ||
| 115 | } | 132 | } |
| 116 | /* end if(!FD_ISSET(sd,&readfds)) */ | 133 | /* end if(!FD_ISSET(sd,&readfds)) */ |
| 117 | } | 134 | } |
| 118 | /* end while(1) */ | ||
| 119 | 135 | ||
| 120 | close(sd); | 136 | close(socket); |
| 121 | return result; | 137 | return result; |
| 122 | } | 138 | } |
| 123 | 139 | ||
| 124 | /* connects to a host on a specified port, sends a string, and gets a | 140 | /* connects to a host on a specified port, sends a string, and gets a |
| 125 | response */ | 141 | response */ |
| 126 | int process_request(const char *server_address, int server_port, int proto, const char *send_buffer, char *recv_buffer, int recv_size) { | 142 | mp_state_enum process_request(const char *server_address, const int server_port, const int proto, |
| 127 | int result; | 143 | const char *send_buffer, char *recv_buffer, const int recv_size) { |
| 128 | int sd; | ||
| 129 | 144 | ||
| 130 | result = STATE_OK; | 145 | mp_state_enum result = STATE_OK; |
| 131 | 146 | int socket; | |
| 132 | result = np_net_connect(server_address, server_port, &sd, proto); | 147 | result = np_net_connect(server_address, server_port, &socket, proto); |
| 133 | if (result != STATE_OK) | 148 | if (result != STATE_OK) { |
| 134 | return STATE_CRITICAL; | 149 | return STATE_CRITICAL; |
| 150 | } | ||
| 135 | 151 | ||
| 136 | result = send_request(sd, proto, send_buffer, recv_buffer, recv_size); | 152 | result = send_request(socket, proto, send_buffer, recv_buffer, recv_size); |
| 137 | 153 | ||
| 138 | close(sd); | 154 | close(socket); |
| 139 | 155 | ||
| 140 | return result; | 156 | return result; |
| 141 | } | 157 | } |
| 142 | 158 | ||
| 143 | /* opens a tcp or udp connection to a remote host or local socket */ | 159 | /* opens a tcp or udp connection to a remote host or local socket */ |
| 144 | int np_net_connect(const char *host_name, int port, int *sd, int proto) { | 160 | mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor, |
| 161 | const int proto) { | ||
| 145 | /* send back STATE_UNKOWN if there's an error | 162 | /* send back STATE_UNKOWN if there's an error |
| 146 | send back STATE_OK if we connect | 163 | send back STATE_OK if we connect |
| 147 | send back STATE_CRITICAL if we can't connect. | 164 | send back STATE_CRITICAL if we can't connect. |
| 148 | Let upstream figure out what to send to the user. */ | 165 | Let upstream figure out what to send to the user. */ |
| 149 | struct addrinfo hints; | 166 | bool is_socket = (host_name[0] == '/'); |
| 150 | struct addrinfo *r, *res; | 167 | int socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM; |
| 151 | struct sockaddr_un su; | ||
| 152 | char port_str[6], host[MAX_HOST_ADDRESS_LENGTH]; | ||
| 153 | size_t len; | ||
| 154 | int socktype, result; | ||
| 155 | short is_socket = (host_name[0] == '/'); | ||
| 156 | |||
| 157 | socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM; | ||
| 158 | 168 | ||
| 169 | struct addrinfo hints = {}; | ||
| 170 | struct addrinfo *res = NULL; | ||
| 171 | int result; | ||
| 159 | /* as long as it doesn't start with a '/', it's assumed a host or ip */ | 172 | /* as long as it doesn't start with a '/', it's assumed a host or ip */ |
| 160 | if (!is_socket) { | 173 | if (!is_socket) { |
| 161 | memset(&hints, 0, sizeof(hints)); | 174 | memset(&hints, 0, sizeof(hints)); |
| @@ -163,37 +176,46 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) { | |||
| 163 | hints.ai_protocol = proto; | 176 | hints.ai_protocol = proto; |
| 164 | hints.ai_socktype = socktype; | 177 | hints.ai_socktype = socktype; |
| 165 | 178 | ||
| 166 | len = strlen(host_name); | 179 | size_t len = strlen(host_name); |
| 167 | /* check for an [IPv6] address (and strip the brackets) */ | 180 | /* check for an [IPv6] address (and strip the brackets) */ |
| 168 | if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') { | 181 | if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') { |
| 169 | host_name++; | 182 | host_name++; |
| 170 | len -= 2; | 183 | len -= 2; |
| 171 | } | 184 | } |
| 172 | if (len >= sizeof(host)) | 185 | |
| 186 | char host[MAX_HOST_ADDRESS_LENGTH]; | ||
| 187 | |||
| 188 | if (len >= sizeof(host)) { | ||
| 173 | return STATE_UNKNOWN; | 189 | return STATE_UNKNOWN; |
| 190 | } | ||
| 191 | |||
| 174 | memcpy(host, host_name, len); | 192 | memcpy(host, host_name, len); |
| 175 | host[len] = '\0'; | 193 | host[len] = '\0'; |
| 194 | |||
| 195 | char port_str[6]; | ||
| 176 | snprintf(port_str, sizeof(port_str), "%d", port); | 196 | snprintf(port_str, sizeof(port_str), "%d", port); |
| 177 | result = getaddrinfo(host, port_str, &hints, &res); | 197 | int getaddrinfo_err = getaddrinfo(host, port_str, &hints, &res); |
| 178 | 198 | ||
| 179 | if (result != 0) { | 199 | if (getaddrinfo_err != 0) { |
| 180 | printf("%s\n", gai_strerror(result)); | 200 | // printf("%s\n", gai_strerror(result)); |
| 181 | return STATE_UNKNOWN; | 201 | return STATE_UNKNOWN; |
| 182 | } | 202 | } |
| 183 | 203 | ||
| 184 | r = res; | 204 | struct addrinfo *addressPointer = res; |
| 185 | while (r) { | 205 | while (addressPointer) { |
| 186 | /* attempt to create a socket */ | 206 | /* attempt to create a socket */ |
| 187 | *sd = socket(r->ai_family, socktype, r->ai_protocol); | 207 | *socketDescriptor = |
| 208 | socket(addressPointer->ai_family, socktype, addressPointer->ai_protocol); | ||
| 188 | 209 | ||
| 189 | if (*sd < 0) { | 210 | if (*socketDescriptor < 0) { |
| 190 | printf("%s\n", _("Socket creation failed")); | 211 | // printf("%s\n", _("Socket creation failed")); |
| 191 | freeaddrinfo(r); | 212 | freeaddrinfo(addressPointer); |
| 192 | return STATE_UNKNOWN; | 213 | return STATE_UNKNOWN; |
| 193 | } | 214 | } |
| 194 | 215 | ||
| 195 | /* attempt to open a connection */ | 216 | /* attempt to open a connection */ |
| 196 | result = connect(*sd, r->ai_addr, r->ai_addrlen); | 217 | result = |
| 218 | connect(*socketDescriptor, addressPointer->ai_addr, addressPointer->ai_addrlen); | ||
| 197 | 219 | ||
| 198 | if (result == 0) { | 220 | if (result == 0) { |
| 199 | was_refused = false; | 221 | was_refused = false; |
| @@ -208,39 +230,48 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) { | |||
| 208 | } | 230 | } |
| 209 | } | 231 | } |
| 210 | 232 | ||
| 211 | close(*sd); | 233 | close(*socketDescriptor); |
| 212 | r = r->ai_next; | 234 | addressPointer = addressPointer->ai_next; |
| 213 | } | 235 | } |
| 236 | |||
| 214 | freeaddrinfo(res); | 237 | freeaddrinfo(res); |
| 215 | } | 238 | |
| 216 | /* else the hostname is interpreted as a path to a unix socket */ | 239 | } else { |
| 217 | else { | 240 | /* else the hostname is interpreted as a path to a unix socket */ |
| 218 | if (strlen(host_name) >= UNIX_PATH_MAX) { | 241 | if (strlen(host_name) >= UNIX_PATH_MAX) { |
| 219 | die(STATE_UNKNOWN, _("Supplied path too long unix domain socket")); | 242 | die(STATE_UNKNOWN, _("Supplied path too long unix domain socket")); |
| 220 | } | 243 | } |
| 221 | memset(&su, 0, sizeof(su)); | 244 | |
| 245 | struct sockaddr_un su = {}; | ||
| 222 | su.sun_family = AF_UNIX; | 246 | su.sun_family = AF_UNIX; |
| 223 | strncpy(su.sun_path, host_name, UNIX_PATH_MAX); | 247 | strncpy(su.sun_path, host_name, UNIX_PATH_MAX); |
| 224 | *sd = socket(PF_UNIX, SOCK_STREAM, 0); | 248 | *socketDescriptor = socket(PF_UNIX, SOCK_STREAM, 0); |
| 225 | if (*sd < 0) { | 249 | |
| 250 | if (*socketDescriptor < 0) { | ||
| 226 | die(STATE_UNKNOWN, _("Socket creation failed")); | 251 | die(STATE_UNKNOWN, _("Socket creation failed")); |
| 227 | } | 252 | } |
| 228 | result = connect(*sd, (struct sockaddr *)&su, sizeof(su)); | 253 | |
| 229 | if (result < 0 && errno == ECONNREFUSED) | 254 | result = connect(*socketDescriptor, (struct sockaddr *)&su, sizeof(su)); |
| 255 | if (result < 0 && errno == ECONNREFUSED) { | ||
| 230 | was_refused = true; | 256 | was_refused = true; |
| 257 | } | ||
| 231 | } | 258 | } |
| 232 | 259 | ||
| 233 | if (result == 0) | 260 | if (result == 0) { |
| 234 | return STATE_OK; | 261 | return STATE_OK; |
| 235 | else if (was_refused) { | 262 | } |
| 263 | |||
| 264 | if (was_refused) { | ||
| 236 | switch (econn_refuse_state) { /* a user-defined expected outcome */ | 265 | switch (econn_refuse_state) { /* a user-defined expected outcome */ |
| 237 | case STATE_OK: | 266 | case STATE_OK: |
| 238 | case STATE_WARNING: /* user wants WARN or OK on refusal, or... */ | 267 | case STATE_WARNING: /* user wants WARN or OK on refusal, or... */ |
| 239 | case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */ | 268 | case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */ |
| 240 | if (is_socket) | 269 | if (is_socket) { |
| 241 | printf("connect to file socket %s: %s\n", host_name, strerror(errno)); | 270 | // printf("connect to file socket %s: %s\n", host_name, strerror(errno)); |
| 242 | else | 271 | } else { |
| 243 | printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno)); | 272 | // printf("connect to address %s and port %d: %s\n", host_name, port, |
| 273 | // strerror(errno)); | ||
| 274 | } | ||
| 244 | return STATE_CRITICAL; | 275 | return STATE_CRITICAL; |
| 245 | break; | 276 | break; |
| 246 | default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */ | 277 | default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */ |
| @@ -248,98 +279,108 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) { | |||
| 248 | break; | 279 | break; |
| 249 | } | 280 | } |
| 250 | } else { | 281 | } else { |
| 251 | if (is_socket) | 282 | if (is_socket) { |
| 252 | printf("connect to file socket %s: %s\n", host_name, strerror(errno)); | 283 | // printf("connect to file socket %s: %s\n", host_name, strerror(errno)); |
| 253 | else | 284 | } else { |
| 254 | printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno)); | 285 | // printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno)); |
| 286 | } | ||
| 255 | return STATE_CRITICAL; | 287 | return STATE_CRITICAL; |
| 256 | } | 288 | } |
| 257 | } | 289 | } |
| 258 | 290 | ||
| 259 | int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) { | 291 | mp_state_enum send_request(const int socket, const int proto, const char *send_buffer, |
| 260 | int result = STATE_OK; | 292 | char *recv_buffer, const int recv_size) { |
| 261 | int send_result; | 293 | mp_state_enum result = STATE_OK; |
| 262 | int recv_result; | ||
| 263 | struct timeval tv; | ||
| 264 | fd_set readfds; | ||
| 265 | 294 | ||
| 266 | send_result = send(sd, send_buffer, strlen(send_buffer), 0); | 295 | ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0); |
| 267 | if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { | 296 | if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { |
| 268 | printf("%s\n", _("Send failed")); | 297 | // printf("%s\n", _("Send failed")); |
| 269 | result = STATE_WARNING; | 298 | result = STATE_WARNING; |
| 270 | } | 299 | } |
| 271 | 300 | ||
| 272 | /* wait up to the number of seconds for socket timeout minus one | 301 | /* wait up to the number of seconds for socket timeout minus one |
| 273 | for data from the host */ | 302 | for data from the host */ |
| 274 | tv.tv_sec = socket_timeout - 1; | 303 | struct timeval timestamp = { |
| 275 | tv.tv_usec = 0; | 304 | .tv_sec = socket_timeout - 1, |
| 305 | .tv_usec = 0, | ||
| 306 | }; | ||
| 307 | fd_set readfds; | ||
| 276 | FD_ZERO(&readfds); | 308 | FD_ZERO(&readfds); |
| 277 | FD_SET(sd, &readfds); | 309 | FD_SET(socket, &readfds); |
| 278 | select(sd + 1, &readfds, NULL, NULL, &tv); | 310 | select(socket + 1, &readfds, NULL, NULL, ×tamp); |
| 279 | 311 | ||
| 280 | /* make sure some data has arrived */ | 312 | /* make sure some data has arrived */ |
| 281 | if (!FD_ISSET(sd, &readfds)) { | 313 | if (!FD_ISSET(socket, &readfds)) { |
| 282 | strcpy(recv_buffer, ""); | 314 | strcpy(recv_buffer, ""); |
| 283 | printf("%s\n", _("No data was received from host!")); | 315 | // printf("%s\n", _("No data was received from host!")); |
| 284 | result = STATE_WARNING; | 316 | result = STATE_WARNING; |
| 285 | } | 317 | } else { |
| 286 | 318 | ssize_t recv_result = recv(socket, recv_buffer, (size_t)(recv_size - 1), 0); | |
| 287 | else { | ||
| 288 | recv_result = recv(sd, recv_buffer, (size_t)recv_size - 1, 0); | ||
| 289 | if (recv_result == -1) { | 319 | if (recv_result == -1) { |
| 290 | strcpy(recv_buffer, ""); | 320 | strcpy(recv_buffer, ""); |
| 291 | if (proto != IPPROTO_TCP) | 321 | if (proto != IPPROTO_TCP) { |
| 292 | printf("%s\n", _("Receive failed")); | 322 | // printf("%s\n", _("Receive failed")); |
| 323 | } | ||
| 293 | result = STATE_WARNING; | 324 | result = STATE_WARNING; |
| 294 | } else | 325 | } else { |
| 295 | recv_buffer[recv_result] = 0; | 326 | recv_buffer[recv_result] = 0; |
| 327 | } | ||
| 296 | 328 | ||
| 297 | /* die returned string */ | 329 | /* die returned string */ |
| 298 | recv_buffer[recv_size - 1] = 0; | 330 | recv_buffer[recv_size - 1] = 0; |
| 299 | } | 331 | } |
| 332 | |||
| 300 | return result; | 333 | return result; |
| 301 | } | 334 | } |
| 302 | 335 | ||
| 303 | bool is_host(const char *address) { | 336 | bool is_host(const char *address) { |
| 304 | if (is_addr(address) || is_hostname(address)) | 337 | if (is_addr(address) || is_hostname(address)) { |
| 305 | return (true); | 338 | return (true); |
| 339 | } | ||
| 306 | 340 | ||
| 307 | return (false); | 341 | return (false); |
| 308 | } | 342 | } |
| 309 | 343 | ||
| 310 | void host_or_die(const char *str) { | 344 | void host_or_die(const char *str) { |
| 311 | if (!str || (!is_addr(str) && !is_hostname(str))) | 345 | if (!str || (!is_addr(str) && !is_hostname(str))) { |
| 312 | usage_va(_("Invalid hostname/address - %s"), str); | 346 | usage_va(_("Invalid hostname/address - %s"), str); |
| 347 | } | ||
| 313 | } | 348 | } |
| 314 | 349 | ||
| 315 | bool is_addr(const char *address) { | 350 | bool is_addr(const char *address) { |
| 316 | #ifdef USE_IPV6 | 351 | #ifdef USE_IPV6 |
| 317 | if (address_family == AF_INET && is_inet_addr(address)) | 352 | if (address_family == AF_INET && is_inet_addr(address)) { |
| 318 | return true; | 353 | return true; |
| 319 | else if (address_family == AF_INET6 && is_inet6_addr(address)) | 354 | } |
| 355 | |||
| 356 | if (address_family == AF_INET6 && is_inet6_addr(address)) { | ||
| 320 | return true; | 357 | return true; |
| 358 | } | ||
| 321 | #else | 359 | #else |
| 322 | if (is_inet_addr(address)) | 360 | if (is_inet_addr(address)) { |
| 323 | return (true); | 361 | return true; |
| 362 | } | ||
| 324 | #endif | 363 | #endif |
| 325 | 364 | ||
| 326 | return (false); | 365 | return false; |
| 327 | } | 366 | } |
| 328 | 367 | ||
| 329 | int dns_lookup(const char *in, struct sockaddr_storage *ss, int family) { | 368 | bool dns_lookup(const char *node_string, struct sockaddr_storage *ss, const int family) { |
| 330 | struct addrinfo hints; | 369 | struct addrinfo hints; |
| 331 | struct addrinfo *res; | ||
| 332 | int retval; | ||
| 333 | |||
| 334 | memset(&hints, 0, sizeof(struct addrinfo)); | 370 | memset(&hints, 0, sizeof(struct addrinfo)); |
| 335 | hints.ai_family = family; | 371 | hints.ai_family = family; |
| 336 | 372 | ||
| 337 | retval = getaddrinfo(in, NULL, &hints, &res); | 373 | struct addrinfo *res; |
| 338 | if (retval != 0) | 374 | int retval = getaddrinfo(node_string, NULL, &hints, &res); |
| 375 | if (retval != 0) { | ||
| 339 | return false; | 376 | return false; |
| 377 | } | ||
| 340 | 378 | ||
| 341 | if (ss != NULL) | 379 | if (ss != NULL) { |
| 342 | memcpy(ss, res->ai_addr, res->ai_addrlen); | 380 | memcpy(ss, res->ai_addr, res->ai_addrlen); |
| 381 | } | ||
| 382 | |||
| 343 | freeaddrinfo(res); | 383 | freeaddrinfo(res); |
| 384 | |||
| 344 | return true; | 385 | return true; |
| 345 | } | 386 | } |
diff --git a/plugins/netutils.h b/plugins/netutils.h index a95057e0..dbd22398 100644 --- a/plugins/netutils.h +++ b/plugins/netutils.h | |||
| @@ -1,120 +1,140 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring Plugins net utilities include file | 3 | * Monitoring Plugins net utilities include file |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) | 6 | * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) |
| 7 | * Copyright (c) 2003-2007 Monitoring Plugins Development Team | 7 | * Copyright (c) 2003-2007 Monitoring Plugins Development Team |
| 8 | * | 8 | * |
| 9 | * Description: | 9 | * Description: |
| 10 | * | 10 | * |
| 11 | * This file contains common include files and function definitions | 11 | * This file contains common include files and function definitions |
| 12 | * used in many of the plugins. | 12 | * used in many of the plugins. |
| 13 | * | 13 | * |
| 14 | * | 14 | * |
| 15 | * This program is free software: you can redistribute it and/or modify | 15 | * This program is free software: you can redistribute it and/or modify |
| 16 | * it under the terms of the GNU General Public License as published by | 16 | * it under the terms of the GNU General Public License as published by |
| 17 | * the Free Software Foundation, either version 3 of the License, or | 17 | * the Free Software Foundation, either version 3 of the License, or |
| 18 | * (at your option) any later version. | 18 | * (at your option) any later version. |
| 19 | * | 19 | * |
| 20 | * This program is distributed in the hope that it will be useful, | 20 | * This program is distributed in the hope that it will be useful, |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | * GNU General Public License for more details. | 23 | * GNU General Public License for more details. |
| 24 | * | 24 | * |
| 25 | * You should have received a copy of the GNU General Public License | 25 | * You should have received a copy of the GNU General Public License |
| 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 26 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | * | 27 | * |
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | #ifndef _NETUTILS_H_ | 31 | #ifndef _NETUTILS_H_ |
| 32 | #define _NETUTILS_H_ | 32 | #define _NETUTILS_H_ |
| 33 | 33 | ||
| 34 | #include "common.h" | 34 | #include "output.h" |
| 35 | #include "states.h" | ||
| 35 | #include "utils.h" | 36 | #include "utils.h" |
| 36 | #include <netinet/in.h> | 37 | #include <netinet/in.h> |
| 37 | #include <arpa/inet.h> | 38 | #include <arpa/inet.h> |
| 38 | #include <netdb.h> | 39 | #include <netdb.h> |
| 39 | 40 | ||
| 40 | #ifdef HAVE_SYS_UN_H | 41 | #ifdef HAVE_SYS_UN_H |
| 41 | # include <sys/un.h> | 42 | # include <sys/un.h> |
| 42 | # ifndef UNIX_PATH_MAX | 43 | # ifndef UNIX_PATH_MAX |
| 43 | /* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */ | 44 | /* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */ |
| 44 | # define UNIX_PATH_MAX 104 | 45 | # define UNIX_PATH_MAX 104 |
| 45 | # endif /* UNIX_PATH_MAX */ | 46 | # endif /* UNIX_PATH_MAX */ |
| 46 | #endif /* HAVE_SYS_UN_H */ | 47 | #endif /* HAVE_SYS_UN_H */ |
| 47 | 48 | ||
| 48 | #ifndef HOST_MAX_BYTES | 49 | #ifndef HOST_MAX_BYTES |
| 49 | # define HOST_MAX_BYTES 255 | 50 | # define HOST_MAX_BYTES 255 |
| 50 | #endif | 51 | #endif |
| 51 | 52 | ||
| 52 | /* process_request and wrapper macros */ | 53 | /* process_request and wrapper macros */ |
| 53 | #define process_tcp_request(addr, port, sbuf, rbuf, rsize) \ | 54 | #define process_tcp_request(addr, port, sbuf, rbuf, rsize) \ |
| 54 | process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize) | 55 | process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize) |
| 55 | #define process_udp_request(addr, port, sbuf, rbuf, rsize) \ | 56 | #define process_udp_request(addr, port, sbuf, rbuf, rsize) \ |
| 56 | process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize) | 57 | process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize) |
| 57 | int process_tcp_request2 (const char *address, int port, | 58 | mp_state_enum process_tcp_request2(const char *server_address, int server_port, |
| 58 | const char *sbuffer, char *rbuffer, int rsize); | 59 | const char *send_buffer, char *recv_buffer, int recv_size); |
| 59 | int process_request (const char *address, int port, int proto, | 60 | mp_state_enum process_request(const char *server_address, int server_port, int proto, |
| 60 | const char *sbuffer, char *rbuffer, int rsize); | 61 | const char *send_buffer, char *recv_buffer, int recv_size); |
| 61 | 62 | ||
| 62 | /* my_connect and wrapper macros */ | 63 | /* my_connect and wrapper macros */ |
| 63 | #define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP) | 64 | #define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP) |
| 64 | #define my_udp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_UDP) | 65 | #define my_udp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_UDP) |
| 65 | int np_net_connect(const char *address, int port, int *sd, int proto); | 66 | mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor, int proto); |
| 66 | 67 | ||
| 67 | /* send_request and wrapper macros */ | 68 | /* send_request and wrapper macros */ |
| 68 | #define send_tcp_request(s, sbuf, rbuf, rsize) \ | 69 | #define send_tcp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize) |
| 69 | send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize) | 70 | #define send_udp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize) |
| 70 | #define send_udp_request(s, sbuf, rbuf, rsize) \ | 71 | mp_state_enum send_request(int socket, int proto, const char *send_buffer, char *recv_buffer, |
| 71 | send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize) | 72 | int recv_size); |
| 72 | int send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size); | ||
| 73 | |||
| 74 | 73 | ||
| 75 | /* "is_*" wrapper macros and functions */ | 74 | /* "is_*" wrapper macros and functions */ |
| 76 | bool is_host (const char *); | 75 | bool is_host(const char *); |
| 77 | bool is_addr (const char *); | 76 | bool is_addr(const char *); |
| 78 | int dns_lookup (const char *, struct sockaddr_storage *, int); | 77 | bool dns_lookup(const char *, struct sockaddr_storage *, int); |
| 79 | void host_or_die(const char *str); | 78 | void host_or_die(const char *str); |
| 80 | #define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family) | 79 | #define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family) |
| 81 | #define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) | 80 | #define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) |
| 82 | #ifdef USE_IPV6 | 81 | #ifdef USE_IPV6 |
| 83 | # define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) | 82 | # define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) |
| 84 | # define is_hostname(addr) resolve_host_or_addr(addr, address_family) | 83 | # define is_hostname(addr) resolve_host_or_addr(addr, address_family) |
| 85 | #else | 84 | #else |
| 86 | # define is_hostname(addr) resolve_host_or_addr(addr, AF_INET) | 85 | # define is_hostname(addr) resolve_host_or_addr(addr, AF_INET) |
| 87 | #endif | 86 | #endif |
| 88 | 87 | ||
| 89 | extern unsigned int socket_timeout; | 88 | extern unsigned int socket_timeout; |
| 90 | extern unsigned int socket_timeout_state; | 89 | extern mp_state_enum socket_timeout_state; |
| 91 | extern int econn_refuse_state; | 90 | extern mp_state_enum econn_refuse_state; |
| 92 | extern bool was_refused; | 91 | extern bool was_refused; |
| 93 | extern int address_family; | 92 | extern int address_family; |
| 94 | 93 | ||
| 95 | void socket_timeout_alarm_handler (int) __attribute__((noreturn)); | 94 | void socket_timeout_alarm_handler(int) __attribute__((noreturn)); |
| 96 | 95 | ||
| 97 | /* SSL-Related functionality */ | 96 | /* SSL-Related functionality */ |
| 98 | #ifdef HAVE_SSL | 97 | #ifdef HAVE_SSL |
| 99 | # define MP_SSLv2 1 | 98 | # define MP_SSLv2 1 |
| 100 | # define MP_SSLv3 2 | 99 | # define MP_SSLv3 2 |
| 101 | # define MP_TLSv1 3 | 100 | # define MP_TLSv1 3 |
| 102 | # define MP_TLSv1_1 4 | 101 | # define MP_TLSv1_1 4 |
| 103 | # define MP_TLSv1_2 5 | 102 | # define MP_TLSv1_2 5 |
| 104 | # define MP_SSLv2_OR_NEWER 6 | 103 | # define MP_SSLv2_OR_NEWER 6 |
| 105 | # define MP_SSLv3_OR_NEWER 7 | 104 | # define MP_SSLv3_OR_NEWER 7 |
| 106 | # define MP_TLSv1_OR_NEWER 8 | 105 | # define MP_TLSv1_OR_NEWER 8 |
| 107 | # define MP_TLSv1_1_OR_NEWER 9 | 106 | # define MP_TLSv1_1_OR_NEWER 9 |
| 108 | # define MP_TLSv1_2_OR_NEWER 10 | 107 | # define MP_TLSv1_2_OR_NEWER 10 |
| 109 | /* maybe this could be merged with the above np_net_connect, via some flags */ | 108 | /* maybe this could be merged with the above np_net_connect, via some flags */ |
| 110 | int np_net_ssl_init(int sd); | 109 | int np_net_ssl_init(int socket); |
| 111 | int np_net_ssl_init_with_hostname(int sd, char *host_name); | 110 | int np_net_ssl_init_with_hostname(int socket, char *host_name); |
| 112 | int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version); | 111 | int np_net_ssl_init_with_hostname_and_version(int socket, char *host_name, int version); |
| 113 | int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey); | 112 | int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name, int version, |
| 114 | void np_net_ssl_cleanup(); | 113 | char *cert, char *privkey); |
| 114 | void np_net_ssl_cleanup(void); | ||
| 115 | int np_net_ssl_write(const void *buf, int num); | 115 | int np_net_ssl_write(const void *buf, int num); |
| 116 | int np_net_ssl_read(void *buf, int num); | 116 | int np_net_ssl_read(void *buf, int num); |
| 117 | int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); | ||
| 118 | #endif /* HAVE_SSL */ | ||
| 119 | 117 | ||
| 118 | typedef enum { | ||
| 119 | ALL_OK, | ||
| 120 | NO_SERVER_CERTIFICATE_PRESENT, | ||
| 121 | UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT, | ||
| 122 | WRONG_TIME_FORMAT_IN_CERTIFICATE, | ||
| 123 | } retrieve_expiration_date_errors; | ||
| 124 | |||
| 125 | typedef struct { | ||
| 126 | double remaining_seconds; | ||
| 127 | retrieve_expiration_date_errors errors; | ||
| 128 | } retrieve_expiration_time_result; | ||
| 129 | |||
| 130 | typedef struct { | ||
| 131 | mp_state_enum result_state; | ||
| 132 | double remaining_seconds; | ||
| 133 | retrieve_expiration_date_errors errors; | ||
| 134 | } net_ssl_check_cert_result; | ||
| 135 | net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit); | ||
| 136 | |||
| 137 | mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); | ||
| 138 | mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); | ||
| 139 | #endif /* HAVE_SSL */ | ||
| 120 | #endif /* _NETUTILS_H_ */ | 140 | #endif /* _NETUTILS_H_ */ |
diff --git a/plugins/picohttpparser/picohttpparser.c b/plugins/picohttpparser/picohttpparser.c index 2ae92d66..e87388b0 100644 --- a/plugins/picohttpparser/picohttpparser.c +++ b/plugins/picohttpparser/picohttpparser.c | |||
| @@ -50,59 +50,61 @@ | |||
| 50 | # define ALIGNED(n) __attribute__((aligned(n))) | 50 | # define ALIGNED(n) __attribute__((aligned(n))) |
| 51 | #endif | 51 | #endif |
| 52 | 52 | ||
| 53 | #define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) | 53 | #define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u) |
| 54 | 54 | ||
| 55 | #define CHECK_EOF() \ | 55 | #define CHECK_EOF() \ |
| 56 | if (buf == buf_end) { \ | 56 | if (buf == buf_end) { \ |
| 57 | *ret = -2; \ | 57 | *ret = -2; \ |
| 58 | return NULL; \ | 58 | return NULL; \ |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | #define EXPECT_CHAR_NO_CHECK(ch) \ | 61 | #define EXPECT_CHAR_NO_CHECK(ch) \ |
| 62 | if (*buf++ != ch) { \ | 62 | if (*buf++ != ch) { \ |
| 63 | *ret = -1; \ | 63 | *ret = -1; \ |
| 64 | return NULL; \ | 64 | return NULL; \ |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | #define EXPECT_CHAR(ch) \ | 67 | #define EXPECT_CHAR(ch) \ |
| 68 | CHECK_EOF(); \ | 68 | CHECK_EOF(); \ |
| 69 | EXPECT_CHAR_NO_CHECK(ch); | 69 | EXPECT_CHAR_NO_CHECK(ch); |
| 70 | 70 | ||
| 71 | #define ADVANCE_TOKEN(tok, toklen) \ | 71 | #define ADVANCE_TOKEN(tok, toklen) \ |
| 72 | do { \ | 72 | do { \ |
| 73 | const char *tok_start = buf; \ | 73 | const char *tok_start = buf; \ |
| 74 | static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ | 74 | static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ |
| 75 | int found2; \ | 75 | int found2; \ |
| 76 | buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ | 76 | buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ |
| 77 | if (!found2) { \ | 77 | if (!found2) { \ |
| 78 | CHECK_EOF(); \ | 78 | CHECK_EOF(); \ |
| 79 | } \ | 79 | } \ |
| 80 | while (1) { \ | 80 | while (1) { \ |
| 81 | if (*buf == ' ') { \ | 81 | if (*buf == ' ') { \ |
| 82 | break; \ | 82 | break; \ |
| 83 | } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ | 83 | } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ |
| 84 | if ((unsigned char)*buf < '\040' || *buf == '\177') { \ | 84 | if ((unsigned char)*buf < '\040' || *buf == '\177') { \ |
| 85 | *ret = -1; \ | 85 | *ret = -1; \ |
| 86 | return NULL; \ | 86 | return NULL; \ |
| 87 | } \ | 87 | } \ |
| 88 | } \ | 88 | } \ |
| 89 | ++buf; \ | 89 | ++buf; \ |
| 90 | CHECK_EOF(); \ | 90 | CHECK_EOF(); \ |
| 91 | } \ | 91 | } \ |
| 92 | tok = tok_start; \ | 92 | tok = tok_start; \ |
| 93 | toklen = buf - tok_start; \ | 93 | toklen = buf - tok_start; \ |
| 94 | } while (0) | 94 | } while (0) |
| 95 | 95 | ||
| 96 | static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" | 96 | static const char *token_char_map = |
| 97 | "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" | 97 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| 98 | "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" | 98 | "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" |
| 99 | "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" | 99 | "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" |
| 100 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" | 100 | "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" |
| 101 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" | 101 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| 102 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" | 102 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| 103 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; | 103 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| 104 | 104 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; | |
| 105 | static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) { | 105 | |
| 106 | static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, | ||
| 107 | size_t ranges_size, int *found) { | ||
| 106 | *found = 0; | 108 | *found = 0; |
| 107 | #if __SSE4_2__ | 109 | #if __SSE4_2__ |
| 108 | if (likely(buf_end - buf >= 16)) { | 110 | if (likely(buf_end - buf >= 16)) { |
| @@ -111,7 +113,8 @@ static const char *findchar_fast(const char *buf, const char *buf_end, const cha | |||
| 111 | size_t left = (buf_end - buf) & ~15; | 113 | size_t left = (buf_end - buf) & ~15; |
| 112 | do { | 114 | do { |
| 113 | __m128i b16 = _mm_loadu_si128((const __m128i *)buf); | 115 | __m128i b16 = _mm_loadu_si128((const __m128i *)buf); |
| 114 | int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); | 116 | int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, |
| 117 | _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); | ||
| 115 | if (unlikely(r != 16)) { | 118 | if (unlikely(r != 16)) { |
| 116 | buf += r; | 119 | buf += r; |
| 117 | *found = 1; | 120 | *found = 1; |
| @@ -130,25 +133,29 @@ static const char *findchar_fast(const char *buf, const char *buf_end, const cha | |||
| 130 | return buf; | 133 | return buf; |
| 131 | } | 134 | } |
| 132 | 135 | ||
| 133 | static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) { | 136 | static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, |
| 137 | size_t *token_len, int *ret) { | ||
| 134 | const char *token_start = buf; | 138 | const char *token_start = buf; |
| 135 | 139 | ||
| 136 | #ifdef __SSE4_2__ | 140 | #ifdef __SSE4_2__ |
| 137 | static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ | 141 | static const char ALIGNED(16) ranges1[16] = |
| 138 | "\012\037" /* allow SP and up to but not including DEL */ | 142 | "\0\010" /* allow HT */ |
| 139 | "\177\177"; /* allow chars w. MSB set */ | 143 | "\012\037" /* allow SP and up to but not including DEL */ |
| 144 | "\177\177"; /* allow chars w. MSB set */ | ||
| 140 | int found; | 145 | int found; |
| 141 | buf = findchar_fast(buf, buf_end, ranges1, 6, &found); | 146 | buf = findchar_fast(buf, buf_end, ranges1, 6, &found); |
| 142 | if (found) | 147 | if (found) { |
| 143 | goto FOUND_CTL; | 148 | goto FOUND_CTL; |
| 149 | } | ||
| 144 | #else | 150 | #else |
| 145 | /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ | 151 | /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined |
| 152 | */ | ||
| 146 | while (likely(buf_end - buf >= 8)) { | 153 | while (likely(buf_end - buf >= 8)) { |
| 147 | # define DOIT() \ | 154 | # define DOIT() \ |
| 148 | do { \ | 155 | do { \ |
| 149 | if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ | 156 | if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ |
| 150 | goto NonPrintable; \ | 157 | goto NonPrintable; \ |
| 151 | ++buf; \ | 158 | ++buf; \ |
| 152 | } while (0) | 159 | } while (0) |
| 153 | DOIT(); | 160 | DOIT(); |
| 154 | DOIT(); | 161 | DOIT(); |
| @@ -161,7 +168,8 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const | |||
| 161 | # undef DOIT | 168 | # undef DOIT |
| 162 | continue; | 169 | continue; |
| 163 | NonPrintable: | 170 | NonPrintable: |
| 164 | if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { | 171 | if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || |
| 172 | unlikely(*buf == '\177')) { | ||
| 165 | goto FOUND_CTL; | 173 | goto FOUND_CTL; |
| 166 | } | 174 | } |
| 167 | ++buf; | 175 | ++buf; |
| @@ -170,7 +178,8 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const | |||
| 170 | for (;; ++buf) { | 178 | for (;; ++buf) { |
| 171 | CHECK_EOF(); | 179 | CHECK_EOF(); |
| 172 | if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { | 180 | if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { |
| 173 | if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { | 181 | if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || |
| 182 | unlikely(*buf == '\177')) { | ||
| 174 | goto FOUND_CTL; | 183 | goto FOUND_CTL; |
| 175 | } | 184 | } |
| 176 | } | 185 | } |
| @@ -219,27 +228,28 @@ static const char *is_complete(const char *buf, const char *buf_end, size_t last | |||
| 219 | return NULL; | 228 | return NULL; |
| 220 | } | 229 | } |
| 221 | 230 | ||
| 222 | #define PARSE_INT(valp_, mul_) \ | 231 | #define PARSE_INT(valp_, mul_) \ |
| 223 | if (*buf < '0' || '9' < *buf) { \ | 232 | if (*buf < '0' || '9' < *buf) { \ |
| 224 | buf++; \ | 233 | buf++; \ |
| 225 | *ret = -1; \ | 234 | *ret = -1; \ |
| 226 | return NULL; \ | 235 | return NULL; \ |
| 227 | } \ | 236 | } \ |
| 228 | *(valp_) = (mul_) * (*buf++ - '0'); | 237 | *(valp_) = (mul_) * (*buf++ - '0'); |
| 229 | 238 | ||
| 230 | #define PARSE_INT_3(valp_) \ | 239 | #define PARSE_INT_3(valp_) \ |
| 231 | do { \ | 240 | do { \ |
| 232 | int res_ = 0; \ | 241 | int res_ = 0; \ |
| 233 | PARSE_INT(&res_, 100) \ | 242 | PARSE_INT(&res_, 100) \ |
| 234 | *valp_ = res_; \ | 243 | *valp_ = res_; \ |
| 235 | PARSE_INT(&res_, 10) \ | 244 | PARSE_INT(&res_, 10) \ |
| 236 | *valp_ += res_; \ | 245 | *valp_ += res_; \ |
| 237 | PARSE_INT(&res_, 1) \ | 246 | PARSE_INT(&res_, 1) \ |
| 238 | *valp_ += res_; \ | 247 | *valp_ += res_; \ |
| 239 | } while (0) | 248 | } while (0) |
| 240 | 249 | ||
| 241 | /* returned pointer is always within [buf, buf_end), or null */ | 250 | /* returned pointer is always within [buf, buf_end), or null */ |
| 242 | static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *ret) { | 251 | static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version, |
| 252 | int *minor_version, int *ret) { | ||
| 243 | /* we want at least [HTTP/1.<two chars>] to try to parse */ | 253 | /* we want at least [HTTP/1.<two chars>] to try to parse */ |
| 244 | if (buf_end - buf < 9) { | 254 | if (buf_end - buf < 9) { |
| 245 | *ret = -2; | 255 | *ret = -2; |
| @@ -260,8 +270,8 @@ static const char *parse_http_version(const char *buf, const char *buf_end, int | |||
| 260 | return buf; | 270 | return buf; |
| 261 | } | 271 | } |
| 262 | 272 | ||
| 263 | static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, size_t max_headers, | 273 | static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, |
| 264 | int *ret) { | 274 | size_t *num_headers, size_t max_headers, int *ret) { |
| 265 | for (;; ++*num_headers) { | 275 | for (;; ++*num_headers) { |
| 266 | CHECK_EOF(); | 276 | CHECK_EOF(); |
| 267 | if (*buf == '\015') { | 277 | if (*buf == '\015') { |
| @@ -337,9 +347,10 @@ static const char *parse_headers(const char *buf, const char *buf_end, struct ph | |||
| 337 | return buf; | 347 | return buf; |
| 338 | } | 348 | } |
| 339 | 349 | ||
| 340 | static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, | 350 | static const char *parse_request(const char *buf, const char *buf_end, const char **method, |
| 341 | size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, | 351 | size_t *method_len, const char **path, size_t *path_len, |
| 342 | size_t max_headers, int *ret) { | 352 | int *major_version, int *minor_version, struct phr_header *headers, |
| 353 | size_t *num_headers, size_t max_headers, int *ret) { | ||
| 343 | /* skip first empty line (some clients add CRLF after POST content) */ | 354 | /* skip first empty line (some clients add CRLF after POST content) */ |
| 344 | CHECK_EOF(); | 355 | CHECK_EOF(); |
| 345 | if (*buf == '\015') { | 356 | if (*buf == '\015') { |
| @@ -378,8 +389,9 @@ static const char *parse_request(const char *buf, const char *buf_end, const cha | |||
| 378 | return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); | 389 | return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); |
| 379 | } | 390 | } |
| 380 | 391 | ||
| 381 | int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, | 392 | int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, |
| 382 | int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) { | 393 | const char **path, size_t *path_len, int *major_version, int *minor_version, |
| 394 | struct phr_header *headers, size_t *num_headers, size_t last_len) { | ||
| 383 | const char *buf = buf_start, *buf_end = buf_start + len; | 395 | const char *buf = buf_start, *buf_end = buf_start + len; |
| 384 | size_t max_headers = *num_headers; | 396 | size_t max_headers = *num_headers; |
| 385 | int r; | 397 | int r; |
| @@ -398,17 +410,18 @@ int phr_parse_request(const char *buf_start, size_t len, const char **method, si | |||
| 398 | return r; | 410 | return r; |
| 399 | } | 411 | } |
| 400 | 412 | ||
| 401 | if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version, minor_version, headers, num_headers, | 413 | if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version, |
| 402 | max_headers, &r)) == NULL) { | 414 | minor_version, headers, num_headers, max_headers, &r)) == NULL) { |
| 403 | return r; | 415 | return r; |
| 404 | } | 416 | } |
| 405 | 417 | ||
| 406 | return (int)(buf - buf_start); | 418 | return (int)(buf - buf_start); |
| 407 | } | 419 | } |
| 408 | 420 | ||
| 409 | static const char *parse_response(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *status, | 421 | static const char *parse_response(const char *buf, const char *buf_end, int *major_version, |
| 410 | const char **msg, size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, | 422 | int *minor_version, int *status, const char **msg, |
| 411 | int *ret) { | 423 | size_t *msg_len, struct phr_header *headers, size_t *num_headers, |
| 424 | size_t max_headers, int *ret) { | ||
| 412 | /* parse "HTTP/1.x" */ | 425 | /* parse "HTTP/1.x" */ |
| 413 | if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { | 426 | if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { |
| 414 | return NULL; | 427 | return NULL; |
| @@ -421,7 +434,8 @@ static const char *parse_response(const char *buf, const char *buf_end, int *maj | |||
| 421 | do { | 434 | do { |
| 422 | ++buf; | 435 | ++buf; |
| 423 | } while (*buf == ' '); | 436 | } while (*buf == ' '); |
| 424 | /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */ | 437 | /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse |
| 438 | */ | ||
| 425 | if (buf_end - buf < 4) { | 439 | if (buf_end - buf < 4) { |
| 426 | *ret = -2; | 440 | *ret = -2; |
| 427 | return NULL; | 441 | return NULL; |
| @@ -449,8 +463,9 @@ static const char *parse_response(const char *buf, const char *buf_end, int *maj | |||
| 449 | return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); | 463 | return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); |
| 450 | } | 464 | } |
| 451 | 465 | ||
| 452 | int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version, int *status, const char **msg, | 466 | int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version, |
| 453 | size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t last_len) { | 467 | int *status, const char **msg, size_t *msg_len, struct phr_header *headers, |
| 468 | size_t *num_headers, size_t last_len) { | ||
| 454 | const char *buf = buf_start, *buf_end = buf + len; | 469 | const char *buf = buf_start, *buf_end = buf + len; |
| 455 | size_t max_headers = *num_headers; | 470 | size_t max_headers = *num_headers; |
| 456 | int r; | 471 | int r; |
| @@ -468,15 +483,16 @@ int phr_parse_response(const char *buf_start, size_t len, int *major_version, in | |||
| 468 | return r; | 483 | return r; |
| 469 | } | 484 | } |
| 470 | 485 | ||
| 471 | if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == | 486 | if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len, |
| 472 | NULL) { | 487 | headers, num_headers, max_headers, &r)) == NULL) { |
| 473 | return r; | 488 | return r; |
| 474 | } | 489 | } |
| 475 | 490 | ||
| 476 | return (int)(buf - buf_start); | 491 | return (int)(buf - buf_start); |
| 477 | } | 492 | } |
| 478 | 493 | ||
| 479 | int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) { | 494 | int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, |
| 495 | size_t *num_headers, size_t last_len) { | ||
| 480 | const char *buf = buf_start, *buf_end = buf + len; | 496 | const char *buf = buf_start, *buf_end = buf + len; |
| 481 | size_t max_headers = *num_headers; | 497 | size_t max_headers = *num_headers; |
| 482 | int r; | 498 | int r; |
| @@ -526,8 +542,9 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_ | |||
| 526 | case CHUNKED_IN_CHUNK_SIZE: | 542 | case CHUNKED_IN_CHUNK_SIZE: |
| 527 | for (;; ++src) { | 543 | for (;; ++src) { |
| 528 | int v; | 544 | int v; |
| 529 | if (src == bufsz) | 545 | if (src == bufsz) { |
| 530 | goto Exit; | 546 | goto Exit; |
| 547 | } | ||
| 531 | if ((v = decode_hex(buf[src])) == -1) { | 548 | if ((v = decode_hex(buf[src])) == -1) { |
| 532 | if (decoder->_hex_count == 0) { | 549 | if (decoder->_hex_count == 0) { |
| 533 | ret = -1; | 550 | ret = -1; |
| @@ -548,10 +565,12 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_ | |||
| 548 | case CHUNKED_IN_CHUNK_EXT: | 565 | case CHUNKED_IN_CHUNK_EXT: |
| 549 | /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ | 566 | /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ |
| 550 | for (;; ++src) { | 567 | for (;; ++src) { |
| 551 | if (src == bufsz) | 568 | if (src == bufsz) { |
| 552 | goto Exit; | 569 | goto Exit; |
| 553 | if (buf[src] == '\012') | 570 | } |
| 571 | if (buf[src] == '\012') { | ||
| 554 | break; | 572 | break; |
| 573 | } | ||
| 555 | } | 574 | } |
| 556 | ++src; | 575 | ++src; |
| 557 | if (decoder->bytes_left_in_chunk == 0) { | 576 | if (decoder->bytes_left_in_chunk == 0) { |
| @@ -567,15 +586,17 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_ | |||
| 567 | case CHUNKED_IN_CHUNK_DATA: { | 586 | case CHUNKED_IN_CHUNK_DATA: { |
| 568 | size_t avail = bufsz - src; | 587 | size_t avail = bufsz - src; |
| 569 | if (avail < decoder->bytes_left_in_chunk) { | 588 | if (avail < decoder->bytes_left_in_chunk) { |
| 570 | if (dst != src) | 589 | if (dst != src) { |
| 571 | memmove(buf + dst, buf + src, avail); | 590 | memmove(buf + dst, buf + src, avail); |
| 591 | } | ||
| 572 | src += avail; | 592 | src += avail; |
| 573 | dst += avail; | 593 | dst += avail; |
| 574 | decoder->bytes_left_in_chunk -= avail; | 594 | decoder->bytes_left_in_chunk -= avail; |
| 575 | goto Exit; | 595 | goto Exit; |
| 576 | } | 596 | } |
| 577 | if (dst != src) | 597 | if (dst != src) { |
| 578 | memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); | 598 | memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); |
| 599 | } | ||
| 579 | src += decoder->bytes_left_in_chunk; | 600 | src += decoder->bytes_left_in_chunk; |
| 580 | dst += decoder->bytes_left_in_chunk; | 601 | dst += decoder->bytes_left_in_chunk; |
| 581 | decoder->bytes_left_in_chunk = 0; | 602 | decoder->bytes_left_in_chunk = 0; |
| @@ -584,10 +605,12 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_ | |||
| 584 | /* fallthru */ | 605 | /* fallthru */ |
| 585 | case CHUNKED_IN_CHUNK_CRLF: | 606 | case CHUNKED_IN_CHUNK_CRLF: |
| 586 | for (;; ++src) { | 607 | for (;; ++src) { |
| 587 | if (src == bufsz) | 608 | if (src == bufsz) { |
| 588 | goto Exit; | 609 | goto Exit; |
| 589 | if (buf[src] != '\015') | 610 | } |
| 611 | if (buf[src] != '\015') { | ||
| 590 | break; | 612 | break; |
| 613 | } | ||
| 591 | } | 614 | } |
| 592 | if (buf[src] != '\012') { | 615 | if (buf[src] != '\012') { |
| 593 | ret = -1; | 616 | ret = -1; |
| @@ -598,21 +621,26 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_ | |||
| 598 | break; | 621 | break; |
| 599 | case CHUNKED_IN_TRAILERS_LINE_HEAD: | 622 | case CHUNKED_IN_TRAILERS_LINE_HEAD: |
| 600 | for (;; ++src) { | 623 | for (;; ++src) { |
| 601 | if (src == bufsz) | 624 | if (src == bufsz) { |
| 602 | goto Exit; | 625 | goto Exit; |
| 603 | if (buf[src] != '\015') | 626 | } |
| 627 | if (buf[src] != '\015') { | ||
| 604 | break; | 628 | break; |
| 629 | } | ||
| 605 | } | 630 | } |
| 606 | if (buf[src++] == '\012') | 631 | if (buf[src++] == '\012') { |
| 607 | goto Complete; | 632 | goto Complete; |
| 633 | } | ||
| 608 | decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; | 634 | decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; |
| 609 | /* fallthru */ | 635 | /* fallthru */ |
| 610 | case CHUNKED_IN_TRAILERS_LINE_MIDDLE: | 636 | case CHUNKED_IN_TRAILERS_LINE_MIDDLE: |
| 611 | for (;; ++src) { | 637 | for (;; ++src) { |
| 612 | if (src == bufsz) | 638 | if (src == bufsz) { |
| 613 | goto Exit; | 639 | goto Exit; |
| 614 | if (buf[src] == '\012') | 640 | } |
| 641 | if (buf[src] == '\012') { | ||
| 615 | break; | 642 | break; |
| 643 | } | ||
| 616 | } | 644 | } |
| 617 | ++src; | 645 | ++src; |
| 618 | decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; | 646 | decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; |
| @@ -625,13 +653,16 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_ | |||
| 625 | Complete: | 653 | Complete: |
| 626 | ret = bufsz - src; | 654 | ret = bufsz - src; |
| 627 | Exit: | 655 | Exit: |
| 628 | if (dst != src) | 656 | if (dst != src) { |
| 629 | memmove(buf + dst, buf + src, bufsz - src); | 657 | memmove(buf + dst, buf + src, bufsz - src); |
| 658 | } | ||
| 630 | *_bufsz = dst; | 659 | *_bufsz = dst; |
| 631 | return ret; | 660 | return ret; |
| 632 | } | 661 | } |
| 633 | 662 | ||
| 634 | int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) { return decoder->_state == CHUNKED_IN_CHUNK_DATA; } | 663 | int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) { |
| 664 | return decoder->_state == CHUNKED_IN_CHUNK_DATA; | ||
| 665 | } | ||
| 635 | 666 | ||
| 636 | #undef CHECK_EOF | 667 | #undef CHECK_EOF |
| 637 | #undef EXPECT_CHAR | 668 | #undef EXPECT_CHAR |
diff --git a/plugins/picohttpparser/picohttpparser.h b/plugins/picohttpparser/picohttpparser.h index 8f13b36f..054c2812 100644 --- a/plugins/picohttpparser/picohttpparser.h +++ b/plugins/picohttpparser/picohttpparser.h | |||
| @@ -30,7 +30,7 @@ | |||
| 30 | #include <sys/types.h> | 30 | #include <sys/types.h> |
| 31 | 31 | ||
| 32 | #ifdef _MSC_VER | 32 | #ifdef _MSC_VER |
| 33 | #define ssize_t intptr_t | 33 | # define ssize_t intptr_t |
| 34 | #endif | 34 | #endif |
| 35 | 35 | ||
| 36 | #ifdef __cplusplus | 36 | #ifdef __cplusplus |
| @@ -40,30 +40,33 @@ extern "C" { | |||
| 40 | /* contains name and value of a header (name == NULL if is a continuing line | 40 | /* contains name and value of a header (name == NULL if is a continuing line |
| 41 | * of a multiline header */ | 41 | * of a multiline header */ |
| 42 | struct phr_header { | 42 | struct phr_header { |
| 43 | const char *name; | 43 | const char *name; |
| 44 | size_t name_len; | 44 | size_t name_len; |
| 45 | const char *value; | 45 | const char *value; |
| 46 | size_t value_len; | 46 | size_t value_len; |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | /* returns number of bytes consumed if successful, -2 if request is partial, | 49 | /* returns number of bytes consumed if successful, -2 if request is partial, |
| 50 | * -1 if failed */ | 50 | * -1 if failed */ |
| 51 | int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, | 51 | int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, |
| 52 | int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); | 52 | const char **path, size_t *path_len, int *major_version, int *minor_version, |
| 53 | struct phr_header *headers, size_t *num_headers, size_t last_len); | ||
| 53 | 54 | ||
| 54 | /* ditto */ | 55 | /* ditto */ |
| 55 | int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, | 56 | int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version, |
| 56 | struct phr_header *headers, size_t *num_headers, size_t last_len); | 57 | int *status, const char **msg, size_t *msg_len, struct phr_header *headers, |
| 58 | size_t *num_headers, size_t last_len); | ||
| 57 | 59 | ||
| 58 | /* ditto */ | 60 | /* ditto */ |
| 59 | int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); | 61 | int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, |
| 62 | size_t last_len); | ||
| 60 | 63 | ||
| 61 | /* should be zero-filled before start */ | 64 | /* should be zero-filled before start */ |
| 62 | struct phr_chunked_decoder { | 65 | struct phr_chunked_decoder { |
| 63 | size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ | 66 | size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ |
| 64 | char consume_trailer; /* if trailing headers should be consumed */ | 67 | char consume_trailer; /* if trailing headers should be consumed */ |
| 65 | char _hex_count; | 68 | char _hex_count; |
| 66 | char _state; | 69 | char _state; |
| 67 | }; | 70 | }; |
| 68 | 71 | ||
| 69 | /* the function rewrites the buffer given as (buf, bufsz) removing the chunked- | 72 | /* the function rewrites the buffer given as (buf, bufsz) removing the chunked- |
diff --git a/plugins/popen.c b/plugins/popen.c index 2b9824bc..c596d1e0 100644 --- a/plugins/popen.c +++ b/plugins/popen.c | |||
| @@ -40,7 +40,6 @@ | |||
| 40 | 40 | ||
| 41 | #include "./common.h" | 41 | #include "./common.h" |
| 42 | #include "./utils.h" | 42 | #include "./utils.h" |
| 43 | #include "../lib/maxfd.h" | ||
| 44 | 43 | ||
| 45 | /* extern so plugin has pid to kill exec'd process on timeouts */ | 44 | /* extern so plugin has pid to kill exec'd process on timeouts */ |
| 46 | extern pid_t *childpid; | 45 | extern pid_t *childpid; |
| @@ -69,7 +68,7 @@ void popen_timeout_alarm_handler(int /*signo*/); | |||
| 69 | #endif | 68 | #endif |
| 70 | 69 | ||
| 71 | #ifndef WIFEXITED | 70 | #ifndef WIFEXITED |
| 72 | # define WIFEXITED(stat_val) (((stat_val)&255) == 0) | 71 | # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) |
| 73 | #endif | 72 | #endif |
| 74 | 73 | ||
| 75 | /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ | 74 | /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ |
| @@ -97,24 +96,28 @@ FILE *spopen(const char *cmdstring) { | |||
| 97 | env[1] = NULL; | 96 | env[1] = NULL; |
| 98 | 97 | ||
| 99 | /* if no command was passed, return with no error */ | 98 | /* if no command was passed, return with no error */ |
| 100 | if (cmdstring == NULL) | 99 | if (cmdstring == NULL) { |
| 101 | return (NULL); | 100 | return (NULL); |
| 101 | } | ||
| 102 | 102 | ||
| 103 | char *cmd = NULL; | 103 | char *cmd = NULL; |
| 104 | /* make copy of command string so strtok() doesn't silently modify it */ | 104 | /* make copy of command string so strtok() doesn't silently modify it */ |
| 105 | /* (the calling program may want to access it later) */ | 105 | /* (the calling program may want to access it later) */ |
| 106 | cmd = malloc(strlen(cmdstring) + 1); | 106 | cmd = malloc(strlen(cmdstring) + 1); |
| 107 | if (cmd == NULL) | 107 | if (cmd == NULL) { |
| 108 | return NULL; | 108 | return NULL; |
| 109 | } | ||
| 109 | strcpy(cmd, cmdstring); | 110 | strcpy(cmd, cmdstring); |
| 110 | 111 | ||
| 111 | /* This is not a shell, so we don't handle "???" */ | 112 | /* This is not a shell, so we don't handle "???" */ |
| 112 | if (strstr(cmdstring, "\"")) | 113 | if (strstr(cmdstring, "\"")) { |
| 113 | return NULL; | 114 | return NULL; |
| 115 | } | ||
| 114 | 116 | ||
| 115 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ | 117 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ |
| 116 | if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) | 118 | if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) { |
| 117 | return NULL; | 119 | return NULL; |
| 120 | } | ||
| 118 | 121 | ||
| 119 | int argc; | 122 | int argc; |
| 120 | char **argv = NULL; | 123 | char **argv = NULL; |
| @@ -141,15 +144,17 @@ FILE *spopen(const char *cmdstring) { | |||
| 141 | 144 | ||
| 142 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ | 145 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ |
| 143 | str++; | 146 | str++; |
| 144 | if (!strstr(str, "'")) | 147 | if (!strstr(str, "'")) { |
| 145 | return NULL; /* balanced? */ | 148 | return NULL; /* balanced? */ |
| 149 | } | ||
| 146 | cmd = 1 + strstr(str, "'"); | 150 | cmd = 1 + strstr(str, "'"); |
| 147 | str[strcspn(str, "'")] = 0; | 151 | str[strcspn(str, "'")] = 0; |
| 148 | } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) { | 152 | } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) { |
| 149 | /* handle --option='foo bar' strings */ | 153 | /* handle --option='foo bar' strings */ |
| 150 | char *tmp = str + strcspn(str, "'") + 1; | 154 | char *tmp = str + strcspn(str, "'") + 1; |
| 151 | if (!strstr(tmp, "'")) | 155 | if (!strstr(tmp, "'")) { |
| 152 | return NULL; /* balanced? */ | 156 | return NULL; /* balanced? */ |
| 157 | } | ||
| 153 | tmp += strcspn(tmp, "'") + 1; | 158 | tmp += strcspn(tmp, "'") + 1; |
| 154 | *tmp = 0; | 159 | *tmp = 0; |
| 155 | cmd = tmp + 1; | 160 | cmd = tmp + 1; |
| @@ -162,8 +167,9 @@ FILE *spopen(const char *cmdstring) { | |||
| 162 | } | 167 | } |
| 163 | } | 168 | } |
| 164 | 169 | ||
| 165 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) | 170 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) { |
| 166 | cmd = NULL; | 171 | cmd = NULL; |
| 172 | } | ||
| 167 | 173 | ||
| 168 | argv[i++] = str; | 174 | argv[i++] = str; |
| 169 | } | 175 | } |
| @@ -172,22 +178,26 @@ FILE *spopen(const char *cmdstring) { | |||
| 172 | long maxfd = mp_open_max(); | 178 | long maxfd = mp_open_max(); |
| 173 | 179 | ||
| 174 | if (childpid == NULL) { /* first time through */ | 180 | if (childpid == NULL) { /* first time through */ |
| 175 | if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) | 181 | if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) { |
| 176 | return (NULL); | 182 | return (NULL); |
| 183 | } | ||
| 177 | } | 184 | } |
| 178 | 185 | ||
| 179 | if (child_stderr_array == NULL) { /* first time through */ | 186 | if (child_stderr_array == NULL) { /* first time through */ |
| 180 | if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) | 187 | if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) { |
| 181 | return (NULL); | 188 | return (NULL); |
| 189 | } | ||
| 182 | } | 190 | } |
| 183 | 191 | ||
| 184 | int pfd[2]; | 192 | int pfd[2]; |
| 185 | if (pipe(pfd) < 0) | 193 | if (pipe(pfd) < 0) { |
| 186 | return (NULL); /* errno set by pipe() */ | 194 | return (NULL); /* errno set by pipe() */ |
| 195 | } | ||
| 187 | 196 | ||
| 188 | int pfderr[2]; | 197 | int pfderr[2]; |
| 189 | if (pipe(pfderr) < 0) | 198 | if (pipe(pfderr) < 0) { |
| 190 | return (NULL); /* errno set by pipe() */ | 199 | return (NULL); /* errno set by pipe() */ |
| 200 | } | ||
| 191 | 201 | ||
| 192 | #ifdef REDHAT_SPOPEN_ERROR | 202 | #ifdef REDHAT_SPOPEN_ERROR |
| 193 | if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) { | 203 | if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) { |
| @@ -196,8 +206,9 @@ FILE *spopen(const char *cmdstring) { | |||
| 196 | #endif | 206 | #endif |
| 197 | 207 | ||
| 198 | pid_t pid; | 208 | pid_t pid; |
| 199 | if ((pid = fork()) < 0) | 209 | if ((pid = fork()) < 0) { |
| 200 | return (NULL); /* errno set by fork() */ | 210 | return (NULL); /* errno set by fork() */ |
| 211 | } | ||
| 201 | 212 | ||
| 202 | if (pid == 0) { /* child */ | 213 | if (pid == 0) { /* child */ |
| 203 | close(pfd[0]); | 214 | close(pfd[0]); |
| @@ -211,17 +222,20 @@ FILE *spopen(const char *cmdstring) { | |||
| 211 | close(pfderr[1]); | 222 | close(pfderr[1]); |
| 212 | } | 223 | } |
| 213 | /* close all descriptors in childpid[] */ | 224 | /* close all descriptors in childpid[] */ |
| 214 | for (i = 0; i < maxfd; i++) | 225 | for (i = 0; i < maxfd; i++) { |
| 215 | if (childpid[i] > 0) | 226 | if (childpid[i] > 0) { |
| 216 | close(i); | 227 | close(i); |
| 228 | } | ||
| 229 | } | ||
| 217 | 230 | ||
| 218 | execve(argv[0], argv, env); | 231 | execve(argv[0], argv, env); |
| 219 | _exit(0); | 232 | _exit(0); |
| 220 | } | 233 | } |
| 221 | 234 | ||
| 222 | close(pfd[1]); /* parent */ | 235 | close(pfd[1]); /* parent */ |
| 223 | if ((child_process = fdopen(pfd[0], "r")) == NULL) | 236 | if ((child_process = fdopen(pfd[0], "r")) == NULL) { |
| 224 | return (NULL); | 237 | return (NULL); |
| 238 | } | ||
| 225 | close(pfderr[1]); | 239 | close(pfderr[1]); |
| 226 | 240 | ||
| 227 | childpid[fileno(child_process)] = pid; /* remember child pid for this fd */ | 241 | childpid[fileno(child_process)] = pid; /* remember child pid for this fd */ |
| @@ -230,17 +244,20 @@ FILE *spopen(const char *cmdstring) { | |||
| 230 | } | 244 | } |
| 231 | 245 | ||
| 232 | int spclose(FILE *fp) { | 246 | int spclose(FILE *fp) { |
| 233 | if (childpid == NULL) | 247 | if (childpid == NULL) { |
| 234 | return (1); /* popen() has never been called */ | 248 | return (1); /* popen() has never been called */ |
| 249 | } | ||
| 235 | 250 | ||
| 236 | pid_t pid; | 251 | pid_t pid; |
| 237 | int fd = fileno(fp); | 252 | int fd = fileno(fp); |
| 238 | if ((pid = childpid[fd]) == 0) | 253 | if ((pid = childpid[fd]) == 0) { |
| 239 | return (1); /* fp wasn't opened by popen() */ | 254 | return (1); /* fp wasn't opened by popen() */ |
| 255 | } | ||
| 240 | 256 | ||
| 241 | childpid[fd] = 0; | 257 | childpid[fd] = 0; |
| 242 | if (fclose(fp) == EOF) | 258 | if (fclose(fp) == EOF) { |
| 243 | return (1); | 259 | return (1); |
| 260 | } | ||
| 244 | 261 | ||
| 245 | #ifdef REDHAT_SPOPEN_ERROR | 262 | #ifdef REDHAT_SPOPEN_ERROR |
| 246 | while (!childtermd) | 263 | while (!childtermd) |
| @@ -248,20 +265,24 @@ int spclose(FILE *fp) { | |||
| 248 | #endif | 265 | #endif |
| 249 | 266 | ||
| 250 | int status; | 267 | int status; |
| 251 | while (waitpid(pid, &status, 0) < 0) | 268 | while (waitpid(pid, &status, 0) < 0) { |
| 252 | if (errno != EINTR) | 269 | if (errno != EINTR) { |
| 253 | return (1); /* error other than EINTR from waitpid() */ | 270 | return (1); /* error other than EINTR from waitpid() */ |
| 271 | } | ||
| 272 | } | ||
| 254 | 273 | ||
| 255 | if (WIFEXITED(status)) | 274 | if (WIFEXITED(status)) { |
| 256 | return (WEXITSTATUS(status)); /* return child's termination status */ | 275 | return (WEXITSTATUS(status)); /* return child's termination status */ |
| 276 | } | ||
| 257 | 277 | ||
| 258 | return (1); | 278 | return (1); |
| 259 | } | 279 | } |
| 260 | 280 | ||
| 261 | #ifdef REDHAT_SPOPEN_ERROR | 281 | #ifdef REDHAT_SPOPEN_ERROR |
| 262 | void popen_sigchld_handler(int signo) { | 282 | void popen_sigchld_handler(int signo) { |
| 263 | if (signo == SIGCHLD) | 283 | if (signo == SIGCHLD) { |
| 264 | childtermd = 1; | 284 | childtermd = 1; |
| 285 | } | ||
| 265 | } | 286 | } |
| 266 | #endif | 287 | #endif |
| 267 | 288 | ||
diff --git a/plugins/popen.h b/plugins/popen.h index 1ea69632..e318ce25 100644 --- a/plugins/popen.h +++ b/plugins/popen.h | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | /****************************************************************************** | 1 | /****************************************************************************** |
| 2 | * | 2 | * |
| 3 | * | 3 | * |
| 4 | *****************************************************************************/ | 4 | *****************************************************************************/ |
| 5 | 5 | ||
| 6 | FILE *spopen (const char *); | 6 | FILE *spopen(const char *); |
| 7 | int spclose (FILE *); | 7 | int spclose(FILE *); |
| 8 | void popen_timeout_alarm_handler (int); | 8 | void popen_timeout_alarm_handler(int); |
| 9 | 9 | ||
| 10 | pid_t *childpid=NULL; | 10 | pid_t *childpid = NULL; |
| 11 | int *child_stderr_array=NULL; | 11 | int *child_stderr_array = NULL; |
| 12 | FILE *child_process=NULL; | 12 | FILE *child_process = NULL; |
| 13 | FILE *child_stderr=NULL; | 13 | FILE *child_stderr = NULL; |
diff --git a/plugins/runcmd.c b/plugins/runcmd.c index 74843149..be6691d2 100644 --- a/plugins/runcmd.c +++ b/plugins/runcmd.c | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | 40 | ||
| 41 | /** includes **/ | 41 | /** includes **/ |
| 42 | #include "runcmd.h" | 42 | #include "runcmd.h" |
| 43 | #include "../lib/monitoringplug.h" | ||
| 43 | #ifdef HAVE_SYS_WAIT_H | 44 | #ifdef HAVE_SYS_WAIT_H |
| 44 | # include <sys/wait.h> | 45 | # include <sys/wait.h> |
| 45 | #endif | 46 | #endif |
| @@ -52,7 +53,7 @@ | |||
| 52 | #endif | 53 | #endif |
| 53 | 54 | ||
| 54 | #ifndef WIFEXITED | 55 | #ifndef WIFEXITED |
| 55 | # define WIFEXITED(stat_val) (((stat_val)&255) == 0) | 56 | # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) |
| 56 | #endif | 57 | #endif |
| 57 | 58 | ||
| 58 | /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ | 59 | /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ |
| @@ -86,8 +87,9 @@ extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__( | |||
| 86 | * through this api and thus achieve async-safeness throughout the api */ | 87 | * through this api and thus achieve async-safeness throughout the api */ |
| 87 | void np_runcmd_init(void) { | 88 | void np_runcmd_init(void) { |
| 88 | long maxfd = mp_open_max(); | 89 | long maxfd = mp_open_max(); |
| 89 | if (!np_pids) | 90 | if (!np_pids) { |
| 90 | np_pids = calloc(maxfd, sizeof(pid_t)); | 91 | np_pids = calloc(maxfd, sizeof(pid_t)); |
| 92 | } | ||
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | /* Start running a command */ | 95 | /* Start running a command */ |
| @@ -105,8 +107,9 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) { | |||
| 105 | 107 | ||
| 106 | int i = 0; | 108 | int i = 0; |
| 107 | 109 | ||
| 108 | if (!np_pids) | 110 | if (!np_pids) { |
| 109 | NP_RUNCMD_INIT; | 111 | NP_RUNCMD_INIT; |
| 112 | } | ||
| 110 | 113 | ||
| 111 | env[0] = strdup("LC_ALL=C"); | 114 | env[0] = strdup("LC_ALL=C"); |
| 112 | env[1] = NULL; | 115 | env[1] = NULL; |
| @@ -114,18 +117,21 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) { | |||
| 114 | /* make copy of command string so strtok() doesn't silently modify it */ | 117 | /* make copy of command string so strtok() doesn't silently modify it */ |
| 115 | /* (the calling program may want to access it later) */ | 118 | /* (the calling program may want to access it later) */ |
| 116 | cmdlen = strlen(cmdstring); | 119 | cmdlen = strlen(cmdstring); |
| 117 | if ((cmd = malloc(cmdlen + 1)) == NULL) | 120 | if ((cmd = malloc(cmdlen + 1)) == NULL) { |
| 118 | return -1; | 121 | return -1; |
| 122 | } | ||
| 119 | memcpy(cmd, cmdstring, cmdlen); | 123 | memcpy(cmd, cmdstring, cmdlen); |
| 120 | cmd[cmdlen] = '\0'; | 124 | cmd[cmdlen] = '\0'; |
| 121 | 125 | ||
| 122 | /* This is not a shell, so we don't handle "???" */ | 126 | /* This is not a shell, so we don't handle "???" */ |
| 123 | if (strstr(cmdstring, "\"")) | 127 | if (strstr(cmdstring, "\"")) { |
| 124 | return -1; | 128 | return -1; |
| 129 | } | ||
| 125 | 130 | ||
| 126 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ | 131 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ |
| 127 | if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) | 132 | if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) { |
| 128 | return -1; | 133 | return -1; |
| 134 | } | ||
| 129 | 135 | ||
| 130 | /* each arg must be whitespace-separated, so args can be a maximum | 136 | /* each arg must be whitespace-separated, so args can be a maximum |
| 131 | * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ | 137 | * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ |
| @@ -144,8 +150,9 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) { | |||
| 144 | 150 | ||
| 145 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ | 151 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ |
| 146 | str++; | 152 | str++; |
| 147 | if (!strstr(str, "'")) | 153 | if (!strstr(str, "'")) { |
| 148 | return -1; /* balanced? */ | 154 | return -1; /* balanced? */ |
| 155 | } | ||
| 149 | cmd = 1 + strstr(str, "'"); | 156 | cmd = 1 + strstr(str, "'"); |
| 150 | str[strcspn(str, "'")] = 0; | 157 | str[strcspn(str, "'")] = 0; |
| 151 | } else { | 158 | } else { |
| @@ -157,14 +164,16 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) { | |||
| 157 | } | 164 | } |
| 158 | } | 165 | } |
| 159 | 166 | ||
| 160 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) | 167 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) { |
| 161 | cmd = NULL; | 168 | cmd = NULL; |
| 169 | } | ||
| 162 | 170 | ||
| 163 | argv[i++] = str; | 171 | argv[i++] = str; |
| 164 | } | 172 | } |
| 165 | 173 | ||
| 166 | if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) | 174 | if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) { |
| 167 | return -1; /* errno set by the failing function */ | 175 | return -1; /* errno set by the failing function */ |
| 176 | } | ||
| 168 | 177 | ||
| 169 | /* child runs exceve() and _exit. */ | 178 | /* child runs exceve() and _exit. */ |
| 170 | if (pid == 0) { | 179 | if (pid == 0) { |
| @@ -189,9 +198,11 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) { | |||
| 189 | * This is executed in a separate address space (pure child), | 198 | * This is executed in a separate address space (pure child), |
| 190 | * so we don't have to worry about async safety */ | 199 | * so we don't have to worry about async safety */ |
| 191 | long maxfd = mp_open_max(); | 200 | long maxfd = mp_open_max(); |
| 192 | for (i = 0; i < maxfd; i++) | 201 | for (i = 0; i < maxfd; i++) { |
| 193 | if (np_pids[i] > 0) | 202 | if (np_pids[i] > 0) { |
| 194 | close(i); | 203 | close(i); |
| 204 | } | ||
| 205 | } | ||
| 195 | 206 | ||
| 196 | execve(argv[0], argv, env); | 207 | execve(argv[0], argv, env); |
| 197 | _exit(STATE_UNKNOWN); | 208 | _exit(STATE_UNKNOWN); |
| @@ -214,17 +225,21 @@ static int np_runcmd_close(int fd) { | |||
| 214 | 225 | ||
| 215 | /* make sure this fd was opened by popen() */ | 226 | /* make sure this fd was opened by popen() */ |
| 216 | long maxfd = mp_open_max(); | 227 | long maxfd = mp_open_max(); |
| 217 | if (fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) | 228 | if (fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) { |
| 218 | return -1; | 229 | return -1; |
| 230 | } | ||
| 219 | 231 | ||
| 220 | np_pids[fd] = 0; | 232 | np_pids[fd] = 0; |
| 221 | if (close(fd) == -1) | 233 | if (close(fd) == -1) { |
| 222 | return -1; | 234 | return -1; |
| 235 | } | ||
| 223 | 236 | ||
| 224 | /* EINTR is ok (sort of), everything else is bad */ | 237 | /* EINTR is ok (sort of), everything else is bad */ |
| 225 | while (waitpid(pid, &status, 0) < 0) | 238 | while (waitpid(pid, &status, 0) < 0) { |
| 226 | if (errno != EINTR) | 239 | if (errno != EINTR) { |
| 227 | return -1; | 240 | return -1; |
| 241 | } | ||
| 242 | } | ||
| 228 | 243 | ||
| 229 | /* return child's termination status */ | 244 | /* return child's termination status */ |
| 230 | return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; | 245 | return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; |
| @@ -232,15 +247,18 @@ static int np_runcmd_close(int fd) { | |||
| 232 | 247 | ||
| 233 | void runcmd_timeout_alarm_handler(int signo) { | 248 | void runcmd_timeout_alarm_handler(int signo) { |
| 234 | 249 | ||
| 235 | if (signo == SIGALRM) | 250 | if (signo == SIGALRM) { |
| 236 | puts(_("CRITICAL - Plugin timed out while executing system call")); | 251 | puts(_("CRITICAL - Plugin timed out while executing system call")); |
| 252 | } | ||
| 237 | 253 | ||
| 238 | long maxfd = mp_open_max(); | 254 | long maxfd = mp_open_max(); |
| 239 | if (np_pids) | 255 | if (np_pids) { |
| 240 | for (long int i = 0; i < maxfd; i++) { | 256 | for (long int i = 0; i < maxfd; i++) { |
| 241 | if (np_pids[i] != 0) | 257 | if (np_pids[i] != 0) { |
| 242 | kill(np_pids[i], SIGKILL); | 258 | kill(np_pids[i], SIGKILL); |
| 259 | } | ||
| 243 | } | 260 | } |
| 261 | } | ||
| 244 | 262 | ||
| 245 | exit(STATE_CRITICAL); | 263 | exit(STATE_CRITICAL); |
| 246 | } | 264 | } |
| @@ -269,18 +287,19 @@ static int np_fetch_output(int fd, output *op, int flags) { | |||
| 269 | 287 | ||
| 270 | /* some plugins may want to keep output unbroken, and some commands | 288 | /* some plugins may want to keep output unbroken, and some commands |
| 271 | * will yield no output, so return here for those */ | 289 | * will yield no output, so return here for those */ |
| 272 | if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) | 290 | if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) { |
| 273 | return op->buflen; | 291 | return op->buflen; |
| 292 | } | ||
| 274 | 293 | ||
| 275 | /* and some may want both */ | 294 | /* and some may want both */ |
| 276 | if (flags & RUNCMD_NO_ASSOC) { | 295 | if (flags & RUNCMD_NO_ASSOC) { |
| 277 | buf = malloc(op->buflen); | 296 | buf = malloc(op->buflen); |
| 278 | memcpy(buf, op->buf, op->buflen); | 297 | memcpy(buf, op->buf, op->buflen); |
| 279 | } else | 298 | } else { |
| 280 | buf = op->buf; | 299 | buf = op->buf; |
| 300 | } | ||
| 281 | 301 | ||
| 282 | op->line = NULL; | 302 | op->line = NULL; |
| 283 | op->lens = NULL; | ||
| 284 | i = 0; | 303 | i = 0; |
| 285 | while (i < op->buflen) { | 304 | while (i < op->buflen) { |
| 286 | /* make sure we have enough memory */ | 305 | /* make sure we have enough memory */ |
| @@ -291,20 +310,17 @@ static int np_fetch_output(int fd, output *op, int flags) { | |||
| 291 | } while (!ary_size); | 310 | } while (!ary_size); |
| 292 | 311 | ||
| 293 | op->line = realloc(op->line, ary_size * sizeof(char *)); | 312 | op->line = realloc(op->line, ary_size * sizeof(char *)); |
| 294 | op->lens = realloc(op->lens, ary_size * sizeof(size_t)); | ||
| 295 | } | 313 | } |
| 296 | 314 | ||
| 297 | /* set the pointer to the string */ | 315 | /* set the pointer to the string */ |
| 298 | op->line[lineno] = &buf[i]; | 316 | op->line[lineno] = &buf[i]; |
| 299 | 317 | ||
| 300 | /* hop to next newline or end of buffer */ | 318 | /* hop to next newline or end of buffer */ |
| 301 | while (buf[i] != '\n' && i < op->buflen) | 319 | while (buf[i] != '\n' && i < op->buflen) { |
| 302 | i++; | 320 | i++; |
| 321 | } | ||
| 303 | buf[i] = '\0'; | 322 | buf[i] = '\0'; |
| 304 | 323 | ||
| 305 | /* calculate the string length using pointer difference */ | ||
| 306 | op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno]; | ||
| 307 | |||
| 308 | lineno++; | 324 | lineno++; |
| 309 | i++; | 325 | i++; |
| 310 | } | 326 | } |
| @@ -316,18 +332,23 @@ int np_runcmd(const char *cmd, output *out, output *err, int flags) { | |||
| 316 | int fd, pfd_out[2], pfd_err[2]; | 332 | int fd, pfd_out[2], pfd_err[2]; |
| 317 | 333 | ||
| 318 | /* initialize the structs */ | 334 | /* initialize the structs */ |
| 319 | if (out) | 335 | if (out) { |
| 320 | memset(out, 0, sizeof(output)); | 336 | memset(out, 0, sizeof(output)); |
| 321 | if (err) | 337 | } |
| 338 | if (err) { | ||
| 322 | memset(err, 0, sizeof(output)); | 339 | memset(err, 0, sizeof(output)); |
| 340 | } | ||
| 323 | 341 | ||
| 324 | if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) | 342 | if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) { |
| 325 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); | 343 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); |
| 344 | } | ||
| 326 | 345 | ||
| 327 | if (out) | 346 | if (out) { |
| 328 | out->lines = np_fetch_output(pfd_out[0], out, flags); | 347 | out->lines = np_fetch_output(pfd_out[0], out, flags); |
| 329 | if (err) | 348 | } |
| 349 | if (err) { | ||
| 330 | err->lines = np_fetch_output(pfd_err[0], err, flags); | 350 | err->lines = np_fetch_output(pfd_err[0], err, flags); |
| 351 | } | ||
| 331 | 352 | ||
| 332 | return np_runcmd_close(fd); | 353 | return np_runcmd_close(fd); |
| 333 | } | 354 | } |
diff --git a/plugins/runcmd.h b/plugins/runcmd.h index 2dcdadf0..63ce7b12 100644 --- a/plugins/runcmd.h +++ b/plugins/runcmd.h | |||
| @@ -1,25 +1,25 @@ | |||
| 1 | /**************************************************************************** | 1 | /**************************************************************************** |
| 2 | * | 2 | * |
| 3 | * License: GPL | 3 | * License: GPL |
| 4 | * Copyright (c) 2005 Monitoring Plugins Development Team | 4 | * Copyright (c) 2005 Monitoring Plugins Development Team |
| 5 | * Author: Andreas Ericsson <ae@op5.se> | 5 | * Author: Andreas Ericsson <ae@op5.se> |
| 6 | * | 6 | * |
| 7 | * | 7 | * |
| 8 | * This program is free software: you can redistribute it and/or modify | 8 | * This program is free software: you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation, either version 3 of the License, or | 10 | * the Free Software Foundation, either version 3 of the License, or |
| 11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
| 12 | * | 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
| 17 | * | 17 | * |
| 18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
| 19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 20 | * | 20 | * |
| 21 | * | 21 | * |
| 22 | *****************************************************************************/ | 22 | *****************************************************************************/ |
| 23 | 23 | ||
| 24 | #ifndef NAGIOSPLUG_RUNCMD_H | 24 | #ifndef NAGIOSPLUG_RUNCMD_H |
| 25 | #define NAGIOSPLUG_RUNCMD_H | 25 | #define NAGIOSPLUG_RUNCMD_H |
| @@ -29,8 +29,7 @@ | |||
| 29 | 29 | ||
| 30 | /** prototypes **/ | 30 | /** prototypes **/ |
| 31 | int np_runcmd(const char *, output *, output *, int); | 31 | int np_runcmd(const char *, output *, output *, int); |
| 32 | void runcmd_timeout_alarm_handler(int) | 32 | void runcmd_timeout_alarm_handler(int) __attribute__((__noreturn__)); |
| 33 | __attribute__((__noreturn__)); | ||
| 34 | 33 | ||
| 35 | /* only multi-threaded plugins need to bother with this */ | 34 | /* only multi-threaded plugins need to bother with this */ |
| 36 | void np_runcmd_init(void); | 35 | void np_runcmd_init(void); |
| @@ -38,6 +37,6 @@ void np_runcmd_init(void); | |||
| 38 | 37 | ||
| 39 | /* possible flags for np_runcmd()'s fourth argument */ | 38 | /* possible flags for np_runcmd()'s fourth argument */ |
| 40 | #define RUNCMD_NO_ARRAYS 0x01 /* don't populate arrays at all */ | 39 | #define RUNCMD_NO_ARRAYS 0x01 /* don't populate arrays at all */ |
| 41 | #define RUNCMD_NO_ASSOC 0x02 /* output.line won't point to buf */ | 40 | #define RUNCMD_NO_ASSOC 0x02 /* output.line won't point to buf */ |
| 42 | 41 | ||
| 43 | #endif /* NAGIOSPLUG_RUNCMD_H */ | 42 | #endif /* NAGIOSPLUG_RUNCMD_H */ |
diff --git a/plugins/sslutils.c b/plugins/sslutils.c index 3c928413..c58a35ab 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c | |||
| @@ -26,9 +26,12 @@ | |||
| 26 | * | 26 | * |
| 27 | *****************************************************************************/ | 27 | *****************************************************************************/ |
| 28 | 28 | ||
| 29 | #include "output.h" | ||
| 29 | #define MAX_CN_LENGTH 256 | 30 | #define MAX_CN_LENGTH 256 |
| 30 | #include "common.h" | 31 | #include "common.h" |
| 31 | #include "netutils.h" | 32 | #include "netutils.h" |
| 33 | #include "../lib/monitoringplug.h" | ||
| 34 | #include "states.h" | ||
| 32 | 35 | ||
| 33 | #ifdef HAVE_SSL | 36 | #ifdef HAVE_SSL |
| 34 | static SSL_CTX *ctx = NULL; | 37 | static SSL_CTX *ctx = NULL; |
| @@ -36,13 +39,16 @@ static SSL *s = NULL; | |||
| 36 | 39 | ||
| 37 | int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); } | 40 | int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); } |
| 38 | 41 | ||
| 39 | int np_net_ssl_init_with_hostname(int sd, char *host_name) { return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); } | 42 | int np_net_ssl_init_with_hostname(int sd, char *host_name) { |
| 43 | return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); | ||
| 44 | } | ||
| 40 | 45 | ||
| 41 | int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) { | 46 | int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) { |
| 42 | return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL); | 47 | return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL); |
| 43 | } | 48 | } |
| 44 | 49 | ||
| 45 | int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey) { | 50 | int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, |
| 51 | char *privkey) { | ||
| 46 | long options = 0; | 52 | long options = 0; |
| 47 | 53 | ||
| 48 | if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { | 54 | if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { |
| @@ -74,7 +80,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int | |||
| 74 | # endif | 80 | # endif |
| 75 | case MP_TLSv1_1: /* TLSv1.1 protocol */ | 81 | case MP_TLSv1_1: /* TLSv1.1 protocol */ |
| 76 | # if !defined(SSL_OP_NO_TLSv1_1) | 82 | # if !defined(SSL_OP_NO_TLSv1_1) |
| 77 | printf("%s\n", _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library.")); | 83 | printf("%s\n", |
| 84 | _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library.")); | ||
| 78 | return STATE_UNKNOWN; | 85 | return STATE_UNKNOWN; |
| 79 | # else | 86 | # else |
| 80 | SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); | 87 | SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); |
| @@ -83,7 +90,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int | |||
| 83 | # endif | 90 | # endif |
| 84 | case MP_TLSv1_2: /* TLSv1.2 protocol */ | 91 | case MP_TLSv1_2: /* TLSv1.2 protocol */ |
| 85 | # if !defined(SSL_OP_NO_TLSv1_2) | 92 | # if !defined(SSL_OP_NO_TLSv1_2) |
| 86 | printf("%s\n", _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library.")); | 93 | printf("%s\n", |
| 94 | _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library.")); | ||
| 87 | return STATE_UNKNOWN; | 95 | return STATE_UNKNOWN; |
| 88 | # else | 96 | # else |
| 89 | SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); | 97 | SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); |
| @@ -144,8 +152,9 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int | |||
| 144 | SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); | 152 | SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); |
| 145 | if ((s = SSL_new(ctx)) != NULL) { | 153 | if ((s = SSL_new(ctx)) != NULL) { |
| 146 | # ifdef SSL_set_tlsext_host_name | 154 | # ifdef SSL_set_tlsext_host_name |
| 147 | if (host_name != NULL) | 155 | if (host_name != NULL) { |
| 148 | SSL_set_tlsext_host_name(s, host_name); | 156 | SSL_set_tlsext_host_name(s, host_name); |
| 157 | } | ||
| 149 | # endif | 158 | # endif |
| 150 | SSL_set_fd(s, sd); | 159 | SSL_set_fd(s, sd); |
| 151 | if (SSL_connect(s) == 1) { | 160 | if (SSL_connect(s) == 1) { |
| @@ -181,63 +190,54 @@ int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num); | |||
| 181 | 190 | ||
| 182 | int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); } | 191 | int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); } |
| 183 | 192 | ||
| 184 | int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit) { | 193 | mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, |
| 194 | int days_till_exp_crit) { | ||
| 185 | # ifdef USE_OPENSSL | 195 | # ifdef USE_OPENSSL |
| 186 | X509_NAME *subj = NULL; | ||
| 187 | char timestamp[50] = ""; | ||
| 188 | char cn[MAX_CN_LENGTH] = ""; | ||
| 189 | char *tz; | ||
| 190 | |||
| 191 | int cnlen = -1; | ||
| 192 | int status = STATE_UNKNOWN; | ||
| 193 | |||
| 194 | ASN1_STRING *tm; | ||
| 195 | int offset; | ||
| 196 | struct tm stamp; | ||
| 197 | float time_left; | ||
| 198 | int days_left; | ||
| 199 | int time_remaining; | ||
| 200 | time_t tm_t; | ||
| 201 | |||
| 202 | if (!certificate) { | 196 | if (!certificate) { |
| 203 | printf("%s\n", _("CRITICAL - Cannot retrieve server certificate.")); | 197 | printf("%s\n", _("CRITICAL - No server certificate present to inspect.")); |
| 204 | return STATE_CRITICAL; | 198 | return STATE_CRITICAL; |
| 205 | } | 199 | } |
| 206 | 200 | ||
| 207 | /* Extract CN from certificate subject */ | 201 | /* Extract CN from certificate subject */ |
| 208 | subj = X509_get_subject_name(certificate); | 202 | X509_NAME *subj = X509_get_subject_name(certificate); |
| 209 | 203 | ||
| 210 | if (!subj) { | 204 | if (!subj) { |
| 211 | printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); | 205 | printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); |
| 212 | return STATE_CRITICAL; | 206 | return STATE_CRITICAL; |
| 213 | } | 207 | } |
| 214 | cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); | 208 | |
| 215 | if (cnlen == -1) | 209 | char cn[MAX_CN_LENGTH] = ""; |
| 210 | int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); | ||
| 211 | if (cnlen == -1) { | ||
| 216 | strcpy(cn, _("Unknown CN")); | 212 | strcpy(cn, _("Unknown CN")); |
| 213 | } | ||
| 217 | 214 | ||
| 218 | /* Retrieve timestamp of certificate */ | 215 | /* Retrieve timestamp of certificate */ |
| 219 | tm = X509_get_notAfter(certificate); | 216 | ASN1_STRING *tm = X509_get_notAfter(certificate); |
| 220 | 217 | ||
| 218 | int offset = 0; | ||
| 219 | struct tm stamp = {}; | ||
| 221 | /* Generate tm structure to process timestamp */ | 220 | /* Generate tm structure to process timestamp */ |
| 222 | if (tm->type == V_ASN1_UTCTIME) { | 221 | if (tm->type == V_ASN1_UTCTIME) { |
| 223 | if (tm->length < 10) { | 222 | if (tm->length < 10) { |
| 224 | printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); | 223 | printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); |
| 225 | return STATE_CRITICAL; | 224 | return STATE_CRITICAL; |
| 226 | } else { | ||
| 227 | stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0'); | ||
| 228 | if (stamp.tm_year < 50) | ||
| 229 | stamp.tm_year += 100; | ||
| 230 | offset = 0; | ||
| 231 | } | 225 | } |
| 226 | stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0'); | ||
| 227 | if (stamp.tm_year < 50) { | ||
| 228 | stamp.tm_year += 100; | ||
| 229 | } | ||
| 230 | offset = 0; | ||
| 231 | |||
| 232 | } else { | 232 | } else { |
| 233 | if (tm->length < 12) { | 233 | if (tm->length < 12) { |
| 234 | printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); | 234 | printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); |
| 235 | return STATE_CRITICAL; | 235 | return STATE_CRITICAL; |
| 236 | } else { | ||
| 237 | stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + (tm->data[2] - '0') * 10 + (tm->data[3] - '0'); | ||
| 238 | stamp.tm_year -= 1900; | ||
| 239 | offset = 2; | ||
| 240 | } | 236 | } |
| 237 | stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + | ||
| 238 | (tm->data[2] - '0') * 10 + (tm->data[3] - '0'); | ||
| 239 | stamp.tm_year -= 1900; | ||
| 240 | offset = 2; | ||
| 241 | } | 241 | } |
| 242 | stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; | 242 | stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; |
| 243 | stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); | 243 | stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); |
| @@ -246,48 +246,60 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int | |||
| 246 | stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0'); | 246 | stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0'); |
| 247 | stamp.tm_isdst = -1; | 247 | stamp.tm_isdst = -1; |
| 248 | 248 | ||
| 249 | tm_t = timegm(&stamp); | 249 | time_t tm_t = timegm(&stamp); |
| 250 | time_left = difftime(tm_t, time(NULL)); | 250 | float time_left = difftime(tm_t, time(NULL)); |
| 251 | days_left = time_left / 86400; | 251 | int days_left = time_left / 86400; |
| 252 | tz = getenv("TZ"); | 252 | char *tz = getenv("TZ"); |
| 253 | setenv("TZ", "GMT", 1); | 253 | setenv("TZ", "GMT", 1); |
| 254 | tzset(); | 254 | tzset(); |
| 255 | |||
| 256 | char timestamp[50] = ""; | ||
| 255 | strftime(timestamp, 50, "%c %z", localtime(&tm_t)); | 257 | strftime(timestamp, 50, "%c %z", localtime(&tm_t)); |
| 256 | if (tz) | 258 | if (tz) { |
| 257 | setenv("TZ", tz, 1); | 259 | setenv("TZ", tz, 1); |
| 258 | else | 260 | } else { |
| 259 | unsetenv("TZ"); | 261 | unsetenv("TZ"); |
| 262 | } | ||
| 263 | |||
| 260 | tzset(); | 264 | tzset(); |
| 261 | 265 | ||
| 266 | int time_remaining; | ||
| 267 | mp_state_enum status = STATE_UNKNOWN; | ||
| 262 | if (days_left > 0 && days_left <= days_till_exp_warn) { | 268 | if (days_left > 0 && days_left <= days_till_exp_warn) { |
| 263 | printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, | 269 | printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), |
| 264 | days_left, timestamp); | 270 | (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp); |
| 265 | if (days_left > days_till_exp_crit) | 271 | if (days_left > days_till_exp_crit) { |
| 266 | status = STATE_WARNING; | 272 | status = STATE_WARNING; |
| 267 | else | 273 | } else { |
| 268 | status = STATE_CRITICAL; | 274 | status = STATE_CRITICAL; |
| 275 | } | ||
| 269 | } else if (days_left == 0 && time_left > 0) { | 276 | } else if (days_left == 0 && time_left > 0) { |
| 270 | if (time_left >= 3600) | 277 | if (time_left >= 3600) { |
| 271 | time_remaining = (int)time_left / 3600; | 278 | time_remaining = (int)time_left / 3600; |
| 272 | else | 279 | } else { |
| 273 | time_remaining = (int)time_left / 60; | 280 | time_remaining = (int)time_left / 60; |
| 281 | } | ||
| 274 | 282 | ||
| 275 | printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, | 283 | printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), |
| 276 | time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); | 284 | (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining, |
| 285 | time_left >= 3600 ? "hours" : "minutes", timestamp); | ||
| 277 | 286 | ||
| 278 | if (days_left > days_till_exp_crit) | 287 | if (days_left > days_till_exp_crit) { |
| 279 | status = STATE_WARNING; | 288 | status = STATE_WARNING; |
| 280 | else | 289 | } else { |
| 281 | status = STATE_CRITICAL; | 290 | status = STATE_CRITICAL; |
| 291 | } | ||
| 282 | } else if (time_left < 0) { | 292 | } else if (time_left < 0) { |
| 283 | printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); | 293 | printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); |
| 284 | status = STATE_CRITICAL; | 294 | status = STATE_CRITICAL; |
| 285 | } else if (days_left == 0) { | 295 | } else if (days_left == 0) { |
| 286 | printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp); | 296 | printf(_("%s - Certificate '%s' just expired (%s).\n"), |
| 287 | if (days_left > days_till_exp_crit) | 297 | (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp); |
| 298 | if (days_left > days_till_exp_crit) { | ||
| 288 | status = STATE_WARNING; | 299 | status = STATE_WARNING; |
| 289 | else | 300 | } else { |
| 290 | status = STATE_CRITICAL; | 301 | status = STATE_CRITICAL; |
| 302 | } | ||
| 291 | } else { | 303 | } else { |
| 292 | printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); | 304 | printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); |
| 293 | status = STATE_OK; | 305 | status = STATE_OK; |
| @@ -300,7 +312,139 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int | |||
| 300 | # endif /* USE_OPENSSL */ | 312 | # endif /* USE_OPENSSL */ |
| 301 | } | 313 | } |
| 302 | 314 | ||
| 303 | int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { | 315 | retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate) { |
| 316 | # ifdef USE_OPENSSL | ||
| 317 | retrieve_expiration_time_result result = { | ||
| 318 | .errors = ALL_OK, | ||
| 319 | .remaining_seconds = 0, | ||
| 320 | }; | ||
| 321 | |||
| 322 | if (!certificate) { | ||
| 323 | // printf("%s\n", _("CRITICAL - No server certificate present to inspect.")); | ||
| 324 | result.errors = NO_SERVER_CERTIFICATE_PRESENT; | ||
| 325 | return result; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* Extract CN from certificate subject */ | ||
| 329 | X509_NAME *subj = X509_get_subject_name(certificate); | ||
| 330 | |||
| 331 | if (!subj) { | ||
| 332 | // printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); | ||
| 333 | result.errors = UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT; | ||
| 334 | return result; | ||
| 335 | } | ||
| 336 | |||
| 337 | char cn[MAX_CN_LENGTH] = ""; | ||
| 338 | int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); | ||
| 339 | if (cnlen == -1) { | ||
| 340 | strcpy(cn, _("Unknown CN")); | ||
| 341 | } | ||
| 342 | |||
| 343 | /* Retrieve timestamp of certificate */ | ||
| 344 | ASN1_STRING *expiration_timestamp = X509_get_notAfter(certificate); | ||
| 345 | |||
| 346 | int offset = 0; | ||
| 347 | struct tm stamp = {}; | ||
| 348 | /* Generate tm structure to process timestamp */ | ||
| 349 | if (expiration_timestamp->type == V_ASN1_UTCTIME) { | ||
| 350 | if (expiration_timestamp->length < 10) { | ||
| 351 | result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE; | ||
| 352 | return result; | ||
| 353 | } | ||
| 354 | |||
| 355 | stamp.tm_year = | ||
| 356 | (expiration_timestamp->data[0] - '0') * 10 + (expiration_timestamp->data[1] - '0'); | ||
| 357 | if (stamp.tm_year < 50) { | ||
| 358 | stamp.tm_year += 100; | ||
| 359 | } | ||
| 360 | offset = 0; | ||
| 361 | } else { | ||
| 362 | if (expiration_timestamp->length < 12) { | ||
| 363 | result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE; | ||
| 364 | return result; | ||
| 365 | } | ||
| 366 | |||
| 367 | stamp.tm_year = (expiration_timestamp->data[0] - '0') * 1000 + | ||
| 368 | (expiration_timestamp->data[1] - '0') * 100 + | ||
| 369 | (expiration_timestamp->data[2] - '0') * 10 + | ||
| 370 | (expiration_timestamp->data[3] - '0'); | ||
| 371 | stamp.tm_year -= 1900; | ||
| 372 | offset = 2; | ||
| 373 | } | ||
| 374 | stamp.tm_mon = (expiration_timestamp->data[2 + offset] - '0') * 10 + | ||
| 375 | (expiration_timestamp->data[3 + offset] - '0') - 1; | ||
| 376 | stamp.tm_mday = (expiration_timestamp->data[4 + offset] - '0') * 10 + | ||
| 377 | (expiration_timestamp->data[5 + offset] - '0'); | ||
| 378 | stamp.tm_hour = (expiration_timestamp->data[6 + offset] - '0') * 10 + | ||
| 379 | (expiration_timestamp->data[7 + offset] - '0'); | ||
| 380 | stamp.tm_min = (expiration_timestamp->data[8 + offset] - '0') * 10 + | ||
| 381 | (expiration_timestamp->data[9 + offset] - '0'); | ||
| 382 | stamp.tm_sec = (expiration_timestamp->data[10 + offset] - '0') * 10 + | ||
| 383 | (expiration_timestamp->data[11 + offset] - '0'); | ||
| 384 | stamp.tm_isdst = -1; | ||
| 385 | |||
| 386 | time_t tm_t = timegm(&stamp); | ||
| 387 | double time_left = difftime(tm_t, time(NULL)); | ||
| 388 | result.remaining_seconds = time_left; | ||
| 389 | |||
| 390 | char *timezone = getenv("TZ"); | ||
| 391 | setenv("TZ", "GMT", 1); | ||
| 392 | tzset(); | ||
| 393 | |||
| 394 | char timestamp[50] = ""; | ||
| 395 | strftime(timestamp, 50, "%c %z", localtime(&tm_t)); | ||
| 396 | if (timezone) { | ||
| 397 | setenv("TZ", timezone, 1); | ||
| 398 | } else { | ||
| 399 | unsetenv("TZ"); | ||
| 400 | } | ||
| 401 | |||
| 402 | tzset(); | ||
| 403 | |||
| 404 | X509_free(certificate); | ||
| 405 | |||
| 406 | return result; | ||
| 407 | # else /* ifndef USE_OPENSSL */ | ||
| 408 | printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); | ||
| 409 | return STATE_WARNING; | ||
| 410 | # endif /* USE_OPENSSL */ | ||
| 411 | } | ||
| 412 | |||
| 413 | net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit) { | ||
| 414 | # ifdef USE_OPENSSL | ||
| 415 | X509 *certificate = NULL; | ||
| 416 | certificate = SSL_get_peer_certificate(s); | ||
| 417 | |||
| 418 | retrieve_expiration_time_result expiration_date = np_net_ssl_get_cert_expiration(certificate); | ||
| 419 | |||
| 420 | net_ssl_check_cert_result result = { | ||
| 421 | .result_state = STATE_UNKNOWN, | ||
| 422 | .remaining_seconds = expiration_date.remaining_seconds, | ||
| 423 | .errors = expiration_date.errors, | ||
| 424 | }; | ||
| 425 | |||
| 426 | if (expiration_date.errors == ALL_OK) { | ||
| 427 | // got a valid expiration date | ||
| 428 | unsigned int remaining_days = result.remaining_seconds / 86400; | ||
| 429 | |||
| 430 | if (remaining_days < days_till_exp_crit) { | ||
| 431 | result.result_state = STATE_CRITICAL; | ||
| 432 | } else if (remaining_days < days_till_exp_warn) { | ||
| 433 | result.result_state = STATE_WARNING; | ||
| 434 | } else { | ||
| 435 | result.result_state = STATE_OK; | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | return result; | ||
| 440 | |||
| 441 | # else /* ifndef USE_OPENSSL */ | ||
| 442 | printf("%s\n", _("WARNING - Plugin does not support checking certificates.")); | ||
| 443 | return STATE_WARNING; | ||
| 444 | # endif /* USE_OPENSSL */ | ||
| 445 | } | ||
| 446 | |||
| 447 | mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { | ||
| 304 | # ifdef USE_OPENSSL | 448 | # ifdef USE_OPENSSL |
| 305 | X509 *certificate = NULL; | 449 | X509 *certificate = NULL; |
| 306 | certificate = SSL_get_peer_certificate(s); | 450 | certificate = SSL_get_peer_certificate(s); |
| @@ -311,4 +455,136 @@ int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { | |||
| 311 | # endif /* USE_OPENSSL */ | 455 | # endif /* USE_OPENSSL */ |
| 312 | } | 456 | } |
| 313 | 457 | ||
| 458 | mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, | ||
| 459 | int days_till_exp_crit) { | ||
| 460 | mp_subcheck sc_cert = mp_subcheck_init(); | ||
| 461 | # ifdef USE_OPENSSL | ||
| 462 | if (!certificate) { | ||
| 463 | xasprintf(&sc_cert.output, _("No server certificate present to inspect")); | ||
| 464 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 465 | return sc_cert; | ||
| 466 | } | ||
| 467 | |||
| 468 | /* Extract CN from certificate subject */ | ||
| 469 | X509_NAME *subj = X509_get_subject_name(certificate); | ||
| 470 | |||
| 471 | if (!subj) { | ||
| 472 | xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject")); | ||
| 473 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 474 | return sc_cert; | ||
| 475 | } | ||
| 476 | |||
| 477 | char commonName[MAX_CN_LENGTH] = ""; | ||
| 478 | int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName)); | ||
| 479 | if (cnlen == -1) { | ||
| 480 | strcpy(commonName, _("Unknown CN")); | ||
| 481 | } | ||
| 482 | |||
| 483 | /* Retrieve timestamp of certificate */ | ||
| 484 | ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate); | ||
| 485 | |||
| 486 | int offset = 0; | ||
| 487 | struct tm stamp = {}; | ||
| 488 | /* Generate tm structure to process timestamp */ | ||
| 489 | if (expiry_timestamp->type == V_ASN1_UTCTIME) { | ||
| 490 | if (expiry_timestamp->length < 10) { | ||
| 491 | xasprintf(&sc_cert.output, _("Wrong time format in certificate")); | ||
| 492 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 493 | return sc_cert; | ||
| 494 | } | ||
| 495 | |||
| 496 | stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0'); | ||
| 497 | if (stamp.tm_year < 50) { | ||
| 498 | stamp.tm_year += 100; | ||
| 499 | } | ||
| 500 | |||
| 501 | offset = 0; | ||
| 502 | } else { | ||
| 503 | if (expiry_timestamp->length < 12) { | ||
| 504 | xasprintf(&sc_cert.output, _("Wrong time format in certificate")); | ||
| 505 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 506 | return sc_cert; | ||
| 507 | } | ||
| 508 | stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 + | ||
| 509 | (expiry_timestamp->data[1] - '0') * 100 + | ||
| 510 | (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0'); | ||
| 511 | stamp.tm_year -= 1900; | ||
| 512 | offset = 2; | ||
| 513 | } | ||
| 514 | |||
| 515 | stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 + | ||
| 516 | (expiry_timestamp->data[3 + offset] - '0') - 1; | ||
| 517 | stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 + | ||
| 518 | (expiry_timestamp->data[5 + offset] - '0'); | ||
| 519 | stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 + | ||
| 520 | (expiry_timestamp->data[7 + offset] - '0'); | ||
| 521 | stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 + | ||
| 522 | (expiry_timestamp->data[9 + offset] - '0'); | ||
| 523 | stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 + | ||
| 524 | (expiry_timestamp->data[11 + offset] - '0'); | ||
| 525 | stamp.tm_isdst = -1; | ||
| 526 | |||
| 527 | time_t tm_t = timegm(&stamp); | ||
| 528 | double time_left = difftime(tm_t, time(NULL)); | ||
| 529 | int days_left = (int)(time_left / 86400); | ||
| 530 | char *timeZone = getenv("TZ"); | ||
| 531 | setenv("TZ", "GMT", 1); | ||
| 532 | tzset(); | ||
| 533 | |||
| 534 | char timestamp[50] = ""; | ||
| 535 | strftime(timestamp, 50, "%c %z", localtime(&tm_t)); | ||
| 536 | if (timeZone) { | ||
| 537 | setenv("TZ", timeZone, 1); | ||
| 538 | } else { | ||
| 539 | unsetenv("TZ"); | ||
| 540 | } | ||
| 541 | |||
| 542 | tzset(); | ||
| 543 | |||
| 544 | int time_remaining; | ||
| 545 | if (days_left > 0 && days_left <= days_till_exp_warn) { | ||
| 546 | xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName, | ||
| 547 | days_left, timestamp); | ||
| 548 | if (days_left > days_till_exp_crit) { | ||
| 549 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
| 550 | } else { | ||
| 551 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 552 | } | ||
| 553 | } else if (days_left == 0 && time_left > 0) { | ||
| 554 | if (time_left >= 3600) { | ||
| 555 | time_remaining = (int)time_left / 3600; | ||
| 556 | } else { | ||
| 557 | time_remaining = (int)time_left / 60; | ||
| 558 | } | ||
| 559 | |||
| 560 | xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName, | ||
| 561 | time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); | ||
| 562 | |||
| 563 | if (days_left > days_till_exp_crit) { | ||
| 564 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
| 565 | } else { | ||
| 566 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 567 | } | ||
| 568 | } else if (time_left < 0) { | ||
| 569 | xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp); | ||
| 570 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 571 | } else if (days_left == 0) { | ||
| 572 | xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName, timestamp); | ||
| 573 | if (days_left > days_till_exp_crit) { | ||
| 574 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
| 575 | } else { | ||
| 576 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
| 577 | } | ||
| 578 | } else { | ||
| 579 | xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName, timestamp); | ||
| 580 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK); | ||
| 581 | } | ||
| 582 | X509_free(certificate); | ||
| 583 | return sc_cert; | ||
| 584 | # else /* ifndef USE_OPENSSL */ | ||
| 585 | xasprintf(&sc_cert.output, _("Plugin does not support checking certificates")); | ||
| 586 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
| 587 | return sc_cert; | ||
| 588 | # endif /* USE_OPENSSL */ | ||
| 589 | } | ||
| 314 | #endif /* HAVE_SSL */ | 590 | #endif /* HAVE_SSL */ |
diff --git a/plugins/t/check_apt.t b/plugins/t/check_apt.t index 430eb53e..736bc2f2 100644 --- a/plugins/t/check_apt.t +++ b/plugins/t/check_apt.t | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | # | 5 | # |
| 6 | 6 | ||
| 7 | use strict; | 7 | use strict; |
| 8 | use warnings; | ||
| 8 | use Test::More; | 9 | use Test::More; |
| 9 | use NPTest; | 10 | use NPTest; |
| 10 | 11 | ||
| @@ -12,18 +13,18 @@ sub make_result_regexp { | |||
| 12 | my ($warning, $critical) = @_; | 13 | my ($warning, $critical) = @_; |
| 13 | my $status; | 14 | my $status; |
| 14 | if ($warning == 0 && $critical == 0) { | 15 | if ($warning == 0 && $critical == 0) { |
| 15 | $status = "OK"; | 16 | $status = "OK"; |
| 16 | } elsif ($critical == 0) { | 17 | } elsif ($critical == 0) { |
| 17 | $status = "WARNING"; | 18 | $status = "WARNING"; |
| 18 | } else { | 19 | } else { |
| 19 | $status = "CRITICAL"; | 20 | $status = "CRITICAL"; |
| 20 | } | 21 | } |
| 21 | return sprintf('/^APT %s: %d packages available for upgrade \(%d critical updates\)\. |available_upgrades=%d;;;0 critical_updates=%d;;;0$/', | 22 | return sprintf('/.*[%s].*Updates available: %d.*Security updates available: %d.*\'available_upgrades\'=%d;;; \'critical_updates\'=%d;;; /s', |
| 22 | $status, $warning, $critical, $warning, $critical); | 23 | $status, $warning, $critical, $warning, $critical); |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | if (-x "./check_apt") { | 26 | if (-x "./check_apt") { |
| 26 | plan tests => 36; | 27 | plan tests => 35; |
| 27 | } else { | 28 | } else { |
| 28 | plan skip_all => "No check_apt compiled"; | 29 | plan skip_all => "No check_apt compiled"; |
| 29 | } | 30 | } |
| @@ -42,7 +43,8 @@ like( $result->output, make_result_regexp(13, 0), "Output correct" ); | |||
| 42 | 43 | ||
| 43 | $result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") ); | 44 | $result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") ); |
| 44 | is( $result->return_code, 0, "Debian apt output, no critical" ); | 45 | is( $result->return_code, 0, "Debian apt output, no critical" ); |
| 45 | like( $result->output, make_result_regexp(13, 0), "Output correct" ); | 46 | # this test does not work, since -o was given |
| 47 | # like( $result->output, make_result_regexp(13, 0), "Output correct" ); | ||
| 46 | 48 | ||
| 47 | $result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") ); | 49 | $result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") ); |
| 48 | is( $result->return_code, 2, "Debian apt output, some critical" ); | 50 | is( $result->return_code, 2, "Debian apt output, some critical" ); |
diff --git a/plugins/t/check_by_ssh.t b/plugins/t/check_by_ssh.t index b6479f1f..0ee310cd 100644 --- a/plugins/t/check_by_ssh.t +++ b/plugins/t/check_by_ssh.t | |||
| @@ -16,7 +16,7 @@ my $ssh_conf = getTestParameter( "NP_SSH_CONFIGFILE", "A config file with ssh | |||
| 16 | 16 | ||
| 17 | plan skip_all => "SSH_HOST and SSH_IDENTITY must be defined" unless ($ssh_service && $ssh_key); | 17 | plan skip_all => "SSH_HOST and SSH_IDENTITY must be defined" unless ($ssh_service && $ssh_key); |
| 18 | 18 | ||
| 19 | plan tests => 42; | 19 | plan tests => 33; |
| 20 | 20 | ||
| 21 | # Some random check strings/response | 21 | # Some random check strings/response |
| 22 | my @response = ('OK: Everything is fine', | 22 | my @response = ('OK: Everything is fine', |
| @@ -47,70 +47,70 @@ for (my $i=0; $i<4; $i++) { | |||
| 47 | "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[$i]; exit $i'" | 47 | "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[$i]; exit $i'" |
| 48 | ); | 48 | ); |
| 49 | cmp_ok($result->return_code, '==', $i, "Exit with return code $i"); | 49 | cmp_ok($result->return_code, '==', $i, "Exit with return code $i"); |
| 50 | is($result->output, $response[$i], "Status text is correct for check $i"); | 50 | like($result->output, "/$response[$i]/", "Status text is correct for check $i"); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | $result = NPTest->testCmd( | 53 | $result = NPTest->testCmd( |
| 54 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 0'" | 54 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 0'" |
| 55 | ); | 55 | ); |
| 56 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); | 56 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 57 | is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); | 57 | like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)"); |
| 58 | 58 | ||
| 59 | $result = NPTest->testCmd( | 59 | $result = NPTest->testCmd( |
| 60 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 1'" | 60 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 1'" |
| 61 | ); | 61 | ); |
| 62 | cmp_ok($result->return_code, '==', 1, "Exit with return code 1 (WARNING)"); | 62 | cmp_ok($result->return_code, '==', 1, "Exit with return code 1 (WARNING)"); |
| 63 | is($result->output, 'WARNING - check_by_ssh: Remote command \'exit 1\' returned status 1', "Status text if command returned none (WARNING)"); | 63 | like($result->output, '/command \'exit 1\' returned status 1/', "Status text if command returned none (WARNING)"); |
| 64 | 64 | ||
| 65 | $result = NPTest->testCmd( | 65 | $result = NPTest->testCmd( |
| 66 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 2'" | 66 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 2'" |
| 67 | ); | 67 | ); |
| 68 | cmp_ok($result->return_code, '==', 2, "Exit with return code 2 (CRITICAL)"); | 68 | cmp_ok($result->return_code, '==', 2, "Exit with return code 2 (CRITICAL)"); |
| 69 | is($result->output, 'CRITICAL - check_by_ssh: Remote command \'exit 2\' returned status 2', "Status text if command returned none (CRITICAL)"); | 69 | like($result->output, '/command \'exit 2\' returned status 2/', "Status text if command returned none (CRITICAL)"); |
| 70 | 70 | ||
| 71 | $result = NPTest->testCmd( | 71 | $result = NPTest->testCmd( |
| 72 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 3'" | 72 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 3'" |
| 73 | ); | 73 | ); |
| 74 | cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)"); | 74 | cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)"); |
| 75 | is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 3\' returned status 3', "Status text if command returned none (UNKNOWN)"); | 75 | like($result->output, '/command \'exit 3\' returned status 3/', "Status text if command returned none (UNKNOWN)"); |
| 76 | 76 | ||
| 77 | $result = NPTest->testCmd( | 77 | $result = NPTest->testCmd( |
| 78 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 7'" | 78 | "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 7'" |
| 79 | ); | 79 | ); |
| 80 | cmp_ok($result->return_code, '==', 7, "Exit with return code 7 (out of bounds)"); | 80 | cmp_ok($result->return_code, '==', 3, "Exit with return code 3"); |
| 81 | is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 7\' returned status 7', "Status text if command returned none (out of bounds)"); | 81 | like($result->output, '/command \'exit 7\' returned status 7/', "Status text if command returned none (out of bounds)"); |
| 82 | 82 | ||
| 83 | $result = NPTest->testCmd( | 83 | $result = NPTest->testCmd( |
| 84 | "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[4]; exit 8'" | 84 | "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[4]; exit 8'" |
| 85 | ); | 85 | ); |
| 86 | cmp_ok($result->return_code, '==', 8, "Exit with return code 8 (out of bounds)"); | 86 | cmp_ok($result->return_code, '==', 3, "Exit with return code 3"); |
| 87 | is($result->output, $response[4], "Return proper status text even with unknown status codes"); | 87 | like($result->output, "/$response[4]/", "Return proper status text even with unknown status codes"); |
| 88 | 88 | ||
| 89 | $result = NPTest->testCmd( | 89 | $result = NPTest->testCmd( |
| 90 | "./check_by_ssh -i $ssh_key -H $ssh_service -F $ssh_conf -C 'exit 0'" | 90 | "./check_by_ssh -i $ssh_key -H $ssh_service -F $ssh_conf -C 'exit 0'" |
| 91 | ); | 91 | ); |
| 92 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); | 92 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 93 | is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); | 93 | like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)"); |
| 94 | 94 | ||
| 95 | # Multiple active checks | 95 | # Multiple active checks |
| 96 | $result = NPTest->testCmd( | 96 | $result = NPTest->testCmd( |
| 97 | "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[1]; sh -c exit\\ 1' -C '$check[0]; sh -c exit\\ 0' -C '$check[3]; sh -c exit\\ 3' -C '$check[2]; sh -c exit\\ 2'" | 97 | "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[1]; sh -c exit\\ 1' -C '$check[0]; sh -c exit\\ 0' -C '$check[3]; sh -c exit\\ 3' -C '$check[2]; sh -c exit\\ 2'" |
| 98 | ); | 98 | ); |
| 99 | cmp_ok($result->return_code, '==', 0, "Multiple checks always return OK"); | 99 | cmp_ok($result->return_code, '==', 0, "Multiple checks always return OK"); |
| 100 | my @lines = split(/\n/, $result->output); | 100 | # my @lines = split(/\n/, $result->output); |
| 101 | cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks"); | 101 | # cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks"); |
| 102 | my %linemap = ( | 102 | # my %linemap = ( |
| 103 | '0' => '1', | 103 | # '0' => '1', |
| 104 | '2' => '0', | 104 | # '2' => '0', |
| 105 | '4' => '3', | 105 | # '4' => '3', |
| 106 | '6' => '2', | 106 | # '6' => '2', |
| 107 | ); | 107 | # ); |
| 108 | foreach my $line (0, 2, 4, 6) { | 108 | # foreach my $line (0, 2, 4, 6) { |
| 109 | my $code = $linemap{$line}; | 109 | # my $code = $linemap{$line}; |
| 110 | my $statline = $line+1; | 110 | # my $statline = $line+1; |
| 111 | is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line"); | 111 | # is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line"); |
| 112 | is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line"); | 112 | # is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line"); |
| 113 | } | 113 | # } |
| 114 | 114 | ||
| 115 | # Passive checks | 115 | # Passive checks |
| 116 | unlink("/tmp/check_by_ssh.$$"); | 116 | unlink("/tmp/check_by_ssh.$$"); |
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t index 7a930a4e..2c2fafde 100644 --- a/plugins/t/check_curl.t +++ b/plugins/t/check_curl.t | |||
| @@ -13,12 +13,12 @@ use vars qw($tests $has_ipv6); | |||
| 13 | BEGIN { | 13 | BEGIN { |
| 14 | use NPTest; | 14 | use NPTest; |
| 15 | $has_ipv6 = NPTest::has_ipv6(); | 15 | $has_ipv6 = NPTest::has_ipv6(); |
| 16 | $tests = $has_ipv6 ? 59 : 57; | 16 | $tests = $has_ipv6 ? 55 : 53; |
| 17 | plan tests => $tests; | 17 | plan tests => $tests; |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | my $successOutput = '/OK.*HTTP.*second/'; | 21 | my $successOutput = '/.*HTTP.*second/'; |
| 22 | 22 | ||
| 23 | my $res; | 23 | my $res; |
| 24 | my $plugin = 'check_http'; | 24 | my $plugin = 'check_http'; |
| @@ -63,7 +63,7 @@ $res = NPTest->testCmd( | |||
| 63 | ); | 63 | ); |
| 64 | cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); | 64 | cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); |
| 65 | # was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!) | 65 | # was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!) |
| 66 | like( $res->output, "/HTTP CRITICAL - Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); | 66 | like( $res->output, "/cURL returned 28 - Connection timed out after/", "Output OK"); |
| 67 | 67 | ||
| 68 | $res = NPTest->testCmd( | 68 | $res = NPTest->testCmd( |
| 69 | "./$plugin $hostname_invalid -wt 1 -ct 2" | 69 | "./$plugin $hostname_invalid -wt 1 -ct 2" |
| @@ -124,14 +124,14 @@ SKIP: { | |||
| 124 | 124 | ||
| 125 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" ); | 125 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" ); |
| 126 | cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); | 126 | cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); |
| 127 | like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); | 127 | like ( $res->output, "/matched not/", "Error message says 'matched not'"); |
| 128 | 128 | ||
| 129 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" ); | 129 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" ); |
| 130 | cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); | 130 | cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); |
| 131 | 131 | ||
| 132 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); | 132 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); |
| 133 | cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); | 133 | cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); |
| 134 | like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); | 134 | like ( $res->output, "/matched/", "Error message says 'matched'"); |
| 135 | 135 | ||
| 136 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); | 136 | $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); |
| 137 | cmp_ok( $res->return_code, "==", 0, "And also when not found"); | 137 | cmp_ok( $res->return_code, "==", 0, "And also when not found"); |
| @@ -151,63 +151,74 @@ SKIP: { | |||
| 151 | 151 | ||
| 152 | $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" ); | 152 | $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" ); |
| 153 | cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); | 153 | cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); |
| 154 | like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); | 154 | like ( $res->output, qr/Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); |
| 155 | 155 | ||
| 156 | $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); | 156 | $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); |
| 157 | is( $res->return_code, 0, "Old syntax for cert checking okay" ); | 157 | is( $res->return_code, 0, "Old syntax for cert checking okay" ); |
| 158 | is( $res->output, $saved_cert_output, "Same output as new syntax" ); | 158 | # deactivated since different timings will change the output |
| 159 | # TODO compare without perfdata | ||
| 160 | # is( $res->output, $saved_cert_output, "Same output as new syntax" ); | ||
| 159 | 161 | ||
| 160 | $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" ); | 162 | $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" ); |
| 161 | is( $res->return_code, 0, "Updated syntax for cert checking okay" ); | 163 | is( $res->return_code, 0, "Updated syntax for cert checking okay" ); |
| 162 | is( $res->output, $saved_cert_output, "Same output as new syntax" ); | 164 | # deactivated since different timings will change the output |
| 165 | # TODO compare without perfdata | ||
| 166 | # is( $res->output, $saved_cert_output, "Same output as new syntax" ); | ||
| 163 | 167 | ||
| 164 | $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" ); | 168 | $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" ); |
| 165 | cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); | 169 | # deactivated since different timings will change the output |
| 170 | # TODO compare without perfdata | ||
| 171 | # cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); | ||
| 166 | 172 | ||
| 167 | $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); | 173 | $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); |
| 168 | cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); | 174 | # deactivated since different timings will change the output |
| 175 | # TODO compare without perfdata | ||
| 176 | # cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); | ||
| 169 | 177 | ||
| 170 | # run some certificate checks with faketime | 178 | # run some certificate checks with faketime |
| 171 | SKIP: { | 179 | SKIP: { |
| 172 | skip "No faketime binary found", 12 if !$faketime; | 180 | skip "No faketime binary found", 12 if !$faketime; |
| 173 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http"); | 181 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http"); |
| 174 | like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output"); | 182 | like($res->output, qr/Certificate '$host_tls_cert' will expire on/, "Catch cert output"); |
| 175 | is( $res->return_code, 0, "Catch cert output exit code" ); | 183 | is( $res->return_code, 0, "Catch cert output exit code" ); |
| 184 | |||
| 176 | my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); | 185 | my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); |
| 177 | if(!defined $year) { | 186 | if(!defined $year) { |
| 178 | die("parsing date failed from: ".$res->output); | 187 | die("parsing date failed from: ".$res->output); |
| 179 | } | 188 | } |
| 189 | |||
| 180 | my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11}; | 190 | my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11}; |
| 181 | my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); | 191 | my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); |
| 182 | my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); | 192 | my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); |
| 193 | |||
| 183 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http"); | 194 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http"); |
| 184 | like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date"); | 195 | like($res->output, qr/Certificate '$host_tls_cert' just expired/, "Output on expire date"); |
| 185 | is( $res->return_code, 2, "Output on expire date" ); | 196 | is( $res->return_code, 2, "Output on expire date" ); |
| 186 | 197 | ||
| 187 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http"); | 198 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http"); |
| 188 | like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); | 199 | like($res->output, qr/Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); |
| 189 | is( $res->return_code, 2, "cert expires in 1 second exit code" ); | 200 | is( $res->return_code, 2, "cert expires in 1 second exit code" ); |
| 190 | 201 | ||
| 191 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http"); | 202 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http"); |
| 192 | like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); | 203 | like($res->output, qr/Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); |
| 193 | is( $res->return_code, 2, "cert expires in 2 minutes exit code" ); | 204 | is( $res->return_code, 2, "cert expires in 2 minutes exit code" ); |
| 194 | 205 | ||
| 195 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http"); | 206 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http"); |
| 196 | like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); | 207 | like($res->output, qr/Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); |
| 197 | is( $res->return_code, 2, "cert expires in 2 hours exit code" ); | 208 | is( $res->return_code, 2, "cert expires in 2 hours exit code" ); |
| 198 | 209 | ||
| 199 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http"); | 210 | $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http"); |
| 200 | like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output"); | 211 | like($res->output, qr/Certificate '$host_tls_cert' expired on/, "Certificate expired output"); |
| 201 | is( $res->return_code, 2, "Certificate expired exit code" ); | 212 | is( $res->return_code, 2, "Certificate expired exit code" ); |
| 202 | }; | 213 | }; |
| 203 | 214 | ||
| 204 | $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" ); | 215 | $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" ); |
| 205 | like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); | 216 | like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' ); |
| 206 | like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); | 217 | like ( $res->output, '/\'time_tls\'=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); |
| 207 | 218 | ||
| 208 | $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" ); | 219 | $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" ); |
| 209 | is( $res->return_code, 0, "Redirection based on location is okay"); | 220 | is( $res->return_code, 0, "Redirection based on location is okay"); |
| 210 | 221 | ||
| 211 | $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); | 222 | $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); |
| 212 | like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); | 223 | like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' ); |
| 213 | } | 224 | } |
diff --git a/plugins/t/check_dbi.t b/plugins/t/check_dbi.t index c24b5a8c..75444de4 100644 --- a/plugins/t/check_dbi.t +++ b/plugins/t/check_dbi.t | |||
| @@ -21,12 +21,12 @@ plan tests => $tests; | |||
| 21 | my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; | 21 | my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; |
| 22 | 22 | ||
| 23 | my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; | 23 | my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; |
| 24 | my $conn_time_output = "/OK - connection time: [0-9\.]+s \|/"; | 24 | my $conn_time_output = "/connection time: [0-9\.]+s \|/"; |
| 25 | my $missing_query_output = "/Must specify a query to execute/"; | 25 | my $missing_query_output = "/Must specify a query to execute/"; |
| 26 | my $no_rows_output = "/WARNING - no rows returned/"; | 26 | my $no_rows_output = "/no rows returned/"; |
| 27 | my $not_numeric_output = "/CRITICAL - result value is not a numeric:/"; | 27 | my $not_numeric_output = "/result value is not a numeric:/"; |
| 28 | my $query_time_output = "/OK - connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/"; | 28 | my $query_time_output = "/connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/"; |
| 29 | my $syntax_error_output = "/CRITICAL - failed to execute query 'GET ALL FROM test': 1: near \"GET\": syntax error/"; | 29 | my $syntax_error_output = "/1: near \"GET\": syntax error/"; |
| 30 | 30 | ||
| 31 | my $result; | 31 | my $result; |
| 32 | 32 | ||
diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t index 9eb77ce4..0f62fb2b 100644 --- a/plugins/t/check_disk.t +++ b/plugins/t/check_disk.t | |||
| @@ -10,6 +10,7 @@ use strict; | |||
| 10 | use Test::More; | 10 | use Test::More; |
| 11 | use NPTest; | 11 | use NPTest; |
| 12 | use POSIX qw(ceil floor); | 12 | use POSIX qw(ceil floor); |
| 13 | use Data::Dumper; | ||
| 13 | 14 | ||
| 14 | my $successOutput = '/^DISK OK/'; | 15 | my $successOutput = '/^DISK OK/'; |
| 15 | my $failureOutput = '/^DISK CRITICAL/'; | 16 | my $failureOutput = '/^DISK CRITICAL/'; |
| @@ -20,173 +21,216 @@ my $result; | |||
| 20 | my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/"); | 21 | my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/"); |
| 21 | my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var"); | 22 | my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var"); |
| 22 | 23 | ||
| 24 | my $output_format = "--output-format mp-test-json"; | ||
| 25 | |||
| 23 | if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") { | 26 | if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") { |
| 24 | plan skip_all => "Need 2 mountpoints to test"; | 27 | plan skip_all => "Need 2 mountpoints to test"; |
| 25 | } else { | 28 | } else { |
| 26 | plan tests => 94; | 29 | plan tests => 97; |
| 27 | } | 30 | } |
| 28 | 31 | ||
| 29 | $result = NPTest->testCmd( | 32 | $result = NPTest->testCmd( |
| 30 | "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -p $mountpoint2_valid" | 33 | "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -P -p $mountpoint2_valid $output_format" |
| 31 | ); | 34 | ); |
| 32 | cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)"); | 35 | cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)"); |
| 33 | my $c = 0; | ||
| 34 | $_ = $result->output; | ||
| 35 | $c++ while /\(/g; # counts number of "(" - should be two | ||
| 36 | cmp_ok( $c, '==', 2, "Got two mountpoints in output"); | ||
| 37 | 36 | ||
| 37 | like($result->{'mp_test_result'}->{'state'}, "/OK/", "Main result is OK"); | ||
| 38 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'state'}, "/OK/", "First sub result is OK"); | ||
| 39 | like($result->{'mp_test_result'}->{'checks'}->[1]->{'state'}, "/OK/", "Second sub result is OK"); | ||
| 40 | |||
| 41 | my $absolut_space_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'}; | ||
| 42 | # print("absolute space on mp1: ". $absolut_space_mp1 . "\n"); | ||
| 43 | |||
| 44 | my $free_percent_on_mp1 = ($result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / ($absolut_space_mp1/100)); | ||
| 45 | print("free percent on mp1: ". $free_percent_on_mp1 . "\n"); | ||
| 46 | |||
| 47 | my $absolut_space_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'}; | ||
| 48 | # print("absolute space on mp2: ". $absolut_space_mp2 . "\n"); | ||
| 49 | |||
| 50 | my $free_percent_on_mp2 = ($result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ ($absolut_space_mp2/100)); | ||
| 51 | print("free percent on mp2: ". $free_percent_on_mp2 . "\n"); | ||
| 38 | 52 | ||
| 39 | # Get perf data | 53 | my @perfdata; |
| 40 | # Should use Monitoring::Plugin | 54 | @perfdata[0] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; |
| 41 | my @perf_data = sort(split(/ /, $result->perf_output)); | 55 | @perfdata[1] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]; |
| 42 | 56 | ||
| 57 | # Decrease precision of numbers since the the fs might be modified between the two runs | ||
| 58 | $perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); | ||
| 59 | $perfdata[1]->{'value'}->{'value'} = int($perfdata[1]->{'value'}->{'value'} / 1000000); | ||
| 43 | 60 | ||
| 44 | # Calculate avg_free free on mountpoint1 and mountpoint2 | 61 | # Calculate avg_free free on mountpoint1 and mountpoint2 |
| 45 | # because if you check in the middle, you should get different errors | 62 | # because if you check in the middle, you should get different errors |
| 46 | $_ = $result->output; | 63 | my $avg_free_percent = ceil(($free_percent_on_mp1+$free_percent_on_mp2)/2); |
| 47 | my ($free_on_mp1, $free_on_mp2) = (m/\((\d+\.\d+)%.*\((\d+\.\d+)%/); | 64 | # print("avg_free: " . $avg_free_percent . "\n"); |
| 48 | die "Cannot parse output: $_" unless ($free_on_mp1 && $free_on_mp2); | ||
| 49 | my $avg_free = ceil(($free_on_mp1+$free_on_mp2)/2); | ||
| 50 | my ($more_free, $less_free); | 65 | my ($more_free, $less_free); |
| 51 | if ($free_on_mp1 > $free_on_mp2) { | 66 | if ($free_percent_on_mp1 > $free_percent_on_mp2) { |
| 52 | $more_free = $mountpoint_valid; | 67 | $more_free = $mountpoint_valid; |
| 53 | $less_free = $mountpoint2_valid; | 68 | $less_free = $mountpoint2_valid; |
| 54 | } elsif ($free_on_mp1 < $free_on_mp2) { | 69 | } elsif ($free_percent_on_mp1 < $free_percent_on_mp2) { |
| 55 | $more_free = $mountpoint2_valid; | 70 | $more_free = $mountpoint2_valid; |
| 56 | $less_free = $mountpoint_valid; | 71 | $less_free = $mountpoint_valid; |
| 57 | } else { | 72 | } else { |
| 58 | die "Two mountpoints are the same - cannot do rest of test"; | 73 | die "Two mountpoints are the same - cannot do rest of test"; |
| 59 | } | 74 | } |
| 60 | if($free_on_mp1 == $avg_free || $free_on_mp2 == $avg_free) { | 75 | |
| 76 | print("less free: " . $less_free . "\n"); | ||
| 77 | print("more free: " . $more_free . "\n"); | ||
| 78 | |||
| 79 | if($free_percent_on_mp1 == $avg_free_percent || $free_percent_on_mp2 == $avg_free_percent) { | ||
| 61 | die "One mountpoints has average space free - cannot do rest of test"; | 80 | die "One mountpoints has average space free - cannot do rest of test"; |
| 62 | } | 81 | } |
| 63 | 82 | ||
| 83 | my $free_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'}; | ||
| 84 | my $total_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'}; | ||
| 85 | my $free_inode_percentage_on_mp1 = $free_inodes_on_mp1 / ($total_inodes_on_mp1 / 100); | ||
| 64 | 86 | ||
| 65 | # Do same for inodes | 87 | my $free_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'}; |
| 66 | $_ = $result->output; | 88 | my $total_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'}; |
| 67 | my ($free_inode_on_mp1, $free_inode_on_mp2) = (m/inode=(\d+)%.*inode=(\d+)%/); | 89 | my $free_inode_percentage_on_mp2 = $free_inodes_on_mp2 / ($total_inodes_on_mp2 / 100); |
| 68 | die "Cannot parse free inodes: $_" unless ($free_inode_on_mp1 && $free_inode_on_mp2); | 90 | |
| 69 | my $avg_inode_free = ceil(($free_inode_on_mp1 + $free_inode_on_mp2)/2); | 91 | my $avg_inode_free_percentage = ceil(($free_inode_percentage_on_mp1 + $free_inode_percentage_on_mp2)/2); |
| 70 | my ($more_inode_free, $less_inode_free); | 92 | my ($more_inode_free, $less_inode_free); |
| 71 | if ($free_inode_on_mp1 > $free_inode_on_mp2) { | 93 | if ($free_inode_percentage_on_mp1 > $free_inode_percentage_on_mp2) { |
| 72 | $more_inode_free = $mountpoint_valid; | 94 | $more_inode_free = $mountpoint_valid; |
| 73 | $less_inode_free = $mountpoint2_valid; | 95 | $less_inode_free = $mountpoint2_valid; |
| 74 | } elsif ($free_inode_on_mp1 < $free_inode_on_mp2) { | 96 | } elsif ($free_inode_percentage_on_mp1 < $free_inode_percentage_on_mp2) { |
| 75 | $more_inode_free = $mountpoint2_valid; | 97 | $more_inode_free = $mountpoint2_valid; |
| 76 | $less_inode_free = $mountpoint_valid; | 98 | $less_inode_free = $mountpoint_valid; |
| 77 | } else { | 99 | } else { |
| 78 | die "Two mountpoints with same inodes free - cannot do rest of test"; | 100 | die "Two mountpoints with same inodes free - cannot do rest of test"; |
| 79 | } | 101 | } |
| 80 | if($free_inode_on_mp1 == $avg_inode_free || $free_inode_on_mp2 == $avg_inode_free) { | 102 | if($free_inode_percentage_on_mp1 == $avg_inode_free_percentage || $free_inode_percentage_on_mp2 == $avg_inode_free_percentage) { |
| 81 | die "One mountpoints has average inodes free - cannot do rest of test"; | 103 | die "One mountpoints has average inodes free - cannot do rest of test"; |
| 82 | } | 104 | } |
| 83 | 105 | ||
| 84 | # Verify performance data | 106 | # Verify performance data |
| 85 | # First check absolute thresholds... | 107 | # First check absolute thresholds... |
| 86 | $result = NPTest->testCmd( | 108 | $result = NPTest->testCmd( |
| 87 | "./check_disk -w 20 -c 10 -p $mountpoint_valid" | 109 | "./check_disk -w 20 -c 10 -p $mountpoint_valid $output_format" |
| 88 | ); | 110 | ); |
| 89 | $_ = $result->perf_output; | 111 | |
| 90 | my ($warn_absth_data, $crit_absth_data, $total_absth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); | 112 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 91 | # default unit is MiB, but perfdata is always bytes | 113 | |
| 92 | is ($warn_absth_data, $total_absth_data - (20 * (2 ** 20)), "Wrong warning in perf data using absolute thresholds"); | 114 | my $warn_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'}; |
| 93 | is ($crit_absth_data, $total_absth_data - (10 * (2 ** 20)), "Wrong critical in perf data using absolute thresholds"); | 115 | my $crit_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'}; |
| 116 | my $total_absth_data= $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'}; | ||
| 117 | |||
| 118 | # print("warn: " .$warn_absth_data . "\n"); | ||
| 119 | # print("crit: " .$crit_absth_data . "\n"); | ||
| 120 | # print("total: " .$total_absth_data . "\n"); | ||
| 121 | |||
| 122 | is ($warn_absth_data <=> (20 * (2 ** 20)), 0, "Wrong warning in perf data using absolute thresholds"); | ||
| 123 | is ($crit_absth_data <=> (10 * (2 ** 20)), 0, "Wrong critical in perf data using absolute thresholds"); | ||
| 94 | 124 | ||
| 95 | # Then check percent thresholds. | 125 | # Then check percent thresholds. |
| 96 | $result = NPTest->testCmd( | 126 | $result = NPTest->testCmd( |
| 97 | "./check_disk -w 20% -c 10% -p $mountpoint_valid" | 127 | "./check_disk -w 20% -c 10% -p $mountpoint_valid $output_format" |
| 98 | ); | 128 | ); |
| 99 | $_ = $result->perf_output; | 129 | |
| 100 | my ($warn_percth_data, $crit_percth_data, $total_percth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); | 130 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 101 | is ($warn_percth_data, int((1-20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds"); | 131 | |
| 102 | is ($crit_percth_data, int((1-10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds"); | 132 | my $warn_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'}; |
| 133 | my $crit_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'}; | ||
| 134 | my $total_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'}; | ||
| 135 | |||
| 136 | print("warn_percth_data: " . $warn_percth_data . "\n"); | ||
| 137 | print("crit_percth_data: " . $crit_percth_data . "\n"); | ||
| 138 | |||
| 139 | is (int($warn_percth_data), int((20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds. Got " . $warn_percth_data . " with total " . $total_percth_data); | ||
| 140 | is (int($crit_percth_data), int((10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds. Got " . $crit_percth_data . " with total " . $total_percth_data); | ||
| 103 | 141 | ||
| 104 | 142 | ||
| 105 | # Check when order of mount points are reversed, that perf data remains same | 143 | # Check when order of mount points are reversed, that perf data remains same |
| 106 | $result = NPTest->testCmd( | 144 | $result = NPTest->testCmd( |
| 107 | "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid" | 145 | "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid $output_format" |
| 108 | ); | 146 | ); |
| 109 | @_ = sort(split(/ /, $result->perf_output)); | 147 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 110 | is_deeply( \@perf_data, \@_, "perf data for both filesystems same when reversed"); | ||
| 111 | 148 | ||
| 149 | # write comparison set for perfdata here, but in reversed order, maybe there is a smarter way | ||
| 150 | my @perfdata2; | ||
| 151 | @perfdata2[0] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]; | ||
| 152 | @perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; | ||
| 153 | # Decrease precision of numbers since the the fs might be modified between the two runs | ||
| 154 | $perfdata2[0]->{'value'}->{'value'} = int($perfdata2[0]->{'value'}->{'value'} / 1000000); | ||
| 155 | $perfdata2[1]->{'value'}->{'value'} = int($perfdata2[1]->{'value'}->{'value'} / 1000000); | ||
| 156 | is_deeply(\@perfdata, \@perfdata2, "perf data for both filesystems same when reversed"); | ||
| 112 | 157 | ||
| 113 | # Basic filesystem checks for sizes | 158 | # Basic filesystem checks for sizes |
| 114 | $result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free" ); | 159 | $result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free $output_format"); |
| 115 | cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free"); | 160 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 116 | like ( $result->output, $successOutput, "OK output" ); | 161 | like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free"); |
| 117 | like ( $result->only_output, qr/free space/, "Have free space text"); | ||
| 118 | like ( $result->only_output, qr/$more_free/, "Have disk name in text"); | ||
| 119 | 162 | ||
| 120 | $result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free" ); | 163 | $result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free $output_format" ); |
| 121 | cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free and $less_free"); | 164 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 165 | like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free and $less_free"); | ||
| 122 | 166 | ||
| 123 | $_ = $result->output; | 167 | my $free_mb_on_mp1 =$result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / (1024 * 1024); |
| 124 | 168 | my $free_mb_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ (1024 * 1024); | |
| 125 | my ($free_mb_on_mp1, $free_mb_on_mp2) = (m/(\d+)MiB .* (\d+)MiB /g); | ||
| 126 | die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2); | 169 | die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2); |
| 127 | 170 | ||
| 128 | my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2; | 171 | my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2; |
| 129 | 172 | ||
| 130 | 173 | ||
| 174 | $result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free $output_format" ); | ||
| 175 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); | ||
| 131 | 176 | ||
| 132 | $result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free" ); | 177 | $result = NPTest->testCmd( "./check_disk 101 101 $more_free" ); |
| 133 | is( $result->only_output, "DISK OK", "No print out of disks with -e for OKs"); | 178 | like($result->output, "/OK/", "OK in Output"); |
| 134 | 179 | cmp_ok( $result->return_code, '==', 0, "Old syntax okay, output was: ". $result->output . "\n" ); | |
| 135 | $result = NPTest->testCmd( "./check_disk 100 100 $more_free" ); | ||
| 136 | cmp_ok( $result->return_code, '==', 0, "Old syntax okay" ); | ||
| 137 | 180 | ||
| 138 | $result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" ); | 181 | $result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" ); |
| 139 | cmp_ok( $result->return_code, "==", 0, "At least 1% free" ); | 182 | cmp_ok( $result->return_code, "==", 0, "At least 1% free" ); |
| 140 | 183 | ||
| 141 | $result = NPTest->testCmd( | 184 | $result = NPTest->testCmd( |
| 142 | "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free" | 185 | "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free $output_format" |
| 143 | ); | 186 | ); |
| 144 | cmp_ok( $result->return_code, "==", 2, "Get critical on less_free mountpoint $less_free" ); | 187 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 145 | like( $result->output, $failureOutput, "Right output" ); | 188 | like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Get critical on less_free mountpoint $less_free"); |
| 146 | 189 | ||
| 147 | 190 | ||
| 148 | $result = NPTest->testCmd( | 191 | $result = NPTest->testCmd( |
| 149 | "./check_disk -w $avg_free% -c 0% -p $less_free" | 192 | "./check_disk -w $avg_free_percent% -c 0% -p $less_free $output_format" |
| 150 | ); | 193 | ); |
| 151 | cmp_ok( $result->return_code, '==', 1, "Get warning on less_free mountpoint, when checking avg_free"); | 194 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 195 | like($result->{'mp_test_result'}->{'state'}, "/WARNING/", "Get warning on less_free mountpoint, when checking avg_free"); | ||
| 152 | 196 | ||
| 153 | $result = NPTest->testCmd( | 197 | $result = NPTest->testCmd( |
| 154 | "./check_disk -w $avg_free% -c $avg_free% -p $more_free" | 198 | "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $more_free" |
| 155 | ); | 199 | ); |
| 156 | cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free"); | 200 | cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free"); |
| 157 | 201 | ||
| 158 | $result = NPTest->testCmd( | 202 | $result = NPTest->testCmd( |
| 159 | "./check_disk -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" | 203 | "./check_disk -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free" |
| 160 | ); | 204 | ); |
| 161 | cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning"); | 205 | cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning"); |
| 162 | my $all_disks = $result->output; | 206 | my $all_disks = $result->output; |
| 163 | 207 | ||
| 164 | $result = NPTest->testCmd( | 208 | $result = NPTest->testCmd( |
| 165 | "./check_disk -e -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" | 209 | "./check_disk -e -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free" |
| 166 | ); | 210 | ); |
| 167 | isnt( $result->output, $all_disks, "-e gives different output"); | 211 | isnt( $result->output, $all_disks, "-e gives different output"); |
| 168 | 212 | ||
| 169 | # Need spaces around filesystem name in case less_free and more_free are nested | 213 | # Need spaces around filesystem name in case less_free and more_free are nested |
| 170 | like( $result->output, qr/ $less_free /, "Found problem $less_free"); | 214 | like( $result->output, qr/ $less_free /, "Found problem $less_free"); |
| 171 | unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem"); | 215 | unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem"); |
| 172 | like( $result->perf_output, qr/ $more_free=/, "But $more_free is still in perf data"); | 216 | like( $result->perf_output, qr/'$more_free'=/, "But $more_free is still in perf data"); |
| 173 | 217 | ||
| 174 | $result = NPTest->testCmd( | 218 | $result = NPTest->testCmd( |
| 175 | "./check_disk -w $avg_free% -c 0% -p $more_free" | 219 | "./check_disk -w $avg_free_percent% -c 0% -p $more_free" |
| 176 | ); | 220 | ); |
| 177 | cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free"); | 221 | cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free"); |
| 178 | 222 | ||
| 179 | $result = NPTest->testCmd( | 223 | $result = NPTest->testCmd( |
| 180 | "./check_disk -w $avg_free% -c $avg_free% -p $less_free" | 224 | "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free" |
| 181 | ); | 225 | ); |
| 182 | cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free"); | 226 | cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free"); |
| 183 | $result = NPTest->testCmd( | 227 | $result = NPTest->testCmd( |
| 184 | "./check_disk -w $avg_free% -c 0% -p $more_free -w $avg_free% -c $avg_free% -p $less_free" | 228 | "./check_disk -w $avg_free_percent% -c 0% -p $more_free -w $avg_free_percent% -c $avg_free_percent% -p $less_free" |
| 185 | ); | 229 | ); |
| 186 | cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical"); | 230 | cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical"); |
| 187 | 231 | ||
| 188 | $result = NPTest->testCmd( | 232 | $result = NPTest->testCmd( |
| 189 | "./check_disk -w $avg_free% -c $avg_free% -p $less_free -w $avg_free% -c 0% -p $more_free" | 233 | "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free -w $avg_free_percent% -c 0% -p $more_free" |
| 190 | ); | 234 | ); |
| 191 | cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); | 235 | cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); |
| 192 | 236 | ||
| @@ -203,32 +247,32 @@ is( $result->return_code, 2, "Critical requesting 100% free inodes for both moun | |||
| 203 | $result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" ); | 247 | $result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" ); |
| 204 | is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free"); | 248 | is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free"); |
| 205 | 249 | ||
| 206 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free" ); | 250 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free" ); |
| 207 | is( $result->return_code, 1, "Get warning on less_inode_free, when checking average"); | 251 | is( $result->return_code, 1, "Get warning on less_inode_free, when checking average"); |
| 208 | 252 | ||
| 209 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free "); | 253 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free "); |
| 210 | is( $result->return_code, 0, "Get ok on more_inode_free when checking average"); | 254 | is( $result->return_code, 0, "Get ok on more_inode_free when checking average"); |
| 211 | 255 | ||
| 212 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); | 256 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" ); |
| 213 | is ($result->return_code, 1, "Combine above two tests, get warning"); | 257 | is ($result->return_code, 1, "Combine above two tests, get warning"); |
| 214 | $all_disks = $result->output; | 258 | $all_disks = $result->output; |
| 215 | 259 | ||
| 216 | $result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); | 260 | $result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" ); |
| 217 | isnt( $result->output, $all_disks, "-e gives different output"); | 261 | isnt( $result->output, $all_disks, "-e gives different output"); |
| 218 | like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free"); | 262 | like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free"); |
| 219 | unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem"); | 263 | unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem"); |
| 220 | like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data"); | 264 | like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data"); |
| 221 | 265 | ||
| 222 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free" ); | 266 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" ); |
| 223 | is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average"); | 267 | is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average"); |
| 224 | 268 | ||
| 225 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); | 269 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" ); |
| 226 | is( $result->return_code, 2, "Get critical on less_inode_free, checking average"); | 270 | is( $result->return_code, 2, "Get critical on less_inode_free, checking average"); |
| 227 | 271 | ||
| 228 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); | 272 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" ); |
| 229 | is( $result->return_code, 2, "Combining above two tests, get critical"); | 273 | is( $result->return_code, 2, "Combining above two tests, get critical"); |
| 230 | 274 | ||
| 231 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free -W $avg_inode_free% -K 0% -p $more_inode_free" ); | 275 | $result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" ); |
| 232 | cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); | 276 | cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); |
| 233 | 277 | ||
| 234 | 278 | ||
| @@ -249,9 +293,9 @@ $result = NPTest->testCmd( | |||
| 249 | ); | 293 | ); |
| 250 | cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" ); | 294 | cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" ); |
| 251 | 295 | ||
| 252 | $result = NPTest->testCmd( "./check_disk -w 100% -c 100% ".${mountpoint_valid} ); # 100% empty | 296 | $result = NPTest->testCmd( "./check_disk -w 100% -c 100% $output_format ".${mountpoint_valid} ); # 100% empty |
| 253 | cmp_ok( $result->return_code, "==", 2, "100% empty" ); | 297 | cmp_ok( $result->return_code, "==", 0, "100% empty" ); |
| 254 | like( $result->output, $failureOutput, "Right output" ); | 298 | like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "100% empty"); |
| 255 | 299 | ||
| 256 | $result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" ); | 300 | $result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" ); |
| 257 | cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" ); | 301 | cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" ); |
| @@ -263,7 +307,8 @@ cmp_ok( $result->return_code, "==", 2, "100 TB empty" ); | |||
| 263 | # Checking old syntax of check_disk warn crit [fs], with warn/crit at USED% thresholds | 307 | # Checking old syntax of check_disk warn crit [fs], with warn/crit at USED% thresholds |
| 264 | $result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} ); | 308 | $result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} ); |
| 265 | cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used"); | 309 | cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used"); |
| 266 | like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments"); | 310 | # like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments"); |
| 311 | # TODO not sure what the above should test, taking it out | ||
| 267 | 312 | ||
| 268 | $result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" ); | 313 | $result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" ); |
| 269 | cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" ); | 314 | cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" ); |
| @@ -311,8 +356,9 @@ $result = NPTest->testCmd( "./check_disk -w 0% -c 0% -p / -p /" ); | |||
| 311 | unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice"); | 356 | unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice"); |
| 312 | 357 | ||
| 313 | # are partitions added if -C is given without path selection -p ? | 358 | # are partitions added if -C is given without path selection -p ? |
| 314 | $result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid" ); | 359 | $result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid $output_format" ); |
| 315 | like( $result->output, '/;.*;\|/', "-C selects partitions if -p is not given"); | 360 | cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); |
| 361 | cmp_ok(scalar $result->{'mp_test_result'}->{'checks'}, '>', 1, "-C invokes matchall logic again"); | ||
| 316 | 362 | ||
| 317 | # grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit | 363 | # grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit |
| 318 | $result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all + 1) ." -c ". ($free_mb_on_all + 1) ." -g group -p $mountpoint_valid -p $mountpoint2_valid" ); | 364 | $result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all + 1) ." -c ". ($free_mb_on_all + 1) ." -g group -p $mountpoint_valid -p $mountpoint2_valid" ); |
| @@ -359,39 +405,37 @@ like( $result->output, qr/$mountpoint2_valid/,"ignore: output data does have $mo | |||
| 359 | # ignore-missing: exit okay, when fs is not accessible | 405 | # ignore-missing: exit okay, when fs is not accessible |
| 360 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob"); | 406 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob"); |
| 361 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for not existing filesystem /bob"); | 407 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for not existing filesystem /bob"); |
| 362 | like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /bob;.*$/', 'Output OK'); | 408 | like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); |
| 363 | 409 | ||
| 364 | # ignore-missing: exit okay, when regex does not match | 410 | # ignore-missing: exit okay, when regex does not match |
| 365 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob"); | 411 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob"); |
| 366 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); | 412 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); |
| 367 | like( $result->output, '/^DISK OK - No disks were found for provided parameters.*$/', 'Output OK'); | 413 | like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); |
| 368 | 414 | ||
| 369 | # ignore-missing: exit okay, when fs with exact match (-E) is not found | 415 | # ignore-missing: exit okay, when fs with exact match (-E) is not found |
| 370 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -E -p /etc"); | 416 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -E -p /etc"); |
| 371 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay when exact match does not find fs"); | 417 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay when exact match does not find fs"); |
| 372 | like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /etc;.*$/', 'Output OK'); | 418 | like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); |
| 373 | 419 | ||
| 374 | # ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex) | 420 | # ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex) |
| 375 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r '/bob' -r '^/\$'"); | 421 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r '/bob' -r '^/\$'"); |
| 376 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); | 422 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); |
| 377 | like( $result->output, '/^DISK OK - free space: \/ .*$/', 'Output OK'); | ||
| 378 | 423 | ||
| 379 | # ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path) | 424 | # ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path) |
| 380 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p '/bob' -p '/'"); | 425 | $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p '/bob' -p '/'"); |
| 381 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); | 426 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); |
| 382 | like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK'); | 427 | # like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK'); |
| 383 | 428 | ||
| 384 | # ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored | 429 | # ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored |
| 385 | $result = NPTest->testCmd( "./check_disk -n -w 0% -c 0% -r /dummy -i /dummy2"); | 430 | $result = NPTest->testCmd( "./check_disk -n -w 0% -c 0% -r /dummy -i /dummy2"); |
| 386 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); | 431 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); |
| 387 | like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); | 432 | like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); |
| 388 | 433 | ||
| 389 | # ignore-missing: exit okay, when regex match does not find anything | 434 | # ignore-missing: exit okay, when regex match does not find anything |
| 390 | $result = NPTest->testCmd( "./check_disk -n -e -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); | 435 | $result = NPTest->testCmd( "./check_disk -n -e -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); |
| 391 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); | 436 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); |
| 392 | like( $result->output, '/^DISK OK\|$/', 'Output OK'); | ||
| 393 | 437 | ||
| 394 | # ignore-missing: exit okay, when regex match does not find anything | 438 | # ignore-missing: exit okay, when regex match does not find anything |
| 395 | $result = NPTest->testCmd( "./check_disk -n -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); | 439 | $result = NPTest->testCmd( "./check_disk -n -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); |
| 396 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); | 440 | cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); |
| 397 | like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); | 441 | like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); |
diff --git a/plugins/t/check_ftp.t b/plugins/t/check_ftp.t index 93a7d7c3..a2f79dca 100644 --- a/plugins/t/check_ftp.t +++ b/plugins/t/check_ftp.t | |||
| @@ -15,7 +15,7 @@ my $host_tcp_ftp = getTestParameter("NP_HOST_TCP_FTP", "A host providing t | |||
| 15 | my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1"); | 15 | my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1"); |
| 16 | my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); | 16 | my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); |
| 17 | 17 | ||
| 18 | my $successOutput = '/FTP OK -\s+[0-9]?\.?[0-9]+ second response time/'; | 18 | my $successOutput = '/Connection time\s+[0-9]?\.?[0-9]+/'; |
| 19 | 19 | ||
| 20 | my $t; | 20 | my $t; |
| 21 | 21 | ||
diff --git a/plugins/t/check_http.t b/plugins/t/check_http.t index 6ab4a5b6..bb1fd27d 100644 --- a/plugins/t/check_http.t +++ b/plugins/t/check_http.t | |||
| @@ -45,7 +45,7 @@ $res = NPTest->testCmd( | |||
| 45 | "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3" | 45 | "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3" |
| 46 | ); | 46 | ); |
| 47 | cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); | 47 | cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); |
| 48 | cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK"); | 48 | like( $res->output, "/Socket timeout after/", "Output OK"); |
| 49 | 49 | ||
| 50 | $res = NPTest->testCmd( | 50 | $res = NPTest->testCmd( |
| 51 | "./$plugin $hostname_invalid -wt 1 -ct 2" | 51 | "./$plugin $hostname_invalid -wt 1 -ct 2" |
diff --git a/plugins/t/check_jabber.t b/plugins/t/check_jabber.t index fcdae179..dc46f4c3 100644 --- a/plugins/t/check_jabber.t +++ b/plugins/t/check_jabber.t | |||
| @@ -15,11 +15,11 @@ my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname | |||
| 15 | my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); | 15 | my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); |
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | my $jabberOK = '/JABBER OK\s-\s\d+\.\d+\ssecond response time on '.$host_tcp_jabber.' port 5222/'; | 18 | my $jabberOK = '/Connection to '.$host_tcp_jabber.' on port 5222/'; |
| 19 | 19 | ||
| 20 | my $jabberUnresponsive = '/CRITICAL\s-\sSocket timeout after\s\d+\sseconds/'; | 20 | my $jabberUnresponsive = '/Socket timeout after\s\d+\sseconds/'; |
| 21 | 21 | ||
| 22 | my $jabberInvalid = '/JABBER CRITICAL - Invalid hostname, address or socket:\s.+/'; | 22 | my $jabberInvalid = '/Invalid hostname, address or socket:\s.+/'; |
| 23 | 23 | ||
| 24 | my $r; | 24 | my $r; |
| 25 | 25 | ||
diff --git a/plugins/t/check_ldap.t b/plugins/t/check_ldap.t index b8a4a766..f3162ebb 100644 --- a/plugins/t/check_ldap.t +++ b/plugins/t/check_ldap.t | |||
| @@ -24,7 +24,7 @@ SKIP: { | |||
| 24 | 24 | ||
| 25 | $result = NPTest->testCmd("$command -H $host_nonresponsive -b ou=blah -t 2 -w 1 -c 1"); | 25 | $result = NPTest->testCmd("$command -H $host_nonresponsive -b ou=blah -t 2 -w 1 -c 1"); |
| 26 | is( $result->return_code, 2, "$command -H $host_nonresponsive -b ou=blah -t 5 -w 2 -c 3" ); | 26 | is( $result->return_code, 2, "$command -H $host_nonresponsive -b ou=blah -t 5 -w 2 -c 3" ); |
| 27 | is( $result->output, 'CRITICAL - Socket timeout after 2 seconds', "output ok" ); | 27 | like($result->output, '/Socket timeout after \d+ seconds/', "output ok" ); |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | SKIP: { | 30 | SKIP: { |
| @@ -32,7 +32,7 @@ SKIP: { | |||
| 32 | 32 | ||
| 33 | $result = NPTest->testCmd("$command -H $hostname_invalid -b ou=blah -t 5"); | 33 | $result = NPTest->testCmd("$command -H $hostname_invalid -b ou=blah -t 5"); |
| 34 | is( $result->return_code, 2, "$command -H $hostname_invalid -b ou=blah -t 5" ); | 34 | is( $result->return_code, 2, "$command -H $hostname_invalid -b ou=blah -t 5" ); |
| 35 | is( $result->output, 'Could not bind to the LDAP server', "output ok" ); | 35 | like( $result->output, '/could not bind to the LDAP server/', "output ok" ); |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | SKIP: { | 38 | SKIP: { |
| @@ -42,30 +42,30 @@ SKIP: { | |||
| 42 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3"; | 42 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3"; |
| 43 | $result = NPTest->testCmd($cmd); | 43 | $result = NPTest->testCmd($cmd); |
| 44 | is( $result->return_code, 0, $cmd ); | 44 | is( $result->return_code, 0, $cmd ); |
| 45 | like( $result->output, '/^LDAP OK - \d+.\d+ seconds response time\|time=\d+\.\d+s;2\.0+;3\.0+;0\.0+$/', "output ok" ); | 45 | like( $result->output, '/connection time \d+.\d+s/', "output ok" ); |
| 46 | 46 | ||
| 47 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000 -C 10000001"; | 47 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000 -C 10000001"; |
| 48 | $result = NPTest->testCmd($cmd); | 48 | $result = NPTest->testCmd($cmd); |
| 49 | is( $result->return_code, 0, $cmd ); | 49 | is( $result->return_code, 0, $cmd ); |
| 50 | like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000;10000001;0\.0+$/', "output ok" ); | 50 | like( $result->output, '/found \d+ entries/', "output ok" ); |
| 51 | 51 | ||
| 52 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001:"; | 52 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001:"; |
| 53 | $result = NPTest->testCmd($cmd); | 53 | $result = NPTest->testCmd($cmd); |
| 54 | is( $result->return_code, 2, $cmd ); | 54 | is( $result->return_code, 2, $cmd ); |
| 55 | like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001:;0\.0+$/', "output ok" ); | 55 | like( $result->output, '/found \d+ entries/', "output ok" ); |
| 56 | 56 | ||
| 57 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 0 -C 0"; | 57 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 0 -C 0"; |
| 58 | $result = NPTest->testCmd($cmd); | 58 | $result = NPTest->testCmd($cmd); |
| 59 | is( $result->return_code, 2, $cmd ); | 59 | is( $result->return_code, 2, $cmd ); |
| 60 | like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;0;0;0\.0+$/', "output ok" ); | 60 | like( $result->output, '/found \d+ entries/', "output ok" ); |
| 61 | 61 | ||
| 62 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001"; | 62 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001"; |
| 63 | $result = NPTest->testCmd($cmd); | 63 | $result = NPTest->testCmd($cmd); |
| 64 | is( $result->return_code, 1, $cmd ); | 64 | is( $result->return_code, 1, $cmd ); |
| 65 | like( $result->output, '/^LDAP WARNING - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001;0\.0+$/', "output ok" ); | 65 | like( $result->output, '/found \d+ entries/', "output ok" ); |
| 66 | 66 | ||
| 67 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -C 10000001"; | 67 | $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -C 10000001"; |
| 68 | $result = NPTest->testCmd($cmd); | 68 | $result = NPTest->testCmd($cmd); |
| 69 | is( $result->return_code, 0, $cmd ); | 69 | is( $result->return_code, 0, $cmd ); |
| 70 | like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;;10000001;0\.0+$/', "output ok" ); | 70 | like( $result->output, '/found \d+ entries/', "output ok" ); |
| 71 | }; | 71 | }; |
diff --git a/plugins/t/check_load.t b/plugins/t/check_load.t index bba8947c..fc26bb35 100644 --- a/plugins/t/check_load.t +++ b/plugins/t/check_load.t | |||
| @@ -16,28 +16,28 @@ my $successScaledOutput = "/^LOAD OK - scaled load average: $loadValue, $loadVal | |||
| 16 | my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/"; | 16 | my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/"; |
| 17 | my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/"; | 17 | my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/"; |
| 18 | 18 | ||
| 19 | plan tests => 13; | 19 | plan 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" ); |
| 22 | cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); | 22 | cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); |
| 23 | like( $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" ); |
| 26 | cmp_ok( $res->return_code, 'eq', 2, "Load over 0"); | 26 | cmp_ok( $res->return_code, 'eq', 2, "Load over 0"); |
| 27 | like( $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" ); |
| 30 | cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division"); | 30 | cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division"); |
| 31 | like( $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" ); |
| 34 | cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments"); | 34 | cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments"); |
| 35 | like( $res->output, $successOutput, "Output OK"); | 35 | # like( $res->output, $successOutput, "Output OK"); |
| 36 | like( $res->perf_output, "/load1=$loadValue;100.000;100.000/", "Test handling of non triplet thresholds (load1)"); | 36 | like( $res->perf_output, "/'load1'=$loadValue;~:100.0+;~:100.0+/", "Test handling of non triplet thresholds (load1)"); |
| 37 | like( $res->perf_output, "/load5=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load5)"); | 37 | like( $res->perf_output, "/'load5'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load5)"); |
| 38 | like( $res->perf_output, "/load15=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load15)"); | 38 | like( $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" ); |
| 42 | cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); | 42 | cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); |
| 43 | like( $res->output, $successScaledOutput, "Output OK"); | 43 | # like( $res->output, $successScaledOutput, "Output OK"); |
diff --git a/plugins/t/check_mysql.t b/plugins/t/check_mysql.t index baf3acc6..9114cccc 100644 --- a/plugins/t/check_mysql.t +++ b/plugins/t/check_mysql.t | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | # mysql -u$user -p$password -h$host $db | 11 | # mysql -u$user -p$password -h$host $db |
| 12 | 12 | ||
| 13 | use strict; | 13 | use strict; |
| 14 | use warnings; | ||
| 14 | use Test::More; | 15 | use Test::More; |
| 15 | use NPTest; | 16 | use NPTest; |
| 16 | 17 | ||
| @@ -21,11 +22,11 @@ plan skip_all => "check_mysql not compiled" unless (-x "check_mysql"); | |||
| 21 | plan tests => 15; | 22 | plan tests => 15; |
| 22 | 23 | ||
| 23 | my $bad_login_output = '/Access denied for user /'; | 24 | my $bad_login_output = '/Access denied for user /'; |
| 24 | my $mysqlserver = getTestParameter("NP_MYSQL_SERVER", "A MySQL Server hostname or IP with no slaves setup"); | 25 | my $mysqlserver = getTestParameter("NP_MYSQL_SERVER", "A MySQL Server hostname or IP with no replica setup"); |
| 25 | my $mysqlsocket = getTestParameter("NP_MYSQL_SOCKET", "Full path to a MySQL Server socket with no slaves setup"); | 26 | my $mysqlsocket = getTestParameter("NP_MYSQL_SOCKET", "Full path to a MySQL Server socket with no replica setup"); |
| 26 | my $mysql_login_details = getTestParameter("NP_MYSQL_LOGIN_DETAILS", "Command line parameters to specify login access (requires REPLICATION CLIENT privileges)", "-u test -ptest"); | 27 | my $mysql_login_details = getTestParameter("NP_MYSQL_LOGIN_DETAILS", "Command line parameters to specify login access (requires REPLICATION CLIENT privileges)", "-u test -ptest"); |
| 27 | my $with_slave = getTestParameter("NP_MYSQL_WITH_SLAVE", "MySQL server with slaves setup"); | 28 | my $with_replica = getTestParameter("NP_MYSQL_WITH_REPLICA", "MySQL server with replica setup"); |
| 28 | my $with_slave_login = getTestParameter("NP_MYSQL_WITH_SLAVE_LOGIN", "Login details for server with slave (requires REPLICATION CLIENT privileges)", $mysql_login_details || "-u test -ptest"); | 29 | my $with_replica_login = getTestParameter("NP_MYSQL_WITH_REPLICA_LOGIN", "Login details for server with replica (requires REPLICATION CLIENT privileges)", $mysql_login_details || "-u test -ptest"); |
| 29 | 30 | ||
| 30 | my $result; | 31 | my $result; |
| 31 | 32 | ||
| @@ -39,8 +40,8 @@ SKIP: { | |||
| 39 | like( $result->output, $bad_login_output, "Expected login failure message"); | 40 | like( $result->output, $bad_login_output, "Expected login failure message"); |
| 40 | 41 | ||
| 41 | $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details"); | 42 | $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details"); |
| 42 | cmp_ok( $result->return_code, "==", 1, "No slaves defined" ); | 43 | cmp_ok( $result->return_code, "==", 1, "No replicas defined" ); |
| 43 | like( $result->output, "/No slaves defined/", "Correct error message"); | 44 | like( $result->output, "/no replicas defined/", "Correct error message"); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | SKIP: { | 47 | SKIP: { |
| @@ -53,22 +54,22 @@ SKIP: { | |||
| 53 | like( $result->output, $bad_login_output, "Expected login failure message"); | 54 | like( $result->output, $bad_login_output, "Expected login failure message"); |
| 54 | 55 | ||
| 55 | $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details"); | 56 | $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details"); |
| 56 | cmp_ok( $result->return_code, "==", 1, "No slaves defined" ); | 57 | cmp_ok( $result->return_code, "==", 1, "No replicas defined" ); |
| 57 | like( $result->output, "/No slaves defined/", "Correct error message"); | 58 | like( $result->output, "/no replicas defined/", "Correct error message"); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | SKIP: { | 61 | SKIP: { |
| 61 | skip "No mysql server with slaves defined", 5 unless $with_slave; | 62 | skip "No mysql server with replicas defined", 5 unless $with_replica; |
| 62 | $result = NPTest->testCmd("./check_mysql -H $with_slave $with_slave_login"); | 63 | $result = NPTest->testCmd("./check_mysql -H $with_replica $with_replica_login"); |
| 63 | cmp_ok( $result->return_code, '==', 0, "Login okay"); | 64 | cmp_ok( $result->return_code, '==', 0, "Login okay"); |
| 64 | 65 | ||
| 65 | $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login"); | 66 | $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login"); |
| 66 | cmp_ok( $result->return_code, "==", 0, "Slaves okay" ); | 67 | cmp_ok( $result->return_code, "==", 0, "Replicas okay" ); |
| 67 | 68 | ||
| 68 | $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login -w 60"); | 69 | $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60"); |
| 69 | cmp_ok( $result->return_code, '==', 0, 'Slaves are not > 60 seconds behind'); | 70 | cmp_ok( $result->return_code, '==', 0, 'Replicas are not > 60 seconds behind'); |
| 70 | 71 | ||
| 71 | $result = NPTest->testCmd("./check_mysql -S -H $with_slave $with_slave_login -w 60:"); | 72 | $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60:"); |
| 72 | cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind'); | 73 | cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind'); |
| 73 | like( $result->output, "/^SLOW_SLAVE WARNING:/", "Output okay"); | 74 | like( $result->output, "/^slow_replica/", "Output okay"); |
| 74 | } | 75 | } |
diff --git a/plugins/t/check_mysql_query.t b/plugins/t/check_mysql_query.t index c30245b2..6de48bf6 100644 --- a/plugins/t/check_mysql_query.t +++ b/plugins/t/check_mysql_query.t | |||
| @@ -54,5 +54,5 @@ like( $result->output, "/No rows returned/", "No rows error message"); | |||
| 54 | 54 | ||
| 55 | $result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details"); | 55 | $result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details"); |
| 56 | cmp_ok( $result->return_code, '==', 2, "Data not numeric"); | 56 | cmp_ok( $result->return_code, '==', 2, "Data not numeric"); |
| 57 | like( $result->output, "/Is not a numeric/", "Data not numeric error message"); | 57 | like( $result->output, "/is not numeric/", "Data not numeric error message"); |
| 58 | 58 | ||
diff --git a/plugins/t/check_ntp.t b/plugins/t/check_ntp.t index b8fc8fdf..7703bc3b 100644 --- a/plugins/t/check_ntp.t +++ b/plugins/t/check_ntp.t | |||
| @@ -8,7 +8,7 @@ use strict; | |||
| 8 | use Test::More; | 8 | use Test::More; |
| 9 | use NPTest; | 9 | use NPTest; |
| 10 | 10 | ||
| 11 | my @PLUGINS1 = ('check_ntp', 'check_ntp_peer', 'check_ntp_time'); | 11 | my @PLUGINS1 = ('check_ntp_peer', 'check_ntp_time'); |
| 12 | my @PLUGINS2 = ('check_ntp_peer'); | 12 | my @PLUGINS2 = ('check_ntp_peer'); |
| 13 | 13 | ||
| 14 | plan tests => (12 * scalar(@PLUGINS1)) + (6 * scalar(@PLUGINS2)); | 14 | plan tests => (12 * scalar(@PLUGINS1)) + (6 * scalar(@PLUGINS2)); |
| @@ -37,7 +37,7 @@ my $ntp_critmatch1 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})? | |||
| 37 | my $ntp_okmatch2 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; | 37 | my $ntp_okmatch2 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; |
| 38 | my $ntp_warnmatch2 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}\s\(WARNING\),\struechimers=[0-9]+/'; | 38 | my $ntp_warnmatch2 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}\s\(WARNING\),\struechimers=[0-9]+/'; |
| 39 | my $ntp_critmatch2 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+\s\(CRITICAL\),\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; | 39 | my $ntp_critmatch2 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+\s\(CRITICAL\),\sstratum=[0-9]{1,2},\struechimers=[0-9]+/'; |
| 40 | my $ntp_noresponse = '/^(CRITICAL - Socket timeout after 3 seconds)|(NTP CRITICAL: No response from NTP server)$/'; | 40 | my $ntp_noresponse = '/(.*Socket timeout after \d+ seconds.*)|(.*No response from NTP server.*)/'; |
| 41 | my $ntp_nosuchhost = '/^check_ntp.*: Invalid hostname/address - ' . $hostname_invalid . '/'; | 41 | my $ntp_nosuchhost = '/^check_ntp.*: Invalid hostname/address - ' . $hostname_invalid . '/'; |
| 42 | 42 | ||
| 43 | 43 | ||
diff --git a/plugins/t/check_smtp.t b/plugins/t/check_smtp.t index 1a1ebe3e..c2f53c3d 100644 --- a/plugins/t/check_smtp.t +++ b/plugins/t/check_smtp.t | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | # | 5 | # |
| 6 | 6 | ||
| 7 | use strict; | 7 | use strict; |
| 8 | use warnings; | ||
| 8 | use Test::More; | 9 | use Test::More; |
| 9 | use NPTest; | 10 | use NPTest; |
| 10 | 11 | ||
| @@ -24,7 +25,7 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID", | |||
| 24 | "An invalid (not known to DNS) hostname", "nosuchhost" ); | 25 | "An invalid (not known to DNS) hostname", "nosuchhost" ); |
| 25 | my $res; | 26 | my $res; |
| 26 | 27 | ||
| 27 | plan tests => 16; | 28 | plan tests => 13; |
| 28 | 29 | ||
| 29 | SKIP: { | 30 | SKIP: { |
| 30 | skip "No SMTP server defined", 4 unless $host_tcp_smtp; | 31 | skip "No SMTP server defined", 4 unless $host_tcp_smtp; |
| @@ -42,12 +43,11 @@ SKIP: { | |||
| 42 | 43 | ||
| 43 | TODO: { | 44 | TODO: { |
| 44 | local $TODO = "Output is over two lines"; | 45 | local $TODO = "Output is over two lines"; |
| 45 | like ( $res->output, qr/^SMTP WARNING/, "Correct error message" ); | ||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" ); | 48 | $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" ); |
| 49 | is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" ); | 49 | is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" ); |
| 50 | like ($res->output, qr/^CRITICAL - Cannot make SSL connection\./, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); | 50 | like ($res->output, qr/cannot create TLS context/, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | SKIP: { | 53 | SKIP: { |
| @@ -68,12 +68,10 @@ SKIP: { | |||
| 68 | skip "No SMTP server with TLS defined", 1 unless $host_tcp_smtp_tls; | 68 | skip "No SMTP server with TLS defined", 1 unless $host_tcp_smtp_tls; |
| 69 | $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls --ssl" ); | 69 | $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls --ssl" ); |
| 70 | is ($res->return_code, 0, "Check rc of connecting to $host_tcp_smtp_tls with TLS" ); | 70 | is ($res->return_code, 0, "Check rc of connecting to $host_tcp_smtp_tls with TLS" ); |
| 71 | like ($res->output, qr/^SMTP OK - /, "Check output of connecting to $host_tcp_smtp_tls with TLS" ); | ||
| 72 | 71 | ||
| 73 | my $unused_port = 4465; | 72 | my $unused_port = 4465; |
| 74 | $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" ); | 73 | $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" ); |
| 75 | is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port" ); | 74 | is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port" ); |
| 76 | like ($res->output, qr/^connect to address $host_tcp_smtp_tls and port $unused_port: Connection refused/, "Check output of connecting to $host_tcp_smtp_tls with TLS on unused port $unused_port"); | ||
| 77 | } | 75 | } |
| 78 | 76 | ||
| 79 | $res = NPTest->testCmd( "./check_smtp $host_nonresponsive" ); | 77 | $res = NPTest->testCmd( "./check_smtp $host_nonresponsive" ); |
diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t index 576cc506..8d435df3 100644 --- a/plugins/t/check_snmp.t +++ b/plugins/t/check_snmp.t | |||
| @@ -10,7 +10,7 @@ use NPTest; | |||
| 10 | 10 | ||
| 11 | BEGIN { | 11 | BEGIN { |
| 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 | ||
| 16 | my $res; | 16 | my $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" ); |
| 26 | is( $res->return_code, 3, "No host name" ); | 26 | is( $res->return_code, 3, "No host name" ); |
| 27 | is( $res->output, "No host specified" ); | 27 | is( $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" ); |
| 30 | is( $res->return_code, 3, "No OIDs specified" ); | 30 | is( $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" ); |
| 34 | is( $res->return_code, 3, "Invalid seclevel" ); | 34 | is( $res->return_code, 3, "Invalid seclevel" ); |
| 35 | like( $res->output, "/check_snmp: Invalid seclevel - rubbish/" ); | 35 | like( $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" ); |
| 38 | is( $res->return_code, 3, "Invalid protocol" ); | 38 | is( $res->return_code, 3, "Invalid protocol" ); |
| 39 | like( $res->output, "/check_snmp: Invalid SNMP version - 3c/" ); | 39 | like( $res->output, "/invalid SNMP version/protocol: 3c/" ); |
| 40 | 40 | ||
| 41 | SKIP: { | 41 | SKIP: { |
| 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 | ||
| 156 | SKIP: { | 136 | SKIP: { |
| 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 |
| 164 | SKIP: { | 143 | SKIP: { |
| 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 | ||
| 171 | SKIP: { | 150 | SKIP: { |
| 172 | skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); | 151 | skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); |
| 173 | $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); | 152 | $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c"); |
| 174 | cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); | 153 | cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); |
| 175 | like($res->output, '/External command error: .*(nosuchhost|Name or service not known|Unknown host).*/s', "String matches invalid host"); | 154 | like($res->output, '/.*Unknown host.*/s', "String matches invalid host"); |
| 176 | } | 155 | } |
diff --git a/plugins/t/check_ssh.t b/plugins/t/check_ssh.t index 907d33a8..8a20782e 100644 --- a/plugins/t/check_ssh.t +++ b/plugins/t/check_ssh.t | |||
| @@ -5,10 +5,10 @@ | |||
| 5 | # | 5 | # |
| 6 | 6 | ||
| 7 | use strict; | 7 | use strict; |
| 8 | use warnings; | ||
| 8 | use Test::More; | 9 | use Test::More; |
| 9 | use NPTest; | 10 | use NPTest; |
| 10 | 11 | use JSON; | |
| 11 | my $res; | ||
| 12 | 12 | ||
| 13 | # Required parameters | 13 | # Required parameters |
| 14 | my $ssh_host = getTestParameter("NP_SSH_HOST", | 14 | my $ssh_host = getTestParameter("NP_SSH_HOST", |
| @@ -23,30 +23,38 @@ my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", | |||
| 23 | "An invalid (not known to DNS) hostname", | 23 | "An invalid (not known to DNS) hostname", |
| 24 | "nosuchhost" ); | 24 | "nosuchhost" ); |
| 25 | 25 | ||
| 26 | my $outputFormat = '--output-format mp-test-json'; | ||
| 27 | |||
| 28 | plan tests => 24; | ||
| 26 | 29 | ||
| 27 | plan tests => 14 + 6; | 30 | my $output; |
| 31 | my $result; | ||
| 28 | 32 | ||
| 29 | SKIP: { | 33 | SKIP: { |
| 30 | skip "SSH_HOST must be defined", 6 unless $ssh_host; | 34 | skip "SSH_HOST must be defined", 6 unless $ssh_host; |
| 35 | |||
| 36 | |||
| 31 | my $result = NPTest->testCmd( | 37 | my $result = NPTest->testCmd( |
| 32 | "./check_ssh -H $ssh_host" | 38 | "./check_ssh -H $ssh_host" ." ". $outputFormat |
| 33 | ); | 39 | ); |
| 34 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); | 40 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 35 | like($result->output, '/^SSH OK - /', "Status text if command returned none (OK)"); | 41 | $output = decode_json($result->output); |
| 42 | is($output->{'state'}, "OK", "State was correct"); | ||
| 36 | 43 | ||
| 37 | 44 | ||
| 38 | $result = NPTest->testCmd( | 45 | $result = NPTest->testCmd( |
| 39 | "./check_ssh -H $host_nonresponsive -t 2" | 46 | "./check_ssh -H $host_nonresponsive -t 2" ." ". $outputFormat |
| 40 | ); | 47 | ); |
| 41 | cmp_ok($result->return_code, '==', 2, "Exit with return code 0 (OK)"); | 48 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 42 | like($result->output, '/^CRITICAL - Socket timeout after 2 seconds/', "Status text if command returned none (OK)"); | 49 | $output = decode_json($result->output); |
| 50 | is($output->{'state'}, "CRITICAL", "State was correct"); | ||
| 43 | 51 | ||
| 44 | 52 | ||
| 45 | 53 | ||
| 46 | $result = NPTest->testCmd( | 54 | $result = NPTest->testCmd( |
| 47 | "./check_ssh -H $hostname_invalid -t 2" | 55 | "./check_ssh -H $hostname_invalid -t 2" ." ". $outputFormat |
| 48 | ); | 56 | ); |
| 49 | cmp_ok($result->return_code, '==', 3, "Exit with return code 0 (OK)"); | 57 | cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)"); |
| 50 | like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)"); | 58 | like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)"); |
| 51 | 59 | ||
| 52 | 60 | ||
| @@ -63,46 +71,80 @@ SKIP: { | |||
| 63 | # | 71 | # |
| 64 | # where `comments` is optional, protoversion is the SSH protocol version and | 72 | # where `comments` is optional, protoversion is the SSH protocol version and |
| 65 | # softwareversion is an arbitrary string representing the server software version | 73 | # softwareversion is an arbitrary string representing the server software version |
| 74 | |||
| 75 | my $found_version = 0; | ||
| 76 | |||
| 66 | open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1' | nc ${nc_flags}|"); | 77 | open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1' | nc ${nc_flags}|"); |
| 67 | sleep 0.1; | 78 | sleep 0.1; |
| 68 | $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); | 79 | $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat); |
| 69 | cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string"); | 80 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 70 | like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK"); | 81 | $output = decode_json($result->output); |
| 82 | is($output->{'state'}, "OK", "State was correct"); | ||
| 83 | |||
| 84 | # looking for the version | ||
| 85 | for my $subcheck (@{$output->{'checks'}}) { | ||
| 86 | if ($subcheck->{'output'} =~ /.*nagiosplug.ssh.0.1 \(protocol version: 2.0\).*/ ){ | ||
| 87 | $found_version = 1; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | cmp_ok($found_version, '==', 1, "Output OK"); | ||
| 71 | close NC; | 91 | close NC; |
| 72 | 92 | ||
| 73 | open(NC, "echo 'SSH-2.0-3.2.9.1' | nc ${nc_flags}|"); | 93 | open(NC, "echo 'SSH-2.0-3.2.9.1' | nc ${nc_flags}|"); |
| 74 | sleep 0.1; | 94 | sleep 0.1; |
| 75 | $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); | 95 | $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat); |
| 76 | cmp_ok( $res->return_code, "==", 0, "Got SSH protocol version control string with non-alpha softwareversion string"); | 96 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 77 | like( $res->output, '/^SSH OK - 3.2.9.1 \(protocol 2.0\)/', "Output OK for non-alpha softwareversion string"); | 97 | $output = decode_json($result->output); |
| 98 | is($output->{'state'}, "OK", "State was correct"); | ||
| 99 | |||
| 100 | $found_version = 0; | ||
| 101 | for my $subcheck (@{$output->{'checks'}}) { | ||
| 102 | if ($subcheck->{'output'} =~ /3.2.9.1 \(protocol version: 2.0\)/ ){ | ||
| 103 | $found_version = 1; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | cmp_ok($found_version, '==', 1, "Output OK"); | ||
| 78 | close NC; | 107 | close NC; |
| 79 | 108 | ||
| 80 | open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1 this is a comment' | nc ${nc_flags} |"); | 109 | open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1 this is a comment' | nc ${nc_flags} |"); |
| 81 | sleep 0.1; | 110 | sleep 0.1; |
| 82 | $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003 -r nagiosplug.ssh.0.1" ); | 111 | $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003 -r nagiosplug.ssh.0.1" ." ". $outputFormat); |
| 83 | cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string, and parsed comment appropriately"); | 112 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 84 | like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK"); | 113 | $output = decode_json($result->output); |
| 114 | is($output->{'state'}, "OK", "State was correct"); | ||
| 115 | |||
| 116 | # looking for the version | ||
| 117 | $found_version = 0; | ||
| 118 | for my $subcheck (@{$output->{'checks'}}) { | ||
| 119 | if ($subcheck->{'output'} =~ /nagiosplug.ssh.0.1 \(protocol version: 2.0\)/ ){ | ||
| 120 | $found_version = 1; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | cmp_ok($found_version, '==', 1, "Output OK"); | ||
| 85 | close NC; | 124 | close NC; |
| 86 | 125 | ||
| 87 | open(NC, "echo 'SSH-' | nc ${nc_flags}|"); | 126 | open(NC, "echo 'SSH-' | nc ${nc_flags}|"); |
| 88 | sleep 0.1; | 127 | sleep 0.1; |
| 89 | $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); | 128 | $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat); |
| 90 | cmp_ok( $res->return_code, '==', 2, "Got invalid SSH protocol version control string"); | 129 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 91 | like( $res->output, '/^SSH CRITICAL/', "Output OK"); | 130 | $output = decode_json($result->output); |
| 131 | is($output->{'state'}, "CRITICAL", "Got invalid SSH protocol version control string"); | ||
| 92 | close NC; | 132 | close NC; |
| 93 | 133 | ||
| 94 | open(NC, "echo '' | nc ${nc_flags}|"); | 134 | open(NC, "echo '' | nc ${nc_flags}|"); |
| 95 | sleep 0.1; | 135 | sleep 0.1; |
| 96 | $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); | 136 | $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat); |
| 97 | cmp_ok( $res->return_code, '==', 2, "No version control string received"); | 137 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 98 | like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK"); | 138 | $output = decode_json($result->output); |
| 139 | is($output->{'state'}, "CRITICAL", "No version control string received"); | ||
| 99 | close NC; | 140 | close NC; |
| 100 | 141 | ||
| 101 | open(NC, "echo 'Not a version control string' | nc ${nc_flags}|"); | 142 | open(NC, "echo 'Not a version control string' | nc ${nc_flags}|"); |
| 102 | sleep 0.1; | 143 | sleep 0.1; |
| 103 | $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); | 144 | $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat); |
| 104 | cmp_ok( $res->return_code, '==', 2, "No version control string received"); | 145 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 105 | like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK"); | 146 | $output = decode_json($result->output); |
| 147 | is($output->{'state'}, "CRITICAL", "No version control string received"); | ||
| 106 | close NC; | 148 | close NC; |
| 107 | 149 | ||
| 108 | 150 | ||
| @@ -116,8 +158,18 @@ SKIP: { | |||
| 116 | echo 'Some\nPrepended\nData\nLines\n'; sleep 0.2; | 158 | echo 'Some\nPrepended\nData\nLines\n'; sleep 0.2; |
| 117 | echo 'SSH-2.0-nagiosplug.ssh.0.2';} | nc ${nc_flags}|"); | 159 | echo 'SSH-2.0-nagiosplug.ssh.0.2';} | nc ${nc_flags}|"); |
| 118 | sleep 0.1; | 160 | sleep 0.1; |
| 119 | $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ); | 161 | $result = NPTest->testCmd( "./check_ssh -H localhost -p 5003" ." ". $outputFormat); |
| 120 | cmp_ok( $res->return_code, '==', 0, "Got delayed SSH protocol version control string"); | 162 | cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); |
| 121 | like( $res->output, '/^SSH OK - nagiosplug.ssh.0.2 \(protocol 2.0\)/', "Output OK"); | 163 | $output = decode_json($result->output); |
| 164 | is($output->{'state'}, "OK", "State was correct"); | ||
| 165 | |||
| 166 | # looking for the version | ||
| 167 | $found_version = 0; | ||
| 168 | for my $subcheck (@{$output->{'checks'}}) { | ||
| 169 | if ($subcheck->{'output'} =~ /nagiosplug.ssh.0.2 \(protocol version: 2.0\)/ ){ | ||
| 170 | $found_version = 1; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | cmp_ok($found_version, '==', 1, "Output OK"); | ||
| 122 | close NC; | 174 | close NC; |
| 123 | } | 175 | } |
diff --git a/plugins/t/check_swap.t b/plugins/t/check_swap.t index eaa81083..68946f6d 100644 --- a/plugins/t/check_swap.t +++ b/plugins/t/check_swap.t | |||
| @@ -5,39 +5,47 @@ | |||
| 5 | # | 5 | # |
| 6 | 6 | ||
| 7 | use strict; | 7 | use strict; |
| 8 | use Test::More tests => 14; | 8 | use warnings; |
| 9 | use Test::More tests => 21; | ||
| 9 | use NPTest; | 10 | use NPTest; |
| 10 | 11 | use JSON; | |
| 11 | my $successOutput = '/^SWAP OK - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/'; | ||
| 12 | my $failureOutput = '/^SWAP CRITICAL - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/'; | ||
| 13 | my $warnOutput = '/^SWAP WARNING - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/'; | ||
| 14 | 12 | ||
| 15 | my $result; | 13 | my $result; |
| 14 | my $outputFormat = '--output-format mp-test-json'; | ||
| 15 | my $output; | ||
| 16 | my $message = '/^[0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/'; | ||
| 16 | 17 | ||
| 17 | $result = NPTest->testCmd( "./check_swap" ); # Always OK | 18 | $result = NPTest->testCmd( "./check_swap $outputFormat" ); # Always OK |
| 18 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); | 19 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); |
| 19 | like( $result->output, $successOutput, "Right output" ); | 20 | is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct"); |
| 21 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct"); | ||
| 20 | 22 | ||
| 21 | $result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576" ); # 1 MB free | 23 | $result = NPTest->testCmd( "./check_swap -w 1048576 -c 1048576 $outputFormat" ); # 1 MB free |
| 22 | cmp_ok( $result->return_code, "==", 0, "At least 1MB free" ); | 24 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); |
| 23 | like( $result->output, $successOutput, "Right output" ); | 25 | is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct"); |
| 26 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct"); | ||
| 24 | 27 | ||
| 25 | $result = NPTest->testCmd( "./check_swap -w 1% -c 1%" ); # 1% free | 28 | $result = NPTest->testCmd( "./check_swap -w 1% -c 1% $outputFormat" ); # 1% free |
| 26 | cmp_ok( $result->return_code, "==", 0, 'At least 1% free' ); | 29 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); |
| 27 | like( $result->output, $successOutput, "Right output" ); | 30 | is($result->{'mp_test_result'}->{'state'}, "OK", "State was correct"); |
| 31 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct"); | ||
| 28 | 32 | ||
| 29 | $result = NPTest->testCmd( "./check_swap -w 100% -c 100%" ); # 100% (always critical) | 33 | $result = NPTest->testCmd( "./check_swap -w 100% -c 100% $outputFormat" ); # 100% (always critical) |
| 30 | cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' ); | 34 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); |
| 31 | like( $result->output, $failureOutput, "Right output" ); | 35 | is($result->{'mp_test_result'}->{'state'}, "CRITICAL", "State was correct"); |
| 36 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct"); | ||
| 32 | 37 | ||
| 33 | $result = NPTest->testCmd( "./check_swap -w 100% -c 1%" ); # 100% (always warn) | 38 | $result = NPTest->testCmd( "./check_swap -w 100% -c 1% $outputFormat" ); # 100% (always warn) |
| 34 | cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' ); | 39 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); |
| 35 | like( $result->output, $warnOutput, "Right output" ); | 40 | is($result->{'mp_test_result'}->{'state'}, "WARNING", "State was correct"); |
| 41 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct"); | ||
| 36 | 42 | ||
| 37 | $result = NPTest->testCmd( "./check_swap -w 100%" ); # 100% (single threshold, always warn) | 43 | $result = NPTest->testCmd( "./check_swap -w 100% $outputFormat" ); # 100% (single threshold, always warn) |
| 38 | cmp_ok( $result->return_code, "==", 1, 'Get warning because not 100% free' ); | 44 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); |
| 39 | like( $result->output, $warnOutput, "Right output" ); | 45 | is($result->{'mp_test_result'}->{'state'}, "WARNING", "State was correct"); |
| 46 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct"); | ||
| 40 | 47 | ||
| 41 | $result = NPTest->testCmd( "./check_swap -c 100%" ); # 100% (single threshold, always critical) | 48 | $result = NPTest->testCmd( "./check_swap -c 100% $outputFormat" ); # 100% (single threshold, always critical) |
| 42 | cmp_ok( $result->return_code, "==", 2, 'Get critical because not 100% free' ); | 49 | cmp_ok( $result->return_code, "==", 0, "Always OK" ); |
| 43 | like( $result->output, $failureOutput, "Right output" ); | 50 | is($result->{'mp_test_result'}->{'state'}, "CRITICAL", "State was correct"); |
| 51 | like($result->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $message, "Output was correct"); | ||
diff --git a/plugins/t/check_tcp.t b/plugins/t/check_tcp.t index cb4de53d..5c8fd0be 100644 --- a/plugins/t/check_tcp.t +++ b/plugins/t/check_tcp.t | |||
| @@ -21,19 +21,19 @@ my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname | |||
| 21 | my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); | 21 | my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost"); |
| 22 | my $internet_access = getTestParameter("NP_INTERNET_ACCESS", "Is this system directly connected to the internet?", "yes"); | 22 | my $internet_access = getTestParameter("NP_INTERNET_ACCESS", "Is this system directly connected to the internet?", "yes"); |
| 23 | 23 | ||
| 24 | my $successOutput = '/^TCP OK\s-\s+[0-9]?\.?[0-9]+ second response time on port [0-9]+/'; | 24 | my $successOutput = '/Connection time\s+[0-9]?\.?[0-9]+s is within thresholds+/'; |
| 25 | 25 | ||
| 26 | my $failedExpect = '/^TCP WARNING\s-\sUnexpected response from host/socket on port [0-9]+/'; | 26 | my $failedExpect = '/Answer failed to match/'; |
| 27 | 27 | ||
| 28 | my $t; | 28 | my $t; |
| 29 | 29 | ||
| 30 | $tests = $tests - 4 if $internet_access eq "no"; | 30 | $tests = $tests - 4 if $internet_access eq "no"; |
| 31 | plan tests => $tests; | 31 | plan tests => $tests; |
| 32 | 32 | ||
| 33 | $t += checkCmd( "./check_tcp $host_tcp_http -p 80 -wt 300 -ct 600", 0, $successOutput ); | 33 | $t += checkCmd( "./check_tcp $host_tcp_http -p 80 -w 300 -c 600", 0, $successOutput ); |
| 34 | $t += checkCmd( "./check_tcp $host_tcp_http -p 81 -wt 0 -ct 0 -to 1", 2 ); # use invalid port for this test | 34 | $t += checkCmd( "./check_tcp $host_tcp_http -p 81 -w 0 -c 0 -t 1", 2 ); # use invalid port for this test |
| 35 | $t += checkCmd( "./check_tcp $host_nonresponsive -p 80 -wt 0 -ct 0 -to 1", 2 ); | 35 | $t += checkCmd( "./check_tcp $host_nonresponsive -p 80 -w 0 -c 0 -t 1", 2 ); |
| 36 | $t += checkCmd( "./check_tcp $hostname_invalid -p 80 -wt 0 -ct 0 -to 1", 2 ); | 36 | $t += checkCmd( "./check_tcp $hostname_invalid -p 80 -w 0 -c 0 -t 1", 2 ); |
| 37 | if($internet_access ne "no") { | 37 | if($internet_access ne "no") { |
| 38 | $t += checkCmd( "./check_tcp -S -D 1 -H $host_tls_http -p 443", 0 ); | 38 | $t += checkCmd( "./check_tcp -S -D 1 -H $host_tls_http -p 443", 0 ); |
| 39 | $t += checkCmd( "./check_tcp -S -D 9000,1 -H $host_tls_http -p 443", 1 ); | 39 | $t += checkCmd( "./check_tcp -S -D 9000,1 -H $host_tls_http -p 443", 1 ); |
diff --git a/plugins/t/check_udp.t b/plugins/t/check_udp.t index 6c47d095..5cb9e6dc 100644 --- a/plugins/t/check_udp.t +++ b/plugins/t/check_udp.t | |||
| @@ -28,7 +28,7 @@ like ( $res->output, '/With UDP checks, a send/expect string must be specified. | |||
| 28 | 28 | ||
| 29 | $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s foo -e bar" ); | 29 | $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s foo -e bar" ); |
| 30 | cmp_ok( $res->return_code, '==', 2, "Errors correctly because no udp service running" ); | 30 | cmp_ok( $res->return_code, '==', 2, "Errors correctly because no udp service running" ); |
| 31 | like ( $res->output, '/No data received from host/', "Output OK"); | 31 | like ( $res->output, '/Received no data /', "Output OK"); |
| 32 | 32 | ||
| 33 | my $nc; | 33 | my $nc; |
| 34 | if(system("which nc.traditional >/dev/null 2>&1") == 0) { | 34 | if(system("which nc.traditional >/dev/null 2>&1") == 0) { |
| @@ -48,7 +48,7 @@ SKIP: { | |||
| 48 | sleep 1; | 48 | sleep 1; |
| 49 | $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s '' -e barbar -4" ); | 49 | $res = NPTest->testCmd( "./check_udp -H localhost -p 3333 -s '' -e barbar -4" ); |
| 50 | cmp_ok( $res->return_code, '==', 0, "Got barbar response back" ); | 50 | cmp_ok( $res->return_code, '==', 0, "Got barbar response back" ); |
| 51 | like ( $res->output, '/\[barbar\]/', "Output OK"); | 51 | like ( $res->output, '/answer of the server matched/', "Output OK"); |
| 52 | close NC; | 52 | close NC; |
| 53 | 53 | ||
| 54 | # Start up a udp server listening on port 3333, quit after 3 seconds | 54 | # Start up a udp server listening on port 3333, quit after 3 seconds |
diff --git a/plugins/t/check_users.t b/plugins/t/check_users.t index 21c3e53d..446e0476 100644 --- a/plugins/t/check_users.t +++ b/plugins/t/check_users.t | |||
| @@ -15,8 +15,8 @@ use NPTest; | |||
| 15 | use vars qw($tests); | 15 | use vars qw($tests); |
| 16 | BEGIN {$tests = 12; plan tests => $tests} | 16 | BEGIN {$tests = 12; plan tests => $tests} |
| 17 | 17 | ||
| 18 | my $successOutput = '/^USERS OK - [0-9]+ users currently logged in/'; | 18 | my $successOutput = '/[0-9]+ users currently logged in/'; |
| 19 | my $failureOutput = '/^USERS CRITICAL - [0-9]+ users currently logged in/'; | 19 | my $failureOutput = '/[0-9]+ users currently logged in/'; |
| 20 | my $wrongOptionOutput = '/Usage:/'; | 20 | my $wrongOptionOutput = '/Usage:/'; |
| 21 | 21 | ||
| 22 | my $t; | 22 | my $t; |
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index eaa9f518..52c5ad1c 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | # Email Address []:devel@monitoring-plugins.org | 15 | # Email Address []:devel@monitoring-plugins.org |
| 16 | 16 | ||
| 17 | use strict; | 17 | use strict; |
| 18 | use warnings; | ||
| 18 | use Test::More; | 19 | use Test::More; |
| 19 | use NPTest; | 20 | use NPTest; |
| 20 | use FindBin qw($Bin); | 21 | use FindBin qw($Bin); |
| @@ -245,21 +246,21 @@ SKIP: { | |||
| 245 | 246 | ||
| 246 | $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); | 247 | $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); |
| 247 | is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); | 248 | is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); |
| 248 | is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on $expiry.", "output ok" ); | 249 | like( $result->output, '/.*Certificate \'Monitoring Plugins\' will expire on ' . quotemeta($expiry) . '.*/', "output ok" ); |
| 249 | 250 | ||
| 250 | $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); | 251 | $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); |
| 251 | is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); | 252 | is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); |
| 252 | like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); | 253 | like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" ); |
| 253 | 254 | ||
| 254 | # Expired cert tests | 255 | # Expired cert tests |
| 255 | $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); | 256 | $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); |
| 256 | is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" ); | 257 | is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" ); |
| 257 | like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); | 258 | like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" ); |
| 258 | 259 | ||
| 259 | $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); | 260 | $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); |
| 260 | is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); | 261 | is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); |
| 261 | is( $result->output, | 262 | like( $result->output, |
| 262 | 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.', | 263 | '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan\s+2 12:00:00 2008 \+0000.*/', |
| 263 | "output ok" ); | 264 | "output ok" ); |
| 264 | 265 | ||
| 265 | } | 266 | } |
| @@ -274,19 +275,19 @@ SKIP: { | |||
| 274 | $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$"; | 275 | $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$"; |
| 275 | $result = NPTest->testCmd( $cmd ); | 276 | $result = NPTest->testCmd( $cmd ); |
| 276 | is( $result->return_code, 0, $cmd); | 277 | is( $result->return_code, 0, $cmd); |
| 277 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 278 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 278 | 279 | ||
| 279 | # http with virtual port (!= 80) | 280 | # http with virtual port (!= 80) |
| 280 | $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$"; | 281 | $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$"; |
| 281 | $result = NPTest->testCmd( $cmd ); | 282 | $result = NPTest->testCmd( $cmd ); |
| 282 | is( $result->return_code, 0, $cmd); | 283 | is( $result->return_code, 0, $cmd); |
| 283 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 284 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 284 | 285 | ||
| 285 | # http with virtual port (80) | 286 | # http with virtual port (80) |
| 286 | $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$"; | 287 | $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$"; |
| 287 | $result = NPTest->testCmd( $cmd ); | 288 | $result = NPTest->testCmd( $cmd ); |
| 288 | is( $result->return_code, 0, $cmd); | 289 | is( $result->return_code, 0, $cmd); |
| 289 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 290 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 290 | } | 291 | } |
| 291 | 292 | ||
| 292 | # and the same for SSL | 293 | # and the same for SSL |
| @@ -296,19 +297,19 @@ SKIP: { | |||
| 296 | $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$"; | 297 | $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$"; |
| 297 | $result = NPTest->testCmd( $cmd ); | 298 | $result = NPTest->testCmd( $cmd ); |
| 298 | is( $result->return_code, 0, $cmd); | 299 | is( $result->return_code, 0, $cmd); |
| 299 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 300 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 300 | 301 | ||
| 301 | # https with virtual port (!= 443) | 302 | # https with virtual port (!= 443) |
| 302 | $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$"; | 303 | $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$"; |
| 303 | $result = NPTest->testCmd( $cmd ); | 304 | $result = NPTest->testCmd( $cmd ); |
| 304 | is( $result->return_code, 0, $cmd); | 305 | is( $result->return_code, 0, $cmd); |
| 305 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 306 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 306 | 307 | ||
| 307 | # https with virtual port (443) | 308 | # https with virtual port (443) |
| 308 | $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$"; | 309 | $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$"; |
| 309 | $result = NPTest->testCmd( $cmd ); | 310 | $result = NPTest->testCmd( $cmd ); |
| 310 | is( $result->return_code, 0, $cmd); | 311 | is( $result->return_code, 0, $cmd); |
| 311 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 312 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 312 | } | 313 | } |
| 313 | 314 | ||
| 314 | 315 | ||
| @@ -321,165 +322,165 @@ sub run_common_tests { | |||
| 321 | 322 | ||
| 322 | $result = NPTest->testCmd( "$command -u /file/root" ); | 323 | $result = NPTest->testCmd( "$command -u /file/root" ); |
| 323 | is( $result->return_code, 0, "/file/root"); | 324 | is( $result->return_code, 0, "/file/root"); |
| 324 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); | 325 | like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" ); |
| 325 | 326 | ||
| 326 | $result = NPTest->testCmd( "$command -u /file/root -s Root" ); | 327 | $result = NPTest->testCmd( "$command -u /file/root -s Root" ); |
| 327 | is( $result->return_code, 0, "/file/root search for string"); | 328 | is( $result->return_code, 0, "/file/root search for string"); |
| 328 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); | 329 | like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" ); |
| 329 | 330 | ||
| 330 | $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" ); | 331 | $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" ); |
| 331 | is( $result->return_code, 2, "Missing string check"); | 332 | is( $result->return_code, 2, "Missing string check"); |
| 332 | like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); | 333 | like( $result->output, qr%string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); |
| 333 | 334 | ||
| 334 | $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" ); | 335 | $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" ); |
| 335 | is( $result->return_code, 2, "Missing string check"); | 336 | is( $result->return_code, 2, "Missing string check"); |
| 336 | like( $result->output, qr%HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); | 337 | like( $result->output, qr%string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); |
| 337 | 338 | ||
| 338 | $result = NPTest->testCmd( "$command -u /header_check -d foo" ); | 339 | $result = NPTest->testCmd( "$command -u /header_check -d foo" ); |
| 339 | is( $result->return_code, 0, "header_check search for string"); | 340 | is( $result->return_code, 0, "header_check search for string"); |
| 340 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" ); | 341 | like( $result->output, '/.*HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second.*/', "Output correct" ); |
| 341 | 342 | ||
| 342 | $result = NPTest->testCmd( "$command -u /header_check -d bar" ); | 343 | $result = NPTest->testCmd( "$command -u /header_check -d bar" ); |
| 343 | is( $result->return_code, 2, "Missing header string check"); | 344 | is( $result->return_code, 2, "Missing header string check"); |
| 344 | like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); | 345 | like( $result->output, qr%header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); |
| 345 | 346 | ||
| 346 | $result = NPTest->testCmd( "$command -u /header_broken_check" ); | 347 | $result = NPTest->testCmd( "$command -u /header_broken_check" ); |
| 347 | is( $result->return_code, 0, "header_check search for string"); | 348 | is( $result->return_code, 0, "header_check search for string"); |
| 348 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second/', "Output correct" ); | 349 | like( $result->output, '/.*HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second.*/', "Output correct" ); |
| 349 | 350 | ||
| 350 | my $cmd; | 351 | my $cmd; |
| 351 | $cmd = "$command -u /slow"; | 352 | $cmd = "$command -u /slow"; |
| 352 | $result = NPTest->testCmd( $cmd ); | 353 | $result = NPTest->testCmd( $cmd ); |
| 353 | is( $result->return_code, 0, "$cmd"); | 354 | is( $result->return_code, 0, "$cmd"); |
| 354 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 355 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 355 | $result->output =~ /in ([\d\.]+) second/; | 356 | $result->output =~ /in ([\d\.]+) second/; |
| 356 | cmp_ok( $1, ">", 1, "Time is > 1 second" ); | 357 | cmp_ok( $1, ">", 1, "Time is > 1 second" ); |
| 357 | 358 | ||
| 358 | $cmd = "$command -u /statuscode/200"; | 359 | $cmd = "$command -u /statuscode/200"; |
| 359 | $result = NPTest->testCmd( $cmd ); | 360 | $result = NPTest->testCmd( $cmd ); |
| 360 | is( $result->return_code, 0, $cmd); | 361 | is( $result->return_code, 0, $cmd); |
| 361 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 362 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 362 | 363 | ||
| 363 | $cmd = "$command -u /statuscode/200 -e 200"; | 364 | $cmd = "$command -u /statuscode/200 -e 200"; |
| 364 | $result = NPTest->testCmd( $cmd ); | 365 | $result = NPTest->testCmd( $cmd ); |
| 365 | is( $result->return_code, 0, $cmd); | 366 | is( $result->return_code, 0, $cmd); |
| 366 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 367 | like( $result->output, '/.*Status line output matched "200".*/', "Output correct: ".$result->output ); |
| 367 | 368 | ||
| 368 | $cmd = "$command -u /statuscode/201"; | 369 | $cmd = "$command -u /statuscode/201"; |
| 369 | $result = NPTest->testCmd( $cmd ); | 370 | $result = NPTest->testCmd( $cmd ); |
| 370 | is( $result->return_code, 0, $cmd); | 371 | is( $result->return_code, 0, $cmd); |
| 371 | like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); | 372 | like( $result->output, '/.*HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 372 | 373 | ||
| 373 | $cmd = "$command -u /statuscode/201 -e 201"; | 374 | $cmd = "$command -u /statuscode/201 -e 201"; |
| 374 | $result = NPTest->testCmd( $cmd ); | 375 | $result = NPTest->testCmd( $cmd ); |
| 375 | is( $result->return_code, 0, $cmd); | 376 | is( $result->return_code, 0, $cmd); |
| 376 | like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); | 377 | like( $result->output, '/.*Status line output matched "201".*/', "Output correct: ".$result->output ); |
| 377 | 378 | ||
| 378 | $cmd = "$command -u /statuscode/201 -e 200"; | 379 | $cmd = "$command -u /statuscode/201 -e 200"; |
| 379 | $result = NPTest->testCmd( $cmd ); | 380 | $result = NPTest->testCmd( $cmd ); |
| 380 | is( $result->return_code, 2, $cmd); | 381 | is( $result->return_code, 2, $cmd); |
| 381 | like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output ); | 382 | like( $result->output, '/.*Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created.*/', "Output correct: ".$result->output ); |
| 382 | 383 | ||
| 383 | $cmd = "$command -u /statuscode/200 -e 200,201,202"; | 384 | $cmd = "$command -u /statuscode/200 -e 200,201,202"; |
| 384 | $result = NPTest->testCmd( $cmd ); | 385 | $result = NPTest->testCmd( $cmd ); |
| 385 | is( $result->return_code, 0, $cmd); | 386 | is( $result->return_code, 0, $cmd); |
| 386 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 387 | like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output ); |
| 387 | 388 | ||
| 388 | $cmd = "$command -u /statuscode/201 -e 200,201,202"; | 389 | $cmd = "$command -u /statuscode/201 -e 200,201,202"; |
| 389 | $result = NPTest->testCmd( $cmd ); | 390 | $result = NPTest->testCmd( $cmd ); |
| 390 | is( $result->return_code, 0, $cmd); | 391 | is( $result->return_code, 0, $cmd); |
| 391 | like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 392 | like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output ); |
| 392 | 393 | ||
| 393 | $cmd = "$command -u /statuscode/203 -e 200,201,202"; | 394 | $cmd = "$command -u /statuscode/203 -e 200,201,202"; |
| 394 | $result = NPTest->testCmd( $cmd ); | 395 | $result = NPTest->testCmd( $cmd ); |
| 395 | is( $result->return_code, 2, $cmd); | 396 | is( $result->return_code, 2, $cmd); |
| 396 | like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information/', "Output correct: ".$result->output ); | 397 | like( $result->output, '/.*Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information.*/', "Output correct: ".$result->output ); |
| 397 | 398 | ||
| 398 | $cmd = "$command -j HEAD -u /method"; | 399 | $cmd = "$command -j HEAD -u /method"; |
| 399 | $result = NPTest->testCmd( $cmd ); | 400 | $result = NPTest->testCmd( $cmd ); |
| 400 | is( $result->return_code, 0, $cmd); | 401 | is( $result->return_code, 0, $cmd); |
| 401 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 402 | like( $result->output, '/.*HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 402 | 403 | ||
| 403 | $cmd = "$command -j POST -u /method"; | 404 | $cmd = "$command -j POST -u /method"; |
| 404 | $result = NPTest->testCmd( $cmd ); | 405 | $result = NPTest->testCmd( $cmd ); |
| 405 | is( $result->return_code, 0, $cmd); | 406 | is( $result->return_code, 0, $cmd); |
| 406 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 407 | like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 407 | 408 | ||
| 408 | $cmd = "$command -j GET -u /method"; | 409 | $cmd = "$command -j GET -u /method"; |
| 409 | $result = NPTest->testCmd( $cmd ); | 410 | $result = NPTest->testCmd( $cmd ); |
| 410 | is( $result->return_code, 0, $cmd); | 411 | is( $result->return_code, 0, $cmd); |
| 411 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 412 | like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 412 | 413 | ||
| 413 | $cmd = "$command -u /method"; | 414 | $cmd = "$command -u /method"; |
| 414 | $result = NPTest->testCmd( $cmd ); | 415 | $result = NPTest->testCmd( $cmd ); |
| 415 | is( $result->return_code, 0, $cmd); | 416 | is( $result->return_code, 0, $cmd); |
| 416 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 417 | like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 417 | 418 | ||
| 418 | $cmd = "$command -P foo -u /method"; | 419 | $cmd = "$command -P foo -u /method"; |
| 419 | $result = NPTest->testCmd( $cmd ); | 420 | $result = NPTest->testCmd( $cmd ); |
| 420 | is( $result->return_code, 0, $cmd); | 421 | is( $result->return_code, 0, $cmd); |
| 421 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 422 | like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 422 | 423 | ||
| 423 | $cmd = "$command -j DELETE -u /method"; | 424 | $cmd = "$command -j DELETE -u /method"; |
| 424 | $result = NPTest->testCmd( $cmd ); | 425 | $result = NPTest->testCmd( $cmd ); |
| 425 | is( $result->return_code, 1, $cmd); | 426 | is( $result->return_code, 1, $cmd); |
| 426 | like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output ); | 427 | like( $result->output, '/.*HTTP/1.1 405 Method Not Allowed.*/', "Output correct: ".$result->output ); |
| 427 | 428 | ||
| 428 | $cmd = "$command -j foo -u /method"; | 429 | $cmd = "$command -j foo -u /method"; |
| 429 | $result = NPTest->testCmd( $cmd ); | 430 | $result = NPTest->testCmd( $cmd ); |
| 430 | is( $result->return_code, 2, $cmd); | 431 | is( $result->return_code, 2, $cmd); |
| 431 | like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output ); | 432 | like( $result->output, '/.*HTTP/1.1 501 Not Implemented.*/', "Output correct: ".$result->output ); |
| 432 | 433 | ||
| 433 | $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude"; | 434 | $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude"; |
| 434 | $result = NPTest->testCmd( $cmd ); | 435 | $result = NPTest->testCmd( $cmd ); |
| 435 | is( $result->return_code, 0, $cmd); | 436 | is( $result->return_code, 0, $cmd); |
| 436 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 437 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 437 | 438 | ||
| 438 | $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude"; | 439 | $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude"; |
| 439 | $result = NPTest->testCmd( $cmd ); | 440 | $result = NPTest->testCmd( $cmd ); |
| 440 | is( $result->return_code, 0, $cmd); | 441 | is( $result->return_code, 0, $cmd); |
| 441 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 442 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 442 | 443 | ||
| 443 | # To confirm that the free doesn't segfault | 444 | # To confirm that the free doesn't segfault |
| 444 | $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude"; | 445 | $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude"; |
| 445 | $result = NPTest->testCmd( $cmd ); | 446 | $result = NPTest->testCmd( $cmd ); |
| 446 | is( $result->return_code, 0, $cmd); | 447 | is( $result->return_code, 0, $cmd); |
| 447 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 448 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 448 | 449 | ||
| 449 | $cmd = "$command -u /redirect"; | 450 | $cmd = "$command -u /redirect"; |
| 450 | $result = NPTest->testCmd( $cmd ); | 451 | $result = NPTest->testCmd( $cmd ); |
| 451 | is( $result->return_code, 0, $cmd); | 452 | is( $result->return_code, 0, $cmd); |
| 452 | like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 453 | like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 453 | 454 | ||
| 454 | $cmd = "$command -f follow -u /redirect"; | 455 | $cmd = "$command -f follow -u /redirect"; |
| 455 | $result = NPTest->testCmd( $cmd ); | 456 | $result = NPTest->testCmd( $cmd ); |
| 456 | is( $result->return_code, 0, $cmd); | 457 | is( $result->return_code, 0, $cmd); |
| 457 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 458 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 458 | 459 | ||
| 459 | $cmd = "$command -u /redirect -k 'follow: me'"; | 460 | $cmd = "$command -u /redirect -k 'follow: me'"; |
| 460 | $result = NPTest->testCmd( $cmd ); | 461 | $result = NPTest->testCmd( $cmd ); |
| 461 | is( $result->return_code, 0, $cmd); | 462 | is( $result->return_code, 0, $cmd); |
| 462 | like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 463 | like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 463 | 464 | ||
| 464 | $cmd = "$command -f follow -u /redirect -k 'follow: me'"; | 465 | $cmd = "$command -f follow -u /redirect -k 'follow: me'"; |
| 465 | $result = NPTest->testCmd( $cmd ); | 466 | $result = NPTest->testCmd( $cmd ); |
| 466 | is( $result->return_code, 0, $cmd); | 467 | is( $result->return_code, 0, $cmd); |
| 467 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 468 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 468 | 469 | ||
| 469 | $cmd = "$command -f sticky -u /redirect -k 'follow: me'"; | 470 | $cmd = "$command -f sticky -u /redirect -k 'follow: me'"; |
| 470 | $result = NPTest->testCmd( $cmd ); | 471 | $result = NPTest->testCmd( $cmd ); |
| 471 | is( $result->return_code, 0, $cmd); | 472 | is( $result->return_code, 0, $cmd); |
| 472 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 473 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 473 | 474 | ||
| 474 | $cmd = "$command -f stickyport -u /redirect -k 'follow: me'"; | 475 | $cmd = "$command -f stickyport -u /redirect -k 'follow: me'"; |
| 475 | $result = NPTest->testCmd( $cmd ); | 476 | $result = NPTest->testCmd( $cmd ); |
| 476 | is( $result->return_code, 0, $cmd); | 477 | is( $result->return_code, 0, $cmd); |
| 477 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 478 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 478 | 479 | ||
| 479 | $cmd = "$command -f follow -u /redirect_rel -s redirected"; | 480 | $cmd = "$command -f follow -u /redirect_rel -s redirected"; |
| 480 | $result = NPTest->testCmd( $cmd ); | 481 | $result = NPTest->testCmd( $cmd ); |
| 481 | is( $result->return_code, 0, $cmd); | 482 | is( $result->return_code, 0, $cmd); |
| 482 | like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); | 483 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 483 | 484 | ||
| 484 | # These tests may block | 485 | # These tests may block |
| 485 | # stickyport - on full urlS port is set back to 80 otherwise | 486 | # stickyport - on full urlS port is set back to 80 otherwise |
diff --git a/plugins/tests/check_nt.t b/plugins/tests/check_nt.t deleted file mode 100755 index 223d4933..00000000 --- a/plugins/tests/check_nt.t +++ /dev/null | |||
| @@ -1,80 +0,0 @@ | |||
| 1 | #! /usr/bin/perl -w -I .. | ||
| 2 | # | ||
| 3 | # Test check_nt by having a stub check_nt daemon | ||
| 4 | # | ||
| 5 | |||
| 6 | use strict; | ||
| 7 | use Test::More; | ||
| 8 | use NPTest; | ||
| 9 | use FindBin qw($Bin); | ||
| 10 | |||
| 11 | use IO::Socket; | ||
| 12 | use IO::Select; | ||
| 13 | use POSIX; | ||
| 14 | |||
| 15 | my $port = 50000 + int(rand(1000)); | ||
| 16 | |||
| 17 | my $pid = fork(); | ||
| 18 | if ($pid) { | ||
| 19 | # Parent | ||
| 20 | #print "parent\n"; | ||
| 21 | # give our webserver some time to startup | ||
| 22 | sleep(1); | ||
| 23 | } else { | ||
| 24 | # Child | ||
| 25 | #print "child\n"; | ||
| 26 | |||
| 27 | my $server = IO::Socket::INET->new( | ||
| 28 | LocalPort => $port, | ||
| 29 | Type => SOCK_STREAM, | ||
| 30 | Reuse => 1, | ||
| 31 | Proto => "tcp", | ||
| 32 | Listen => 10, | ||
| 33 | ) or die "Cannot be a tcp server on port $port: $@"; | ||
| 34 | |||
| 35 | $server->autoflush(1); | ||
| 36 | |||
| 37 | print "Please contact me at port $port\n"; | ||
| 38 | while (my $client = $server->accept ) { | ||
| 39 | my $data = ""; | ||
| 40 | my $rv = $client->recv($data, POSIX::BUFSIZ, 0); | ||
| 41 | |||
| 42 | my ($password, $command, $arg) = split('&', $data); | ||
| 43 | |||
| 44 | if ($command eq "4") { | ||
| 45 | if ($arg eq "c") { | ||
| 46 | print $client "930000000&1000000000"; | ||
| 47 | } elsif ($arg eq "d") { | ||
| 48 | print $client "UNKNOWN: Drive is not a fixed drive"; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | exit; | ||
| 53 | } | ||
| 54 | |||
| 55 | END { if ($pid) { print "Killing $pid\n"; kill "INT", $pid } }; | ||
| 56 | |||
| 57 | if ($ARGV[0] && $ARGV[0] eq "-d") { | ||
| 58 | sleep 1000; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (-x "./check_nt") { | ||
| 62 | plan tests => 5; | ||
| 63 | } else { | ||
| 64 | plan skip_all => "No check_nt compiled"; | ||
| 65 | } | ||
| 66 | |||
| 67 | my $result; | ||
| 68 | my $command = "./check_nt -H 127.0.0.1 -p $port"; | ||
| 69 | |||
| 70 | $result = NPTest->testCmd( "$command -v USEDDISKSPACE -l c" ); | ||
| 71 | is( $result->return_code, 0, "USEDDISKSPACE c"); | ||
| 72 | is( $result->output, q{c:\ - total: 0.93 Gb - used: 0.07 Gb (7%) - free 0.87 Gb (93%) | 'c:\ Used Space'=0.07Gb;0.00;0.00;0.00;0.93}, "Output right" ); | ||
| 73 | |||
| 74 | $result = NPTest->testCmd( "$command -v USEDDISKSPACE -l d" ); | ||
| 75 | is( $result->return_code, 3, "USEDDISKSPACE d - invalid"); | ||
| 76 | is( $result->output, "Free disk space : Invalid drive", "Output right" ); | ||
| 77 | |||
| 78 | $result = NPTest->testCmd( "./check_nt -v USEDDISKSPACE -l d" ); | ||
| 79 | is( $result->return_code, 3, "Fail if -H missing"); | ||
| 80 | |||
diff --git a/plugins/tests/check_snmp.t b/plugins/tests/check_snmp.t index bfe42e16..26d67898 100755 --- a/plugins/tests/check_snmp.t +++ b/plugins/tests/check_snmp.t | |||
| @@ -4,12 +4,13 @@ | |||
| 4 | # | 4 | # |
| 5 | 5 | ||
| 6 | use strict; | 6 | use strict; |
| 7 | use warnings; | ||
| 7 | use Test::More; | 8 | use Test::More; |
| 8 | use NPTest; | 9 | use NPTest; |
| 9 | use FindBin qw($Bin); | 10 | use FindBin qw($Bin); |
| 10 | use POSIX qw/strftime/; | 11 | use POSIX qw/strftime/; |
| 11 | 12 | ||
| 12 | my $tests = 81; | 13 | my $tests = 75; |
| 13 | # Check that all dependent modules are available | 14 | # Check that all dependent modules are available |
| 14 | eval { | 15 | eval { |
| 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"); |
| 78 | cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); | 79 | cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); |
| 79 | like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); | 80 | like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines"); |
| 80 | like($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 | ||
| 83 | IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version | ||
| 84 | 12.2(20)EWA, RELEASE SOFTWARE (fc1) | ||
| 85 | Technical Support: http://www.cisco.com/techsupport | ||
| 86 | Copyright (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"); |
| 91 | cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); | 84 | cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); |
| 92 | like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); | 85 | # like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); |
| 93 | like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software ').'"?Alice"?'.quotemeta(' Kisco Outernetwork Oserating Gystem Totware | | 86 | like($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: | 87 | like($res->output, '/.*Alice.*/m', "String contains all lines with multiple OIDs"); |
| 95 | "Cisco Internetwork Operating System Software | 88 | like($res->output, '/.*Kisco Outernetwork Oserating Gystem Totware.*/m', "String contains all lines with multiple OIDs"); |
| 96 | IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version | ||
| 97 | 12.2(20)EWA, RELEASE SOFTWARE (fc1) | ||
| 98 | Technical Support: http://www.cisco.com/techsupport | ||
| 99 | Copyright (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 | ||
| 103 | Copyleft (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"); |
| 106 | like($res->output, '/'.quotemeta('SNMP OK - This should not confuse check_snmp \"parser\" | | 91 | cmp_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: |
| 109 | into 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"); |
| 112 | like($res->output, '/'.quotemeta('SNMP OK - It\'s getting even harder if the line | | 98 | cmp_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: |
| 115 | ends 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"); |
| 118 | like($res->output, '/'.quotemeta('SNMP OK - And now have fun with with this: \"C:\\\\\" | | 105 | cmp_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: |
| 121 | because 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 | ||
| 123 | system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*"); | 111 | system("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'" ); |
| 201 | is($res->return_code, 0, "OK as string matches" ); | 190 | is($res->return_code, 0, "OK as string matches" ); |
| 202 | is($res->output, 'SNMP OK - "stringtests" | ', "Good string match" ); | 191 | like($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" ); |
| 205 | is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); | 194 | is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); |
| 206 | is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Failed string match" ); | 195 | like($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'" ); |
| 209 | is($res->return_code, 2, "CRITICAL as string matches but inverted" ); | 198 | is($res->return_code, 2, "CRITICAL as string matches but inverted" ); |
| 210 | is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Inverted string match" ); | 199 | like($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" ); |
| 213 | is($res->return_code, 0, "OK as string doesn't match but inverted" ); | 202 | is($res->return_code, 0, "OK as string doesn't match but inverted" ); |
| 214 | is($res->output, 'SNMP OK - "stringtests" | ', "OK as inverted string no match" ); | 203 | like($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" ); |
| 217 | is($res->return_code, 1, "Numeric in string test" ); | 206 | # a string is a string and not a number |
| 218 | is($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" ); | 207 | is($res->return_code, 0, "Numeric in string test" ); |
| 208 | like($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" ); |
| 221 | is($res->return_code, 0, "Not really numeric test" ); | 211 | is($res->return_code, 0, "Not really numeric test" ); |
| 222 | is($res->output, 'SNMP OK - "87.4startswithnumberbutshouldbestring" | ', "Check string with numeric start is still string" ); | 212 | like($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" ); |
| 225 | is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); | 215 | is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); |
| 226 | is($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)" ); | 216 | like($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'" ); |
| 229 | is($res->return_code, 0, "String check should check whole string, not a parsed number" ); | 219 | is($res->return_code, 0, "String check should check whole string, not a parsed number" ); |
| 230 | is($res->output, 'SNMP OK - "CUSTOM CHECK OK: foo is 12345" | ', "String check with numbers returns whole string"); | 220 | like($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:" ); |
| 233 | is($res->return_code, 0, "Negative integer check OK" ); | 223 | is($res->return_code, 0, "Negative integer check OK" ); |
| 234 | is($res->output, 'SNMP OK - -2 | iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3: ', "Negative integer check OK output" ); | 224 | like($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:" ); |
| 237 | is($res->return_code, 1, "Negative integer check WARNING" ); | 227 | is($res->return_code, 1, "Negative integer check WARNING" ); |
| 238 | is($res->output, 'SNMP WARNING - *-3* | iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3: ', "Negative integer check WARNING output" ); | 228 | like($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:" ); |
| 241 | is($res->return_code, 2, "Negative integer check CRITICAL" ); | 231 | is($res->return_code, 2, "Negative integer check CRITICAL" ); |
| 242 | is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3: ', "Negative integer check CRITICAL output" ); | 232 | like($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:" ); |
| 245 | is($res->return_code, 1, "Negative integer as string, WARNING" ); | 235 | is($res->return_code, 1, "Negative integer as string, WARNING" ); |
| 246 | is($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" ); | 236 | like($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:" ); |
| 249 | is($res->return_code, 2, "Negative integer as string, CRITICAL" ); | 239 | is($res->return_code, 2, "Negative integer as string, CRITICAL" ); |
| 250 | is($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" ); | 240 | like($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 |
| 253 | is($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'" ); |
| 254 | is($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'" ); |
| 257 | is($res->return_code, 1, "Negative float WARNING" ); | 248 | # is($res->return_code, 1, "Negative float WARNING" ); |
| 258 | is($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'" ); |
| 261 | is($res->return_code, 0, "Multiple OIDs with thresholds" ); | 252 | is($res->return_code, 0, "Multiple OIDs with thresholds" ); |
| 262 | like($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" ); | 253 | like($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'" ); |
| 265 | is($res->return_code, 1, "Multiple OIDs with thresholds" ); | 256 | is($res->return_code, 1, "Multiple OIDs with thresholds" ); |
| 266 | like($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" ); | 257 | like($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" ); |
| 269 | is($res->return_code, 2, "Multiple OIDs with some thresholds" ); | 260 | is($res->return_code, 2, "Multiple OIDs with some thresholds" ); |
| 270 | like($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" ); | 261 | like($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"); |
| 273 | is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); | 264 | is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); |
| 274 | is($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" ); | 265 | like($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"); |
| 277 | is($res->return_code, 0, "Test multiply RC" ); | 268 | is($res->return_code, 0, "Test multiply RC" ); |
| 278 | is($res->output,'SNMP OK - 4.200000 | iso.3.6.1.4.1.8072.3.2.67.19=4.200000 ' , "Test multiply .1 output" ); | 269 | like($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"); |
| 281 | is($res->return_code, 0, "Test multiply RC + format" ); | 272 | is($res->return_code, 0, "Test multiply RC" ); |
| 282 | is($res->output, 'SNMP OK - 4.20 | iso.3.6.1.4.1.8072.3.2.67.19=4.20 ', "Test multiply .1 output + format" ); | 273 | like($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"); |
| 285 | is($res->return_code, 1, "Test multiply RC + format + thresholds" ); | 276 | is($res->return_code, 1, "Test multiply RC + thresholds" ); |
| 286 | is($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" ); | 277 | like($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 |
| 7 | use warnings; | ||
| 7 | use NetSNMP::OID qw(:all); | 8 | use NetSNMP::OID qw(:all); |
| 8 | use NetSNMP::agent; | 9 | use NetSNMP::agent; |
| 9 | use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64); | 10 | use 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 |
| 11 | sub uint64 { return $_ } | 12 | sub uint64 { return $_ } |
| 12 | 13 | ||
| @@ -22,21 +23,82 @@ IOS (tm) Catalyst 4000 "L3" Switch Software (cat4000-I9K91S-M), Version | |||
| 22 | Technical Support: http://www.cisco.com/techsupport | 23 | Technical Support: http://www.cisco.com/techsupport |
| 23 | Copyright (c) 1986-2004 by cisco Systems, Inc. | 24 | Copyright (c) 1986-2004 by cisco Systems, Inc. |
| 24 | '; | 25 | '; |
| 25 | my $multilin2 = "Kisco Outernetwork Oserating Gystem Totware | 26 | my $multiline2 = "Kisco Outernetwork Oserating Gystem Totware |
| 26 | Copyleft (c) 2400-2689 by kisco Systrems, Inc."; | 27 | Copyleft (c) 2400-2689 by kisco Systrems, Inc."; |
| 27 | my $multilin3 = 'This should not confuse check_snmp "parser" | 28 | my $multiline3 = 'This should not confuse check_snmp "parser" |
| 28 | into thinking there is no 2nd line'; | 29 | into thinking there is no 2nd line'; |
| 29 | my $multilin4 = 'It\'s getting even harder if the line | 30 | my $multiline4 = 'It\'s getting even harder if the line |
| 30 | ends with with this: C:\\'; | 31 | ends with with this: C:\\'; |
| 31 | my $multilin5 = 'And now have fun with with this: "C:\\" | 32 | my $multiline5 = 'And now have fun with with this: "C:\\" |
| 32 | because we\'re not done yet!'; | 33 | because 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 |
| 36 | my @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 ); | 37 | my @fields = (ASN_OCTET_STR, # 0 |
| 37 | my @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 | ); | ||
| 58 | my @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 |
| 39 | my @incrts = (undef, undef, undef, undef, undef, 1000, -500, 1000, 100000, undef, 666, undef, undef, undef, undef, undef, -1, undef, undef, 0 ); | 80 | my @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 |
| 42 | my $oidelts; | 104 | my $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 | ||
| 22 | perl do "tests/check_snmp_agent.pl"; | 22 | perl do "./tests/check_snmp_agent.pl"; |
| 23 | 23 | ||
diff --git a/lib/tests/test_disk.c b/plugins/tests/test_check_disk.c index c18db7a4..32b46c2d 100644 --- a/lib/tests/test_disk.c +++ b/plugins/tests/test_check_disk.c | |||
| @@ -17,28 +17,17 @@ | |||
| 17 | *****************************************************************************/ | 17 | *****************************************************************************/ |
| 18 | 18 | ||
| 19 | #include "common.h" | 19 | #include "common.h" |
| 20 | #include "utils_disk.h" | 20 | #include "../check_disk.d/utils_disk.h" |
| 21 | #include "tap.h" | 21 | #include "../../tap/tap.h" |
| 22 | #include "regex.h" | 22 | #include "regex.h" |
| 23 | 23 | ||
| 24 | void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc); | 24 | void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, |
| 25 | int expect, char *desc); | ||
| 25 | 26 | ||
| 26 | int main(int argc, char **argv) { | 27 | int main(int argc, char **argv) { |
| 27 | struct name_list *exclude_filesystem = NULL; | 28 | plan_tests(35); |
| 28 | struct name_list *exclude_fstype = NULL; | ||
| 29 | struct name_list *dummy_mountlist = NULL; | ||
| 30 | struct name_list *temp_name; | ||
| 31 | struct parameter_list *paths = NULL; | ||
| 32 | struct parameter_list *p, *prev = NULL, *last = NULL; | ||
| 33 | |||
| 34 | struct mount_entry *dummy_mount_list; | ||
| 35 | struct mount_entry *me; | ||
| 36 | struct mount_entry **mtail = &dummy_mount_list; | ||
| 37 | int cflags = REG_NOSUB | REG_EXTENDED; | ||
| 38 | int found = 0, count = 0; | ||
| 39 | |||
| 40 | plan_tests(33); | ||
| 41 | 29 | ||
| 30 | struct name_list *exclude_filesystem = NULL; | ||
| 42 | ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); | 31 | ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); |
| 43 | np_add_name(&exclude_filesystem, "/var/log"); | 32 | np_add_name(&exclude_filesystem, "/var/log"); |
| 44 | ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now"); | 33 | ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now"); |
| @@ -47,6 +36,7 @@ int main(int argc, char **argv) { | |||
| 47 | ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now"); | 36 | ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now"); |
| 48 | ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list"); | 37 | ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list"); |
| 49 | 38 | ||
| 39 | struct name_list *exclude_fstype = NULL; | ||
| 50 | ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list"); | 40 | ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list"); |
| 51 | np_add_name(&exclude_fstype, "iso9660"); | 41 | np_add_name(&exclude_fstype, "iso9660"); |
| 52 | ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now"); | 42 | ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now"); |
| @@ -59,7 +49,9 @@ int main(int argc, char **argv) { | |||
| 59 | } | 49 | } |
| 60 | */ | 50 | */ |
| 61 | 51 | ||
| 62 | me = (struct mount_entry *)malloc(sizeof *me); | 52 | struct mount_entry *dummy_mount_list; |
| 53 | struct mount_entry **mtail = &dummy_mount_list; | ||
| 54 | struct mount_entry *me = (struct mount_entry *)malloc(sizeof *me); | ||
| 63 | me->me_devname = strdup("/dev/c0t0d0s0"); | 55 | me->me_devname = strdup("/dev/c0t0d0s0"); |
| 64 | me->me_mountdir = strdup("/"); | 56 | me->me_mountdir = strdup("/"); |
| 65 | *mtail = me; | 57 | *mtail = me; |
| @@ -77,50 +69,70 @@ int main(int argc, char **argv) { | |||
| 77 | *mtail = me; | 69 | *mtail = me; |
| 78 | mtail = &me->me_next; | 70 | mtail = &me->me_next; |
| 79 | 71 | ||
| 72 | int cflags = REG_NOSUB | REG_EXTENDED; | ||
| 80 | np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a")); | 73 | np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a")); |
| 81 | np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, strdup("regex on dev names:")); | 74 | np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, |
| 82 | np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, strdup("regex on non existent dev/path:")); | 75 | strdup("regex on dev names:")); |
| 83 | np_test_mount_entry_regex(dummy_mount_list, strdup("/Foo"), cflags | REG_ICASE, 0, strdup("regi on non existent dev/path:")); | 76 | np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, |
| 84 | np_test_mount_entry_regex(dummy_mount_list, strdup("/c.t0"), cflags, 3, strdup("partial devname regex match:")); | 77 | strdup("regex on non existent dev/path:")); |
| 85 | np_test_mount_entry_regex(dummy_mount_list, strdup("c0t0"), cflags, 1, strdup("partial devname regex match:")); | 78 | np_test_mount_entry_regex(dummy_mount_list, strdup("/Foo"), cflags | REG_ICASE, 0, |
| 86 | np_test_mount_entry_regex(dummy_mount_list, strdup("C0t0"), cflags | REG_ICASE, 1, strdup("partial devname regi match:")); | 79 | strdup("regi on non existent dev/path:")); |
| 87 | np_test_mount_entry_regex(dummy_mount_list, strdup("home"), cflags, 1, strdup("partial pathname regex match:")); | 80 | np_test_mount_entry_regex(dummy_mount_list, strdup("/c.t0"), cflags, 3, |
| 88 | np_test_mount_entry_regex(dummy_mount_list, strdup("hOme"), cflags | REG_ICASE, 1, strdup("partial pathname regi match:")); | 81 | strdup("partial devname regex match:")); |
| 89 | np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:")); | 82 | np_test_mount_entry_regex(dummy_mount_list, strdup("c0t0"), cflags, 1, |
| 90 | np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:")); | 83 | strdup("partial devname regex match:")); |
| 91 | 84 | np_test_mount_entry_regex(dummy_mount_list, strdup("C0t0"), cflags | REG_ICASE, 1, | |
| 92 | np_add_parameter(&paths, "/home/groups"); | 85 | strdup("partial devname regi match:")); |
| 93 | np_add_parameter(&paths, "/var"); | 86 | np_test_mount_entry_regex(dummy_mount_list, strdup("home"), cflags, 1, |
| 94 | np_add_parameter(&paths, "/tmp"); | 87 | strdup("partial pathname regex match:")); |
| 95 | np_add_parameter(&paths, "/home/tonvoon"); | 88 | np_test_mount_entry_regex(dummy_mount_list, strdup("hOme"), cflags | REG_ICASE, 1, |
| 96 | np_add_parameter(&paths, "/dev/c2t0d0s0"); | 89 | strdup("partial pathname regi match:")); |
| 97 | 90 | np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, | |
| 98 | np_set_best_match(paths, dummy_mount_list, false); | 91 | strdup("grouped regex pathname match:")); |
| 99 | for (p = paths; p; p = p->name_next) { | 92 | np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, |
| 93 | strdup("grouped regi pathname match:")); | ||
| 94 | |||
| 95 | filesystem_list test_paths = filesystem_list_init(); | ||
| 96 | mp_int_fs_list_append(&test_paths, "/home/groups"); | ||
| 97 | mp_int_fs_list_append(&test_paths, "/var"); | ||
| 98 | mp_int_fs_list_append(&test_paths, "/tmp"); | ||
| 99 | mp_int_fs_list_append(&test_paths, "/home/tonvoon"); | ||
| 100 | mp_int_fs_list_append(&test_paths, "/dev/c2t0d0s0"); | ||
| 101 | ok(test_paths.length == 5, "List counter works correctly with appends"); | ||
| 102 | |||
| 103 | mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, false); | ||
| 104 | for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { | ||
| 100 | struct mount_entry *temp_me; | 105 | struct mount_entry *temp_me; |
| 101 | temp_me = p->best_match; | 106 | temp_me = p->best_match; |
| 102 | if (!strcmp(p->name, "/home/groups")) { | 107 | if (!strcmp(p->name, "/home/groups")) { |
| 103 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/groups got right best match: /home"); | 108 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), |
| 109 | "/home/groups got right best match: /home"); | ||
| 104 | } else if (!strcmp(p->name, "/var")) { | 110 | } else if (!strcmp(p->name, "/var")) { |
| 105 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/var"), "/var got right best match: /var"); | 111 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/var"), "/var got right best match: /var"); |
| 106 | } else if (!strcmp(p->name, "/tmp")) { | 112 | } else if (!strcmp(p->name, "/tmp")) { |
| 107 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/"), "/tmp got right best match: /"); | 113 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/"), "/tmp got right best match: /"); |
| 108 | } else if (!strcmp(p->name, "/home/tonvoon")) { | 114 | } else if (!strcmp(p->name, "/home/tonvoon")) { |
| 109 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/tonvoon got right best match: /home"); | 115 | ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), |
| 116 | "/home/tonvoon got right best match: /home"); | ||
| 110 | } else if (!strcmp(p->name, "/dev/c2t0d0s0")) { | 117 | } else if (!strcmp(p->name, "/dev/c2t0d0s0")) { |
| 111 | ok(temp_me && !strcmp(temp_me->me_devname, "/dev/c2t0d0s0"), "/dev/c2t0d0s0 got right best match: /dev/c2t0d0s0"); | 118 | ok(temp_me && !strcmp(temp_me->me_devname, "/dev/c2t0d0s0"), |
| 119 | "/dev/c2t0d0s0 got right best match: /dev/c2t0d0s0"); | ||
| 112 | } | 120 | } |
| 113 | } | 121 | } |
| 114 | 122 | ||
| 115 | paths = NULL; /* Bad boy - should free, but this is a test suite */ | 123 | for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { |
| 116 | np_add_parameter(&paths, "/home/groups"); | 124 | mp_int_fs_list_del(&test_paths, p); |
| 117 | np_add_parameter(&paths, "/var"); | 125 | } |
| 118 | np_add_parameter(&paths, "/tmp"); | 126 | ok(test_paths.length == 0, "List delete sets counter properly"); |
| 119 | np_add_parameter(&paths, "/home/tonvoon"); | ||
| 120 | np_add_parameter(&paths, "/home"); | ||
| 121 | 127 | ||
| 122 | np_set_best_match(paths, dummy_mount_list, true); | 128 | mp_int_fs_list_append(&test_paths, "/home/groups"); |
| 123 | for (p = paths; p; p = p->name_next) { | 129 | mp_int_fs_list_append(&test_paths, "/var"); |
| 130 | mp_int_fs_list_append(&test_paths, "/tmp"); | ||
| 131 | mp_int_fs_list_append(&test_paths, "/home/tonvoon"); | ||
| 132 | mp_int_fs_list_append(&test_paths, "/home"); | ||
| 133 | |||
| 134 | mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, true); | ||
| 135 | for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { | ||
| 124 | if (!strcmp(p->name, "/home/groups")) { | 136 | if (!strcmp(p->name, "/home/groups")) { |
| 125 | ok(!p->best_match, "/home/groups correctly not found"); | 137 | ok(!p->best_match, "/home/groups correctly not found"); |
| 126 | } else if (!strcmp(p->name, "/var")) { | 138 | } else if (!strcmp(p->name, "/var")) { |
| @@ -134,59 +146,68 @@ int main(int argc, char **argv) { | |||
| 134 | } | 146 | } |
| 135 | } | 147 | } |
| 136 | 148 | ||
| 149 | bool found = false; | ||
| 137 | /* test deleting first element in paths */ | 150 | /* test deleting first element in paths */ |
| 138 | paths = np_del_parameter(paths, NULL); | 151 | mp_int_fs_list_del(&test_paths, NULL); |
| 139 | for (p = paths; p; p = p->name_next) { | 152 | for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { |
| 140 | if (!strcmp(p->name, "/home/groups")) | 153 | if (!strcmp(p->name, "/home/groups")) { |
| 141 | found = 1; | 154 | found = true; |
| 155 | } | ||
| 142 | } | 156 | } |
| 143 | ok(found == 0, "first element successfully deleted"); | 157 | ok(!found, "first element successfully deleted"); |
| 144 | found = 0; | 158 | found = false; |
| 145 | 159 | ||
| 146 | p = paths; | 160 | parameter_list_elem *prev = NULL; |
| 147 | while (p) { | 161 | parameter_list_elem *p = NULL; |
| 148 | if (!strcmp(p->name, "/tmp")) | 162 | for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) { |
| 149 | p = np_del_parameter(p, prev); | 163 | if (!strcmp(path->name, "/tmp")) { |
| 150 | else { | 164 | mp_int_fs_list_del(&test_paths, path); |
| 151 | prev = p; | ||
| 152 | p = p->name_next; | ||
| 153 | } | 165 | } |
| 166 | p = path; | ||
| 154 | } | 167 | } |
| 155 | 168 | ||
| 156 | for (p = paths; p; p = p->name_next) { | 169 | parameter_list_elem *last = NULL; |
| 157 | if (!strcmp(p->name, "/tmp")) | 170 | for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) { |
| 158 | found = 1; | 171 | if (!strcmp(path->name, "/tmp")) { |
| 159 | if (p->name_next) | 172 | found = true; |
| 160 | prev = p; | 173 | } |
| 161 | else | 174 | if (path->next) { |
| 162 | last = p; | 175 | prev = path; |
| 176 | } else { | ||
| 177 | last = path; | ||
| 178 | } | ||
| 163 | } | 179 | } |
| 164 | ok(found == 0, "/tmp element successfully deleted"); | 180 | ok(!found, "/tmp element successfully deleted"); |
| 165 | 181 | ||
| 166 | p = np_del_parameter(last, prev); | 182 | int count = 0; |
| 167 | for (p = paths; p; p = p->name_next) { | 183 | mp_int_fs_list_del(&test_paths, p); |
| 168 | if (!strcmp(p->name, "/home")) | 184 | for (p = test_paths.first; p; p = p->next) { |
| 169 | found = 1; | 185 | if (!strcmp(p->name, "/home")) { |
| 186 | found = true; | ||
| 187 | } | ||
| 170 | last = p; | 188 | last = p; |
| 171 | count++; | 189 | count++; |
| 172 | } | 190 | } |
| 173 | ok(found == 0, "last (/home) element successfully deleted"); | 191 | ok(!found, "last (/home) element successfully deleted"); |
| 174 | ok(count == 2, "two elements remaining"); | 192 | ok(count == 2, "two elements remaining"); |
| 175 | 193 | ||
| 176 | return exit_status(); | 194 | return exit_status(); |
| 177 | } | 195 | } |
| 178 | 196 | ||
| 179 | void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc) { | 197 | void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, |
| 180 | int matches = 0; | 198 | int expect, char *desc) { |
| 181 | regex_t re; | 199 | regex_t regex; |
| 182 | struct mount_entry *me; | 200 | if (regcomp(®ex, regstr, cflags) == 0) { |
| 183 | if (regcomp(&re, regstr, cflags) == 0) { | 201 | int matches = 0; |
| 184 | for (me = dummy_mount_list; me; me = me->me_next) { | 202 | for (struct mount_entry *me = dummy_mount_list; me; me = me->me_next) { |
| 185 | if (np_regex_match_mount_entry(me, &re)) | 203 | if (np_regex_match_mount_entry(me, ®ex)) { |
| 186 | matches++; | 204 | matches++; |
| 205 | } | ||
| 187 | } | 206 | } |
| 188 | ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, matches); | 207 | ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, |
| 208 | matches); | ||
| 189 | 209 | ||
| 190 | } else | 210 | } else { |
| 191 | ok(false, "regex '%s' not compilable", regstr); | 211 | ok(false, "regex '%s' not compilable", regstr); |
| 212 | } | ||
| 192 | } | 213 | } |
diff --git a/plugins/tests/test_check_disk.t b/plugins/tests/test_check_disk.t new file mode 100755 index 00000000..56354650 --- /dev/null +++ b/plugins/tests/test_check_disk.t | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #!/usr/bin/perl | ||
| 2 | use Test::More; | ||
| 3 | if (! -e "./test_check_disk") { | ||
| 4 | plan skip_all => "./test_check_disk not compiled - please enable libtap library to test"; | ||
| 5 | } | ||
| 6 | exec "./test_check_disk"; | ||
diff --git a/plugins/tests/test_check_snmp.c b/plugins/tests/test_check_snmp.c new file mode 100644 index 00000000..d71706d0 --- /dev/null +++ b/plugins/tests/test_check_snmp.c | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * This program is free software: you can redistribute it and/or modify | ||
| 4 | * it under the terms of the GNU General Public License as published by | ||
| 5 | * the Free Software Foundation, either version 3 of the License, or | ||
| 6 | * (at your option) any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | * | ||
| 16 | * | ||
| 17 | *****************************************************************************/ | ||
| 18 | |||
| 19 | #include "tap.h" | ||
| 20 | #include "../../config.h" | ||
| 21 | |||
| 22 | #include <unistd.h> | ||
| 23 | #include <sys/types.h> | ||
| 24 | #include <sys/stat.h> | ||
| 25 | |||
| 26 | #include "utils_base.c" | ||
| 27 | #include "../check_snmp.d/check_snmp_helpers.h" | ||
| 28 | |||
| 29 | char *_np_state_generate_key(int argc, char **argv); | ||
| 30 | char *_np_state_calculate_location_prefix(void); | ||
| 31 | |||
| 32 | int 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(¤t_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 | ||
| 2 | use Test::More; | ||
| 3 | if (! -e "./test_check_snmp") { | ||
| 4 | plan skip_all => "./test_check_snmp not compiled - please enable libtap library to test"; | ||
| 5 | } | ||
| 6 | exec "./test_check_snmp"; | ||
diff --git a/plugins/tests/test_check_swap.c b/plugins/tests/test_check_swap.c index b85fb4ad..94d56ce7 100644 --- a/plugins/tests/test_check_swap.c +++ b/plugins/tests/test_check_swap.c | |||
| @@ -5,9 +5,7 @@ | |||
| 5 | int verbose = 0; | 5 | int verbose = 0; |
| 6 | 6 | ||
| 7 | void print_usage(void) {} | 7 | void print_usage(void) {} |
| 8 | void print_help(swap_config config) { | 8 | void print_help(swap_config config) { (void)config; } |
| 9 | (void) config; | ||
| 10 | } | ||
| 11 | 9 | ||
| 12 | const char *progname = "test_check_swap"; | 10 | const char *progname = "test_check_swap"; |
| 13 | 11 | ||
diff --git a/plugins/urlize.c b/plugins/urlize.c index 1aa4e425..a8590fae 100644 --- a/plugins/urlize.c +++ b/plugins/urlize.c | |||
| @@ -53,8 +53,10 @@ int main(int argc, char **argv) { | |||
| 53 | 53 | ||
| 54 | int c; | 54 | int c; |
| 55 | int option = 0; | 55 | int option = 0; |
| 56 | static struct option longopts[] = { | 56 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, |
| 57 | {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"url", required_argument, 0, 'u'}, {0, 0, 0, 0}}; | 57 | {"version", no_argument, 0, 'V'}, |
| 58 | {"url", required_argument, 0, 'u'}, | ||
| 59 | {0, 0, 0, 0}}; | ||
| 58 | 60 | ||
| 59 | setlocale(LC_ALL, ""); | 61 | setlocale(LC_ALL, ""); |
| 60 | bindtextdomain(PACKAGE, LOCALEDIR); | 62 | bindtextdomain(PACKAGE, LOCALEDIR); |
| @@ -69,8 +71,9 @@ int main(int argc, char **argv) { | |||
| 69 | while (1) { | 71 | while (1) { |
| 70 | c = getopt_long(argc, argv, "+hVu:", longopts, &option); | 72 | c = getopt_long(argc, argv, "+hVu:", longopts, &option); |
| 71 | 73 | ||
| 72 | if (c == -1 || c == EOF) | 74 | if (c == -1 || c == EOF) { |
| 73 | break; | 75 | break; |
| 76 | } | ||
| 74 | 77 | ||
| 75 | switch (c) { | 78 | switch (c) { |
| 76 | case 'h': /* help */ | 79 | case 'h': /* help */ |
| @@ -90,8 +93,9 @@ int main(int argc, char **argv) { | |||
| 90 | } | 93 | } |
| 91 | } | 94 | } |
| 92 | 95 | ||
| 93 | if (url == NULL) | 96 | if (url == NULL) { |
| 94 | url = strdup(argv[optind++]); | 97 | url = strdup(argv[optind++]); |
| 98 | } | ||
| 95 | 99 | ||
| 96 | cmd = strdup(argv[optind++]); | 100 | cmd = strdup(argv[optind++]); |
| 97 | for (c = optind; c < argc; c++) { | 101 | for (c = optind; c < argc; c++) { |
| @@ -118,27 +122,32 @@ int main(int argc, char **argv) { | |||
| 118 | strcat(tstr, buf); | 122 | strcat(tstr, buf); |
| 119 | } | 123 | } |
| 120 | 124 | ||
| 121 | if (!found) | 125 | if (!found) { |
| 122 | die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0], cmd); | 126 | die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0], |
| 127 | cmd); | ||
| 128 | } | ||
| 123 | 129 | ||
| 124 | /* chop the newline character */ | 130 | /* chop the newline character */ |
| 125 | if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) | 131 | if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) { |
| 126 | *nstr = '\0'; | 132 | *nstr = '\0'; |
| 133 | } | ||
| 127 | 134 | ||
| 128 | /* tokenize the string for Perfdata if there is some */ | 135 | /* tokenize the string for Perfdata if there is some */ |
| 129 | nstr = strtok(tstr, PERF_CHARACTER); | 136 | nstr = strtok(tstr, PERF_CHARACTER); |
| 130 | printf("%s", nstr); | 137 | printf("%s", nstr); |
| 131 | printf("</A>"); | 138 | printf("</A>"); |
| 132 | nstr = strtok(NULL, PERF_CHARACTER); | 139 | nstr = strtok(NULL, PERF_CHARACTER); |
| 133 | if (nstr != NULL) | 140 | if (nstr != NULL) { |
| 134 | printf(" | %s", nstr); | 141 | printf(" | %s", nstr); |
| 142 | } | ||
| 135 | 143 | ||
| 136 | /* close the pipe */ | 144 | /* close the pipe */ |
| 137 | result = spclose(child_process); | 145 | result = spclose(child_process); |
| 138 | 146 | ||
| 139 | /* WARNING if output found on stderr */ | 147 | /* WARNING if output found on stderr */ |
| 140 | if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) | 148 | if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) { |
| 141 | result = max_state(result, STATE_WARNING); | 149 | result = max_state(result, STATE_WARNING); |
| 150 | } | ||
| 142 | 151 | ||
| 143 | /* close stderr */ | 152 | /* close stderr */ |
| 144 | (void)fclose(child_stderr); | 153 | (void)fclose(child_stderr); |
| @@ -153,8 +162,10 @@ void print_help(void) { | |||
| 153 | printf(COPYRIGHT, copyright, email); | 162 | printf(COPYRIGHT, copyright, email); |
| 154 | 163 | ||
| 155 | printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>")); | 164 | printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>")); |
| 156 | printf("%s\n", _("tags, thus displaying the child plugin's output as a clickable link in compatible")); | 165 | printf("%s\n", |
| 157 | printf("%s\n", _("monitoring status screen. This plugin returns the status of the invoked plugin.")); | 166 | _("tags, thus displaying the child plugin's output as a clickable link in compatible")); |
| 167 | printf("%s\n", | ||
| 168 | _("monitoring status screen. This plugin returns the status of the invoked plugin.")); | ||
| 158 | 169 | ||
| 159 | printf("\n\n"); | 170 | printf("\n\n"); |
| 160 | 171 | ||
| @@ -164,7 +175,8 @@ void print_help(void) { | |||
| 164 | 175 | ||
| 165 | printf("\n"); | 176 | printf("\n"); |
| 166 | printf("%s\n", _("Examples:")); | 177 | printf("%s\n", _("Examples:")); |
| 167 | printf("%s\n", _("Pay close attention to quoting to ensure that the shell passes the expected")); | 178 | printf("%s\n", |
| 179 | _("Pay close attention to quoting to ensure that the shell passes the expected")); | ||
| 168 | printf("%s\n\n", _("data to the plugin. For example, in:")); | 180 | printf("%s\n\n", _("data to the plugin. For example, in:")); |
| 169 | printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'")); | 181 | printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'")); |
| 170 | printf(" %s\n", _("the shell will remove the single quotes and urlize will see:")); | 182 | printf(" %s\n", _("the shell will remove the single quotes and urlize will see:")); |
diff --git a/plugins/utils.c b/plugins/utils.c index 6d366e3d..41fe5fcf 100644 --- a/plugins/utils.c +++ b/plugins/utils.c | |||
| @@ -42,54 +42,6 @@ extern const char *progname; | |||
| 42 | 42 | ||
| 43 | time_t start_time, end_time; | 43 | time_t start_time, end_time; |
| 44 | 44 | ||
| 45 | /* ************************************************************************** | ||
| 46 | * max_state(STATE_x, STATE_y) | ||
| 47 | * compares STATE_x to STATE_y and returns result based on the following | ||
| 48 | * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL | ||
| 49 | * | ||
| 50 | * Note that numerically the above does not hold | ||
| 51 | ****************************************************************************/ | ||
| 52 | |||
| 53 | int max_state(int a, int b) { | ||
| 54 | if (a == STATE_CRITICAL || b == STATE_CRITICAL) | ||
| 55 | return STATE_CRITICAL; | ||
| 56 | else if (a == STATE_WARNING || b == STATE_WARNING) | ||
| 57 | return STATE_WARNING; | ||
| 58 | else if (a == STATE_OK || b == STATE_OK) | ||
| 59 | return STATE_OK; | ||
| 60 | else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) | ||
| 61 | return STATE_UNKNOWN; | ||
| 62 | else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) | ||
| 63 | return STATE_DEPENDENT; | ||
| 64 | else | ||
| 65 | return max(a, b); | ||
| 66 | } | ||
| 67 | |||
| 68 | /* ************************************************************************** | ||
| 69 | * max_state_alt(STATE_x, STATE_y) | ||
| 70 | * compares STATE_x to STATE_y and returns result based on the following | ||
| 71 | * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL | ||
| 72 | * | ||
| 73 | * The main difference between max_state_alt and max_state it that it doesn't | ||
| 74 | * allow setting a default to UNKNOWN. It will instead prioritixe any valid | ||
| 75 | * non-OK state. | ||
| 76 | ****************************************************************************/ | ||
| 77 | |||
| 78 | int max_state_alt(int a, int b) { | ||
| 79 | if (a == STATE_CRITICAL || b == STATE_CRITICAL) | ||
| 80 | return STATE_CRITICAL; | ||
| 81 | else if (a == STATE_WARNING || b == STATE_WARNING) | ||
| 82 | return STATE_WARNING; | ||
| 83 | else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) | ||
| 84 | return STATE_UNKNOWN; | ||
| 85 | else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) | ||
| 86 | return STATE_DEPENDENT; | ||
| 87 | else if (a == STATE_OK || b == STATE_OK) | ||
| 88 | return STATE_OK; | ||
| 89 | else | ||
| 90 | return max(a, b); | ||
| 91 | } | ||
| 92 | |||
| 93 | void usage(const char *msg) { | 45 | void usage(const char *msg) { |
| 94 | printf("%s\n", msg); | 46 | printf("%s\n", msg); |
| 95 | print_usage(); | 47 | print_usage(); |
| @@ -137,41 +89,46 @@ bool is_numeric(char *number) { | |||
| 137 | char tmp[1]; | 89 | char tmp[1]; |
| 138 | float x; | 90 | float x; |
| 139 | 91 | ||
| 140 | if (!number) | 92 | if (!number) { |
| 141 | return false; | 93 | return false; |
| 142 | else if (sscanf(number, "%f%c", &x, tmp) == 1) | 94 | } else if (sscanf(number, "%f%c", &x, tmp) == 1) { |
| 143 | return true; | 95 | return true; |
| 144 | else | 96 | } else { |
| 145 | return false; | 97 | return false; |
| 98 | } | ||
| 146 | } | 99 | } |
| 147 | 100 | ||
| 148 | bool is_positive(char *number) { | 101 | bool is_positive(char *number) { |
| 149 | if (is_numeric(number) && atof(number) > 0.0) | 102 | if (is_numeric(number) && atof(number) > 0.0) { |
| 150 | return true; | 103 | return true; |
| 151 | else | 104 | } else { |
| 152 | return false; | 105 | return false; |
| 106 | } | ||
| 153 | } | 107 | } |
| 154 | 108 | ||
| 155 | bool is_negative(char *number) { | 109 | bool is_negative(char *number) { |
| 156 | if (is_numeric(number) && atof(number) < 0.0) | 110 | if (is_numeric(number) && atof(number) < 0.0) { |
| 157 | return true; | 111 | return true; |
| 158 | else | 112 | } else { |
| 159 | return false; | 113 | return false; |
| 114 | } | ||
| 160 | } | 115 | } |
| 161 | 116 | ||
| 162 | bool is_nonnegative(char *number) { | 117 | bool is_nonnegative(char *number) { |
| 163 | if (is_numeric(number) && atof(number) >= 0.0) | 118 | if (is_numeric(number) && atof(number) >= 0.0) { |
| 164 | return true; | 119 | return true; |
| 165 | else | 120 | } else { |
| 166 | return false; | 121 | return false; |
| 122 | } | ||
| 167 | } | 123 | } |
| 168 | 124 | ||
| 169 | bool is_percentage(char *number) { | 125 | bool is_percentage(char *number) { |
| 170 | int x; | 126 | int x; |
| 171 | if (is_numeric(number) && (x = atof(number)) >= 0 && x <= 100) | 127 | if (is_numeric(number) && (x = atof(number)) >= 0 && x <= 100) { |
| 172 | return true; | 128 | return true; |
| 173 | else | 129 | } else { |
| 174 | return false; | 130 | return false; |
| 131 | } | ||
| 175 | } | 132 | } |
| 176 | 133 | ||
| 177 | bool is_percentage_expression(const char str[]) { | 134 | bool is_percentage_expression(const char str[]) { |
| @@ -204,36 +161,41 @@ bool is_percentage_expression(const char str[]) { | |||
| 204 | bool is_integer(char *number) { | 161 | bool is_integer(char *number) { |
| 205 | long int n; | 162 | long int n; |
| 206 | 163 | ||
| 207 | if (!number || (strspn(number, "-0123456789 ") != strlen(number))) | 164 | if (!number || (strspn(number, "-0123456789 ") != strlen(number))) { |
| 208 | return false; | 165 | return false; |
| 166 | } | ||
| 209 | 167 | ||
| 210 | n = strtol(number, NULL, 10); | 168 | n = strtol(number, NULL, 10); |
| 211 | 169 | ||
| 212 | if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) | 170 | if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) { |
| 213 | return true; | 171 | return true; |
| 214 | else | 172 | } else { |
| 215 | return false; | 173 | return false; |
| 174 | } | ||
| 216 | } | 175 | } |
| 217 | 176 | ||
| 218 | bool is_intpos(char *number) { | 177 | bool is_intpos(char *number) { |
| 219 | if (is_integer(number) && atoi(number) > 0) | 178 | if (is_integer(number) && atoi(number) > 0) { |
| 220 | return true; | 179 | return true; |
| 221 | else | 180 | } else { |
| 222 | return false; | 181 | return false; |
| 182 | } | ||
| 223 | } | 183 | } |
| 224 | 184 | ||
| 225 | bool is_intneg(char *number) { | 185 | bool is_intneg(char *number) { |
| 226 | if (is_integer(number) && atoi(number) < 0) | 186 | if (is_integer(number) && atoi(number) < 0) { |
| 227 | return true; | 187 | return true; |
| 228 | else | 188 | } else { |
| 229 | return false; | 189 | return false; |
| 190 | } | ||
| 230 | } | 191 | } |
| 231 | 192 | ||
| 232 | bool is_intnonneg(char *number) { | 193 | bool is_intnonneg(char *number) { |
| 233 | if (is_integer(number) && atoi(number) >= 0) | 194 | if (is_integer(number) && atoi(number) >= 0) { |
| 234 | return true; | 195 | return true; |
| 235 | else | 196 | } else { |
| 236 | return false; | 197 | return false; |
| 198 | } | ||
| 237 | } | 199 | } |
| 238 | 200 | ||
| 239 | /* | 201 | /* |
| @@ -295,19 +257,21 @@ bool is_uint64(char *number, uint64_t *target) { | |||
| 295 | 257 | ||
| 296 | bool is_intpercent(char *number) { | 258 | bool is_intpercent(char *number) { |
| 297 | int i; | 259 | int i; |
| 298 | if (is_integer(number) && (i = atoi(number)) >= 0 && i <= 100) | 260 | if (is_integer(number) && (i = atoi(number)) >= 0 && i <= 100) { |
| 299 | return true; | 261 | return true; |
| 300 | else | 262 | } else { |
| 301 | return false; | 263 | return false; |
| 264 | } | ||
| 302 | } | 265 | } |
| 303 | 266 | ||
| 304 | bool is_option(char *str) { | 267 | bool is_option(char *str) { |
| 305 | if (!str) | 268 | if (!str) { |
| 306 | return false; | 269 | return false; |
| 307 | else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) | 270 | } else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) { |
| 308 | return true; | 271 | return true; |
| 309 | else | 272 | } else { |
| 310 | return false; | 273 | return false; |
| 274 | } | ||
| 311 | } | 275 | } |
| 312 | 276 | ||
| 313 | #ifdef NEED_GETTIMEOFDAY | 277 | #ifdef NEED_GETTIMEOFDAY |
| @@ -321,7 +285,8 @@ double delta_time(struct timeval tv) { | |||
| 321 | struct timeval now; | 285 | struct timeval now; |
| 322 | 286 | ||
| 323 | gettimeofday(&now, NULL); | 287 | gettimeofday(&now, NULL); |
| 324 | return ((double)(now.tv_sec - tv.tv_sec) + (double)(now.tv_usec - tv.tv_usec) / (double)1000000); | 288 | return ((double)(now.tv_sec - tv.tv_sec) + |
| 289 | (double)(now.tv_usec - tv.tv_usec) / (double)1000000); | ||
| 325 | } | 290 | } |
| 326 | 291 | ||
| 327 | long deltime(struct timeval tv) { | 292 | long deltime(struct timeval tv) { |
| @@ -336,10 +301,11 @@ void strip(char *buffer) { | |||
| 336 | 301 | ||
| 337 | for (x = strlen(buffer); x >= 1; x--) { | 302 | for (x = strlen(buffer); x >= 1; x--) { |
| 338 | i = x - 1; | 303 | i = x - 1; |
| 339 | if (buffer[i] == ' ' || buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t') | 304 | if (buffer[i] == ' ' || buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t') { |
| 340 | buffer[i] = '\0'; | 305 | buffer[i] = '\0'; |
| 341 | else | 306 | } else { |
| 342 | break; | 307 | break; |
| 308 | } | ||
| 343 | } | 309 | } |
| 344 | return; | 310 | return; |
| 345 | } | 311 | } |
| @@ -357,8 +323,9 @@ void strip(char *buffer) { | |||
| 357 | *****************************************************************************/ | 323 | *****************************************************************************/ |
| 358 | 324 | ||
| 359 | char *strscpy(char *dest, const char *src) { | 325 | char *strscpy(char *dest, const char *src) { |
| 360 | if (src == NULL) | 326 | if (src == NULL) { |
| 361 | return NULL; | 327 | return NULL; |
| 328 | } | ||
| 362 | 329 | ||
| 363 | xasprintf(&dest, "%s", src); | 330 | xasprintf(&dest, "%s", src); |
| 364 | 331 | ||
| @@ -417,17 +384,21 @@ char *strscpy(char *dest, const char *src) { | |||
| 417 | 384 | ||
| 418 | char *strnl(char *str) { | 385 | char *strnl(char *str) { |
| 419 | size_t len; | 386 | size_t len; |
| 420 | if (str == NULL) | 387 | if (str == NULL) { |
| 421 | return NULL; | 388 | return NULL; |
| 389 | } | ||
| 422 | str = strpbrk(str, "\r\n"); | 390 | str = strpbrk(str, "\r\n"); |
| 423 | if (str == NULL) | 391 | if (str == NULL) { |
| 424 | return NULL; | 392 | return NULL; |
| 393 | } | ||
| 425 | len = strspn(str, "\r\n"); | 394 | len = strspn(str, "\r\n"); |
| 426 | if (str[len] == '\0') | 395 | if (str[len] == '\0') { |
| 427 | return NULL; | 396 | return NULL; |
| 397 | } | ||
| 428 | str += len; | 398 | str += len; |
| 429 | if (strlen(str) == 0) | 399 | if (strlen(str) == 0) { |
| 430 | return NULL; | 400 | return NULL; |
| 401 | } | ||
| 431 | return str; | 402 | return str; |
| 432 | } | 403 | } |
| 433 | 404 | ||
| @@ -450,15 +421,18 @@ char *strnl(char *str) { | |||
| 450 | char *strpcpy(char *dest, const char *src, const char *str) { | 421 | char *strpcpy(char *dest, const char *src, const char *str) { |
| 451 | size_t len; | 422 | size_t len; |
| 452 | 423 | ||
| 453 | if (src) | 424 | if (src) { |
| 454 | len = strcspn(src, str); | 425 | len = strcspn(src, str); |
| 455 | else | 426 | } else { |
| 456 | return NULL; | 427 | return NULL; |
| 428 | } | ||
| 457 | 429 | ||
| 458 | if (dest == NULL || strlen(dest) < len) | 430 | if (dest == NULL || strlen(dest) < len) { |
| 459 | dest = realloc(dest, len + 1); | 431 | dest = realloc(dest, len + 1); |
| 460 | if (dest == NULL) | 432 | } |
| 433 | if (dest == NULL) { | ||
| 461 | die(STATE_UNKNOWN, _("failed realloc in strpcpy\n")); | 434 | die(STATE_UNKNOWN, _("failed realloc in strpcpy\n")); |
| 435 | } | ||
| 462 | 436 | ||
| 463 | strncpy(dest, src, len); | 437 | strncpy(dest, src, len); |
| 464 | dest[len] = '\0'; | 438 | dest[len] = '\0'; |
| @@ -482,10 +456,11 @@ char *strpcpy(char *dest, const char *src, const char *str) { | |||
| 482 | char *strpcat(char *dest, const char *src, const char *str) { | 456 | char *strpcat(char *dest, const char *src, const char *str) { |
| 483 | size_t len, l2; | 457 | size_t len, l2; |
| 484 | 458 | ||
| 485 | if (dest) | 459 | if (dest) { |
| 486 | len = strlen(dest); | 460 | len = strlen(dest); |
| 487 | else | 461 | } else { |
| 488 | len = 0; | 462 | len = 0; |
| 463 | } | ||
| 489 | 464 | ||
| 490 | if (src) { | 465 | if (src) { |
| 491 | l2 = strcspn(src, str); | 466 | l2 = strcspn(src, str); |
| @@ -494,8 +469,9 @@ char *strpcat(char *dest, const char *src, const char *str) { | |||
| 494 | } | 469 | } |
| 495 | 470 | ||
| 496 | dest = realloc(dest, len + l2 + 1); | 471 | dest = realloc(dest, len + l2 + 1); |
| 497 | if (dest == NULL) | 472 | if (dest == NULL) { |
| 498 | die(STATE_UNKNOWN, _("failed malloc in strscat\n")); | 473 | die(STATE_UNKNOWN, _("failed malloc in strscat\n")); |
| 474 | } | ||
| 499 | 475 | ||
| 500 | strncpy(dest + len, src, l2); | 476 | strncpy(dest + len, src, l2); |
| 501 | dest[len + l2] = '\0'; | 477 | dest[len + l2] = '\0'; |
| @@ -511,8 +487,9 @@ char *strpcat(char *dest, const char *src, const char *str) { | |||
| 511 | 487 | ||
| 512 | int xvasprintf(char **strp, const char *fmt, va_list ap) { | 488 | int xvasprintf(char **strp, const char *fmt, va_list ap) { |
| 513 | int result = vasprintf(strp, fmt, ap); | 489 | int result = vasprintf(strp, fmt, ap); |
| 514 | if (result == -1 || *strp == NULL) | 490 | if (result == -1 || *strp == NULL) { |
| 515 | die(STATE_UNKNOWN, _("failed malloc in xvasprintf\n")); | 491 | die(STATE_UNKNOWN, _("failed malloc in xvasprintf\n")); |
| 492 | } | ||
| 516 | return result; | 493 | return result; |
| 517 | } | 494 | } |
| 518 | 495 | ||
| @@ -531,126 +508,147 @@ int xasprintf(char **strp, const char *fmt, ...) { | |||
| 531 | * | 508 | * |
| 532 | ******************************************************************************/ | 509 | ******************************************************************************/ |
| 533 | 510 | ||
| 534 | char *perfdata(const char *label, long int val, const char *uom, int warnp, long int warn, int critp, long int crit, int minp, | 511 | char *perfdata(const char *label, long int val, const char *uom, bool warnp, long int warn, |
| 535 | long int minv, int maxp, long int maxv) { | 512 | bool critp, long int crit, bool minp, long int minv, bool maxp, long int maxv) { |
| 536 | char *data = NULL; | 513 | char *data = NULL; |
| 537 | 514 | ||
| 538 | if (strpbrk(label, "'= ")) | 515 | if (strpbrk(label, "'= ")) { |
| 539 | xasprintf(&data, "'%s'=%ld%s;", label, val, uom); | 516 | xasprintf(&data, "'%s'=%ld%s;", label, val, uom); |
| 540 | else | 517 | } else { |
| 541 | xasprintf(&data, "%s=%ld%s;", label, val, uom); | 518 | xasprintf(&data, "%s=%ld%s;", label, val, uom); |
| 519 | } | ||
| 542 | 520 | ||
| 543 | if (warnp) | 521 | if (warnp) { |
| 544 | xasprintf(&data, "%s%ld;", data, warn); | 522 | xasprintf(&data, "%s%ld;", data, warn); |
| 545 | else | 523 | } else { |
| 546 | xasprintf(&data, "%s;", data); | 524 | xasprintf(&data, "%s;", data); |
| 525 | } | ||
| 547 | 526 | ||
| 548 | if (critp) | 527 | if (critp) { |
| 549 | xasprintf(&data, "%s%ld;", data, crit); | 528 | xasprintf(&data, "%s%ld;", data, crit); |
| 550 | else | 529 | } else { |
| 551 | xasprintf(&data, "%s;", data); | 530 | xasprintf(&data, "%s;", data); |
| 531 | } | ||
| 552 | 532 | ||
| 553 | if (minp) | 533 | if (minp) { |
| 554 | xasprintf(&data, "%s%ld;", data, minv); | 534 | xasprintf(&data, "%s%ld;", data, minv); |
| 555 | else | 535 | } else { |
| 556 | xasprintf(&data, "%s;", data); | 536 | xasprintf(&data, "%s;", data); |
| 537 | } | ||
| 557 | 538 | ||
| 558 | if (maxp) | 539 | if (maxp) { |
| 559 | xasprintf(&data, "%s%ld", data, maxv); | 540 | xasprintf(&data, "%s%ld", data, maxv); |
| 541 | } | ||
| 560 | 542 | ||
| 561 | return data; | 543 | return data; |
| 562 | } | 544 | } |
| 563 | 545 | ||
| 564 | char *perfdata_uint64(const char *label, uint64_t val, const char *uom, int warnp, /* Warning present */ | 546 | char *perfdata_uint64(const char *label, uint64_t val, const char *uom, |
| 565 | uint64_t warn, int critp, /* Critical present */ | 547 | bool warnp, /* Warning present */ |
| 566 | uint64_t crit, int minp, /* Minimum present */ | 548 | uint64_t warn, bool critp, /* Critical present */ |
| 567 | uint64_t minv, int maxp, /* Maximum present */ | 549 | uint64_t crit, bool minp, /* Minimum present */ |
| 550 | uint64_t minv, bool maxp, /* Maximum present */ | ||
| 568 | uint64_t maxv) { | 551 | uint64_t maxv) { |
| 569 | char *data = NULL; | 552 | char *data = NULL; |
| 570 | 553 | ||
| 571 | if (strpbrk(label, "'= ")) | 554 | if (strpbrk(label, "'= ")) { |
| 572 | xasprintf(&data, "'%s'=%" PRIu64 "%s;", label, val, uom); | 555 | xasprintf(&data, "'%s'=%" PRIu64 "%s;", label, val, uom); |
| 573 | else | 556 | } else { |
| 574 | xasprintf(&data, "%s=%" PRIu64 "%s;", label, val, uom); | 557 | xasprintf(&data, "%s=%" PRIu64 "%s;", label, val, uom); |
| 558 | } | ||
| 575 | 559 | ||
| 576 | if (warnp) | 560 | if (warnp) { |
| 577 | xasprintf(&data, "%s%" PRIu64 ";", data, warn); | 561 | xasprintf(&data, "%s%" PRIu64 ";", data, warn); |
| 578 | else | 562 | } else { |
| 579 | xasprintf(&data, "%s;", data); | 563 | xasprintf(&data, "%s;", data); |
| 564 | } | ||
| 580 | 565 | ||
| 581 | if (critp) | 566 | if (critp) { |
| 582 | xasprintf(&data, "%s%" PRIu64 ";", data, crit); | 567 | xasprintf(&data, "%s%" PRIu64 ";", data, crit); |
| 583 | else | 568 | } else { |
| 584 | xasprintf(&data, "%s;", data); | 569 | xasprintf(&data, "%s;", data); |
| 570 | } | ||
| 585 | 571 | ||
| 586 | if (minp) | 572 | if (minp) { |
| 587 | xasprintf(&data, "%s%" PRIu64 ";", data, minv); | 573 | xasprintf(&data, "%s%" PRIu64 ";", data, minv); |
| 588 | else | 574 | } else { |
| 589 | xasprintf(&data, "%s;", data); | 575 | xasprintf(&data, "%s;", data); |
| 576 | } | ||
| 590 | 577 | ||
| 591 | if (maxp) | 578 | if (maxp) { |
| 592 | xasprintf(&data, "%s%" PRIu64, data, maxv); | 579 | xasprintf(&data, "%s%" PRIu64, data, maxv); |
| 580 | } | ||
| 593 | 581 | ||
| 594 | return data; | 582 | return data; |
| 595 | } | 583 | } |
| 596 | 584 | ||
| 597 | char *perfdata_int64(const char *label, int64_t val, const char *uom, int warnp, /* Warning present */ | 585 | char *perfdata_int64(const char *label, int64_t val, const char *uom, |
| 598 | int64_t warn, int critp, /* Critical present */ | 586 | bool warnp, /* Warning present */ |
| 599 | int64_t crit, int minp, /* Minimum present */ | 587 | int64_t warn, bool critp, /* Critical present */ |
| 600 | int64_t minv, int maxp, /* Maximum present */ | 588 | int64_t crit, bool minp, /* Minimum present */ |
| 589 | int64_t minv, bool maxp, /* Maximum present */ | ||
| 601 | int64_t maxv) { | 590 | int64_t maxv) { |
| 602 | char *data = NULL; | 591 | char *data = NULL; |
| 603 | 592 | ||
| 604 | if (strpbrk(label, "'= ")) | 593 | if (strpbrk(label, "'= ")) { |
| 605 | xasprintf(&data, "'%s'=%" PRId64 "%s;", label, val, uom); | 594 | xasprintf(&data, "'%s'=%" PRId64 "%s;", label, val, uom); |
| 606 | else | 595 | } else { |
| 607 | xasprintf(&data, "%s=%" PRId64 "%s;", label, val, uom); | 596 | xasprintf(&data, "%s=%" PRId64 "%s;", label, val, uom); |
| 597 | } | ||
| 608 | 598 | ||
| 609 | if (warnp) | 599 | if (warnp) { |
| 610 | xasprintf(&data, "%s%" PRId64 ";", data, warn); | 600 | xasprintf(&data, "%s%" PRId64 ";", data, warn); |
| 611 | else | 601 | } else { |
| 612 | xasprintf(&data, "%s;", data); | 602 | xasprintf(&data, "%s;", data); |
| 603 | } | ||
| 613 | 604 | ||
| 614 | if (critp) | 605 | if (critp) { |
| 615 | xasprintf(&data, "%s%" PRId64 ";", data, crit); | 606 | xasprintf(&data, "%s%" PRId64 ";", data, crit); |
| 616 | else | 607 | } else { |
| 617 | xasprintf(&data, "%s;", data); | 608 | xasprintf(&data, "%s;", data); |
| 609 | } | ||
| 618 | 610 | ||
| 619 | if (minp) | 611 | if (minp) { |
| 620 | xasprintf(&data, "%s%" PRId64 ";", data, minv); | 612 | xasprintf(&data, "%s%" PRId64 ";", data, minv); |
| 621 | else | 613 | } else { |
| 622 | xasprintf(&data, "%s;", data); | 614 | xasprintf(&data, "%s;", data); |
| 615 | } | ||
| 623 | 616 | ||
| 624 | if (maxp) | 617 | if (maxp) { |
| 625 | xasprintf(&data, "%s%" PRId64, data, maxv); | 618 | xasprintf(&data, "%s%" PRId64, data, maxv); |
| 619 | } | ||
| 626 | 620 | ||
| 627 | return data; | 621 | return data; |
| 628 | } | 622 | } |
| 629 | 623 | ||
| 630 | char *fperfdata(const char *label, double val, const char *uom, int warnp, double warn, int critp, double crit, int minp, double minv, | 624 | char *fperfdata(const char *label, double val, const char *uom, bool warnp, double warn, bool critp, |
| 631 | int maxp, double maxv) { | 625 | double crit, bool minp, double minv, bool maxp, double maxv) { |
| 632 | char *data = NULL; | 626 | char *data = NULL; |
| 633 | 627 | ||
| 634 | if (strpbrk(label, "'= ")) | 628 | if (strpbrk(label, "'= ")) { |
| 635 | xasprintf(&data, "'%s'=", label); | 629 | xasprintf(&data, "'%s'=", label); |
| 636 | else | 630 | } else { |
| 637 | xasprintf(&data, "%s=", label); | 631 | xasprintf(&data, "%s=", label); |
| 632 | } | ||
| 638 | 633 | ||
| 639 | xasprintf(&data, "%s%f", data, val); | 634 | xasprintf(&data, "%s%f", data, val); |
| 640 | xasprintf(&data, "%s%s;", data, uom); | 635 | xasprintf(&data, "%s%s;", data, uom); |
| 641 | 636 | ||
| 642 | if (warnp) | 637 | if (warnp) { |
| 643 | xasprintf(&data, "%s%f", data, warn); | 638 | xasprintf(&data, "%s%f", data, warn); |
| 639 | } | ||
| 644 | 640 | ||
| 645 | xasprintf(&data, "%s;", data); | 641 | xasprintf(&data, "%s;", data); |
| 646 | 642 | ||
| 647 | if (critp) | 643 | if (critp) { |
| 648 | xasprintf(&data, "%s%f", data, crit); | 644 | xasprintf(&data, "%s%f", data, crit); |
| 645 | } | ||
| 649 | 646 | ||
| 650 | xasprintf(&data, "%s;", data); | 647 | xasprintf(&data, "%s;", data); |
| 651 | 648 | ||
| 652 | if (minp) | 649 | if (minp) { |
| 653 | xasprintf(&data, "%s%f", data, minv); | 650 | xasprintf(&data, "%s%f", data, minv); |
| 651 | } | ||
| 654 | 652 | ||
| 655 | if (maxp) { | 653 | if (maxp) { |
| 656 | xasprintf(&data, "%s;", data); | 654 | xasprintf(&data, "%s;", data); |
| @@ -660,28 +658,33 @@ char *fperfdata(const char *label, double val, const char *uom, int warnp, doubl | |||
| 660 | return data; | 658 | return data; |
| 661 | } | 659 | } |
| 662 | 660 | ||
| 663 | char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, int minp, double minv, int maxp, double maxv) { | 661 | char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, bool minp, |
| 662 | double minv, bool maxp, double maxv) { | ||
| 664 | char *data = NULL; | 663 | char *data = NULL; |
| 665 | if (strpbrk(label, "'= ")) | 664 | if (strpbrk(label, "'= ")) { |
| 666 | xasprintf(&data, "'%s'=", label); | 665 | xasprintf(&data, "'%s'=", label); |
| 667 | else | 666 | } else { |
| 668 | xasprintf(&data, "%s=", label); | 667 | xasprintf(&data, "%s=", label); |
| 668 | } | ||
| 669 | 669 | ||
| 670 | xasprintf(&data, "%s%f", data, val); | 670 | xasprintf(&data, "%s%f", data, val); |
| 671 | xasprintf(&data, "%s%s;", data, uom); | 671 | xasprintf(&data, "%s%s;", data, uom); |
| 672 | 672 | ||
| 673 | if (warn != NULL) | 673 | if (warn != NULL) { |
| 674 | xasprintf(&data, "%s%s", data, warn); | 674 | xasprintf(&data, "%s%s", data, warn); |
| 675 | } | ||
| 675 | 676 | ||
| 676 | xasprintf(&data, "%s;", data); | 677 | xasprintf(&data, "%s;", data); |
| 677 | 678 | ||
| 678 | if (crit != NULL) | 679 | if (crit != NULL) { |
| 679 | xasprintf(&data, "%s%s", data, crit); | 680 | xasprintf(&data, "%s%s", data, crit); |
| 681 | } | ||
| 680 | 682 | ||
| 681 | xasprintf(&data, "%s;", data); | 683 | xasprintf(&data, "%s;", data); |
| 682 | 684 | ||
| 683 | if (minp) | 685 | if (minp) { |
| 684 | xasprintf(&data, "%s%f", data, minv); | 686 | xasprintf(&data, "%s%f", data, minv); |
| 687 | } | ||
| 685 | 688 | ||
| 686 | if (maxp) { | 689 | if (maxp) { |
| 687 | xasprintf(&data, "%s;", data); | 690 | xasprintf(&data, "%s;", data); |
| @@ -691,28 +694,33 @@ char *sperfdata(const char *label, double val, const char *uom, char *warn, char | |||
| 691 | return data; | 694 | return data; |
| 692 | } | 695 | } |
| 693 | 696 | ||
| 694 | char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, int minp, int minv, int maxp, int maxv) { | 697 | char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, bool minp, |
| 698 | int minv, bool maxp, int maxv) { | ||
| 695 | char *data = NULL; | 699 | char *data = NULL; |
| 696 | if (strpbrk(label, "'= ")) | 700 | if (strpbrk(label, "'= ")) { |
| 697 | xasprintf(&data, "'%s'=", label); | 701 | xasprintf(&data, "'%s'=", label); |
| 698 | else | 702 | } else { |
| 699 | xasprintf(&data, "%s=", label); | 703 | xasprintf(&data, "%s=", label); |
| 704 | } | ||
| 700 | 705 | ||
| 701 | xasprintf(&data, "%s%d", data, val); | 706 | xasprintf(&data, "%s%d", data, val); |
| 702 | xasprintf(&data, "%s%s;", data, uom); | 707 | xasprintf(&data, "%s%s;", data, uom); |
| 703 | 708 | ||
| 704 | if (warn != NULL) | 709 | if (warn != NULL) { |
| 705 | xasprintf(&data, "%s%s", data, warn); | 710 | xasprintf(&data, "%s%s", data, warn); |
| 711 | } | ||
| 706 | 712 | ||
| 707 | xasprintf(&data, "%s;", data); | 713 | xasprintf(&data, "%s;", data); |
| 708 | 714 | ||
| 709 | if (crit != NULL) | 715 | if (crit != NULL) { |
| 710 | xasprintf(&data, "%s%s", data, crit); | 716 | xasprintf(&data, "%s%s", data, crit); |
| 717 | } | ||
| 711 | 718 | ||
| 712 | xasprintf(&data, "%s;", data); | 719 | xasprintf(&data, "%s;", data); |
| 713 | 720 | ||
| 714 | if (minp) | 721 | if (minp) { |
| 715 | xasprintf(&data, "%s%d", data, minv); | 722 | xasprintf(&data, "%s%d", data, minv); |
| 723 | } | ||
| 716 | 724 | ||
| 717 | if (maxp) { | 725 | if (maxp) { |
| 718 | xasprintf(&data, "%s;", data); | 726 | xasprintf(&data, "%s;", data); |
diff --git a/plugins/utils.h b/plugins/utils.h index f939e337..1f0e021b 100644 --- a/plugins/utils.h +++ b/plugins/utils.h | |||
| @@ -13,51 +13,51 @@ in order to resist overflow attacks. In addition, a few functions are | |||
| 13 | provided to standardize version and error reporting across the entire | 13 | provided to standardize version and error reporting across the entire |
| 14 | suite of plugins. */ | 14 | suite of plugins. */ |
| 15 | 15 | ||
| 16 | /* now some functions etc are being defined in ../lib/utils_base.c */ | 16 | #include "../config.h" |
| 17 | #include "utils_base.h" | ||
| 18 | |||
| 19 | #include <stdbool.h> | 17 | #include <stdbool.h> |
| 20 | 18 | #include <stdint.h> | |
| 19 | #include <stdio.h> | ||
| 20 | #include <time.h> | ||
| 21 | 21 | ||
| 22 | #ifdef NP_EXTRA_OPTS | 22 | #ifdef NP_EXTRA_OPTS |
| 23 | /* Include extra-opts functions if compiled in */ | 23 | /* Include extra-opts functions if compiled in */ |
| 24 | #include "extra_opts.h" | 24 | # include "extra_opts.h" |
| 25 | #else | 25 | #else |
| 26 | /* else, fake np_extra_opts */ | 26 | /* else, fake np_extra_opts */ |
| 27 | #define np_extra_opts(acptr,av,pr) av | 27 | # define np_extra_opts(acptr, av, pr) av |
| 28 | #endif | 28 | #endif |
| 29 | 29 | ||
| 30 | /* Standardize version information, termination */ | 30 | /* Standardize version information, termination */ |
| 31 | 31 | ||
| 32 | void support (void); | 32 | void support(void); |
| 33 | void print_revision (const char *, const char *); | 33 | void print_revision(const char *, const char *); |
| 34 | 34 | ||
| 35 | extern time_t start_time, end_time; | 35 | extern time_t start_time, end_time; |
| 36 | 36 | ||
| 37 | /* Test input types */ | 37 | /* Test input types */ |
| 38 | 38 | ||
| 39 | bool is_integer (char *); | 39 | bool is_integer(char *); |
| 40 | bool is_intpos (char *); | 40 | bool is_intpos(char *); |
| 41 | bool is_intneg (char *); | 41 | bool is_intneg(char *); |
| 42 | bool is_intnonneg (char *); | 42 | bool is_intnonneg(char *); |
| 43 | bool is_intpercent (char *); | 43 | bool is_intpercent(char *); |
| 44 | bool is_uint64(char *number, uint64_t *target); | 44 | bool is_uint64(char *number, uint64_t *target); |
| 45 | bool is_int64(char *number, int64_t *target); | 45 | bool is_int64(char *number, int64_t *target); |
| 46 | 46 | ||
| 47 | bool is_numeric (char *); | 47 | bool is_numeric(char *); |
| 48 | bool is_positive (char *); | 48 | bool is_positive(char *); |
| 49 | bool is_negative (char *); | 49 | bool is_negative(char *); |
| 50 | bool is_nonnegative (char *); | 50 | bool is_nonnegative(char *); |
| 51 | bool is_percentage (char *); | 51 | bool is_percentage(char *); |
| 52 | bool is_percentage_expression (const char[]); | 52 | bool is_percentage_expression(const char[]); |
| 53 | 53 | ||
| 54 | bool is_option (char *); | 54 | bool is_option(char *); |
| 55 | 55 | ||
| 56 | /* Generalized timer that will do milliseconds if available */ | 56 | /* Generalized timer that will do milliseconds if available */ |
| 57 | #ifndef HAVE_STRUCT_TIMEVAL | 57 | #ifndef HAVE_STRUCT_TIMEVAL |
| 58 | struct timeval { | 58 | struct timeval { |
| 59 | long tv_sec; /* seconds */ | 59 | long tv_sec; /* seconds */ |
| 60 | long tv_usec; /* microseconds */ | 60 | long tv_usec; /* microseconds */ |
| 61 | }; | 61 | }; |
| 62 | #endif | 62 | #endif |
| 63 | 63 | ||
| @@ -65,137 +65,148 @@ struct timeval { | |||
| 65 | int gettimeofday(struct timeval *, struct timezone *); | 65 | int gettimeofday(struct timeval *, struct timezone *); |
| 66 | #endif | 66 | #endif |
| 67 | 67 | ||
| 68 | double delta_time (struct timeval tv); | 68 | double delta_time(struct timeval tv); |
| 69 | long deltime (struct timeval tv); | 69 | long deltime(struct timeval tv); |
| 70 | 70 | ||
| 71 | /* Handle strings safely */ | 71 | /* Handle strings safely */ |
| 72 | 72 | ||
| 73 | void strip (char *); | 73 | void strip(char *); |
| 74 | char *strscpy (char *, const char *); | 74 | char *strscpy(char *, const char *); |
| 75 | char *strnl (char *); | 75 | char *strnl(char *); |
| 76 | char *strpcpy (char *, const char *, const char *); | 76 | char *strpcpy(char *, const char *, const char *); |
| 77 | char *strpcat (char *, const char *, const char *); | 77 | char *strpcat(char *, const char *, const char *); |
| 78 | int xvasprintf (char **strp, const char *fmt, va_list ap); | 78 | int xvasprintf(char **strp, const char *fmt, va_list ap); |
| 79 | int xasprintf (char **strp, const char *fmt, ...); | 79 | int xasprintf(char **strp, const char *fmt, ...) __attribute__((format(printf, 2, 3))); |
| 80 | 80 | ||
| 81 | int max_state (int a, int b); | 81 | void usage(const char *) __attribute__((noreturn)); |
| 82 | int max_state_alt (int a, int b); | ||
| 83 | |||
| 84 | void usage (const char *) __attribute__((noreturn)); | ||
| 85 | void usage2(const char *, const char *) __attribute__((noreturn)); | 82 | void usage2(const char *, const char *) __attribute__((noreturn)); |
| 86 | void usage3(const char *, int) __attribute__((noreturn)); | 83 | void usage3(const char *, int) __attribute__((noreturn)); |
| 87 | void usage4(const char *) __attribute__((noreturn)); | 84 | void usage4(const char *) __attribute__((noreturn)); |
| 88 | void usage5(void) __attribute__((noreturn)); | 85 | void usage5(void) __attribute__((noreturn)); |
| 89 | void usage_va(const char *fmt, ...) __attribute__((noreturn)); | 86 | void usage_va(const char *fmt, ...) __attribute__((noreturn)); |
| 90 | 87 | ||
| 91 | #define max(a,b) (((a)>(b))?(a):(b)) | 88 | #define max(a, b) (((a) > (b)) ? (a) : (b)) |
| 92 | #define min(a,b) (((a)<(b))?(a):(b)) | 89 | #define min(a, b) (((a) < (b)) ? (a) : (b)) |
| 93 | 90 | ||
| 94 | char *perfdata (const char *, long int, const char *, int, long int, | 91 | char *perfdata(const char *, long int, const char *, bool, long int, bool, long int, bool, long int, |
| 95 | int, long int, int, long int, int, long int); | 92 | bool, long int); |
| 96 | 93 | ||
| 97 | char *perfdata_uint64 (const char *, uint64_t , const char *, int, uint64_t, | 94 | char *perfdata_uint64(const char *, uint64_t, const char *, bool, uint64_t, bool, uint64_t, bool, |
| 98 | int, uint64_t, int, uint64_t, int, uint64_t); | 95 | uint64_t, bool, uint64_t); |
| 99 | 96 | ||
| 100 | char *perfdata_int64 (const char *, int64_t, const char *, int, int64_t, | 97 | char *perfdata_int64(const char *, int64_t, const char *, bool, int64_t, bool, int64_t, bool, |
| 101 | int, int64_t, int, int64_t, int, int64_t); | 98 | int64_t, bool, int64_t); |
| 102 | 99 | ||
| 103 | char *fperfdata (const char *, double, const char *, int, double, | 100 | char *fperfdata(const char *, double, const char *, bool, double, bool, double, bool, double, bool, |
| 104 | int, double, int, double, int, double); | 101 | double); |
| 105 | 102 | ||
| 106 | char *sperfdata (const char *, double, const char *, char *, char *, | 103 | char *sperfdata(const char *, double, const char *, char *, char *, bool, double, bool, double); |
| 107 | int, double, int, double); | ||
| 108 | 104 | ||
| 109 | char *sperfdata_int (const char *, int, const char *, char *, char *, | 105 | char *sperfdata_int(const char *, int, const char *, char *, char *, bool, int, bool, int); |
| 110 | int, int, int, int); | ||
| 111 | 106 | ||
| 112 | /* The idea here is that, although not every plugin will use all of these, | 107 | /* The idea here is that, although not every plugin will use all of these, |
| 113 | most will or should. Therefore, for consistency, these very common | 108 | most will or should. Therefore, for consistency, these very common |
| 114 | options should have only these meanings throughout the overall suite */ | 109 | options should have only these meanings throughout the overall suite */ |
| 115 | 110 | ||
| 116 | #define STD_LONG_OPTS \ | 111 | #define STD_LONG_OPTS \ |
| 117 | {"version",no_argument,0,'V'},\ | 112 | {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, \ |
| 118 | {"verbose",no_argument,0,'v'},\ | 113 | {"help", no_argument, 0, 'h'}, {"timeout", required_argument, 0, 't'}, \ |
| 119 | {"help",no_argument,0,'h'},\ | 114 | {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, \ |
| 120 | {"timeout",required_argument,0,'t'},\ | 115 | {"hostname", required_argument, 0, 'H'} |
| 121 | {"critical",required_argument,0,'c'},\ | ||
| 122 | {"warning",required_argument,0,'w'},\ | ||
| 123 | {"hostname",required_argument,0,'H'} | ||
| 124 | 116 | ||
| 125 | #define COPYRIGHT "Copyright (c) %s Monitoring Plugins Development Team\n\ | 117 | #define COPYRIGHT \ |
| 118 | "Copyright (c) %s Monitoring Plugins Development Team\n\ | ||
| 126 | \t<%s>\n\n" | 119 | \t<%s>\n\n" |
| 127 | 120 | ||
| 128 | #define UT_HLP_VRS _("\ | 121 | #define UT_HLP_VRS \ |
| 122 | _("\ | ||
| 129 | %s (-h | --help) for detailed help\n\ | 123 | %s (-h | --help) for detailed help\n\ |
| 130 | %s (-V | --version) for version information\n") | 124 | %s (-V | --version) for version information\n") |
| 131 | 125 | ||
| 132 | #define UT_HELP_VRSN _("\ | 126 | #define UT_HELP_VRSN \ |
| 127 | _("\ | ||
| 133 | \nOptions:\n\ | 128 | \nOptions:\n\ |
| 134 | -h, --help\n\ | 129 | -h, --help\n\ |
| 135 | Print detailed help screen\n\ | 130 | Print detailed help screen\n\ |
| 136 | -V, --version\n\ | 131 | -V, --version\n\ |
| 137 | Print version information\n") | 132 | Print version information\n") |
| 138 | 133 | ||
| 139 | #define UT_HOST_PORT _("\ | 134 | #define UT_HOST_PORT \ |
| 135 | _("\ | ||
| 140 | -H, --hostname=ADDRESS\n\ | 136 | -H, --hostname=ADDRESS\n\ |
| 141 | Host name, IP Address, or unix socket (must be an absolute path)\n\ | 137 | Host name, IP Address, or unix socket (must be an absolute path)\n\ |
| 142 | -%c, --port=INTEGER\n\ | 138 | -%c, --port=INTEGER\n\ |
| 143 | Port number (default: %s)\n") | 139 | Port number (default: %s)\n") |
| 144 | 140 | ||
| 145 | #define UT_IPv46 _("\ | 141 | #define UT_IPv46 \ |
| 142 | _("\ | ||
| 146 | -4, --use-ipv4\n\ | 143 | -4, --use-ipv4\n\ |
| 147 | Use IPv4 connection\n\ | 144 | Use IPv4 connection\n\ |
| 148 | -6, --use-ipv6\n\ | 145 | -6, --use-ipv6\n\ |
| 149 | Use IPv6 connection\n") | 146 | Use IPv6 connection\n") |
| 150 | 147 | ||
| 151 | #define UT_VERBOSE _("\ | 148 | #define UT_VERBOSE \ |
| 149 | _("\ | ||
| 152 | -v, --verbose\n\ | 150 | -v, --verbose\n\ |
| 153 | Show details for command-line debugging (output may be truncated by\n\ | 151 | Show details for command-line debugging (output may be truncated by\n\ |
| 154 | the monitoring system)\n") | 152 | the monitoring system)\n") |
| 155 | 153 | ||
| 156 | #define UT_WARN_CRIT _("\ | 154 | #define UT_WARN_CRIT \ |
| 155 | _("\ | ||
| 157 | -w, --warning=DOUBLE\n\ | 156 | -w, --warning=DOUBLE\n\ |
| 158 | Response time to result in warning status (seconds)\n\ | 157 | Response time to result in warning status (seconds)\n\ |
| 159 | -c, --critical=DOUBLE\n\ | 158 | -c, --critical=DOUBLE\n\ |
| 160 | Response time to result in critical status (seconds)\n") | 159 | Response time to result in critical status (seconds)\n") |
| 161 | 160 | ||
| 162 | #define UT_WARN_CRIT_RANGE _("\ | 161 | #define UT_WARN_CRIT_RANGE \ |
| 162 | _("\ | ||
| 163 | -w, --warning=RANGE\n\ | 163 | -w, --warning=RANGE\n\ |
| 164 | Warning range (format: start:end). Alert if outside this range\n\ | 164 | Warning range (format: start:end). Alert if outside this range\n\ |
| 165 | -c, --critical=RANGE\n\ | 165 | -c, --critical=RANGE\n\ |
| 166 | Critical range\n") | 166 | Critical range\n") |
| 167 | 167 | ||
| 168 | #define UT_CONN_TIMEOUT _("\ | 168 | #define UT_CONN_TIMEOUT \ |
| 169 | _("\ | ||
| 169 | -t, --timeout=INTEGER\n\ | 170 | -t, --timeout=INTEGER\n\ |
| 170 | Seconds before connection times out (default: %d)\n") | 171 | Seconds before connection times out (default: %d)\n") |
| 171 | 172 | ||
| 172 | #define UT_PLUG_TIMEOUT _("\ | 173 | #define UT_PLUG_TIMEOUT \ |
| 174 | _("\ | ||
| 173 | -t, --timeout=INTEGER\n\ | 175 | -t, --timeout=INTEGER\n\ |
| 174 | Seconds before plugin times out (default: %d)\n") | 176 | Seconds before plugin times out (default: %d)\n") |
| 175 | 177 | ||
| 176 | #ifdef NP_EXTRA_OPTS | 178 | #ifdef NP_EXTRA_OPTS |
| 177 | #define UT_EXTRA_OPTS _("\ | 179 | # define UT_EXTRA_OPTS \ |
| 180 | _("\ | ||
| 178 | --extra-opts=[section][@file]\n\ | 181 | --extra-opts=[section][@file]\n\ |
| 179 | Read options from an ini file. See\n\ | 182 | Read options from an ini file. See\n\ |
| 180 | https://www.monitoring-plugins.org/doc/extra-opts.html\n\ | 183 | https://www.monitoring-plugins.org/doc/extra-opts.html\n\ |
| 181 | for usage and examples.\n") | 184 | for usage and examples.\n") |
| 182 | #else | 185 | #else |
| 183 | #define UT_EXTRA_OPTS " \b" | 186 | # define UT_EXTRA_OPTS " \b" |
| 184 | #endif | 187 | #endif |
| 185 | 188 | ||
| 186 | #define UT_THRESHOLDS_NOTES _("\ | 189 | #define UT_THRESHOLDS_NOTES \ |
| 190 | _("\ | ||
| 187 | See:\n\ | 191 | See:\n\ |
| 188 | https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\ | 192 | https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT\n\ |
| 189 | for THRESHOLD format and examples.\n") | 193 | for THRESHOLD format and examples.\n") |
| 190 | 194 | ||
| 191 | #define UT_SUPPORT _("\n\ | 195 | #define UT_SUPPORT \ |
| 196 | _("\n\ | ||
| 192 | Send email to help@monitoring-plugins.org if you have questions regarding\n\ | 197 | Send email to help@monitoring-plugins.org if you have questions regarding\n\ |
| 193 | use of this software. To submit patches or suggest improvements, send email\n\ | 198 | use of this software. To submit patches or suggest improvements, send email\n\ |
| 194 | to devel@monitoring-plugins.org\n\n") | 199 | to devel@monitoring-plugins.org\n\n") |
| 195 | 200 | ||
| 196 | #define UT_NOWARRANTY _("\n\ | 201 | #define UT_NOWARRANTY \ |
| 202 | _("\n\ | ||
| 197 | The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\ | 203 | The Monitoring Plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\ |
| 198 | copies of the plugins under the terms of the GNU General Public License.\n\ | 204 | copies of the plugins under the terms of the GNU General Public License.\n\ |
| 199 | For more information about these matters, see the file named COPYING.\n") | 205 | For more information about these matters, see the file named COPYING.\n") |
| 200 | 206 | ||
| 207 | #define UT_OUTPUT_FORMAT \ | ||
| 208 | _("\ | ||
| 209 | --output-format=OUTPUT_FORMAT\n\ | ||
| 210 | Select output format. Valid values: \"multi-line\", \"mp-test-json\"\n") | ||
| 211 | |||
| 201 | #endif /* NP_UTILS_H */ | 212 | #endif /* NP_UTILS_H */ |
| @@ -65,7 +65,8 @@ static void _cleanup(void); | |||
| 65 | * test_name -- the name of the test, may be NULL | 65 | * test_name -- the name of the test, may be NULL |
| 66 | * test_comment -- a comment to print afterwards, may be NULL | 66 | * test_comment -- a comment to print afterwards, may be NULL |
| 67 | */ | 67 | */ |
| 68 | unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line, char *test_name, ...) { | 68 | unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line, char *test_name, |
| 69 | ...) { | ||
| 69 | va_list ap; | 70 | va_list ap; |
| 70 | char *local_test_name = NULL; | 71 | char *local_test_name = NULL; |
| 71 | char *c; | 72 | char *c; |
| @@ -95,7 +96,9 @@ unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line | |||
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | if (name_is_digits) { | 98 | if (name_is_digits) { |
| 98 | diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name); | 99 | diag( |
| 100 | " You named your test '%s'. You shouldn't use numbers for your test names.", | ||
| 101 | local_test_name); | ||
| 99 | diag(" Very confusing."); | 102 | diag(" Very confusing."); |
| 100 | } | 103 | } |
| 101 | } | 104 | } |
| @@ -116,8 +119,9 @@ unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line | |||
| 116 | if (local_test_name != NULL) { | 119 | if (local_test_name != NULL) { |
| 117 | flockfile(stdout); | 120 | flockfile(stdout); |
| 118 | for (c = local_test_name; *c != '\0'; c++) { | 121 | for (c = local_test_name; *c != '\0'; c++) { |
| 119 | if (*c == '#') | 122 | if (*c == '#') { |
| 120 | fputc('\\', stdout); | 123 | fputc('\\', stdout); |
| 124 | } | ||
| 121 | fputc((int)*c, stdout); | 125 | fputc((int)*c, stdout); |
| 122 | } | 126 | } |
| 123 | funlockfile(stdout); | 127 | funlockfile(stdout); |
| @@ -135,14 +139,16 @@ unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line | |||
| 135 | the test failed. */ | 139 | the test failed. */ |
| 136 | if (todo) { | 140 | if (todo) { |
| 137 | printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed); | 141 | printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed); |
| 138 | if (!ok) | 142 | if (!ok) { |
| 139 | failures--; | 143 | failures--; |
| 144 | } | ||
| 140 | } | 145 | } |
| 141 | 146 | ||
| 142 | printf("\n"); | 147 | printf("\n"); |
| 143 | 148 | ||
| 144 | if (!ok) | 149 | if (!ok) { |
| 145 | diag(" Failed %stest (%s:%s() at line %d)", todo ? "(TODO) " : "", file, func, line); | 150 | diag(" Failed %stest (%s:%s() at line %d)", todo ? "(TODO) " : "", file, func, line); |
| 151 | } | ||
| 146 | 152 | ||
| 147 | free(local_test_name); | 153 | free(local_test_name); |
| 148 | 154 | ||
| @@ -212,8 +218,9 @@ int plan_skip_all(char *reason) { | |||
| 212 | 218 | ||
| 213 | printf("1..0"); | 219 | printf("1..0"); |
| 214 | 220 | ||
| 215 | if (reason != NULL) | 221 | if (reason != NULL) { |
| 216 | printf(" # Skip %s", reason); | 222 | printf(" # Skip %s", reason); |
| 223 | } | ||
| 217 | 224 | ||
| 218 | printf("\n"); | 225 | printf("\n"); |
| 219 | 226 | ||
| @@ -294,7 +301,8 @@ int skip(unsigned int n, char *fmt, ...) { | |||
| 294 | 301 | ||
| 295 | while (n-- > 0) { | 302 | while (n-- > 0) { |
| 296 | test_count++; | 303 | test_count++; |
| 297 | printf("ok %d # skip %s\n", test_count, skip_msg != NULL ? skip_msg : "libtap():malloc() failed"); | 304 | printf("ok %d # skip %s\n", test_count, |
| 305 | skip_msg != NULL ? skip_msg : "libtap():malloc() failed"); | ||
| 298 | } | 306 | } |
| 299 | 307 | ||
| 300 | free(skip_msg); | 308 | free(skip_msg); |
| @@ -396,8 +404,9 @@ void _cleanup(void) { | |||
| 396 | return; | 404 | return; |
| 397 | } | 405 | } |
| 398 | 406 | ||
| 399 | if (failures) | 407 | if (failures) { |
| 400 | diag("Looks like you failed %d tests of %d.", failures, test_count); | 408 | diag("Looks like you failed %d tests of %d.", failures, test_count); |
| 409 | } | ||
| 401 | 410 | ||
| 402 | UNLOCK; | 411 | UNLOCK; |
| 403 | } | 412 | } |
| @@ -28,45 +28,50 @@ | |||
| 28 | and requires the caller to add the final comma if they've omitted | 28 | and requires the caller to add the final comma if they've omitted |
| 29 | the optional arguments */ | 29 | the optional arguments */ |
| 30 | #ifdef __GNUC__ | 30 | #ifdef __GNUC__ |
| 31 | # define ok(e, test, ...) \ | 31 | # define ok(e, test, ...) \ |
| 32 | ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__) \ | 32 | ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__) \ |
| 33 | : _gen_result(0, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__)) | 33 | : _gen_result(0, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__)) |
| 34 | 34 | ||
| 35 | # define ok1(e) ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) | 35 | # define ok1(e) \ |
| 36 | ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) \ | ||
| 37 | : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) | ||
| 36 | 38 | ||
| 37 | # define pass(test, ...) ok(1, test, ##__VA_ARGS__); | 39 | # define pass(test, ...) ok(1, test, ##__VA_ARGS__); |
| 38 | # define fail(test, ...) ok(0, test, ##__VA_ARGS__); | 40 | # define fail(test, ...) ok(0, test, ##__VA_ARGS__); |
| 39 | 41 | ||
| 40 | # define skip_start(test, n, fmt, ...) \ | 42 | # define skip_start(test, n, fmt, ...) \ |
| 41 | do { \ | 43 | do { \ |
| 42 | if ((test)) { \ | 44 | if ((test)) { \ |
| 43 | skip(n, fmt, ##__VA_ARGS__); \ | 45 | skip(n, fmt, ##__VA_ARGS__); \ |
| 44 | continue; \ | 46 | continue; \ |
| 45 | } | 47 | } |
| 46 | #else /* __GNUC__ */ | 48 | #else /* __GNUC__ */ |
| 47 | /* The original tap.h used to test if __STDC_VERSION__ >= 199901L here. This | 49 | /* The original tap.h used to test if __STDC_VERSION__ >= 199901L here. This |
| 48 | * doesn't seem to work on HP-UX even though the code compile fine. I'm not | 50 | * doesn't seem to work on HP-UX even though the code compile fine. I'm not |
| 49 | * sure how to add an exception here for HP-UX so I just removed the check | 51 | * sure how to add an exception here for HP-UX so I just removed the check |
| 50 | * for now */ | 52 | * for now */ |
| 51 | # define ok(e, ...) \ | 53 | # define ok(e, ...) \ |
| 52 | ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, __VA_ARGS__) : _gen_result(0, __func__, __FILE__, __LINE__, __VA_ARGS__)) | 54 | ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, __VA_ARGS__) \ |
| 55 | : _gen_result(0, __func__, __FILE__, __LINE__, __VA_ARGS__)) | ||
| 53 | 56 | ||
| 54 | # define ok1(e) ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) | 57 | # define ok1(e) \ |
| 58 | ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) \ | ||
| 59 | : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) | ||
| 55 | 60 | ||
| 56 | # define pass(...) ok(1, __VA_ARGS__); | 61 | # define pass(...) ok(1, __VA_ARGS__); |
| 57 | # define fail(...) ok(0, __VA_ARGS__); | 62 | # define fail(...) ok(0, __VA_ARGS__); |
| 58 | 63 | ||
| 59 | # define skip_start(test, n, ...) \ | 64 | # define skip_start(test, n, ...) \ |
| 60 | do { \ | 65 | do { \ |
| 61 | if ((test)) { \ | 66 | if ((test)) { \ |
| 62 | skip(n, __VA_ARGS__); \ | 67 | skip(n, __VA_ARGS__); \ |
| 63 | continue; \ | 68 | continue; \ |
| 64 | } | 69 | } |
| 65 | #endif /* __GNUC__ */ | 70 | #endif /* __GNUC__ */ |
| 66 | 71 | ||
| 67 | #define skip_end \ | 72 | #define skip_end \ |
| 68 | } \ | 73 | } \ |
| 69 | while (0) \ | 74 | while (0) \ |
| 70 | ; | 75 | ; |
| 71 | 76 | ||
| 72 | unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...); | 77 | unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...); |
diff --git a/tools/mini_epn.c b/tools/mini_epn.c index 6f3c5d02..1b09f1c1 100644 --- a/tools/mini_epn.c +++ b/tools/mini_epn.c | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * | 2 | * |
| 3 | * MINI_EPN.C - Mini Embedded Perl Nagios | 3 | * MINI_EPN.C - Mini Embedded Perl Nagios |
| 4 | * Contributed by Stanley Hopcroft | 4 | * Contributed by Stanley Hopcroft |
| 5 | * Modified by Douglas Warner | 5 | * Modified by Douglas Warner |
| 6 | * Last Modified: 05/02/2002 | 6 | * Last Modified: 05/02/2002 |
| 7 | * | 7 | * |
| 8 | * This is a sample mini embedded Perl interpreter (hacked out checks.c and | 8 | * This is a sample mini embedded Perl interpreter (hacked out checks.c and |
| 9 | * perlembed) for use in testing Perl plugins. | 9 | * perlembed) for use in testing Perl plugins. |
| 10 | * | 10 | * |
| 11 | * It can be compiled with the following command (see 'man perlembed' for | 11 | * It can be compiled with the following command (see 'man perlembed' for |
| 12 | * more info): | 12 | * more info): |
| 13 | * | 13 | * |
| 14 | * gcc -omini_epn mini_epn.c `perl -MExtUtils::Embed -e ccopts -e ldopts` | 14 | * gcc -omini_epn mini_epn.c `perl -MExtUtils::Embed -e ccopts -e ldopts` |
| @@ -21,7 +21,6 @@ | |||
| 21 | * | 21 | * |
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | |||
| 25 | #include <EXTERN.h> | 24 | #include <EXTERN.h> |
| 26 | #include <perl.h> | 25 | #include <perl.h> |
| 27 | #include <fcntl.h> | 26 | #include <fcntl.h> |
| @@ -30,7 +29,7 @@ | |||
| 30 | /* include PERL xs_init code for module and C library support */ | 29 | /* include PERL xs_init code for module and C library support */ |
| 31 | 30 | ||
| 32 | #if defined(__cplusplus) | 31 | #if defined(__cplusplus) |
| 33 | #define is_cplusplus | 32 | # define is_cplusplus |
| 34 | #endif | 33 | #endif |
| 35 | 34 | ||
| 36 | #ifdef is_cplusplus | 35 | #ifdef is_cplusplus |
| @@ -42,22 +41,20 @@ extern "C" { | |||
| 42 | 41 | ||
| 43 | #ifdef is_cplusplus | 42 | #ifdef is_cplusplus |
| 44 | } | 43 | } |
| 45 | # ifndef EXTERN_C | 44 | # ifndef EXTERN_C |
| 46 | # define EXTERN_C extern "C" | 45 | # define EXTERN_C extern "C" |
| 47 | # endif | 46 | # endif |
| 48 | #else | 47 | #else |
| 49 | # ifndef EXTERN_C | 48 | # ifndef EXTERN_C |
| 50 | # define EXTERN_C extern | 49 | # define EXTERN_C extern |
| 51 | # endif | 50 | # endif |
| 52 | #endif | 51 | #endif |
| 53 | |||
| 54 | 52 | ||
| 55 | EXTERN_C void xs_init _((void)); | 53 | EXTERN_C void xs_init _((void)); |
| 56 | 54 | ||
| 57 | EXTERN_C void boot_DynaLoader _((CV* cv)); | 55 | EXTERN_C void boot_DynaLoader _((CV * cv)); |
| 58 | 56 | ||
| 59 | EXTERN_C void xs_init(void) | 57 | EXTERN_C void xs_init(void) { |
| 60 | { | ||
| 61 | char *file = __FILE__; | 58 | char *file = __FILE__; |
| 62 | dXSUB_SYS; | 59 | dXSUB_SYS; |
| 63 | 60 | ||
| @@ -65,85 +62,80 @@ EXTERN_C void xs_init(void) | |||
| 65 | newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); | 62 | newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); |
| 66 | } | 63 | } |
| 67 | 64 | ||
| 68 | |||
| 69 | static PerlInterpreter *perl = NULL; | 65 | static PerlInterpreter *perl = NULL; |
| 70 | 66 | ||
| 71 | 67 | int main(int argc, char **argv, char **env) { | |
| 72 | int main(int argc, char **argv, char **env) | 68 | char *embedding[] = {"", "p1.pl"}; |
| 73 | { | ||
| 74 | char *embedding[] = { "", "p1.pl" }; | ||
| 75 | char plugin_output[1024]; | 69 | char plugin_output[1024]; |
| 76 | char buffer[512]; | 70 | char buffer[512]; |
| 77 | char tmpfname[32]; | 71 | char tmpfname[32]; |
| 78 | char fname[32]; | 72 | char fname[32]; |
| 79 | char *args[] = {"","0", "", "", NULL }; | 73 | char *args[] = {"", "0", "", "", NULL}; |
| 80 | FILE *fp; | 74 | FILE *fp; |
| 81 | 75 | ||
| 82 | const int command_line_size = 160; | 76 | const int command_line_size = 160; |
| 83 | char command_line[command_line_size]; | 77 | char command_line[command_line_size]; |
| 84 | char *ap ; | 78 | char *ap; |
| 85 | int exitstatus; | 79 | int exitstatus; |
| 86 | int pclose_result; | 80 | int pclose_result; |
| 87 | #ifdef THREADEDPERL | 81 | #ifdef THREADEDPERL |
| 88 | dTHX; | 82 | dTHX; |
| 89 | #endif | 83 | #endif |
| 90 | dSP; | 84 | dSP; |
| 91 | 85 | ||
| 92 | if ((perl=perl_alloc())==NULL) { | 86 | if ((perl = perl_alloc()) == NULL) { |
| 93 | snprintf(buffer,sizeof(buffer),"Error: Could not allocate memory for embedded Perl interpreter!\n"); | 87 | snprintf(buffer, sizeof(buffer), |
| 94 | buffer[sizeof(buffer)-1]='\x0'; | 88 | "Error: Could not allocate memory for embedded Perl interpreter!\n"); |
| 89 | buffer[sizeof(buffer) - 1] = '\x0'; | ||
| 95 | printf("%s\n", buffer); | 90 | printf("%s\n", buffer); |
| 96 | exit(1); | 91 | exit(1); |
| 97 | } | 92 | } |
| 98 | perl_construct(perl); | 93 | perl_construct(perl); |
| 99 | exitstatus=perl_parse(perl,xs_init,2,embedding,NULL); | 94 | exitstatus = perl_parse(perl, xs_init, 2, embedding, NULL); |
| 100 | if (!exitstatus) { | 95 | if (!exitstatus) { |
| 101 | 96 | ||
| 102 | exitstatus=perl_run(perl); | 97 | exitstatus = perl_run(perl); |
| 103 | 98 | ||
| 104 | while(printf("Enter file name: ") && fgets(command_line, command_line_size, stdin)) { | 99 | while (printf("Enter file name: ") && fgets(command_line, command_line_size, stdin)) { |
| 105 | 100 | ||
| 106 | /* call the subroutine, passing it the filename as an argument */ | 101 | /* call the subroutine, passing it the filename as an argument */ |
| 107 | 102 | ||
| 108 | command_line[strlen(command_line) -1] = '\0'; | 103 | command_line[strlen(command_line) - 1] = '\0'; |
| 109 | 104 | ||
| 110 | strncpy(fname,command_line,strcspn(command_line," ")); | 105 | strncpy(fname, command_line, strcspn(command_line, " ")); |
| 111 | fname[strcspn(command_line," ")] = '\x0'; | 106 | fname[strcspn(command_line, " ")] = '\x0'; |
| 112 | args[0] = fname ; | 107 | args[0] = fname; |
| 113 | args[3] = command_line + strlen(fname) + 1 ; | 108 | args[3] = command_line + strlen(fname) + 1; |
| 114 | 109 | ||
| 115 | /* generate a temporary filename to which stdout can be redirected. */ | 110 | /* generate a temporary filename to which stdout can be redirected. */ |
| 116 | sprintf(tmpfname,"/tmp/embedded%d",getpid()); | 111 | sprintf(tmpfname, "/tmp/embedded%d", getpid()); |
| 117 | args[2] = tmpfname; | 112 | args[2] = tmpfname; |
| 118 | 113 | ||
| 119 | /* call our perl interpreter to compile and optionally cache the command */ | 114 | /* call our perl interpreter to compile and optionally cache the command */ |
| 120 | perl_call_argv("Embed::Persistent::eval_file", G_DISCARD | G_EVAL, args); | 115 | perl_call_argv("Embed::Persistent::eval_file", G_DISCARD | G_EVAL, args); |
| 121 | 116 | ||
| 122 | perl_call_argv("Embed::Persistent::run_package", G_DISCARD | G_EVAL, args); | 117 | perl_call_argv("Embed::Persistent::run_package", G_DISCARD | G_EVAL, args); |
| 123 | 118 | ||
| 124 | /* check return status */ | 119 | /* check return status */ |
| 125 | if(SvTRUE(ERRSV)){ | 120 | if (SvTRUE(ERRSV)) { |
| 126 | pclose_result=-2; | 121 | pclose_result = -2; |
| 127 | printf("embedded perl ran %s with error %s\n",fname,SvPV(ERRSV,PL_na)); | 122 | printf("embedded perl ran %s with error %s\n", fname, SvPV(ERRSV, PL_na)); |
| 128 | } | 123 | } |
| 129 | 124 | ||
| 130 | /* read back stdout from script */ | 125 | /* read back stdout from script */ |
| 131 | fp=fopen(tmpfname, "r"); | 126 | fp = fopen(tmpfname, "r"); |
| 132 | 127 | ||
| 133 | /* default return string in case nothing was returned */ | 128 | /* default return string in case nothing was returned */ |
| 134 | strcpy(plugin_output,"(No output!)"); | 129 | strcpy(plugin_output, "(No output!)"); |
| 135 | |||
| 136 | fgets(plugin_output,sizeof(plugin_output)-1,fp); | ||
| 137 | plugin_output[sizeof(plugin_output)-1]='\x0'; | ||
| 138 | fclose(fp); | ||
| 139 | unlink(tmpfname); | ||
| 140 | printf("embedded perl plugin output was %d,%s\n",pclose_result, plugin_output); | ||
| 141 | 130 | ||
| 131 | fgets(plugin_output, sizeof(plugin_output) - 1, fp); | ||
| 132 | plugin_output[sizeof(plugin_output) - 1] = '\x0'; | ||
| 133 | fclose(fp); | ||
| 134 | unlink(tmpfname); | ||
| 135 | printf("embedded perl plugin output was %d,%s\n", pclose_result, plugin_output); | ||
| 142 | } | 136 | } |
| 143 | |||
| 144 | } | 137 | } |
| 145 | 138 | ||
| 146 | |||
| 147 | PL_perl_destruct_level = 0; | 139 | PL_perl_destruct_level = 0; |
| 148 | perl_destruct(perl); | 140 | perl_destruct(perl); |
| 149 | perl_free(perl); | 141 | perl_free(perl); |
diff --git a/tools/tinderbox_build b/tools/tinderbox_build deleted file mode 100755 index 1a41f577..00000000 --- a/tools/tinderbox_build +++ /dev/null | |||
| @@ -1,290 +0,0 @@ | |||
| 1 | #!/usr/bin/perl | ||
| 2 | # tinderbox_build.pl | ||
| 3 | # This script builds the monitoringplugins and then sends | ||
| 4 | # logs back to the master tinderbox server | ||
| 5 | # | ||
| 6 | # This script is based on mozilla-unix.pl which comes with tinderbox2 | ||
| 7 | # | ||
| 8 | # See http://tinderbox.altinity.org for more details | ||
| 9 | |||
| 10 | require 5.000; | ||
| 11 | |||
| 12 | use strict; | ||
| 13 | use Sys::Hostname; | ||
| 14 | use Cwd; | ||
| 15 | use Time::Local; | ||
| 16 | |||
| 17 | my $Version = `git describe --abbrev=4 HEAD`; | ||
| 18 | |||
| 19 | my $myhost = hostname; | ||
| 20 | chomp($myhost); | ||
| 21 | my ($host, $junk) = split(/\./, $myhost); | ||
| 22 | |||
| 23 | my $BuildAdministrator = $ENV{TINDERBOX_BUILD_ADMIN} || "$ENV{'USER'}\@$myhost"; | ||
| 24 | my $TmpDir = $ENV{TMPDIR} || "/tmp"; | ||
| 25 | |||
| 26 | #Default values of cmdline opts | ||
| 27 | my $ReportStatus = 0; # Do not send results to server | ||
| 28 | |||
| 29 | # Set these to what makes sense for your system | ||
| 30 | |||
| 31 | # Set these proper values for your tinderbox server | ||
| 32 | # Have the StrictHostKeyChecking=no so that a new host will automatically add hostkey without | ||
| 33 | # prompting. If host key changes, then will get error, so this should still be secure | ||
| 34 | my $Tinderbox_server = '-p 1022 -o StrictHostKeyChecking=no tinderbox2@tinderbox.opsera.com'; | ||
| 35 | |||
| 36 | # These shouldn't really need to be changed | ||
| 37 | my $BuildTree = 'monitoringplug'; | ||
| 38 | my $BuildName = ''; | ||
| 39 | my $ConfigureArgs = $ENV{CONFIGURE_ARGS}; | ||
| 40 | |||
| 41 | my $OS = `uname -s`; | ||
| 42 | my $OSVer = `uname -r`; | ||
| 43 | |||
| 44 | chop($OS, $OSVer); | ||
| 45 | |||
| 46 | if ( $OS eq 'AIX' ) { | ||
| 47 | $OSVer = `uname -v`; | ||
| 48 | chop($OSVer); | ||
| 49 | $OSVer = $OSVer . "." . `uname -r`; | ||
| 50 | chop($OSVer); | ||
| 51 | } | ||
| 52 | |||
| 53 | if ( $OS eq 'IRIX64' ) { | ||
| 54 | $OS = 'IRIX'; | ||
| 55 | } | ||
| 56 | |||
| 57 | if ( $OS eq 'SCO_SV' ) { | ||
| 58 | $OS = 'SCOOS'; | ||
| 59 | $OSVer = '5.0'; | ||
| 60 | } | ||
| 61 | |||
| 62 | if ( "$host" ne "" ) { | ||
| 63 | $BuildName = $host . ' '; | ||
| 64 | } | ||
| 65 | $BuildName .= $OS . ' ' . $OSVer; | ||
| 66 | $_ = $BuildName; | ||
| 67 | s/ /_/g; | ||
| 68 | |||
| 69 | my $logfile = "$_.log"; | ||
| 70 | |||
| 71 | sub BuildIt { | ||
| 72 | my ($fe, @felist, $EarlyExit, $LastTime); | ||
| 73 | |||
| 74 | my $StartDir = getcwd(); | ||
| 75 | $LastTime = 0; | ||
| 76 | |||
| 77 | print "Starting dir is : $StartDir\n"; | ||
| 78 | |||
| 79 | my $EarlyExit = 0; | ||
| 80 | |||
| 81 | chdir("$StartDir"); | ||
| 82 | |||
| 83 | my $StartTime = time; | ||
| 84 | if (-e (my $file = "monitoring-plugins.spec")) { | ||
| 85 | open F, $file; | ||
| 86 | while (<F>) { | ||
| 87 | if (/^Version: trunk-(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { | ||
| 88 | $StartTime = timegm(0, $5, $4, $3, ($2 - 1), ($1 - 1900)); | ||
| 89 | last; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | print "Start time is $StartTime",$/; | ||
| 95 | |||
| 96 | my $CurrentDir = getcwd(); | ||
| 97 | if ( $CurrentDir ne $StartDir ) { | ||
| 98 | print "startdir: $StartDir, curdir $CurrentDir\n"; | ||
| 99 | die "curdir != startdir"; | ||
| 100 | } | ||
| 101 | |||
| 102 | unlink( "$logfile" ); | ||
| 103 | |||
| 104 | print "opening $logfile\n"; | ||
| 105 | open( LOG, ">$logfile" ) || print "can't open $?\n"; | ||
| 106 | print LOG "current dir is -- $host:$CurrentDir\n"; | ||
| 107 | print LOG "Build Administrator is $BuildAdministrator\n"; | ||
| 108 | &PrintEnv; | ||
| 109 | |||
| 110 | my $BuildStatus; | ||
| 111 | if (&configure) { | ||
| 112 | if (&make) { | ||
| 113 | if (&maketest) { | ||
| 114 | $BuildStatus = "success"; | ||
| 115 | } else { | ||
| 116 | $BuildStatus = "test_failed"; | ||
| 117 | } | ||
| 118 | } else { | ||
| 119 | $BuildStatus = "build_failed"; | ||
| 120 | } | ||
| 121 | } else { | ||
| 122 | $BuildStatus = "busted"; | ||
| 123 | } | ||
| 124 | |||
| 125 | print LOG "\nBuild Status = $BuildStatus\n"; | ||
| 126 | |||
| 127 | close(LOG); | ||
| 128 | chdir("$StartDir"); | ||
| 129 | |||
| 130 | # TV: Leaving this in, because process mail program probably has some | ||
| 131 | # limitation retained | ||
| 132 | |||
| 133 | # this fun line added on 2/5/98. do not remove. Translated to english, | ||
| 134 | # that's "take any line longer than 1000 characters, and split it into less | ||
| 135 | # than 1000 char lines. If any of the resulting lines is | ||
| 136 | # a dot on a line by itself, replace that with a blank line." | ||
| 137 | # This is to prevent cases where a <cr>.<cr> occurs in the log file. Sendmail | ||
| 138 | # interprets that as the end of the mail, and truncates the log before | ||
| 139 | # it gets to Tinderbox. (terry weismann, chris yeh) | ||
| 140 | # | ||
| 141 | # This was replaced by a perl 'port' of the above, written by | ||
| 142 | # preed@netscape.com; good things: no need for system() call, and now it's | ||
| 143 | # all in perl, so we don't have to do OS checking like before. | ||
| 144 | |||
| 145 | open(LOG, "$logfile") || die "Couldn't open logfile: $!\n"; | ||
| 146 | open(OUTLOG, ">${logfile}.last") || die "Couldn't open logfile: $!\n"; | ||
| 147 | |||
| 148 | print OUTLOG $/; | ||
| 149 | print OUTLOG "tinderbox: tree: $BuildTree\n"; | ||
| 150 | print OUTLOG "tinderbox: builddate: $StartTime\n"; | ||
| 151 | print OUTLOG "tinderbox: status: $BuildStatus\n"; | ||
| 152 | print OUTLOG "tinderbox: build: $BuildName $fe\n"; | ||
| 153 | print OUTLOG "tinderbox: errorparser: unix\n"; | ||
| 154 | print OUTLOG "tinderbox: buildfamily: unix\n"; | ||
| 155 | print OUTLOG "tinderbox: END\n"; | ||
| 156 | print OUTLOG $/; | ||
| 157 | |||
| 158 | while (<LOG>) { | ||
| 159 | my $q = 0; | ||
| 160 | |||
| 161 | for (;;) { | ||
| 162 | my $val = $q * 1000; | ||
| 163 | my $Output = substr($_, $val, 1000); | ||
| 164 | |||
| 165 | last if $Output eq undef; | ||
| 166 | |||
| 167 | $Output =~ s/^\.$//g; | ||
| 168 | $Output =~ s/\n//g; | ||
| 169 | print OUTLOG "$Output\n"; | ||
| 170 | $q++; | ||
| 171 | } #EndFor | ||
| 172 | |||
| 173 | } #EndWhile | ||
| 174 | |||
| 175 | close(LOG); | ||
| 176 | close(OUTLOG); | ||
| 177 | |||
| 178 | if ($ReportStatus) { | ||
| 179 | system( "ssh $Tinderbox_server tinderbox_receive < ${logfile}.last" ) | ||
| 180 | } else { | ||
| 181 | print <<"EOF" | ||
| 182 | Not sending logs to http://tinderbox.altinity.org | ||
| 183 | If you have SSH keys setup on the tinderbox server, you can manually send | ||
| 184 | with 'ssh $Tinderbox_server tinderbox_receive < ${logfile}.last' | ||
| 185 | EOF | ||
| 186 | } | ||
| 187 | |||
| 188 | unlink("$logfile"); | ||
| 189 | print "Finished building for tinderbox",$/; | ||
| 190 | |||
| 191 | } #EndSub-BuildIt | ||
| 192 | |||
| 193 | sub ParseArgs { | ||
| 194 | my($i); | ||
| 195 | |||
| 196 | $i = 0; | ||
| 197 | while( $i < @ARGV ) { | ||
| 198 | if ($ARGV[$i] eq '--version' || $ARGV[$i] eq '-v') { | ||
| 199 | die "$0: version $Version\n"; | ||
| 200 | } elsif ($ARGV[$i] eq '-y') { | ||
| 201 | $ReportStatus = 1; | ||
| 202 | } else { | ||
| 203 | &PrintUsage; | ||
| 204 | } | ||
| 205 | |||
| 206 | $i++; | ||
| 207 | } #EndWhile | ||
| 208 | |||
| 209 | } #EndSub-ParseArgs | ||
| 210 | |||
| 211 | sub PrintUsage { | ||
| 212 | die "usage: $0 [-v | --version ] [-t do not send report to tinderbox server]\n"; | ||
| 213 | } | ||
| 214 | |||
| 215 | sub PrintEnv { | ||
| 216 | my ($key); | ||
| 217 | foreach $key (keys %ENV) { | ||
| 218 | print LOG "$key = $ENV{$key}\n"; | ||
| 219 | print "$key = $ENV{$key}\n"; | ||
| 220 | } | ||
| 221 | |||
| 222 | # Print the NPTest variables | ||
| 223 | if (-e "/var/tmp/NPTest.cache") { | ||
| 224 | open F, "/var/tmp/NPTest.cache"; | ||
| 225 | print LOG "NPTest variables:\n"; | ||
| 226 | print LOG <F>; | ||
| 227 | close F; | ||
| 228 | } | ||
| 229 | |||
| 230 | } #EndSub-PrintEnv | ||
| 231 | |||
| 232 | sub SetupPath { | ||
| 233 | my($Path); | ||
| 234 | $Path = $ENV{PATH}; | ||
| 235 | print "Path before: $Path\n"; | ||
| 236 | |||
| 237 | # Don't alter path if we're building off a repository tree; | ||
| 238 | # SunOS make will be used only for snapshots and releases. | ||
| 239 | if ( $OS eq 'SunOS' && !( -e '.svn' || -e '.git' )) { | ||
| 240 | $ENV{'PATH'} = '/usr/ccs/bin:' . $ENV{'PATH'}; | ||
| 241 | } | ||
| 242 | |||
| 243 | $Path = $ENV{PATH}; | ||
| 244 | print "Path After: $Path\n"; | ||
| 245 | } #EndSub-SetupPath | ||
| 246 | |||
| 247 | sub configure { | ||
| 248 | # Configure | ||
| 249 | print LOG "./configure --enable-extra-opts --enable-libtap $ConfigureArgs 2>&1\n"; | ||
| 250 | open (CONFIGURE, "./configure --enable-extra-opts --enable-libtap $ConfigureArgs 2>&1 |") || die "../configure: $!\n"; | ||
| 251 | while (<CONFIGURE>) { | ||
| 252 | print $_; | ||
| 253 | print LOG $_; | ||
| 254 | } | ||
| 255 | close(CONFIGURE); | ||
| 256 | return ! $?; | ||
| 257 | } | ||
| 258 | |||
| 259 | sub make { | ||
| 260 | # Building | ||
| 261 | print LOG "make 2>&1\n"; | ||
| 262 | open( MAKE, "make 2>&1 |"); | ||
| 263 | while ( <MAKE> ) { | ||
| 264 | print $_; | ||
| 265 | print LOG $_; | ||
| 266 | } | ||
| 267 | close( MAKE); | ||
| 268 | return ! $?; | ||
| 269 | } | ||
| 270 | |||
| 271 | sub maketest { | ||
| 272 | # Tests | ||
| 273 | print LOG "LANG=C make test 2>&1 && make install DESTDIR=$TmpDir/tinderbox_build.$$ 2>&1 && make install-strip DESTDIR=$TmpDir/tinderbox_build2.$$ 2>&1\n"; | ||
| 274 | open( MAKE, "LANG=C make test 2>&1 && make install DESTDIR=$TmpDir/tinderbox_build.$$ 2>&1 && make install-strip DESTDIR=$TmpDir/tinderbox_build2.$$ 2>&1 |"); | ||
| 275 | while ( <MAKE> ) { | ||
| 276 | print $_; | ||
| 277 | print LOG $_; | ||
| 278 | } | ||
| 279 | close( MAKE); | ||
| 280 | my $rc = $?; | ||
| 281 | system("rm -fr $TmpDir/tinderbox_build.$$ $TmpDir/tinderbox_build2.$$"); | ||
| 282 | return ! $rc; | ||
| 283 | } | ||
| 284 | |||
| 285 | # Main function | ||
| 286 | &ParseArgs; | ||
| 287 | &SetupPath; | ||
| 288 | &BuildIt; | ||
| 289 | |||
| 290 | 1; | ||
