Compare commits

...

53 Commits

Author SHA1 Message Date
Alexander Aleksandrovič Klimov
f2c83fbbf2
Merge pull request #9950 from Icinga/probot/sync-changelog/support/2.14/9fa41f3b4fbae83d4ca5bbfce4771031b6bd1fa4
CHANGELOG.md: add v2.13.9
2023-12-20 17:30:01 +01:00
Alexander A. Klimov
b85cd98eb1 CHANGELOG.md: add v2.13.9 2023-12-20 16:27:56 +00:00
Alexander Aleksandrovič Klimov
9fa41f3b4f
Merge pull request #9940 from Icinga/2141
Icinga 2.14.1
2023-12-20 17:27:43 +01:00
Alexander Aleksandrovič Klimov
61d190f892
Merge pull request #9947 from Icinga/2141morebackport
Truncate too big notification command lines, fix GelfWriter deadlock and return 503 in /v1/console/* during reload
2023-12-20 12:44:07 +01:00
Alexander Aleksandrovič Klimov
3ddbbebc63
Merge pull request #9946 from Icinga/2141backport
Disable TLS renegotiation, bump Windows deps and fix Icinga DB crashes
2023-12-20 12:40:41 +01:00
Alexander A. Klimov
41b692793b Icinga 2.14.1 2023-12-20 10:56:15 +01:00
Alexander A. Klimov
fecb209fe0 GelfWriter: protect m_Stream via m_WorkQueue, not ObjectLock(this)
On shutdown or HA re-connect ConfigObject#SetAuthority(false) is called which
does ObjectLock(this) and ConfigObject#Pause(). GelfWriter#Pause(), with the
above ObjectLock, calls m_WorkQueue.Join(). But items inside that also doing
ObjectLock(this) cause a deadlock.
2023-12-20 10:46:51 +01:00
Mattia Codato
85c5a7c901 Prevent calls to command API while the configuration is reloading.
Fixes #9840
2023-12-20 10:46:51 +01:00
Alexander A. Klimov
0eeac3b385 PluginNotificationTask::ScriptFunc(): on Linux truncate output and comment
not to run into an exec(3) error E2BIG due to a too long argument.
This sends a notification with truncated output instead of not sending.
2023-12-20 10:46:51 +01:00
Alexander A. Klimov
7efdae6a53 IcingaDB#SendConfigDelete(): fix missing nullptr check before deref 2023-12-20 10:30:01 +01:00
Alexander A. Klimov
79efda7a14 Icinga DB downtime history: provide cancel_time where has_been_cancelled may be 1
The table sla_history_downtime requires a downtime_end.
The Go daemon takes the cancel_time if has_been_cancelled is 1.
So we must supply a cancel_time whereever has_been_cancelled is 1.
Otherwise the Go daemon can't process some entries.
2023-12-20 10:30:01 +01:00
Alexander A. Klimov
8c9f3ede4a Bump OpenSSL shipped for Windows to v3.0.12 2023-12-20 10:14:00 +01:00
Alexander A. Klimov
4547c1e5a3 Bump Boost shipped for Windows to v1.83
Note: For doc/21-development.md use:

perl -pi -e 's/(boost[-\w]*?1[-_]?)82/${1}83/g' doc/21-development.md
2023-12-20 10:14:00 +01:00
Alexander A. Klimov
ec77b6f1e3 Disable TLS renegotiation
The API doesn't need it and a customer's security scanner
is afraid of a potential DoS attack vector.
2023-12-20 10:14:00 +01:00
Alexander Aleksandrovič Klimov
bbb45894dd
Merge pull request #9944 from Icinga/targeted-api-filter-214
FilterUtility::GetFilterTargets(): don't run filter for specific object(s) for all objects
2023-12-19 17:40:59 +01:00
Alexander Aleksandrovič Klimov
b55a14d536
Merge pull request #9921 from Icinga/doc2141
Update documentation
2023-12-19 17:01:31 +01:00
Alexander A. Klimov
03aa5adb7a Tests: config_apply/gettargetservices_*: use BOOST_CHECK_EQUAL_COLLECTIONS()
to show the value diff in case of mismatch.

Co-authored-by: Yonas Habteab <yonas.habteab@icinga.com>
2023-12-19 15:19:20 +01:00
Alexander A. Klimov
b99db24100 Test ApplyRule::GetTarget*s() 2023-12-19 15:19:20 +01:00
Alexander A. Klimov
bcbb1aee52 FilterUtility::GetFilterTargets(): don't run filter for specific object(s) for all objects 2023-12-19 15:19:20 +01:00
Alexander A. Klimov
60b7e96adc ApplyRule::GetTarget*s(): support constant strings from variables
in addition to literal strings. This is for sandboxed filters with some
variables pre-set by the caller. They're "constant" in that scope, too.
2023-12-19 15:19:20 +01:00
Alexander A. Klimov
8248fa110c Introduce DictExpression#GetExpressions() 2023-12-19 15:19:20 +01:00
Alexander A. Klimov
5c10bad86f Introduce Dictionary#GetRef() 2023-12-19 15:19:20 +01:00
Alexander Aleksandrovič Klimov
5059d0f8b0
Merge pull request #9933 from Icinga/renew-the-ca-9890-214
ApiListener#Start(): auto-renew CA on its owner
2023-12-19 15:15:00 +01:00
Alexander A. Klimov
12c706a8ac Update AUTHORS 2023-12-19 12:29:51 +01:00
Julian Brost
5835e2e03b Update .mailmap 2023-12-19 12:29:51 +01:00
Alexander A. Klimov
61900b73e1 Doc: Troubleshooting: remove obsolete section "Analyze Notification Result"
This feature has been reverted and won't be re-introduced anytime soon.
2023-12-19 12:29:51 +01:00
Alexander A. Klimov
4195f8d0f0 RequestCertificateHandler(): also renew if CA needs a renewal
and a newer one is available.
2023-12-18 17:04:59 +01:00
Alexander A. Klimov
6b000fbce6 CertificateToString(): allow raw pointer input 2023-12-18 17:04:59 +01:00
Alexander A. Klimov
32f43c4873 ApiListener#Start(): auto-renew CA on its owner
otherwise it would expire.
2023-12-18 17:04:59 +01:00
Alexander A. Klimov
b3dee0bb0a ApiListener#RenewCert(): enable optional CA creation 2023-12-18 17:04:59 +01:00
Alexander A. Klimov
0cb037c698 CreateCertIcingaCA(EVP_PKEY*, X509_NAME*): enable optional CA creation 2023-12-18 17:04:59 +01:00
Alexander A. Klimov
17eac30868 Test IsCertUptodate() and IsCaUptodate() 2023-12-18 17:04:59 +01:00
Alexander A. Klimov
0f4723e567 Introduce IsCaUptodate() by splitting IsCertUptodate() 2023-12-18 17:04:59 +01:00
Alexander Aleksandrovič Klimov
bff7e69991
Merge pull request #9932 from Icinga/do-not-re-notify-if-filtered-states-don-t-change-4503-214
Discard likely duplicate problem notifications via Notification#last_notified_state_per_user
2023-12-13 18:15:56 +01:00
Alexander A. Klimov
d7500ca1bd Notification#BeginExecuteNotification(): on recovery clear last_notified_state_per_user 2023-12-13 16:14:57 +01:00
Alexander A. Klimov
bbadf1f27b Notification#BeginExecuteNotification(): discard likely duplicate problem notifications 2023-12-13 16:14:57 +01:00
Alexander A. Klimov
0ae2bdc444 Cluster-sync Notification#last_notified_state_per_user 2023-12-13 16:14:57 +01:00
Alexander A. Klimov
66ba9f446a Notification#BeginExecuteNotification(): track state change notifications 2023-12-13 16:14:57 +01:00
Alexander A. Klimov
9a08472162 Docs: change "Amazon Linux 2" to "Amazon Linux" where applicable
We also support Amazon Linux 2023 now.
2023-11-24 17:29:35 +01:00
Alvar Penning
0dd66f9886 Document host Common Runtime Attribute 2023-11-24 17:29:35 +01:00
Alvar Penning
bb717cf177 Fix link text for Downtime* Event Stream Types
The link text for all Downtime* Event Stream Types contains "Comment"
instead of "Downtime" even when pointing to the correct object.
2023-11-24 17:29:35 +01:00
Yonas Habteab
04dbf4aa13 Fix downtime host/service name attribute descriptions 2023-11-24 17:29:35 +01:00
Alexander Aleksandrovič Klimov
2e03a2e528 Doc: ITL: correct $ifw_api_crl$ default
In contrast to cert/key/CA, no CRL means no CRL.
(The behavior of the API is the same.)
2023-11-24 17:29:35 +01:00
Mathias Aerts
99aa33b85f Fix typo 2023-11-24 17:29:35 +01:00
Alexander Aleksandrovič Klimov
0ceff4c09b
Merge pull request #9918 from Icinga/gha2141
Update GitHub actions
2023-11-24 17:26:27 +01:00
Alexander A. Klimov
9944437b7b Update AUTHORS 2023-11-24 15:06:52 +01:00
Lord Hepipud
968f7401cf Adds ProgressPreference SilentlyContinue
We should use `$Global:ProgressPreference = 'SilentlyContinue';` to disable the progress bar during download.
By doing so, information are directly written to the disk instead of written inside the memory and dumped to the disk afterwards
2023-11-24 14:43:52 +01:00
Alexander Aleksandrovič Klimov
d583598d08 GHA: drop EOL Fedora 36 2023-11-24 14:38:44 +01:00
Alexander A. Klimov
de47878991 GHA: complain if PR adds commits from people not yet listed in ./AUTHORS
not to have to update ./AUTHORS or .mailmap after merging.
2023-11-24 14:38:40 +01:00
Alexander A. Klimov
3801b8a7cb GHA: cancel runs on PR, but not on push
In a PR one top commit replaces the previous one.
But the central branches are more like timelines.
It's nice to have red crosses in a such timeline
as clear indicators that something was actually broken.
2023-11-24 14:38:00 +01:00
Alexander Aleksandrovič Klimov
35ef622cc6 GHA: add upcoming (already frozen) Ubuntu 23.10 2023-11-24 14:38:00 +01:00
Alexander Aleksandrovič Klimov
1f4ac7e651 GHA: add upcoming (already frozen) Fedora 39 2023-11-24 14:38:00 +01:00
Alexander Aleksandrovič Klimov
19927d0043 GHA: drop EOL Ubuntu 22.10 2023-11-24 14:38:00 +01:00
47 changed files with 1297 additions and 214 deletions

39
.github/workflows/authors-file.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: AUTHORS file
on:
pull_request: { }
jobs:
authors-file:
name: AUTHORS file
runs-on: ubuntu-latest
steps:
- name: Checkout HEAD
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check whether ./AUTHORS is up-to-date
run: |
set -exo pipefail
sort -uo AUTHORS AUTHORS
git add AUTHORS
git log --format='format:%aN <%aE>' "$(
git merge-base "origin/$GITHUB_BASE_REF" "origin/$GITHUB_HEAD_REF"
)..origin/$GITHUB_HEAD_REF" >> AUTHORS
sort -uo AUTHORS AUTHORS
git diff AUTHORS >> AUTHORS.diff
- name: Complain if ./AUTHORS isn't up-to-date
run: |
if [ -s AUTHORS.diff ]; then
cat <<'EOF' >&2
There are the following new authors. If the commit author data is correct,
either add them to the AUTHORS file or update .mailmap. See gitmailmap(5) or:
https://git-scm.com/docs/gitmailmap
Don't hesitate to ask us for help if necessary.
EOF
cat AUTHORS.diff
exit 1
fi

View File

@ -11,7 +11,7 @@ on:
- published - published
concurrency: concurrency:
group: docker-${{ github.ref }} group: docker-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:

View File

@ -8,7 +8,7 @@ on:
pull_request: {} pull_request: {}
concurrency: concurrency:
group: linux-${{ github.ref }} group: linux-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
@ -27,9 +27,9 @@ jobs:
- debian:10 - debian:10
- debian:11 # and Raspbian 11 - debian:11 # and Raspbian 11
- debian:12 # and Raspbian 12 - debian:12 # and Raspbian 12
- fedora:36
- fedora:37 - fedora:37
- fedora:38 - fedora:38
- fedora:39
- opensuse/leap:15.3 # SLES 15.3 - opensuse/leap:15.3 # SLES 15.3
- opensuse/leap:15.4 # and SLES 15.4 - opensuse/leap:15.4 # and SLES 15.4
- opensuse/leap:15.5 # and SLES 15.5 - opensuse/leap:15.5 # and SLES 15.5
@ -37,8 +37,8 @@ jobs:
- rockylinux:9 # RHEL 9 - rockylinux:9 # RHEL 9
- ubuntu:20.04 - ubuntu:20.04
- ubuntu:22.04 - ubuntu:22.04
- ubuntu:22.10
- ubuntu:23.04 - ubuntu:23.04
- ubuntu:23.10
steps: steps:
- name: Checkout HEAD - name: Checkout HEAD

View File

@ -8,7 +8,7 @@ on:
pull_request: {} pull_request: {}
concurrency: concurrency:
group: rpm-${{ github.ref }} group: rpm-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:

View File

@ -8,7 +8,7 @@ on:
pull_request: {} pull_request: {}
concurrency: concurrency:
group: windows-${{ github.ref }} group: windows-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:

View File

@ -23,6 +23,7 @@ Alexander A. Klimov <alexander.klimov@icinga.com> <alexander.klimov@icinga.com>
<marius@graylog.com> <marius@torch.sh> <marius@graylog.com> <marius@torch.sh>
<markus.frosch@icinga.com> <lazyfrosch@icinga.org> <markus.frosch@icinga.com> <lazyfrosch@icinga.org>
<markus.frosch@icinga.com> <markus@lazyfrosch.de> <markus.frosch@icinga.com> <markus@lazyfrosch.de>
<mathias.aerts@delta.blue> <mathiasaerts@users.noreply.github.com>
<michael.friedrich@icinga.com> <michael.friedrich@gmail.com> <michael.friedrich@icinga.com> <michael.friedrich@gmail.com>
<michael.friedrich@icinga.com> <Michael.Friedrich@netways.de> <michael.friedrich@icinga.com> <Michael.Friedrich@netways.de>
<nicole.lang@icinga.com> <nicole.lang@netways.de> <nicole.lang@icinga.com> <nicole.lang@netways.de>

View File

@ -11,6 +11,7 @@ Alexander Fuhr <alexander.fuhr@netways.de>
Alexander Schomburg <script.acc@alex.schomb.org> Alexander Schomburg <script.acc@alex.schomb.org>
Alexander Stoll <astoll@netways.de> Alexander Stoll <astoll@netways.de>
Alexander Wirt <formorer@debian.org> Alexander Wirt <formorer@debian.org>
Alvar Penning <alvar.penning@icinga.com>
Andrea Avancini <andrea.avancini@wuerth-phoenix.com> Andrea Avancini <andrea.avancini@wuerth-phoenix.com>
Andrea Kao <eirinikos@gmail.com> Andrea Kao <eirinikos@gmail.com>
Andreas Maus <maus@badphish.ypbind.de> Andreas Maus <maus@badphish.ypbind.de>
@ -153,6 +154,7 @@ Lennart Betz <lennart.betz@icinga.com>
Leon Stringer <leon@priorsvle.com> Leon Stringer <leon@priorsvle.com>
lihan <tclh123@gmail.com> lihan <tclh123@gmail.com>
log1-c <24474580+log1-c@users.noreply.github.com> log1-c <24474580+log1-c@users.noreply.github.com>
Lord Hepipud <contact@lordhepipud.de>
Lorenz Kästle <lorenz.kaestle@netways.de> Lorenz Kästle <lorenz.kaestle@netways.de>
Louis Sautier <sautier.louis@gmail.com> Louis Sautier <sautier.louis@gmail.com>
Luca Lesinigo <luca@lm-net.it> Luca Lesinigo <luca@lm-net.it>

View File

@ -7,6 +7,32 @@ documentation before upgrading to a new release.
Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed). Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed).
## 2.14.1 (2023-12-21)
Version 2.14.1 is a hotfix release for masters and satellites that mainly
prevents permanent disintegration of a whole cluster due to root CA expiry.
### Security
* Automatically renew own root CA and distribute it to all nodes. #9933
* Update OpenSSL shipped on Windows to v3.0.12. #9946
* Disable TLS renegotiation (handshake on existing connection). #9946
### Bugfixes
* Icinga DB feature: fix crash due to missing NULL pointer check. #9946
* Icinga DB feature: fix data written into Redis crashing the Go daemon. #9946
* GelfWriter: fix deadlock on stop/reload caused by busy queue. #9947
* Don't lose notifications due to too long output, truncate it. #9947
### Enhancements
* Discard duplicate problem notifications due to state filtering. #9932
* Speed up API filters targeting specific hosts/services to O(1). #9944
* POST /v1/console/\*: return HTTP 503 while Icinga is reloading. #9947
* Update Boost shipped on Windows to v1.83. #9946
* Documentation: several fixes and improvements. #9921
## 2.14.0 (2023-07-12) ## 2.14.0 (2023-07-12)
[Issues and PRs](https://github.com/Icinga/icinga2/issues?q=is%3Aclosed+milestone%3A2.14.0) [Issues and PRs](https://github.com/Icinga/icinga2/issues?q=is%3Aclosed+milestone%3A2.14.0)
@ -199,6 +225,26 @@ Add `linux_netdev` check command. #9045
* Several code quality improvements. #8815 #9106 #9250 * Several code quality improvements. #8815 #9106 #9250
#9508 #9517 #9537 #9594 #9605 #9606 #9641 #9658 #9702 #9717 #9738 #9508 #9517 #9537 #9594 #9605 #9606 #9641 #9658 #9702 #9717 #9738
## 2.13.9 (2023-12-21)
Version 2.13.9 is a hotfix release for masters and satellites that mainly
prevents permanent disintegration of a whole cluster due to root CA expiry.
### Security
* Automatically renew own root CA and distribute it to all nodes. #9934
* Update OpenSSL shipped on Windows to v3.0.12. #9945
* Disable TLS renegotiation (handshake on existing connection). #9945
### Bugfixes
* Icinga DB feature: fix crash due to missing NULL pointer check. #9945
* Icinga DB feature: fix data written into Redis crashing the Go daemon. #9945
### Updates
* Update Boost shipped on Windows to v1.83. #9945
## 2.13.8 (2023-07-12) ## 2.13.8 (2023-07-12)
Version 2.13.8 is a maintenance release that fixes some bugs, Version 2.13.8 is a maintenance release that fixes some bugs,

View File

@ -111,6 +111,12 @@ refs #1234
You can add multiple commits during your journey to finish your patch. You can add multiple commits during your journey to finish your patch.
Don't worry, you can squash those changes into a single commit later on. Don't worry, you can squash those changes into a single commit later on.
Ensure your name and email address in the commit metadata are correct.
In your first contribution (PR) also add them to [AUTHORS](./AUTHORS).
If those metadata changed since your last successful contribution,
you should update [AUTHORS](./AUTHORS) and [.mailmap](./.mailmap).
For the latter see [gitmailmap(5)](https://git-scm.com/docs/gitmailmap).
## <a id="contributing-pull-requests"></a> Pull Requests ## <a id="contributing-pull-requests"></a> Pull Requests
Once you've commited your changes, please update your local master Once you've commited your changes, please update your local master

View File

@ -1,2 +1,2 @@
Version: 2.14.0 Version: 2.14.1
Revision: 1 Revision: 1

View File

@ -200,7 +200,7 @@ zypper ar https://download.opensuse.org/repositories/server:/monitoring/15.3/ser
<!-- {% endif %} --> <!-- {% endif %} -->
<!-- {% if amazon_linux %} --> <!-- {% if amazon_linux %} -->
### Amazon Linux 2 Repository <a id="amazon-linux-2-repository"></a> ### Amazon Linux Repository <a id="amazon-linux-2-repository"></a>
!!! info !!! info
@ -214,12 +214,14 @@ rpm --import https://packages.icinga.com/icinga.key
wget https://packages.icinga.com/subscription/amazon/ICINGA-release.repo -O /etc/yum.repos.d/ICINGA-release.repo wget https://packages.icinga.com/subscription/amazon/ICINGA-release.repo -O /etc/yum.repos.d/ICINGA-release.repo
``` ```
The packages for Amazon Linux 2 depend on other packages which are distributed The packages for **Amazon Linux 2** depend on other packages which are distributed
as part of the [EPEL repository](https://fedoraproject.org/wiki/EPEL). as part of the [EPEL repository](https://fedoraproject.org/wiki/EPEL).
```bash ```bash
yum install epel-release yum install epel-release
``` ```
The packages for newer versions of Amazon Linux don't require additional repositories.
<!-- {% endif %} --> <!-- {% endif %} -->
<!-- {% if windows %} --> <!-- {% if windows %} -->
@ -307,7 +309,7 @@ zypper install icinga2
<!-- {% if amazon_linux %} --> <!-- {% if amazon_linux %} -->
<!-- {% if not icingaDocs %} --> <!-- {% if not icingaDocs %} -->
#### Amazon Linux 2 #### Amazon Linux
<!-- {% endif %} --> <!-- {% endif %} -->
```bash ```bash
yum install icinga2 yum install icinga2
@ -418,15 +420,17 @@ zypper install --recommends monitoring-plugins-all
<!-- {% if amazon_linux %} --> <!-- {% if amazon_linux %} -->
<!-- {% if not icingaDocs %} --> <!-- {% if not icingaDocs %} -->
#### Amazon Linux 2 #### Amazon Linux
<!-- {% endif %} --> <!-- {% endif %} -->
The packages for Amazon Linux 2 depend on other packages which are distributed as part of the EPEL repository. The packages for **Amazon Linux 2** depend on other packages which are distributed as part of the EPEL repository.
```bash ```bash
amazon-linux-extras install epel amazon-linux-extras install epel
yum install nagios-plugins-all yum install nagios-plugins-all
``` ```
Unfortunately newer versions of Amazon Linux don't provide those plugins, yet.
<!-- {% endif %} --> <!-- {% endif %} -->
## Set up Icinga 2 API <a id="set-up-icinga2-api"></a> ## Set up Icinga 2 API <a id="set-up-icinga2-api"></a>
@ -505,7 +509,7 @@ Use your distribution's package manager to install the `icingadb-redis` package
<!-- {% if amazon_linux %} --> <!-- {% if amazon_linux %} -->
<!-- {% if not icingaDocs %} --> <!-- {% if not icingaDocs %} -->
##### Amazon Linux 2 ##### Amazon Linux
<!-- {% endif %} --> <!-- {% endif %} -->
```bash ```bash
yum install icingadb-redis yum install icingadb-redis

View File

@ -225,12 +225,12 @@ apply Service "db-size-" for (db_name => config in host.vars.databases) {
check_command = "mysql_health" check_command = "mysql_health"
if (config.mysql_health_username) { if (config.mysql_health_username) {
vars.mysql_healt_username = config.mysql_health_username vars.mysql_health_username = config.mysql_health_username
} else { } else {
vars.mysql_health_username = "root" vars.mysql_health_username = "root"
} }
if (config.mysql_health_password) { if (config.mysql_health_password) {
vars.mysql_healt_password = config.mysql_health_password vars.mysql_health_password = config.mysql_health_password
} else { } else {
vars.mysql_health_password = "icingar0xx" vars.mysql_health_password = "icingar0xx"
} }

View File

@ -34,6 +34,7 @@ the [Icinga 2 API](12-icinga2-api.md#icinga2-api-config-objects).
templates | Array | Templates imported on object compilation. templates | Array | Templates imported on object compilation.
package | String | [Configuration package name](12-icinga2-api.md#icinga2-api-config-management) this object belongs to. Local configuration is set to `_etc`, runtime created objects use `_api`. package | String | [Configuration package name](12-icinga2-api.md#icinga2-api-config-management) this object belongs to. Local configuration is set to `_etc`, runtime created objects use `_api`.
source\_location | Dictionary | Location information where the configuration files are stored. source\_location | Dictionary | Location information where the configuration files are stored.
name | String | Object name. Might be used in [apply rules](03-monitoring-basics.md#using-apply).
## Monitoring Objects <a id="object-types-monitoring"></a> ## Monitoring Objects <a id="object-types-monitoring"></a>
@ -731,7 +732,6 @@ Configuration Attributes:
event\_command | Object name | **Optional.** The name of an event command that should be executed every time the service's state changes or the service is in a `SOFT` state. event\_command | Object name | **Optional.** The name of an event command that should be executed every time the service's state changes or the service is in a `SOFT` state.
volatile | Boolean | **Optional.** Treat all state changes as HARD changes. See [here](08-advanced-topics.md#volatile-services-hosts) for details. Defaults to `false`. volatile | Boolean | **Optional.** Treat all state changes as HARD changes. See [here](08-advanced-topics.md#volatile-services-hosts) for details. Defaults to `false`.
zone | Object name | **Optional.** The zone this object is a member of. Please read the [distributed monitoring](06-distributed-monitoring.md#distributed-monitoring) chapter for details. zone | Object name | **Optional.** The zone this object is a member of. Please read the [distributed monitoring](06-distributed-monitoring.md#distributed-monitoring) chapter for details.
name | String | **Required.** The service name. Must be unique on a per-host basis. For advanced usage in [apply rules](03-monitoring-basics.md#using-apply) only.
command\_endpoint | Object name | **Optional.** The endpoint where commands are executed on. command\_endpoint | Object name | **Optional.** The endpoint where commands are executed on.
notes | String | **Optional.** Notes for the service. notes | String | **Optional.** Notes for the service.
notes\_url | String | **Optional.** URL for notes for the service (for example, in notification commands). notes\_url | String | **Optional.** URL for notes for the service (for example, in notification commands).
@ -1046,8 +1046,8 @@ Configuration Attributes:
Name | Type | Description Name | Type | Description
--------------------------|-----------------------|---------------------------------- --------------------------|-----------------------|----------------------------------
host\_name | Object name | **Required.** The name of the host this comment belongs to. host\_name | Object name | **Required.** The name of the host this downtime belongs to.
service\_name | Object name | **Optional.** The short name of the service this comment belongs to. If omitted, this comment object is treated as host comment. service\_name | Object name | **Optional.** The short name of the service this downtime belongs to. If omitted, this downtime object is treated as host downtime.
author | String | **Required.** The author's name. author | String | **Required.** The author's name.
comment | String | **Required.** The comment text. comment | String | **Required.** The comment text.
start\_time | Timestamp | **Required.** The start time as UNIX timestamp. start\_time | Timestamp | **Required.** The start time as UNIX timestamp.

View File

@ -215,7 +215,7 @@ Optional custom variables passed as [command parameters](03-monitoring-basics.md
| ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. | | ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. |
| ifw\_api\_key | null (Icinga PKI) | TLS client private key path. | | ifw\_api\_key | null (Icinga PKI) | TLS client private key path. |
| ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. | | ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. |
| ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. | | ifw\_api\_crl | null (none) | Path to TLS CRL to check peer against. |
| ifw\_api\_username | null (none) | Basic auth username. | | ifw\_api\_username | null (none) | Basic auth username. |
| ifw\_api\_password | null (none) | Basic auth password. | | ifw\_api\_password | null (none) | Basic auth password. |

View File

@ -1850,7 +1850,7 @@ Example for all object events:
--------------|---------------|-------------------------- --------------|---------------|--------------------------
type | String | Event type `DowntimeAdded`. type | String | Event type `DowntimeAdded`.
timestamp | Timestamp | Unix timestamp when the event happened. timestamp | Timestamp | Unix timestamp when the event happened.
downtime | Dictionary | Serialized [Comment](09-object-types.md#objecttype-downtime) object. downtime | Dictionary | Serialized [Downtime](09-object-types.md#objecttype-downtime) object.
#### <a id="icinga2-api-event-streams-type-downtimeremoved"></a> Event Stream Type: DowntimeRemoved #### <a id="icinga2-api-event-streams-type-downtimeremoved"></a> Event Stream Type: DowntimeRemoved
@ -1858,7 +1858,7 @@ Example for all object events:
--------------|---------------|-------------------------- --------------|---------------|--------------------------
type | String | Event type `DowntimeRemoved`. type | String | Event type `DowntimeRemoved`.
timestamp | Timestamp | Unix timestamp when the event happened. timestamp | Timestamp | Unix timestamp when the event happened.
downtime | Dictionary | Serialized [Comment](09-object-types.md#objecttype-downtime) object. downtime | Dictionary | Serialized [Downtime](09-object-types.md#objecttype-downtime) object.
#### <a id="icinga2-api-event-streams-type-downtimestarted"></a> Event Stream Type: DowntimeStarted #### <a id="icinga2-api-event-streams-type-downtimestarted"></a> Event Stream Type: DowntimeStarted
@ -1867,7 +1867,7 @@ Example for all object events:
--------------|---------------|-------------------------- --------------|---------------|--------------------------
type | String | Event type `DowntimeStarted`. type | String | Event type `DowntimeStarted`.
timestamp | Timestamp | Unix timestamp when the event happened. timestamp | Timestamp | Unix timestamp when the event happened.
downtime | Dictionary | Serialized [Comment](09-object-types.md#objecttype-downtime) object. downtime | Dictionary | Serialized [Downtime](09-object-types.md#objecttype-downtime) object.
#### <a id="icinga2-api-event-streams-type-downtimetriggered"></a> Event Stream Type: DowntimeTriggered #### <a id="icinga2-api-event-streams-type-downtimetriggered"></a> Event Stream Type: DowntimeTriggered
@ -1876,7 +1876,7 @@ Example for all object events:
--------------|---------------|-------------------------- --------------|---------------|--------------------------
type | String | Event type `DowntimeTriggered`. type | String | Event type `DowntimeTriggered`.
timestamp | Timestamp | Unix timestamp when the event happened. timestamp | Timestamp | Unix timestamp when the event happened.
downtime | Dictionary | Serialized [Comment](09-object-types.md#objecttype-downtime) object. downtime | Dictionary | Serialized [Downtime](09-object-types.md#objecttype-downtime) object.
### Event Stream Filter <a id="icinga2-api-event-streams-filter"></a> ### Event Stream Filter <a id="icinga2-api-event-streams-filter"></a>

View File

@ -843,7 +843,7 @@ yum install icinga2-ido-mysql
zypper install icinga2-ido-mysql zypper install icinga2-ido-mysql
``` ```
###### Amazon Linux 2 ###### Amazon Linux
```bash ```bash
yum install icinga2-ido-mysql yum install icinga2-ido-mysql
@ -942,7 +942,7 @@ yum install icinga2-ido-pgsql
zypper install icinga2-ido-pgsql zypper install icinga2-ido-pgsql
``` ```
###### Amazon Linux 2 ###### Amazon Linux
```bash ```bash
yum install icinga2-ido-pgsql yum install icinga2-ido-pgsql

View File

@ -950,95 +950,6 @@ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localho
``` ```
### Analyze Notification Result <a id="troubleshooting-notifications-result"></a>
> **Note**
>
> This feature is available since v2.11 and requires all endpoints
> being updated.
Notifications inside a HA enabled zone are balanced between the endpoints,
just like checks.
Sometimes notifications may fail, and with looking into the (debug) logs
for both masters, you cannot correlate this correctly.
The `last_notification_result` runtime attribute is stored and synced for Notification
objects and can be queried via REST API.
Example for retrieving the notification object and result from all `disk` services using a
[regex match](18-library-reference.md#global-functions-regex) on the name:
```
$ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' -X POST 'https://localhost:5665/v1/objects/notifications' \
-d '{ "filter": "regex(pattern, service.name)", "filter_vars": { "pattern": "^disk" }, "attrs": [ "__name", "last_notification_result" ], "pretty": true }'
{
"results": [
{
"attrs": {
"last_notification_result": {
"active": true,
"command": [
"/etc/icinga2/scripts/mail-service-notification.sh",
"-4",
"",
"-6",
"",
"-b",
"",
"-c",
"",
"-d",
"2019-08-02 10:54:16 +0200",
"-e",
"disk",
"-l",
"icinga2-agent1.localdomain",
"-n",
"icinga2-agent1.localdomain",
"-o",
"DISK OK - free space: / 38108 MB (90.84% inode=100%);",
"-r",
"user@localdomain",
"-s",
"OK",
"-t",
"RECOVERY",
"-u",
"disk"
],
"execution_end": 1564736056.186217,
"execution_endpoint": "icinga2-master1.localdomain",
"execution_start": 1564736056.132323,
"exit_status": 0.0,
"output": "",
"type": "NotificationResult"
}
},
"joins": {},
"meta": {},
"name": "icinga2-agent1.localdomain!disk!mail-service-notification",
"type": "Notification"
}
...
]
}
```
Example with the debug console:
```
$ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' --eval 'get_object(Notification, "icinga2-agent1.localdomain!disk!mail-service-notification").last_notification_result.execution_endpoint' | jq
"icinga2-agent1.localdomain"
```
Whenever a notification command failed to execute, you can fetch the output as well.
## Feature Troubleshooting <a id="troubleshooting-features"></a> ## Feature Troubleshooting <a id="troubleshooting-features"></a>
### Feature is not working <a id="feature-not-working"></a> ### Feature is not working <a id="feature-not-working"></a>

View File

@ -1514,6 +1514,76 @@ Message updates will be dropped when:
* Notification does not exist. * Notification does not exist.
* Origin endpoint's zone is not allowed to access this checkable. * Origin endpoint's zone is not allowed to access this checkable.
#### event::UpdateLastNotifiedStatePerUser <a id="technical-concepts-json-rpc-messages-event-updatelastnotifiedstateperuser"></a>
> Location: `clusterevents.cpp`
##### Message Body
Key | Value
----------|---------
jsonrpc | 2.0
method | event::UpdateLastNotifiedStatePerUser
params | Dictionary
##### Params
Key | Type | Description
-------------|--------|------------------
notification | String | Notification name
user | String | User name
state | Number | Checkable state the user just got a problem notification for
Used to sync the state of a notification object within the same HA zone.
##### Functions
Event Sender: `Notification::OnLastNotifiedStatePerUserUpdated`
Event Receiver: `LastNotifiedStatePerUserUpdatedAPIHandler`
##### Permissions
The receiver will not process messages from not configured endpoints.
Message updates will be dropped when:
* Notification does not exist.
* Origin endpoint is not within the local zone.
#### event::ClearLastNotifiedStatePerUser <a id="technical-concepts-json-rpc-messages-event-clearlastnotifiedstateperuser"></a>
> Location: `clusterevents.cpp`
##### Message Body
Key | Value
----------|---------
jsonrpc | 2.0
method | event::ClearLastNotifiedStatePerUser
params | Dictionary
##### Params
Key | Type | Description
-------------|--------|------------------
notification | String | Notification name
Used to sync the state of a notification object within the same HA zone.
##### Functions
Event Sender: `Notification::OnLastNotifiedStatePerUserCleared`
Event Receiver: `LastNotifiedStatePerUserClearedAPIHandler`
##### Permissions
The receiver will not process messages from not configured endpoints.
Message updates will be dropped when:
* Notification does not exist.
* Origin endpoint is not within the local zone.
#### event::SetForceNextCheck <a id="technical-concepts-json-rpc-messages-event-setforcenextcheck"></a> #### event::SetForceNextCheck <a id="technical-concepts-json-rpc-messages-event-setforcenextcheck"></a>
> Location: `clusterevents.cpp` > Location: `clusterevents.cpp`

View File

@ -477,18 +477,18 @@ File Type: EXECUTABLE IMAGE
Image has the following dependencies: Image has the following dependencies:
boost_coroutine-vc142-mt-gd-x64-1_82.dll boost_coroutine-vc142-mt-gd-x64-1_83.dll
boost_date_time-vc142-mt-gd-x64-1_82.dll boost_date_time-vc142-mt-gd-x64-1_83.dll
boost_filesystem-vc142-mt-gd-x64-1_82.dll boost_filesystem-vc142-mt-gd-x64-1_83.dll
boost_thread-vc142-mt-gd-x64-1_82.dll boost_thread-vc142-mt-gd-x64-1_83.dll
boost_regex-vc142-mt-gd-x64-1_82.dll boost_regex-vc142-mt-gd-x64-1_83.dll
libssl-3_0-x64.dll libssl-3_0-x64.dll
libcrypto-3_0-x64.dll libcrypto-3_0-x64.dll
WS2_32.dll WS2_32.dll
dbghelp.dll dbghelp.dll
SHLWAPI.dll SHLWAPI.dll
msi.dll msi.dll
boost_unit_test_framework-vc142-mt-gd-x64-1_82.dll boost_unit_test_framework-vc142-mt-gd-x64-1_83.dll
KERNEL32.dll KERNEL32.dll
SHELL32.dll SHELL32.dll
ADVAPI32.dll ADVAPI32.dll
@ -1763,7 +1763,7 @@ mkdir build
cd .\build\ cd .\build\
& "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" ` & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" `
-DICINGA2_UNITY_BUILD=OFF -DBoost_INCLUDE_DIR=C:\local\boost_1_82_0-Win64 ` -DICINGA2_UNITY_BUILD=OFF -DBoost_INCLUDE_DIR=C:\local\boost_1_83_0-Win64 `
-DBISON_EXECUTABLE=C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_bison.exe ` -DBISON_EXECUTABLE=C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_bison.exe `
-DFLEX_EXECUTABLE=C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_flex.exe .. -DFLEX_EXECUTABLE=C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_flex.exe ..
@ -1935,16 +1935,16 @@ Download the [boost-binaries](https://sourceforge.net/projects/boost/files/boost
- 64 for 64 bit builds - 64 for 64 bit builds
``` ```
https://sourceforge.net/projects/boost/files/boost-binaries/1.82.0/boost_1_82_0-msvc-14.2-64.exe/download https://sourceforge.net/projects/boost/files/boost-binaries/1.82.0/boost_1_83_0-msvc-14.2-64.exe/download
``` ```
Run the installer and leave the default installation path in `C:\local\boost_1_82_0`. Run the installer and leave the default installation path in `C:\local\boost_1_83_0`.
##### Source & Compile ##### Source & Compile
In order to use the boost development header and library files you need to [download](https://www.boost.org/users/download/) In order to use the boost development header and library files you need to [download](https://www.boost.org/users/download/)
Boost and then extract it to e.g. `C:\local\boost_1_82_0`. Boost and then extract it to e.g. `C:\local\boost_1_83_0`.
> **Note** > **Note**
> >
@ -1952,12 +1952,12 @@ Boost and then extract it to e.g. `C:\local\boost_1_82_0`.
> the archive contains more than 70k files. > the archive contains more than 70k files.
In order to integrate Boost into Visual Studio, open the `Developer Command Prompt` from the start menu, In order to integrate Boost into Visual Studio, open the `Developer Command Prompt` from the start menu,
and navigate to `C:\local\boost_1_82_0`. and navigate to `C:\local\boost_1_83_0`.
Execute `bootstrap.bat` first. Execute `bootstrap.bat` first.
``` ```
cd C:\local\boost_1_82_0 cd C:\local\boost_1_83_0
bootstrap.bat bootstrap.bat
``` ```
@ -2040,8 +2040,8 @@ You need to specify the previously installed component paths.
Variable | Value | Description Variable | Value | Description
----------------------|----------------------------------------------------------------------|------------------------------------------------------- ----------------------|----------------------------------------------------------------------|-------------------------------------------------------
`BOOST_ROOT` | `C:\local\boost_1_82_0` | Root path where you've extracted and compiled Boost. `BOOST_ROOT` | `C:\local\boost_1_83_0` | Root path where you've extracted and compiled Boost.
`BOOST_LIBRARYDIR` | Binary: `C:\local\boost_1_82_0\lib64-msvc-14.2`, Source: `C:\local\boost_1_82_0\stage` | Path to the static compiled Boost libraries, directory must contain `lib`. `BOOST_LIBRARYDIR` | Binary: `C:\local\boost_1_83_0\lib64-msvc-14.2`, Source: `C:\local\boost_1_83_0\stage` | Path to the static compiled Boost libraries, directory must contain `lib`.
`BISON_EXECUTABLE` | `C:\ProgramData\chocolatey\lib\winflexbison\tools\win_bison.exe` | Path to the Bison executable. `BISON_EXECUTABLE` | `C:\ProgramData\chocolatey\lib\winflexbison\tools\win_bison.exe` | Path to the Bison executable.
`FLEX_EXECUTABLE` | `C:\ProgramData\chocolatey\lib\winflexbison\tools\win_flex.exe` | Path to the Flex executable. `FLEX_EXECUTABLE` | `C:\ProgramData\chocolatey\lib\winflexbison\tools\win_flex.exe` | Path to the Flex executable.
`ICINGA2_UNITY_BUILD` | OFF | Disable unity builds for development environments. `ICINGA2_UNITY_BUILD` | OFF | Disable unity builds for development environments.
@ -2076,8 +2076,8 @@ $env:ICINGA2_INSTALLPATH = 'C:\Program Files\Icinga2-debug'
$env:ICINGA2_BUILDPATH='debug' $env:ICINGA2_BUILDPATH='debug'
$env:CMAKE_BUILD_TYPE='Debug' $env:CMAKE_BUILD_TYPE='Debug'
$env:OPENSSL_ROOT_DIR='C:\OpenSSL-Win64' $env:OPENSSL_ROOT_DIR='C:\OpenSSL-Win64'
$env:BOOST_ROOT='C:\local\boost_1_82_0' $env:BOOST_ROOT='C:\local\boost_1_83_0'
$env:BOOST_LIBRARYDIR='C:\local\boost_1_82_0\lib64-msvc-14.2' $env:BOOST_LIBRARYDIR='C:\local\boost_1_83_0\lib64-msvc-14.2'
``` ```
#### Icinga 2 in Visual Studio #### Icinga 2 in Visual Studio

View File

@ -13,8 +13,8 @@ function ThrowOnNativeFailure {
$VsVersion = 2019 $VsVersion = 2019
$MsvcVersion = '14.2' $MsvcVersion = '14.2'
$BoostVersion = @(1, 82, 0) $BoostVersion = @(1, 83, 0)
$OpensslVersion = '3_0_9' $OpensslVersion = '3_0_12'
switch ($Env:BITS) { switch ($Env:BITS) {
32 { } 32 { }
@ -91,6 +91,8 @@ if (-not $Env:GITHUB_ACTIONS) {
ThrowOnNativeFailure ThrowOnNativeFailure
} }
# Disable the progress bar for downloads from the Web, which will speed up the entire download process
$Global:ProgressPreference = 'SilentlyContinue';
Install-Exe -Url "https://packages.icinga.com/windows/dependencies/boost_$($BoostVersion -join '_')-msvc-${MsvcVersion}-${Env:BITS}.exe" -Dir "C:\local\boost_$($BoostVersion -join '_')-Win${Env:BITS}" Install-Exe -Url "https://packages.icinga.com/windows/dependencies/boost_$($BoostVersion -join '_')-msvc-${MsvcVersion}-${Env:BITS}.exe" -Dir "C:\local\boost_$($BoostVersion -join '_')-Win${Env:BITS}"

View File

@ -67,6 +67,20 @@ bool Dictionary::Get(const String& key, Value *result) const
return true; return true;
} }
/**
* Retrieves a value's address from a dictionary.
*
* @param key The key whose value's address should be retrieved.
* @returns nullptr if the key was not found.
*/
const Value * Dictionary::GetRef(const String& key) const
{
std::shared_lock<std::shared_timed_mutex> lock (m_DataMutex);
auto it (m_Data.find(key));
return it == m_Data.end() ? nullptr : &it->second;
}
/** /**
* Sets a value in the dictionary. * Sets a value in the dictionary.
* *

View File

@ -42,6 +42,7 @@ public:
Value Get(const String& key) const; Value Get(const String& key) const;
bool Get(const String& key, Value *result) const; bool Get(const String& key, Value *result) const;
const Value * GetRef(const String& key) const;
void Set(const String& key, Value value, bool overrideFrozen = false); void Set(const String& key, Value value, bool overrideFrozen = false);
bool Contains(const String& key) const; bool Contains(const String& key) const;

View File

@ -11,6 +11,8 @@
#include <boost/asio/ssl/context.hpp> #include <boost/asio/ssl/context.hpp>
#include <openssl/opensslv.h> #include <openssl/opensslv.h>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/ssl3.h>
#include <fstream> #include <fstream>
namespace icinga namespace icinga
@ -91,6 +93,16 @@ static void InitSslContext(const Shared<boost::asio::ssl::context>::Ptr& context
flags |= SSL_OP_CIPHER_SERVER_PREFERENCE; flags |= SSL_OP_CIPHER_SERVER_PREFERENCE;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_CTX_set_info_callback(sslContext, [](const SSL* ssl, int where, int) {
if (where & SSL_CB_HANDSHAKE_DONE) {
ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
}
});
#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
flags |= SSL_OP_NO_RENEGOTIATION;
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
SSL_CTX_set_options(sslContext, flags); SSL_CTX_set_options(sslContext, flags);
SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
@ -714,7 +726,7 @@ String GetIcingaCADir()
return Configuration::DataDir + "/ca"; return Configuration::DataDir + "/ca";
} }
std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject) std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject, bool ca)
{ {
char errbuf[256]; char errbuf[256];
@ -751,7 +763,7 @@ std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject)
EVP_PKEY *privkey = EVP_PKEY_new(); EVP_PKEY *privkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(privkey, rsa); EVP_PKEY_assign_RSA(privkey, rsa);
return CreateCert(pubkey, subject, X509_get_subject_name(cacert.get()), privkey, false); return CreateCert(pubkey, subject, X509_get_subject_name(cacert.get()), privkey, ca);
} }
std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert) std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert)
@ -760,24 +772,37 @@ std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert)
return CreateCertIcingaCA(pkey.get(), X509_get_subject_name(cert.get())); return CreateCertIcingaCA(pkey.get(), X509_get_subject_name(cert.get()));
} }
static inline
bool CertExpiresWithin(X509* cert, int seconds)
{
time_t renewalStart = time(nullptr) + seconds;
return X509_cmp_time(X509_get_notAfter(cert), &renewalStart) < 0;
}
bool IsCertUptodate(const std::shared_ptr<X509>& cert) bool IsCertUptodate(const std::shared_ptr<X509>& cert)
{ {
time_t now; if (CertExpiresWithin(cert.get(), RENEW_THRESHOLD)) {
time(&now); return false;
}
/* auto-renew all certificates which were created before 2017 to force an update of the CA, /* auto-renew all certificates which were created before 2017 to force an update of the CA,
* because Icinga versions older than 2.4 sometimes create certificates with an invalid * because Icinga versions older than 2.4 sometimes create certificates with an invalid
* serial number. */ * serial number. */
time_t forceRenewalEnd = 1483228800; /* January 1st, 2017 */ time_t forceRenewalEnd = 1483228800; /* January 1st, 2017 */
time_t renewalStart = now + RENEW_THRESHOLD;
return X509_cmp_time(X509_get_notBefore(cert.get()), &forceRenewalEnd) != -1 && X509_cmp_time(X509_get_notAfter(cert.get()), &renewalStart) != -1; return X509_cmp_time(X509_get_notBefore(cert.get()), &forceRenewalEnd) >= 0;
} }
String CertificateToString(const std::shared_ptr<X509>& cert) bool IsCaUptodate(X509* cert)
{
return !CertExpiresWithin(cert, LEAF_VALID_FOR);
}
String CertificateToString(X509* cert)
{ {
BIO *mem = BIO_new(BIO_s_mem()); BIO *mem = BIO_new(BIO_s_mem());
PEM_write_bio_X509(mem, cert.get()); PEM_write_bio_X509(mem, cert);
char *data; char *data;
long len = BIO_get_mem_data(mem, &data); long len = BIO_get_mem_data(mem, &data);

View File

@ -58,12 +58,18 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile =
std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca); std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca);
String GetIcingaCADir(); String GetIcingaCADir();
String CertificateToString(const std::shared_ptr<X509>& cert); String CertificateToString(X509* cert);
inline String CertificateToString(const std::shared_ptr<X509>& cert)
{
return CertificateToString(cert.get());
}
std::shared_ptr<X509> StringToCertificate(const String& cert); std::shared_ptr<X509> StringToCertificate(const String& cert);
std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject); std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject, bool ca = false);
std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert); std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert);
bool IsCertUptodate(const std::shared_ptr<X509>& cert); bool IsCertUptodate(const std::shared_ptr<X509>& cert);
bool IsCaUptodate(X509* cert);
String PBKDF2_SHA1(const String& password, const String& salt, int iterations); String PBKDF2_SHA1(const String& password, const String& salt, int iterations);
String PBKDF2_SHA256(const String& password, const String& salt, int iterations); String PBKDF2_SHA256(const String& password, const String& salt, int iterations);

View File

@ -96,16 +96,16 @@ bool ApplyRule::AddTargetedRule(const ApplyRule::Ptr& rule, const String& target
* *
* @returns Whether the given assign filter is like above. * @returns Whether the given assign filter is like above.
*/ */
bool ApplyRule::GetTargetHosts(Expression* assignFilter, std::vector<const String *>& hosts) bool ApplyRule::GetTargetHosts(Expression* assignFilter, std::vector<const String *>& hosts, const Dictionary::Ptr& constants)
{ {
auto lor (dynamic_cast<LogicalOrExpression*>(assignFilter)); auto lor (dynamic_cast<LogicalOrExpression*>(assignFilter));
if (lor) { if (lor) {
return GetTargetHosts(lor->GetOperand1().get(), hosts) return GetTargetHosts(lor->GetOperand1().get(), hosts, constants)
&& GetTargetHosts(lor->GetOperand2().get(), hosts); && GetTargetHosts(lor->GetOperand2().get(), hosts, constants);
} }
auto name (GetComparedName(assignFilter, "host")); auto name (GetComparedName(assignFilter, "host", constants));
if (name) { if (name) {
hosts.emplace_back(name); hosts.emplace_back(name);
@ -124,16 +124,16 @@ bool ApplyRule::GetTargetHosts(Expression* assignFilter, std::vector<const Strin
* *
* @returns Whether the given assign filter is like above. * @returns Whether the given assign filter is like above.
*/ */
bool ApplyRule::GetTargetServices(Expression* assignFilter, std::vector<std::pair<const String *, const String *>>& services) bool ApplyRule::GetTargetServices(Expression* assignFilter, std::vector<std::pair<const String *, const String *>>& services, const Dictionary::Ptr& constants)
{ {
auto lor (dynamic_cast<LogicalOrExpression*>(assignFilter)); auto lor (dynamic_cast<LogicalOrExpression*>(assignFilter));
if (lor) { if (lor) {
return GetTargetServices(lor->GetOperand1().get(), services) return GetTargetServices(lor->GetOperand1().get(), services, constants)
&& GetTargetServices(lor->GetOperand2().get(), services); && GetTargetServices(lor->GetOperand2().get(), services, constants);
} }
auto service (GetTargetService(assignFilter)); auto service (GetTargetService(assignFilter, constants));
if (service.first) { if (service.first) {
services.emplace_back(service); services.emplace_back(service);
@ -152,7 +152,7 @@ bool ApplyRule::GetTargetServices(Expression* assignFilter, std::vector<std::pai
* *
* @returns {host, service} on success and {nullptr, nullptr} on failure. * @returns {host, service} on success and {nullptr, nullptr} on failure.
*/ */
std::pair<const String *, const String *> ApplyRule::GetTargetService(Expression* assignFilter) std::pair<const String *, const String *> ApplyRule::GetTargetService(Expression* assignFilter, const Dictionary::Ptr& constants)
{ {
auto land (dynamic_cast<LogicalAndExpression*>(assignFilter)); auto land (dynamic_cast<LogicalAndExpression*>(assignFilter));
@ -162,15 +162,15 @@ std::pair<const String *, const String *> ApplyRule::GetTargetService(Expression
auto op1 (land->GetOperand1().get()); auto op1 (land->GetOperand1().get());
auto op2 (land->GetOperand2().get()); auto op2 (land->GetOperand2().get());
auto host (GetComparedName(op1, "host")); auto host (GetComparedName(op1, "host", constants));
if (!host) { if (!host) {
std::swap(op1, op2); std::swap(op1, op2);
host = GetComparedName(op1, "host"); host = GetComparedName(op1, "host", constants);
} }
if (host) { if (host) {
auto service (GetComparedName(op2, "service")); auto service (GetComparedName(op2, "service", constants));
if (service) { if (service) {
return {host, service}; return {host, service};
@ -189,7 +189,7 @@ std::pair<const String *, const String *> ApplyRule::GetTargetService(Expression
* *
* @returns The object name on success and nullptr on failure. * @returns The object name on success and nullptr on failure.
*/ */
const String * ApplyRule::GetComparedName(Expression* assignFilter, const char * lcType) const String * ApplyRule::GetComparedName(Expression* assignFilter, const char * lcType, const Dictionary::Ptr& constants)
{ {
auto eq (dynamic_cast<EqualExpression*>(assignFilter)); auto eq (dynamic_cast<EqualExpression*>(assignFilter));
@ -200,12 +200,12 @@ const String * ApplyRule::GetComparedName(Expression* assignFilter, const char *
auto op1 (eq->GetOperand1().get()); auto op1 (eq->GetOperand1().get());
auto op2 (eq->GetOperand2().get()); auto op2 (eq->GetOperand2().get());
if (IsNameIndexer(op1, lcType)) { if (IsNameIndexer(op1, lcType, constants)) {
return GetLiteralStringValue(op2); return GetConstString(op2, constants);
} }
if (IsNameIndexer(op2, lcType)) { if (IsNameIndexer(op2, lcType, constants)) {
return GetLiteralStringValue(op1); return GetConstString(op1, constants);
} }
return nullptr; return nullptr;
@ -214,7 +214,7 @@ const String * ApplyRule::GetComparedName(Expression* assignFilter, const char *
/** /**
* @returns Whether the given expression is like $lcType$.name. * @returns Whether the given expression is like $lcType$.name.
*/ */
bool ApplyRule::IsNameIndexer(Expression* exp, const char * lcType) bool ApplyRule::IsNameIndexer(Expression* exp, const char * lcType, const Dictionary::Ptr& constants)
{ {
auto ixr (dynamic_cast<IndexerExpression*>(exp)); auto ixr (dynamic_cast<IndexerExpression*>(exp));
@ -228,27 +228,39 @@ bool ApplyRule::IsNameIndexer(Expression* exp, const char * lcType)
return false; return false;
} }
auto val (GetLiteralStringValue(ixr->GetOperand2().get())); auto val (GetConstString(ixr->GetOperand2().get(), constants));
return val && *val == "name"; return val && *val == "name";
} }
/** /**
* @returns If the given expression is a string literal, the string. nullptr on failure. * @returns If the given expression is a constant string, its address. nullptr on failure.
*/ */
const String * ApplyRule::GetLiteralStringValue(Expression* exp) const String * ApplyRule::GetConstString(Expression* exp, const Dictionary::Ptr& constants)
{
auto cnst (GetConst(exp, constants));
return cnst && cnst->IsString() ? &cnst->Get<String>() : nullptr;
}
/**
* @returns If the given expression is a constant, its address. nullptr on failure.
*/
const Value * ApplyRule::GetConst(Expression* exp, const Dictionary::Ptr& constants)
{ {
auto lit (dynamic_cast<LiteralExpression*>(exp)); auto lit (dynamic_cast<LiteralExpression*>(exp));
if (!lit) { if (lit) {
return &lit->GetValue();
}
if (constants) {
auto var (dynamic_cast<VariableExpression*>(exp));
if (var) {
return constants->GetRef(var->GetVariable());
}
}
return nullptr; return nullptr;
} }
auto& val (lit->GetValue());
if (!val.IsString()) {
return nullptr;
}
return &val.Get<String>();
}

View File

@ -82,6 +82,8 @@ public:
static const std::vector<ApplyRule::Ptr>& GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType); static const std::vector<ApplyRule::Ptr>& GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType);
static const std::set<ApplyRule::Ptr>& GetTargetedHostRules(const Type::Ptr& sourceType, const String& host); static const std::set<ApplyRule::Ptr>& GetTargetedHostRules(const Type::Ptr& sourceType, const String& host);
static const std::set<ApplyRule::Ptr>& GetTargetedServiceRules(const Type::Ptr& sourceType, const String& host, const String& service); static const std::set<ApplyRule::Ptr>& GetTargetedServiceRules(const Type::Ptr& sourceType, const String& host, const String& service);
static bool GetTargetHosts(Expression* assignFilter, std::vector<const String *>& hosts, const Dictionary::Ptr& constants = nullptr);
static bool GetTargetServices(Expression* assignFilter, std::vector<std::pair<const String *, const String *>>& services, const Dictionary::Ptr& constants = nullptr);
static void RegisterType(const String& sourceType, const std::vector<String>& targetTypes); static void RegisterType(const String& sourceType, const std::vector<String>& targetTypes);
static bool IsValidSourceType(const String& sourceType); static bool IsValidSourceType(const String& sourceType);
@ -108,12 +110,11 @@ private:
static RuleMap m_Rules; static RuleMap m_Rules;
static bool AddTargetedRule(const ApplyRule::Ptr& rule, const String& targetType, PerSourceType& rules); static bool AddTargetedRule(const ApplyRule::Ptr& rule, const String& targetType, PerSourceType& rules);
static bool GetTargetHosts(Expression* assignFilter, std::vector<const String *>& hosts); static std::pair<const String *, const String *> GetTargetService(Expression* assignFilter, const Dictionary::Ptr& constants);
static bool GetTargetServices(Expression* assignFilter, std::vector<std::pair<const String *, const String *>>& services); static const String * GetComparedName(Expression* assignFilter, const char * lcType, const Dictionary::Ptr& constants);
static std::pair<const String *, const String *> GetTargetService(Expression* assignFilter); static bool IsNameIndexer(Expression* exp, const char * lcType, const Dictionary::Ptr& constants);
static const String * GetComparedName(Expression* assignFilter, const char * lcType); static const String * GetConstString(Expression* exp, const Dictionary::Ptr& constants);
static bool IsNameIndexer(Expression* exp, const char * lcType); static const Value * GetConst(Expression* exp, const Dictionary::Ptr& constants);
static const String * GetLiteralStringValue(Expression* exp);
ApplyRule(String name, Expression::Ptr expression, ApplyRule(String name, Expression::Ptr expression,
Expression::Ptr filter, String package, String fkvar, String fvvar, Expression::Ptr fterm, Expression::Ptr filter, String package, String fkvar, String fvvar, Expression::Ptr fterm,

View File

@ -622,6 +622,11 @@ public:
void MakeInline(); void MakeInline();
inline const std::vector<std::unique_ptr<Expression>>& GetExpressions() const noexcept
{
return m_Expressions;
}
protected: protected:
ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;

View File

@ -29,6 +29,8 @@ REGISTER_APIFUNCTION(SetStateBeforeSuppression, event, &ClusterEvents::StateBefo
REGISTER_APIFUNCTION(SetSuppressedNotifications, event, &ClusterEvents::SuppressedNotificationsChangedAPIHandler); REGISTER_APIFUNCTION(SetSuppressedNotifications, event, &ClusterEvents::SuppressedNotificationsChangedAPIHandler);
REGISTER_APIFUNCTION(SetSuppressedNotificationTypes, event, &ClusterEvents::SuppressedNotificationTypesChangedAPIHandler); REGISTER_APIFUNCTION(SetSuppressedNotificationTypes, event, &ClusterEvents::SuppressedNotificationTypesChangedAPIHandler);
REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler); REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler);
REGISTER_APIFUNCTION(UpdateLastNotifiedStatePerUser, event, &ClusterEvents::LastNotifiedStatePerUserUpdatedAPIHandler);
REGISTER_APIFUNCTION(ClearLastNotifiedStatePerUser, event, &ClusterEvents::LastNotifiedStatePerUserClearedAPIHandler);
REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckChangedAPIHandler); REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckChangedAPIHandler);
REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler); REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler);
REGISTER_APIFUNCTION(SetAcknowledgement, event, &ClusterEvents::AcknowledgementSetAPIHandler); REGISTER_APIFUNCTION(SetAcknowledgement, event, &ClusterEvents::AcknowledgementSetAPIHandler);
@ -50,6 +52,8 @@ void ClusterEvents::StaticInitialize()
Checkable::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationsChangedHandler); Checkable::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationsChangedHandler);
Notification::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationTypesChangedHandler); Notification::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationTypesChangedHandler);
Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler); Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler);
Notification::OnLastNotifiedStatePerUserUpdated.connect(&ClusterEvents::LastNotifiedStatePerUserUpdatedHandler);
Notification::OnLastNotifiedStatePerUserCleared.connect(&ClusterEvents::LastNotifiedStatePerUserClearedHandler);
Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler); Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler);
Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler); Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler);
Checkable::OnNotificationsRequested.connect(&ClusterEvents::SendNotificationsHandler); Checkable::OnNotificationsRequested.connect(&ClusterEvents::SendNotificationsHandler);
@ -529,6 +533,115 @@ Value ClusterEvents::NextNotificationChangedAPIHandler(const MessageOrigin::Ptr&
return Empty; return Empty;
} }
void ClusterEvents::LastNotifiedStatePerUserUpdatedHandler(const Notification::Ptr& notification, const String& user, uint_fast8_t state, const MessageOrigin::Ptr& origin)
{
auto listener (ApiListener::GetInstance());
if (!listener) {
return;
}
Dictionary::Ptr params = new Dictionary();
params->Set("notification", notification->GetName());
params->Set("user", user);
params->Set("state", state);
Dictionary::Ptr message = new Dictionary();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::UpdateLastNotifiedStatePerUser");
message->Set("params", params);
listener->RelayMessage(origin, notification, message, true);
}
Value ClusterEvents::LastNotifiedStatePerUserUpdatedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
{
auto endpoint (origin->FromClient->GetEndpoint());
if (!endpoint) {
Log(LogNotice, "ClusterEvents")
<< "Discarding 'last notified state of user updated' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
return Empty;
}
if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
Log(LogNotice, "ClusterEvents")
<< "Discarding 'last notified state of user updated' message from '"
<< origin->FromClient->GetIdentity() << "': Unauthorized access.";
return Empty;
}
auto notification (Notification::GetByName(params->Get("notification")));
if (!notification) {
return Empty;
}
auto state (params->Get("state"));
if (!state.IsNumber()) {
return Empty;
}
notification->GetLastNotifiedStatePerUser()->Set(params->Get("user"), state);
Notification::OnLastNotifiedStatePerUserUpdated(notification, params->Get("user"), state, origin);
return Empty;
}
void ClusterEvents::LastNotifiedStatePerUserClearedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin)
{
auto listener (ApiListener::GetInstance());
if (!listener) {
return;
}
Dictionary::Ptr params = new Dictionary();
params->Set("notification", notification->GetName());
Dictionary::Ptr message = new Dictionary();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::ClearLastNotifiedStatePerUser");
message->Set("params", params);
listener->RelayMessage(origin, notification, message, true);
}
Value ClusterEvents::LastNotifiedStatePerUserClearedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
{
auto endpoint (origin->FromClient->GetEndpoint());
if (!endpoint) {
Log(LogNotice, "ClusterEvents")
<< "Discarding 'last notified state of user cleared' message from '"
<< origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
return Empty;
}
if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
Log(LogNotice, "ClusterEvents")
<< "Discarding 'last notified state of user cleared' message from '"
<< origin->FromClient->GetIdentity() << "': Unauthorized access.";
return Empty;
}
auto notification (Notification::GetByName(params->Get("notification")));
if (!notification) {
return Empty;
}
notification->GetLastNotifiedStatePerUser()->Clear();
Notification::OnLastNotifiedStatePerUserCleared(notification, origin);
return Empty;
}
void ClusterEvents::ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin) void ClusterEvents::ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
{ {
ApiListener::Ptr listener = ApiListener::GetInstance(); ApiListener::Ptr listener = ApiListener::GetInstance();

View File

@ -41,6 +41,12 @@ public:
static void NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin); static void NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin);
static Value NextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value NextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
static void LastNotifiedStatePerUserUpdatedHandler(const Notification::Ptr& notification, const String& user, uint_fast8_t state, const MessageOrigin::Ptr& origin);
static Value LastNotifiedStatePerUserUpdatedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
static void LastNotifiedStatePerUserClearedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin);
static Value LastNotifiedStatePerUserClearedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
static void ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin); static void ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
static Value ForceNextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value ForceNextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);

View File

@ -23,6 +23,8 @@ std::map<String, int> Notification::m_StateFilterMap;
std::map<String, int> Notification::m_TypeFilterMap; std::map<String, int> Notification::m_TypeFilterMap;
boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> Notification::OnNextNotificationChanged; boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> Notification::OnNextNotificationChanged;
boost::signals2::signal<void (const Notification::Ptr&, const String&, uint_fast8_t, const MessageOrigin::Ptr&)> Notification::OnLastNotifiedStatePerUserUpdated;
boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> Notification::OnLastNotifiedStatePerUserCleared;
String NotificationNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const String NotificationNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const
{ {
@ -231,6 +233,13 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe
<< "notifications of type '" << notificationTypeName << "notifications of type '" << notificationTypeName
<< "' for notification object '" << notificationName << "'."; << "' for notification object '" << notificationName << "'.";
if (type == NotificationRecovery) {
auto states (GetLastNotifiedStatePerUser());
states->Clear();
OnLastNotifiedStatePerUserCleared(this, nullptr);
}
Checkable::Ptr checkable = GetCheckable(); Checkable::Ptr checkable = GetCheckable();
if (!force) { if (!force) {
@ -439,6 +448,22 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe
} }
} }
if (type == NotificationProblem && !reminder && !checkable->GetVolatile()) {
auto [host, service] = GetHostService(checkable);
uint_fast8_t state = service ? service->GetState() : host->GetState();
if (state == (uint_fast8_t)GetLastNotifiedStatePerUser()->Get(userName)) {
auto stateStr (service ? NotificationServiceStateToString(service->GetState()) : NotificationHostStateToString(host->GetState()));
Log(LogNotice, "Notification")
<< "Notification object '" << notificationName << "': We already notified user '" << userName << "' for a " << stateStr
<< " problem. Likely after that another state change notification was filtered out by config. Not sending duplicate '"
<< stateStr << "' notification.";
continue;
}
}
Log(LogInformation, "Notification") Log(LogInformation, "Notification")
<< "Sending " << (reminder ? "reminder " : "") << "'" << NotificationTypeToString(type) << "' notification '" << "Sending " << (reminder ? "reminder " : "") << "'" << NotificationTypeToString(type) << "' notification '"
<< notificationName << "' for user '" << userName << "'"; << notificationName << "' for user '" << userName << "'";
@ -452,6 +477,16 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe
/* collect all notified users */ /* collect all notified users */
allNotifiedUsers.insert(user); allNotifiedUsers.insert(user);
if (type == NotificationProblem) {
auto [host, service] = GetHostService(checkable);
uint_fast8_t state = service ? service->GetState() : host->GetState();
if (state != (uint_fast8_t)GetLastNotifiedStatePerUser()->Get(userName)) {
GetLastNotifiedStatePerUser()->Set(userName, state);
OnLastNotifiedStatePerUserUpdated(this, userName, state, nullptr);
}
}
/* store all notified users for later recovery checks */ /* store all notified users for later recovery checks */
if (type == NotificationProblem && !notifiedProblemUsers->Contains(userName)) if (type == NotificationProblem && !notifiedProblemUsers->Contains(userName))
notifiedProblemUsers->Add(userName); notifiedProblemUsers->Add(userName);

View File

@ -13,6 +13,7 @@
#include "remote/endpoint.hpp" #include "remote/endpoint.hpp"
#include "remote/messageorigin.hpp" #include "remote/messageorigin.hpp"
#include "base/array.hpp" #include "base/array.hpp"
#include <cstdint>
namespace icinga namespace icinga
{ {
@ -92,6 +93,8 @@ public:
static String NotificationHostStateToString(HostState state); static String NotificationHostStateToString(HostState state);
static boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> OnNextNotificationChanged; static boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> OnNextNotificationChanged;
static boost::signals2::signal<void (const Notification::Ptr&, const String&, uint_fast8_t, const MessageOrigin::Ptr&)> OnLastNotifiedStatePerUserUpdated;
static boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> OnLastNotifiedStatePerUserCleared;
void Validate(int types, const ValidationUtils& utils) override; void Validate(int types, const ValidationUtils& utils) override;
@ -105,7 +108,6 @@ public:
static const std::map<String, int>& GetStateFilterMap(); static const std::map<String, int>& GetStateFilterMap();
static const std::map<String, int>& GetTypeFilterMap(); static const std::map<String, int>& GetTypeFilterMap();
protected:
void OnConfigLoaded() override; void OnConfigLoaded() override;
void OnAllConfigLoaded() override; void OnAllConfigLoaded() override;
void Start(bool runtimeCreated) override; void Start(bool runtimeCreated) override;

View File

@ -90,6 +90,10 @@ class Notification : CustomVarObject < NotificationNameComposer
default {{{ return 0; }}} default {{{ return 0; }}}
}; };
[state, no_user_view, no_user_modify] Dictionary::Ptr last_notified_state_per_user {
default {{{ return new Dictionary(); }}}
};
[config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) { [config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) {
navigate {{{ navigate {{{
return Endpoint::GetByName(GetCommandEndpointRaw()); return Endpoint::GetByName(GetCommandEndpointRaw());

View File

@ -1577,6 +1577,9 @@ IcingaDB::CreateConfigUpdate(const ConfigObject::Ptr& object, const String typeN
void IcingaDB::SendConfigDelete(const ConfigObject::Ptr& object) void IcingaDB::SendConfigDelete(const ConfigObject::Ptr& object)
{ {
if (!m_Rcon || !m_Rcon->IsConnected())
return;
Type::Ptr type = object->GetReflectionType(); Type::Ptr type = object->GetReflectionType();
String typeName = type->GetName().ToLower(); String typeName = type->GetName().ToLower();
String objectKey = GetObjectIdentifier(object); String objectKey = GetObjectIdentifier(object);
@ -1860,6 +1863,7 @@ void IcingaDB::SendStartedDowntime(const Downtime::Ptr& downtime)
"scheduled_end_time", Convert::ToString(TimestampToMilliseconds(downtime->GetEndTime())), "scheduled_end_time", Convert::ToString(TimestampToMilliseconds(downtime->GetEndTime())),
"has_been_cancelled", Convert::ToString((unsigned short)downtime->GetWasCancelled()), "has_been_cancelled", Convert::ToString((unsigned short)downtime->GetWasCancelled()),
"trigger_time", Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime())), "trigger_time", Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime())),
"cancel_time", Convert::ToString(TimestampToMilliseconds(downtime->GetRemoveTime())),
"event_id", CalcEventID("downtime_start", downtime), "event_id", CalcEventID("downtime_start", downtime),
"event_type", "downtime_start" "event_type", "downtime_start"
}); });

View File

@ -12,6 +12,20 @@
#include "base/process.hpp" #include "base/process.hpp"
#include "base/convert.hpp" #include "base/convert.hpp"
#ifdef __linux__
# include <linux/binfmts.h>
# include <unistd.h>
# ifndef PAGE_SIZE
// MAX_ARG_STRLEN is a multiple of PAGE_SIZE which is missing
# define PAGE_SIZE getpagesize()
# endif /* PAGE_SIZE */
// Make e.g. the $host.output$ itself even 10% shorter to leave enough room
// for e.g. --host-output= as in --host-output=$host.output$, but without int overflow
const static auto l_MaxOutLen = MAX_ARG_STRLEN - MAX_ARG_STRLEN / 10u;
#endif /* __linux__ */
using namespace icinga; using namespace icinga;
REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros"); REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros");
@ -33,7 +47,11 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
Dictionary::Ptr notificationExtra = new Dictionary({ Dictionary::Ptr notificationExtra = new Dictionary({
{ "type", Notification::NotificationTypeToStringCompat(type) }, //TODO: Change that to our types. { "type", Notification::NotificationTypeToStringCompat(type) }, //TODO: Change that to our types.
{ "author", author }, { "author", author },
#ifdef __linux__
{ "comment", comment.SubStr(0, l_MaxOutLen) }
#else /* __linux__ */
{ "comment", comment } { "comment", comment }
#endif /* __linux__ */
}); });
Host::Ptr host; Host::Ptr host;
@ -48,8 +66,35 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
resolvers.emplace_back("user", user); resolvers.emplace_back("user", user);
resolvers.emplace_back("notification", notificationExtra); resolvers.emplace_back("notification", notificationExtra);
resolvers.emplace_back("notification", notification); resolvers.emplace_back("notification", notification);
if (service)
if (service) {
#ifdef __linux__
auto cr (service->GetLastCheckResult());
if (cr) {
auto output (cr->GetOutput());
if (output.GetLength() > l_MaxOutLen) {
resolvers.emplace_back("service", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}}));
}
}
#endif /* __linux__ */
resolvers.emplace_back("service", service); resolvers.emplace_back("service", service);
}
#ifdef __linux__
auto hcr (host->GetLastCheckResult());
if (hcr) {
auto output (hcr->GetOutput());
if (output.GetLength() > l_MaxOutLen) {
resolvers.emplace_back("host", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}}));
}
}
#endif /* __linux__ */
resolvers.emplace_back("host", host); resolvers.emplace_back("host", host);
resolvers.emplace_back("command", commandObj); resolvers.emplace_back("command", commandObj);

View File

@ -114,18 +114,17 @@ void GelfWriter::Pause()
m_ReconnectTimer->Stop(true); m_ReconnectTimer->Stop(true);
m_WorkQueue.Enqueue([this]() {
try { try {
ReconnectInternal(); ReconnectInternal();
} catch (const std::exception&) { } catch (const std::exception&) {
Log(LogInformation, "GelfWriter") Log(LogInformation, "GelfWriter")
<< "'" << GetName() << "' paused. Unable to connect, not flushing buffers. Data may be lost on reload."; << "Unable to connect, not flushing buffers. Data may be lost.";
ObjectImpl<GelfWriter>::Pause();
return;
} }
}, PriorityImmediate);
m_WorkQueue.Enqueue([this]() { DisconnectInternal(); }, PriorityLow);
m_WorkQueue.Join(); m_WorkQueue.Join();
DisconnectInternal();
Log(LogInformation, "GelfWriter") Log(LogInformation, "GelfWriter")
<< "'" << GetName() << "' paused."; << "'" << GetName() << "' paused.";
@ -513,8 +512,6 @@ void GelfWriter::SendLogMessage(const Checkable::Ptr& checkable, const String& g
String log = msgbuf.str(); String log = msgbuf.str();
ObjectLock olock(this);
if (!GetConnected()) if (!GetConnected())
return; return;

View File

@ -181,12 +181,12 @@ void ApiListener::OnConfigLoaded()
UpdateSSLContext(); UpdateSSLContext();
} }
std::shared_ptr<X509> ApiListener::RenewCert(const std::shared_ptr<X509>& cert) std::shared_ptr<X509> ApiListener::RenewCert(const std::shared_ptr<X509>& cert, bool ca)
{ {
std::shared_ptr<EVP_PKEY> pubkey (X509_get_pubkey(cert.get()), EVP_PKEY_free); std::shared_ptr<EVP_PKEY> pubkey (X509_get_pubkey(cert.get()), EVP_PKEY_free);
auto subject (X509_get_subject_name(cert.get())); auto subject (X509_get_subject_name(cert.get()));
auto cacert (GetX509Certificate(GetDefaultCaPath())); auto cacert (GetX509Certificate(GetDefaultCaPath()));
auto newcert (CreateCertIcingaCA(pubkey.get(), subject)); auto newcert (CreateCertIcingaCA(pubkey.get(), subject, ca));
/* verify that the new cert matches the CA we're using for the ApiListener; /* verify that the new cert matches the CA we're using for the ApiListener;
* this ensures that the CA we have in /var/lib/icinga2/ca matches the one * this ensures that the CA we have in /var/lib/icinga2/ca matches the one
@ -248,7 +248,12 @@ void ApiListener::Start(bool runtimeCreated)
if (Utility::PathExists(GetIcingaCADir() + "/ca.key")) { if (Utility::PathExists(GetIcingaCADir() + "/ca.key")) {
RenewOwnCert(); RenewOwnCert();
m_RenewOwnCertTimer->OnTimerExpired.connect([this](const Timer * const&) { RenewOwnCert(); }); RenewCA();
m_RenewOwnCertTimer->OnTimerExpired.connect([this](const Timer * const&) {
RenewOwnCert();
RenewCA();
});
} else { } else {
m_RenewOwnCertTimer->OnTimerExpired.connect([this](const Timer * const&) { m_RenewOwnCertTimer->OnTimerExpired.connect([this](const Timer * const&) {
JsonRpcConnection::SendCertificateRequest(nullptr, nullptr, String()); JsonRpcConnection::SendCertificateRequest(nullptr, nullptr, String());
@ -329,6 +334,31 @@ void ApiListener::RenewOwnCert()
UpdateSSLContext(); UpdateSSLContext();
} }
void ApiListener::RenewCA()
{
auto certPath (GetCaDir() + "/ca.crt");
auto cert (GetX509Certificate(certPath));
if (IsCaUptodate(cert.get())) {
return;
}
Log(LogInformation, "ApiListener")
<< "Our CA will expire soon, but we own it. Renewing.";
cert = RenewCert(cert, true);
if (!cert) {
return;
}
auto certStr (CertificateToString(cert));
AtomicFile::Write(GetDefaultCaPath(), 0644, certStr);
AtomicFile::Write(certPath, 0644, certStr);
UpdateSSLContext();
}
void ApiListener::Stop(bool runtimeDeleted) void ApiListener::Stop(bool runtimeDeleted)
{ {
m_ApiPackageIntegrityTimer->Stop(true); m_ApiPackageIntegrityTimer->Stop(true);

View File

@ -91,7 +91,7 @@ public:
static String GetCaDir(); static String GetCaDir();
static String GetCertificateRequestsDir(); static String GetCertificateRequestsDir();
std::shared_ptr<X509> RenewCert(const std::shared_ptr<X509>& cert); std::shared_ptr<X509> RenewCert(const std::shared_ptr<X509>& cert, bool ca = false);
void UpdateSSLContext(); void UpdateSSLContext();
static ApiListener::Ptr GetInstance(); static ApiListener::Ptr GetInstance();
@ -227,6 +227,7 @@ private:
void SyncLocalZoneDirs() const; void SyncLocalZoneDirs() const;
void SyncLocalZoneDir(const Zone::Ptr& zone) const; void SyncLocalZoneDir(const Zone::Ptr& zone) const;
void RenewOwnCert(); void RenewOwnCert();
void RenewCA();
void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient); void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);

View File

@ -1,5 +1,6 @@
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "remote/configobjectslock.hpp"
#include "remote/consolehandler.hpp" #include "remote/consolehandler.hpp"
#include "remote/httputility.hpp" #include "remote/httputility.hpp"
#include "remote/filterutility.hpp" #include "remote/filterutility.hpp"
@ -88,6 +89,13 @@ bool ConsoleHandler::HandleRequest(
bool sandboxed = HttpUtility::GetLastParameter(params, "sandboxed"); bool sandboxed = HttpUtility::GetLastParameter(params, "sandboxed");
ConfigObjectsSharedLock lock (std::try_to_lock);
if (!lock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading.");
return true;
}
if (methodName == "execute-script") if (methodName == "execute-script")
return ExecuteScriptHelper(request, response, params, command, session, sandboxed); return ExecuteScriptHelper(request, response, params, command, session, sandboxed);
else if (methodName == "auto-complete-script") else if (methodName == "auto-complete-script")

View File

@ -2,6 +2,7 @@
#include "remote/filterutility.hpp" #include "remote/filterutility.hpp"
#include "remote/httputility.hpp" #include "remote/httputility.hpp"
#include "config/applyrule.hpp"
#include "config/configcompiler.hpp" #include "config/configcompiler.hpp"
#include "config/expression.hpp" #include "config/expression.hpp"
#include "base/namespace.hpp" #include "base/namespace.hpp"
@ -271,11 +272,65 @@ std::vector<Value> FilterUtility::GetFilterTargets(const QueryDescription& qd, c
if (query->Contains("filter")) { if (query->Contains("filter")) {
String filter = HttpUtility::GetLastParameter(query, "filter"); String filter = HttpUtility::GetLastParameter(query, "filter");
std::unique_ptr<Expression> ufilter = ConfigCompiler::CompileText("<API query>", filter); std::unique_ptr<Expression> ufilter = ConfigCompiler::CompileText("<API query>", filter);
Dictionary::Ptr filter_vars = query->Get("filter_vars"); Dictionary::Ptr filter_vars = query->Get("filter_vars");
bool targeted = false;
std::vector<ConfigObject::Ptr> targets;
if (dynamic_cast<ConfigObjectTargetProvider*>(provider.get())) {
auto dict (dynamic_cast<DictExpression*>(ufilter.get()));
if (dict) {
auto& subex (dict->GetExpressions());
if (subex.size() == 1u) {
if (type == "Host") {
std::vector<const String *> targetNames;
if (ApplyRule::GetTargetHosts(subex.at(0).get(), targetNames, filter_vars)) {
static const auto typeHost (Type::GetByName("Host"));
static const auto ctypeHost (dynamic_cast<ConfigType*>(typeHost.get()));
targeted = true;
for (auto name : targetNames) {
auto target (ctypeHost->GetObject(*name));
if (target) {
targets.emplace_back(target);
}
}
}
} else if (type == "Service") {
std::vector<std::pair<const String *, const String *>> targetNames;
if (ApplyRule::GetTargetServices(subex.at(0).get(), targetNames, filter_vars)) {
static const auto typeService (Type::GetByName("Service"));
static const auto ctypeService (dynamic_cast<ConfigType*>(typeService.get()));
targeted = true;
for (auto name : targetNames) {
auto target (ctypeService->GetObject(*name.first + "!" + *name.second));
if (target) {
targets.emplace_back(target);
}
}
}
}
}
}
}
if (targeted) {
for (auto& target : targets) {
if (FilterUtility::EvaluateFilter(permissionFrame, permissionFilter.get(), target, variableName)) {
result.emplace_back(std::move(target));
}
}
} else {
if (filter_vars) { if (filter_vars) {
ObjectLock olock (filter_vars); ObjectLock olock (filter_vars);
for (const Dictionary::Pair& kv : filter_vars) {
for (auto& kv : filter_vars) {
frameNS->Set(kv.first, kv.second); frameNS->Set(kv.first, kv.second);
} }
} }
@ -283,6 +338,7 @@ std::vector<Value> FilterUtility::GetFilterTargets(const QueryDescription& qd, c
provider->FindTargets(type, [&permissionFrame, &permissionFilter, &frame, &ufilter, &result, variableName](const Object::Ptr& target) { provider->FindTargets(type, [&permissionFrame, &permissionFilter, &frame, &ufilter, &result, variableName](const Object::Ptr& target) {
FilteredAddTarget(permissionFrame, permissionFilter.get(), frame, &*ufilter, result, variableName, target); FilteredAddTarget(permissionFrame, permissionFilter.get(), frame, &*ufilter, result, variableName, target);
}); });
}
} else { } else {
/* Ensure to pass a nullptr as filter expression. /* Ensure to pass a nullptr as filter expression.
* GCC 8.1.1 on F28 causes problems, see GH #6533. * GCC 8.1.1 on F28 causes problems, see GH #6533.

View File

@ -14,6 +14,7 @@
#include <boost/thread/once.hpp> #include <boost/thread/once.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <fstream> #include <fstream>
#include <openssl/asn1.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/x509.h> #include <openssl/x509.h>
@ -31,11 +32,11 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
std::shared_ptr<X509> cert; std::shared_ptr<X509> cert;
Dictionary::Ptr result = new Dictionary(); Dictionary::Ptr result = new Dictionary();
auto& tlsConn (origin->FromClient->GetStream()->next_layer());
/* Use the presented client certificate if not provided. */ /* Use the presented client certificate if not provided. */
if (certText.IsEmpty()) { if (certText.IsEmpty()) {
auto stream (origin->FromClient->GetStream()); cert = tlsConn.GetPeerCertificate();
cert = stream->next_layer().GetPeerCertificate();
} else { } else {
cert = StringToCertificate(certText); cert = StringToCertificate(certText);
} }
@ -77,13 +78,54 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
} }
} }
if (signedByCA) { std::shared_ptr<X509> parsedRequestorCA;
if (IsCertUptodate(cert)) { X509* requestorCA = nullptr;
if (signedByCA) {
bool uptodate = IsCertUptodate(cert);
if (uptodate) {
// Even if the leaf is up-to-date, the root may expire soon.
// In a regular setup where Icinga manages the PKI, there is only one CA.
// Icinga includes it in handshakes, let's see whether the peer needs a fresh one...
if (cn == origin->FromClient->GetIdentity()) {
auto chain (SSL_get_peer_cert_chain(tlsConn.native_handle()));
if (chain) {
auto len (sk_X509_num(chain));
for (int i = 0; i < len; ++i) {
auto link (sk_X509_value(chain, i));
if (!X509_NAME_cmp(X509_get_subject_name(link), X509_get_issuer_name(link))) {
requestorCA = link;
}
}
}
} else {
Value requestorCaStr;
if (params->Get("requestor_ca", &requestorCaStr)) {
parsedRequestorCA = StringToCertificate(requestorCaStr);
requestorCA = parsedRequestorCA.get();
}
}
if (requestorCA && !IsCaUptodate(requestorCA)) {
int days;
if (ASN1_TIME_diff(&days, nullptr, X509_get_notAfter(requestorCA), X509_get_notAfter(cacert.get())) && days > 0) {
uptodate = false;
}
}
}
if (uptodate) {
Log(LogInformation, "JsonRpcConnection") Log(LogInformation, "JsonRpcConnection")
<< "The certificate for CN '" << cn << "' is valid and uptodate. Skipping automated renewal."; << "The certificates for CN '" << cn << "' and its root CA are valid and uptodate. Skipping automated renewal.";
result->Set("status_code", 1); result->Set("status_code", 1);
result->Set("error", "The certificate for CN '" + cn + "' is valid and uptodate. Skipping automated renewal."); result->Set("error", "The certificates for CN '" + cn + "' and its root CA are valid and uptodate. Skipping automated renewal.");
return result; return result;
} }
} }
@ -230,6 +272,10 @@ delayed_request:
{ "ticket", params->Get("ticket") } { "ticket", params->Get("ticket") }
}); });
if (requestorCA) {
request->Set("requestor_ca", CertificateToString(requestorCA));
}
Utility::SaveJsonFile(requestPath, 0600, request); Utility::SaveJsonFile(requestPath, 0600, request);
JsonRpcConnection::SendCertificateRequest(nullptr, origin, requestPath); JsonRpcConnection::SendCertificateRequest(nullptr, origin, requestPath);
@ -291,8 +337,7 @@ void JsonRpcConnection::SendCertificateRequest(const JsonRpcConnection::Ptr& acl
if (request->Contains("cert_response")) if (request->Contains("cert_response"))
return; return;
params->Set("cert_request", request->Get("cert_request")); request->CopyTo(params);
params->Set("ticket", request->Get("ticket"));
} }
/* Send the request to a) the connected client /* Send the request to a) the connected client

View File

@ -24,6 +24,7 @@ set(base_test_SOURCES
base-type.cpp base-type.cpp
base-utility.cpp base-utility.cpp
base-value.cpp base-value.cpp
config-apply.cpp
config-ops.cpp config-ops.cpp
icinga-checkresult.cpp icinga-checkresult.cpp
icinga-dependencies.cpp icinga-dependencies.cpp
@ -31,12 +32,14 @@ set(base_test_SOURCES
icinga-macros.cpp icinga-macros.cpp
icinga-notification.cpp icinga-notification.cpp
icinga-perfdata.cpp icinga-perfdata.cpp
methods-pluginnotificationtask.cpp
remote-configpackageutility.cpp remote-configpackageutility.cpp
remote-url.cpp remote-url.cpp
${base_OBJS} ${base_OBJS}
$<TARGET_OBJECTS:config> $<TARGET_OBJECTS:config>
$<TARGET_OBJECTS:remote> $<TARGET_OBJECTS:remote>
$<TARGET_OBJECTS:icinga> $<TARGET_OBJECTS:icinga>
$<TARGET_OBJECTS:methods>
) )
if(ICINGA2_UNITY_BUILD) if(ICINGA2_UNITY_BUILD)
@ -109,6 +112,11 @@ add_boost_test(base
base_timer/invoke base_timer/invoke
base_timer/scope base_timer/scope
base_tlsutility/sha1 base_tlsutility/sha1
base_tlsutility/iscauptodate_ok
base_tlsutility/iscauptodate_expiring
base_tlsutility/iscertuptodate_ok
base_tlsutility/iscertuptodate_expiring
base_tlsutility/iscertuptodate_old
base_type/gettype base_type/gettype
base_type/assign base_type/assign
base_type/byname base_type/byname
@ -123,6 +131,38 @@ add_boost_test(base
base_value/scalar base_value/scalar
base_value/convert base_value/convert
base_value/format base_value/format
config_apply/gettargethosts_literal
config_apply/gettargethosts_const
config_apply/gettargethosts_swapped
config_apply/gettargethosts_two
config_apply/gettargethosts_three
config_apply/gettargethosts_mixed
config_apply/gettargethosts_redundant
config_apply/gettargethosts_badconst
config_apply/gettargethosts_notliteral
config_apply/gettargethosts_wrongop
config_apply/gettargethosts_wrongattr
config_apply/gettargethosts_wrongvar
config_apply/gettargethosts_noindexer
config_apply/gettargetservices_literal
config_apply/gettargetservices_const
config_apply/gettargetservices_swapped_outer
config_apply/gettargetservices_swapped_inner
config_apply/gettargetservices_two
config_apply/gettargetservices_three
config_apply/gettargetservices_mixed
config_apply/gettargetservices_redundant
config_apply/gettargetservices_badconst
config_apply/gettargetservices_notliteral
config_apply/gettargetservices_wrongop_outer
config_apply/gettargetservices_wrongop_host
config_apply/gettargetservices_wrongop_service
config_apply/gettargetservices_wrongattr_host
config_apply/gettargetservices_wrongattr_service
config_apply/gettargetservices_wrongvar_host
config_apply/gettargetservices_wrongvar_service
config_apply/gettargetservices_noindexer_host
config_apply/gettargetservices_noindexer_service
config_ops/simple config_ops/simple
config_ops/advanced config_ops/advanced
icinga_checkresult/host_1attempt icinga_checkresult/host_1attempt
@ -138,6 +178,11 @@ add_boost_test(base
icinga_notification/strings icinga_notification/strings
icinga_notification/state_filter icinga_notification/state_filter
icinga_notification/type_filter icinga_notification/type_filter
icinga_notification/no_filter_problem_no_duplicate
icinga_notification/filter_problem_no_duplicate
icinga_notification/volatile_filter_problem_duplicate
icinga_notification/no_recovery_filter_no_duplicate
icinga_notification/recovery_filter_duplicate
icinga_macros/simple icinga_macros/simple
icinga_legacytimeperiod/simple icinga_legacytimeperiod/simple
icinga_legacytimeperiod/advanced icinga_legacytimeperiod/advanced
@ -156,6 +201,7 @@ add_boost_test(base
icinga_perfdata/multi icinga_perfdata/multi
icinga_perfdata/scientificnotation icinga_perfdata/scientificnotation
icinga_perfdata/parse_edgecases icinga_perfdata/parse_edgecases
methods_pluginnotificationtask/truncate_long_output
remote_configpackageutility/ValidateName remote_configpackageutility/ValidateName
remote_url/id_and_path remote_url/id_and_path
remote_url/parameters remote_url/parameters

View File

@ -2,11 +2,61 @@
#include "base/tlsutility.hpp" #include "base/tlsutility.hpp"
#include <BoostTestTargetConfig.h> #include <BoostTestTargetConfig.h>
#include <functional>
#include <memory>
#include <openssl/asn1.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <utility> #include <utility>
#include <vector> #include <vector>
using namespace icinga; using namespace icinga;
static EVP_PKEY* GenKeypair()
{
InitializeOpenSSL();
auto e (BN_new());
BOOST_REQUIRE(e);
auto rsa (RSA_new());
BOOST_REQUIRE(rsa);
auto key (EVP_PKEY_new());
BOOST_REQUIRE(key);
BOOST_REQUIRE(BN_set_word(e, RSA_F4));
BOOST_REQUIRE(RSA_generate_key_ex(rsa, 4096, e, nullptr));
BOOST_REQUIRE(EVP_PKEY_assign_RSA(key, rsa));
return key;
}
static std::shared_ptr<X509> MakeCert(const char* issuer, EVP_PKEY* signer, const char* subject, EVP_PKEY* pubkey, std::function<void(ASN1_TIME*, ASN1_TIME*)> setTimes)
{
auto cert (X509_new());
BOOST_REQUIRE(cert);
auto serial (BN_new());
BOOST_REQUIRE(serial);
BOOST_REQUIRE(X509_set_version(cert, 0x2));
BOOST_REQUIRE(BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(cert)));
BOOST_REQUIRE(X509_NAME_add_entry_by_NID(X509_get_issuer_name(cert), NID_commonName, MBSTRING_ASC, (unsigned char*)issuer, -1, -1, 0));
setTimes(X509_get_notBefore(cert), X509_get_notAfter(cert));
BOOST_REQUIRE(X509_NAME_add_entry_by_NID(X509_get_subject_name(cert), NID_commonName, MBSTRING_ASC, (unsigned char*)subject, -1, -1, 0));
BOOST_REQUIRE(X509_set_pubkey(cert, pubkey));
BOOST_REQUIRE(X509_sign(cert, signer, EVP_sha256()));
return std::shared_ptr<X509>(cert, X509_free);
}
static const long l_2016 = 1480000000; // Thu Nov 24 15:06:40 UTC 2016
static const long l_2017 = 1490000000; // Mon Mar 20 08:53:20 UTC 2017
BOOST_AUTO_TEST_SUITE(base_tlsutility) BOOST_AUTO_TEST_SUITE(base_tlsutility)
BOOST_AUTO_TEST_CASE(sha1) BOOST_AUTO_TEST_CASE(sha1)
@ -35,4 +85,51 @@ BOOST_AUTO_TEST_CASE(sha1)
} }
} }
BOOST_AUTO_TEST_CASE(iscauptodate_ok)
{
auto key (GenKeypair());
BOOST_CHECK(IsCaUptodate(MakeCert("Icinga CA", key, "Icinga CA", key, [](ASN1_TIME* notBefore, ASN1_TIME* notAfter) {
BOOST_REQUIRE(X509_gmtime_adj(notBefore, 0));
BOOST_REQUIRE(X509_gmtime_adj(notAfter, LEAF_VALID_FOR + 60 * 60));
}).get()));
}
BOOST_AUTO_TEST_CASE(iscauptodate_expiring)
{
auto key (GenKeypair());
BOOST_CHECK(!IsCaUptodate(MakeCert("Icinga CA", key, "Icinga CA", key, [](ASN1_TIME* notBefore, ASN1_TIME* notAfter) {
BOOST_REQUIRE(X509_gmtime_adj(notBefore, 0));
BOOST_REQUIRE(X509_gmtime_adj(notAfter, LEAF_VALID_FOR - 60 * 60));
}).get()));
}
BOOST_AUTO_TEST_CASE(iscertuptodate_ok)
{
BOOST_CHECK(IsCertUptodate(MakeCert("Icinga CA", GenKeypair(), "example.com", GenKeypair(), [](ASN1_TIME* notBefore, ASN1_TIME* notAfter) {
time_t epoch = 0;
BOOST_REQUIRE(X509_time_adj(notBefore, l_2017, &epoch));
BOOST_REQUIRE(X509_gmtime_adj(notAfter, RENEW_THRESHOLD + 60 * 60));
})));
}
BOOST_AUTO_TEST_CASE(iscertuptodate_expiring)
{
BOOST_CHECK(!IsCertUptodate(MakeCert("Icinga CA", GenKeypair(), "example.com", GenKeypair(), [](ASN1_TIME* notBefore, ASN1_TIME* notAfter) {
time_t epoch = 0;
BOOST_REQUIRE(X509_time_adj(notBefore, l_2017, &epoch));
BOOST_REQUIRE(X509_gmtime_adj(notAfter, RENEW_THRESHOLD - 60 * 60));
})));
}
BOOST_AUTO_TEST_CASE(iscertuptodate_old)
{
BOOST_CHECK(!IsCertUptodate(MakeCert("Icinga CA", GenKeypair(), "example.com", GenKeypair(), [](ASN1_TIME* notBefore, ASN1_TIME* notAfter) {
time_t epoch = 0;
BOOST_REQUIRE(X509_time_adj(notBefore, l_2016, &epoch));
BOOST_REQUIRE(X509_gmtime_adj(notAfter, RENEW_THRESHOLD + 60 * 60));
})));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

251
test/config-apply.cpp Normal file
View File

@ -0,0 +1,251 @@
/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */
#include "config/applyrule.hpp"
#include "config/configcompiler.hpp"
#include <BoostTestTargetConfig.h>
using namespace icinga;
static Expression* RequireActualExpression(const std::unique_ptr<Expression>& compiledExpression)
{
BOOST_REQUIRE_NE(compiledExpression.get(), nullptr);
auto dict (dynamic_cast<DictExpression*>(compiledExpression.get()));
BOOST_REQUIRE_NE(dict, nullptr);
auto& subex (dict->GetExpressions());
BOOST_REQUIRE_EQUAL(subex.size(), 1u);
auto sub0 (subex.at(0).get());
BOOST_REQUIRE_NE(sub0, nullptr);
return sub0;
}
template<>
struct boost::test_tools::tt_detail::print_log_value<std::pair<String, String>>
{
inline void operator()(std::ostream& os, const std::pair<String, String>& hs)
{
os << hs.first << "!" << hs.second;
}
};
static void GetTargetHostsHelper(
const String& filter, const Dictionary::Ptr& constants, bool targeted, const std::vector<String>& hosts = {}
)
{
auto compiled (ConfigCompiler::CompileText("<test>", filter));
auto expr (RequireActualExpression(compiled));
std::vector<const String*> actualHosts;
BOOST_CHECK_EQUAL(ApplyRule::GetTargetHosts(expr, actualHosts, constants), targeted);
if (targeted) {
std::vector<String> actualHostNames;
actualHostNames.reserve(actualHosts.size());
for (auto h : actualHosts) {
actualHostNames.emplace_back(*h);
}
BOOST_CHECK_EQUAL_COLLECTIONS(actualHostNames.begin(), actualHostNames.end(), hosts.begin(), hosts.end());
}
}
static void GetTargetServicesHelper(
const String& filter, const Dictionary::Ptr& constants, bool targeted, const std::vector<std::pair<String, String>>& services = {}
)
{
auto compiled (ConfigCompiler::CompileText("<test>", filter));
auto expr (RequireActualExpression(compiled));
std::vector<std::pair<const String*, const String*>> actualServices;
BOOST_CHECK_EQUAL(ApplyRule::GetTargetServices(expr, actualServices, constants), targeted);
if (targeted) {
std::vector<std::pair<String, String>> actualServiceNames;
actualServiceNames.reserve(actualServices.size());
for (auto s : actualServices) {
actualServiceNames.emplace_back(*s.first, *s.second);
}
BOOST_CHECK_EQUAL_COLLECTIONS(actualServiceNames.begin(), actualServiceNames.end(), services.begin(), services.end());
}
}
BOOST_AUTO_TEST_SUITE(config_apply)
BOOST_AUTO_TEST_CASE(gettargethosts_literal)
{
GetTargetHostsHelper("host.name == \"foo\"", nullptr, true, {"foo"});
}
BOOST_AUTO_TEST_CASE(gettargethosts_const)
{
GetTargetHostsHelper("host.name == x", new Dictionary({{"x", "foo"}}), true, {"foo"});
}
BOOST_AUTO_TEST_CASE(gettargethosts_swapped)
{
GetTargetHostsHelper("\"foo\" == host.name", nullptr, true, {"foo"});
}
BOOST_AUTO_TEST_CASE(gettargethosts_two)
{
GetTargetHostsHelper("host.name == \"foo\" || host.name == \"bar\"", nullptr, true, {"foo", "bar"});
}
BOOST_AUTO_TEST_CASE(gettargethosts_three)
{
GetTargetHostsHelper(
"host.name == \"foo\" || host.name == \"bar\" || host.name == \"foobar\"",
nullptr, true, {"foo", "bar", "foobar"}
);
}
BOOST_AUTO_TEST_CASE(gettargethosts_mixed)
{
GetTargetHostsHelper("host.name == x || \"bar\" == host.name", new Dictionary({{"x", "foo"}}), true, {"foo", "bar"});
}
BOOST_AUTO_TEST_CASE(gettargethosts_redundant)
{
GetTargetHostsHelper("host.name == \"foo\" && 1", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargethosts_badconst)
{
GetTargetHostsHelper("host.name == NodeName", new Dictionary({{"x", "foo"}}), false);
}
BOOST_AUTO_TEST_CASE(gettargethosts_notliteral)
{
GetTargetHostsHelper("host.name == \"foo\" + \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargethosts_wrongop)
{
GetTargetHostsHelper("host.name != \"foo\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargethosts_wrongattr)
{
GetTargetHostsHelper("host.__name == \"foo\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargethosts_wrongvar)
{
GetTargetHostsHelper("service.name == \"foo\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargethosts_noindexer)
{
GetTargetHostsHelper("name == \"foo\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_literal)
{
GetTargetServicesHelper("host.name == \"foo\" && service.name == \"bar\"", nullptr, true, {{"foo", "bar"}});
}
BOOST_AUTO_TEST_CASE(gettargetservices_const)
{
GetTargetServicesHelper("host.name == x && service.name == y", new Dictionary({{"x", "foo"}, {"y", "bar"}}), true, {{"foo", "bar"}});
}
BOOST_AUTO_TEST_CASE(gettargetservices_swapped_outer)
{
GetTargetServicesHelper("service.name == \"bar\" && host.name == \"foo\"", nullptr, true, {{"foo", "bar"}});
}
BOOST_AUTO_TEST_CASE(gettargetservices_swapped_inner)
{
GetTargetServicesHelper("\"foo\" == host.name && \"bar\" == service.name", nullptr, true, {{"foo", "bar"}});
}
BOOST_AUTO_TEST_CASE(gettargetservices_two)
{
GetTargetServicesHelper(
"host.name == \"foo\" && service.name == \"bar\" || host.name == \"oof\" && service.name == \"rab\"",
nullptr, true, {{"foo", "bar"}, {"oof", "rab"}}
);
}
BOOST_AUTO_TEST_CASE(gettargetservices_three)
{
GetTargetServicesHelper(
"host.name == \"foo\" && service.name == \"bar\" || host.name == \"oof\" && service.name == \"rab\" || host.name == \"ofo\" && service.name == \"rba\"",
nullptr, true, {{"foo", "bar"}, {"oof", "rab"}, {"ofo", "rba"}}
);
}
BOOST_AUTO_TEST_CASE(gettargetservices_mixed)
{
GetTargetServicesHelper("\"bar\" == service.name && x == host.name", new Dictionary({{"x", "foo"}}), true, {{"foo", "bar"}});
}
BOOST_AUTO_TEST_CASE(gettargetservices_redundant)
{
GetTargetServicesHelper("host.name == \"foo\" && service.name == \"bar\" && 1", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_badconst)
{
GetTargetServicesHelper("host.name == NodeName && service.name == \"bar\"", new Dictionary({{"x", "foo"}}), false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_notliteral)
{
GetTargetServicesHelper("host.name == \"foo\" && service.name == \"b\" + \"ar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_wrongop_outer)
{
GetTargetServicesHelper("host.name == \"foo\" & service.name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_wrongop_host)
{
GetTargetServicesHelper("host.name != \"foo\" && service.name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_wrongop_service)
{
GetTargetServicesHelper("host.name == \"foo\" && service.name != \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_wrongattr_host)
{
GetTargetServicesHelper("host.__name == \"foo\" && service.name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_wrongattr_service)
{
GetTargetServicesHelper("host.name == \"foo\" && service.__name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_wrongvar_host)
{
GetTargetServicesHelper("horst.name == \"foo\" && service.name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_wrongvar_service)
{
GetTargetServicesHelper("host.name == \"foo\" && sehrvice.name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_noindexer_host)
{
GetTargetServicesHelper("name == \"foo\" && service.name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_CASE(gettargetservices_noindexer_service)
{
GetTargetServicesHelper("host.name == \"foo\" && name == \"bar\"", nullptr, false);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -1,11 +1,74 @@
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "icinga/host.hpp"
#include "icinga/notification.hpp" #include "icinga/notification.hpp"
#include "icinga/notificationcommand.hpp"
#include "icinga/service.hpp"
#include "icinga/user.hpp"
#include <BoostTestTargetConfig.h> #include <BoostTestTargetConfig.h>
#include <iostream> #include <iostream>
using namespace icinga; using namespace icinga;
struct DuplicateDueToFilterHelper
{
Host::Ptr h = new Host();
Service::Ptr s = new Service();
User::Ptr u = new User();
NotificationCommand::Ptr nc = new NotificationCommand();
Notification::Ptr n = new Notification();
unsigned int called = 0;
DuplicateDueToFilterHelper(int typeFilter, int stateFilter)
{
h->SetName("example.com", true);
h->Register();
s->SetShortName("disk", true);
h->AddService(s);
u->SetName("jdoe", true);
u->SetTypeFilter(~0);
u->SetStateFilter(~0);
u->Register();
nc->SetName("mail", true);
nc->SetExecute(new Function("", [this]() { ++called; }), true);
nc->Register();
n->SetFieldByName("host_name", "example.com", false, DebugInfo());
n->SetFieldByName("service_name", "disk", false, DebugInfo());
n->SetFieldByName("command", "mail", false, DebugInfo());
n->SetUsersRaw(new Array({"jdoe"}), true);
n->SetTypeFilter(typeFilter);
n->SetStateFilter(stateFilter);
n->OnAllConfigLoaded(); // link Service
}
~DuplicateDueToFilterHelper()
{
h->Unregister();
u->Unregister();
nc->Unregister();
}
void SendStateNotification(ServiceState state, bool isSent)
{
auto calledBefore (called);
s->SetStateRaw(state, true);
Application::GetTP().Start();
n->BeginExecuteNotification(
state == ServiceOK ? NotificationRecovery : NotificationProblem,
nullptr, false, false, "", ""
);
Application::GetTP().Stop();
BOOST_CHECK_EQUAL(called > calledBefore, isSent);
}
};
BOOST_AUTO_TEST_SUITE(icinga_notification) BOOST_AUTO_TEST_SUITE(icinga_notification)
BOOST_AUTO_TEST_CASE(strings) BOOST_AUTO_TEST_CASE(strings)
@ -102,4 +165,51 @@ BOOST_AUTO_TEST_CASE(type_filter)
std::cout << "#4 Notification type: " << ftype << " against " << notification->GetTypeFilter() << " must fail." << std::endl; std::cout << "#4 Notification type: " << ftype << " against " << notification->GetTypeFilter() << " must fail." << std::endl;
BOOST_CHECK(!(notification->GetTypeFilter() & ftype)); BOOST_CHECK(!(notification->GetTypeFilter() & ftype));
} }
BOOST_AUTO_TEST_CASE(no_filter_problem_no_duplicate)
{
DuplicateDueToFilterHelper helper (~0, ~0);
helper.SendStateNotification(ServiceCritical, true);
helper.SendStateNotification(ServiceWarning, true);
helper.SendStateNotification(ServiceCritical, true);
}
BOOST_AUTO_TEST_CASE(filter_problem_no_duplicate)
{
DuplicateDueToFilterHelper helper (~0, ~StateFilterWarning);
helper.SendStateNotification(ServiceCritical, true);
helper.SendStateNotification(ServiceWarning, false);
helper.SendStateNotification(ServiceCritical, false);
}
BOOST_AUTO_TEST_CASE(volatile_filter_problem_duplicate)
{
DuplicateDueToFilterHelper helper (~0, ~StateFilterWarning);
helper.s->SetVolatile(true, true);
helper.SendStateNotification(ServiceCritical, true);
helper.SendStateNotification(ServiceWarning, false);
helper.SendStateNotification(ServiceCritical, true);
}
BOOST_AUTO_TEST_CASE(no_recovery_filter_no_duplicate)
{
DuplicateDueToFilterHelper helper (~0, ~0);
helper.SendStateNotification(ServiceCritical, true);
helper.SendStateNotification(ServiceOK, true);
helper.SendStateNotification(ServiceCritical, true);
}
BOOST_AUTO_TEST_CASE(recovery_filter_duplicate)
{
DuplicateDueToFilterHelper helper (~NotificationRecovery, ~0);
helper.SendStateNotification(ServiceCritical, true);
helper.SendStateNotification(ServiceOK, false);
helper.SendStateNotification(ServiceCritical, true);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,88 @@
/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */
#include "base/array.hpp"
#include "icinga/checkresult.hpp"
#include "icinga/host.hpp"
#include "icinga/notification.hpp"
#include "icinga/notificationcommand.hpp"
#include "icinga/service.hpp"
#include "icinga/user.hpp"
#include "methods/pluginnotificationtask.hpp"
#include <BoostTestTargetConfig.h>
#include <future>
using namespace icinga;
BOOST_AUTO_TEST_SUITE(methods_pluginnotificationtask)
BOOST_AUTO_TEST_CASE(truncate_long_output)
{
#ifdef __linux__
Host::Ptr h = new Host();
CheckResult::Ptr hcr = new CheckResult();
CheckResult::Ptr scr = new CheckResult();
Service::Ptr s = new Service();
User::Ptr u = new User();
NotificationCommand::Ptr nc = new NotificationCommand();
Notification::Ptr n = new Notification();
String placeHolder (1024 * 1024, 'x');
std::promise<String> promise;
auto future (promise.get_future());
hcr->SetOutput("H" + placeHolder + "h", true);
scr->SetOutput("S" + placeHolder + "s", true);
h->SetName("example.com", true);
h->SetLastCheckResult(hcr, true);
h->Register();
s->SetHostName("example.com", true);
s->SetShortName("disk", true);
s->SetLastCheckResult(scr, true);
s->OnAllConfigLoaded(); // link Host
nc->SetCommandLine(
new Array({
"echo",
"host_output=$host.output$",
"service_output=$service.output$",
"notification_comment=$notification.comment$",
"output=$output$",
"comment=$comment$"
}),
true
);
nc->SetName("mail", true);
nc->Register();
n->SetFieldByName("host_name", "example.com", false, DebugInfo());
n->SetFieldByName("service_name", "disk", false, DebugInfo());
n->SetFieldByName("command", "mail", false, DebugInfo());
n->OnAllConfigLoaded(); // link Service
Checkable::ExecuteCommandProcessFinishedHandler = [&promise](const Value&, const ProcessResult& pr) {
promise.set_value(pr.Output);
};
PluginNotificationTask::ScriptFunc(n, u, nullptr, NotificationCustom, "jdoe", "C" + placeHolder + "c", nullptr, false);
future.wait();
Checkable::ExecuteCommandProcessFinishedHandler = nullptr;
h->Unregister();
nc->Unregister();
auto output (future.get());
BOOST_CHECK(output.Contains("host_output=Hx"));
BOOST_CHECK(!output.Contains("xh"));
BOOST_CHECK(output.Contains("x service_output=Sx"));
BOOST_CHECK(!output.Contains("xs"));
BOOST_CHECK(output.Contains("x notification_comment=Cx"));
BOOST_CHECK(!output.Contains("xc"));
BOOST_CHECK(output.Contains("x output=Sx"));
BOOST_CHECK(output.Contains("x comment=Cx"));
#endif /* __linux__ */
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -31,10 +31,10 @@ if (-not (Test-Path env:OPENSSL_ROOT_DIR)) {
$env:OPENSSL_ROOT_DIR = 'c:\local\OpenSSL-Win64' $env:OPENSSL_ROOT_DIR = 'c:\local\OpenSSL-Win64'
} }
if (-not (Test-Path env:BOOST_ROOT)) { if (-not (Test-Path env:BOOST_ROOT)) {
$env:BOOST_ROOT = 'c:\local\boost_1_82_0' $env:BOOST_ROOT = 'c:\local\boost_1_83_0'
} }
if (-not (Test-Path env:BOOST_LIBRARYDIR)) { if (-not (Test-Path env:BOOST_LIBRARYDIR)) {
$env:BOOST_LIBRARYDIR = 'c:\local\boost_1_82_0\lib64-msvc-14.2' $env:BOOST_LIBRARYDIR = 'c:\local\boost_1_83_0\lib64-msvc-14.2'
} }
if (-not (Test-Path env:FLEX_BINARY)) { if (-not (Test-Path env:FLEX_BINARY)) {
$env:FLEX_BINARY = 'C:\ProgramData\chocolatey\bin\win_flex.exe' $env:FLEX_BINARY = 'C:\ProgramData\chocolatey\bin\win_flex.exe'

View File

@ -30,13 +30,13 @@ if (-not (Test-Path env:CMAKE_GENERATOR_PLATFORM)) {
} }
} }
if (-not (Test-Path env:OPENSSL_ROOT_DIR)) { if (-not (Test-Path env:OPENSSL_ROOT_DIR)) {
$env:OPENSSL_ROOT_DIR = "c:\local\OpenSSL_3_0_9-Win${env:BITS}" $env:OPENSSL_ROOT_DIR = "c:\local\OpenSSL_3_0_12-Win${env:BITS}"
} }
if (-not (Test-Path env:BOOST_ROOT)) { if (-not (Test-Path env:BOOST_ROOT)) {
$env:BOOST_ROOT = "c:\local\boost_1_82_0-Win${env:BITS}" $env:BOOST_ROOT = "c:\local\boost_1_83_0-Win${env:BITS}"
} }
if (-not (Test-Path env:BOOST_LIBRARYDIR)) { if (-not (Test-Path env:BOOST_LIBRARYDIR)) {
$env:BOOST_LIBRARYDIR = "c:\local\boost_1_82_0-Win${env:BITS}\lib${env:BITS}-msvc-14.2" $env:BOOST_LIBRARYDIR = "c:\local\boost_1_83_0-Win${env:BITS}\lib${env:BITS}-msvc-14.2"
} }
if (-not (Test-Path env:FLEX_BINARY)) { if (-not (Test-Path env:FLEX_BINARY)) {
$env:FLEX_BINARY = 'C:\ProgramData\chocolatey\bin\win_flex.exe' $env:FLEX_BINARY = 'C:\ProgramData\chocolatey\bin\win_flex.exe'