Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorArtjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com>2021-05-07 13:47:40 +0300
committerArtjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com>2021-05-07 13:49:05 +0300
commit41e3dd118cc870619ab69d9ed0b97d568bde55f4 (patch)
tree2587d08132a68bac434cd6f125cb27cf973e0aa8 /src
parent5e5c5444e2f0102bdf34cec94851e4781b719022 (diff)
parentf9b34c32c38493307a3b2ebc73a4130223be96b5 (diff)
........S. [DEV-1836] updated to the latest master
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am9
-rw-r--r--src/go/Makefile.am52
-rw-r--r--src/go/cmd/zabbix_agent2/copyright_extra.go32
-rw-r--r--src/go/cmd/zabbix_agent2/service_windows.go37
-rw-r--r--src/go/cmd/zabbix_agent2/zabbix_agent2.go5
-rw-r--r--src/go/cmd/zabbix_web_service/config.go36
-rw-r--r--src/go/cmd/zabbix_web_service/pdf_report_creator.go214
-rw-r--r--src/go/cmd/zabbix_web_service/zabbix_web_service.go229
-rw-r--r--src/go/conf/zabbix_agent2.conf94
-rw-r--r--src/go/conf/zabbix_agent2.win.conf46
-rw-r--r--src/go/conf/zabbix_web_service.conf106
-rw-r--r--src/go/go.mod7
-rw-r--r--src/go/go.sum22
-rw-r--r--src/go/internal/agent/options_windows.go2
-rw-r--r--src/go/internal/agent/plugin_userparameter_test.go8
-rw-r--r--src/go/internal/agent/serverlistener/serverlistener.go5
-rw-r--r--src/go/pkg/log/log.go16
-rw-r--r--src/go/pkg/version/version.go54
-rw-r--r--src/go/pkg/zbxlib/globals_windows.go1
-rw-r--r--src/go/pkg/zbxnet/allowedpeers.go (renamed from src/go/internal/agent/serverlistener/allowedpeers.go)10
-rw-r--r--src/go/pkg/zbxnet/allowedpeers_test.go (renamed from src/go/internal/agent/serverlistener/allowedpeers_test.go)4
-rw-r--r--src/go/plugins/mongodb/README.md143
-rw-r--r--src/go/plugins/mongodb/config.go57
-rw-r--r--src/go/plugins/mongodb/conn.go293
-rw-r--r--src/go/plugins/mongodb/handler_collection_stats.go54
-rw-r--r--src/go/plugins/mongodb/handler_collection_stats_test.go84
-rw-r--r--src/go/plugins/mongodb/handler_collections_discovery.go69
-rw-r--r--src/go/plugins/mongodb/handler_collections_discovery_test.go78
-rw-r--r--src/go/plugins/mongodb/handler_collections_usage.go55
-rw-r--r--src/go/plugins/mongodb/handler_collections_usage_test.go70
-rw-r--r--src/go/plugins/mongodb/handler_config_discovery.go96
-rw-r--r--src/go/plugins/mongodb/handler_connPool_stats.go54
-rw-r--r--src/go/plugins/mongodb/handler_connPool_stats_test.go90
-rw-r--r--src/go/plugins/mongodb/handler_database_stats.go54
-rw-r--r--src/go/plugins/mongodb/handler_database_stats_test.go91
-rw-r--r--src/go/plugins/mongodb/handler_databases_discovery.go55
-rw-r--r--src/go/plugins/mongodb/handler_databases_discovery_test.go60
-rw-r--r--src/go/plugins/mongodb/handler_jumbo_chunks.go36
-rw-r--r--src/go/plugins/mongodb/handler_oplog_stats.go92
-rw-r--r--src/go/plugins/mongodb/handler_oplog_stats_test.go95
-rw-r--r--src/go/plugins/mongodb/handler_ping.go35
-rw-r--r--src/go/plugins/mongodb/handler_replset_config.go58
-rw-r--r--src/go/plugins/mongodb/handler_replset_config_test.go71
-rw-r--r--src/go/plugins/mongodb/handler_replset_status.go162
-rw-r--r--src/go/plugins/mongodb/handler_server_status.go58
-rw-r--r--src/go/plugins/mongodb/handler_server_status_test.go71
-rw-r--r--src/go/plugins/mongodb/handler_shards_discovery.go87
-rw-r--r--src/go/plugins/mongodb/metrics.go159
-rw-r--r--src/go/plugins/mongodb/mockconn.go190
-rw-r--r--src/go/plugins/mongodb/mongodb.go114
-rw-r--r--src/go/plugins/mongodb/testdata/collStats.json1
-rw-r--r--src/go/plugins/mongodb/testdata/connPoolStats.json1
-rw-r--r--src/go/plugins/mongodb/testdata/dbStats.json1
-rw-r--r--src/go/plugins/mongodb/testdata/replSetGetConfig.json1
-rw-r--r--src/go/plugins/mongodb/testdata/serverStatus.json1
-rw-r--r--src/go/plugins/mongodb/testdata/top.json1
-rw-r--r--src/go/plugins/plugins_darwin.go1
-rw-r--r--src/go/plugins/plugins_linux.go1
-rw-r--r--src/go/plugins/plugins_windows.go1
-rw-r--r--src/go/plugins/smart/smart.go4
-rw-r--r--src/go/plugins/smart/smart_test.go8
-rw-r--r--src/go/plugins/smart/smartfs.go98
-rw-r--r--src/go/plugins/windows/services/services_windows.go2
-rw-r--r--src/libs/zbxalgo/Makefile.am2
-rw-r--r--src/libs/zbxalgo/linked_list.c1
-rw-r--r--src/libs/zbxcommon/misc.c72
-rw-r--r--src/libs/zbxcommon/str.c4
-rw-r--r--src/libs/zbxcrypto/Makefile.am2
-rw-r--r--src/libs/zbxcrypto/aes.c567
-rw-r--r--src/libs/zbxcrypto/aes.h87
-rw-r--r--src/libs/zbxcrypto/sha256crypt.c1
-rw-r--r--src/libs/zbxcrypto/sha512crypt.c1
-rw-r--r--src/libs/zbxdbcache/dbcache.c77
-rw-r--r--src/libs/zbxdbcache/dbconfig.c167
-rw-r--r--src/libs/zbxdbcache/dbconfig.h14
-rw-r--r--src/libs/zbxdbcache/dbconfig_dump.c25
-rw-r--r--src/libs/zbxdbcache/dbsync.c104
-rw-r--r--src/libs/zbxdbcache/dbsync.h1
-rw-r--r--src/libs/zbxdbcache/valuecache.c570
-rw-r--r--src/libs/zbxdbcache/valuecache.h5
-rw-r--r--src/libs/zbxdbhigh/Makefile.am3
-rw-r--r--src/libs/zbxdbhigh/event.c26
-rw-r--r--src/libs/zbxdbhigh/host.c809
-rw-r--r--src/libs/zbxdbhigh/lld_override.c10
-rw-r--r--src/libs/zbxdbhigh/mediatype.c131
-rw-r--r--src/libs/zbxdbhigh/proxy.c5
-rw-r--r--src/libs/zbxdbhigh/template_item.c179
-rw-r--r--src/libs/zbxdbhigh/trigger.c3
-rw-r--r--src/libs/zbxdbupgrade/Makefile.am2
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_4000.c127
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_5010.c49
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_5030.c3814
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_macros.c149
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_macros.h26
-rw-r--r--src/libs/zbxdiag/diag.c3
-rw-r--r--src/libs/zbxipcservice/ipcservice.c37
-rw-r--r--src/libs/zbxmedia/email.c112
-rw-r--r--src/libs/zbxself/Makefile.am13
-rw-r--r--src/libs/zbxself/selfmon.c4
-rw-r--r--src/libs/zbxself/selfmon.h25
-rw-r--r--src/libs/zbxself/selfmon_proxy.c39
-rw-r--r--src/libs/zbxself/selfmon_server.c49
-rw-r--r--src/libs/zbxserver/Makefile.am6
-rw-r--r--src/libs/zbxserver/expression.c318
-rw-r--r--src/libs/zbxserver/get_host_from_event.c158
-rw-r--r--src/libs/zbxserver/get_host_from_event.h25
-rw-r--r--src/libs/zbxserver/zabbix_users.c73
-rw-r--r--src/libs/zbxserver/zabbix_users.h26
-rw-r--r--src/libs/zbxsys/threads.c9
-rw-r--r--src/zabbix_agent/logfiles/logfiles.c156
-rw-r--r--src/zabbix_java/src/com/zabbix/gateway/GeneralInformation.java4
-rw-r--r--src/zabbix_proxy/Makefile.am3
-rw-r--r--src/zabbix_proxy/datasender/datasender.c9
-rw-r--r--src/zabbix_proxy/proxy_alerter_protocol.c73
-rw-r--r--src/zabbix_proxy/proxy_lld.c3
-rw-r--r--src/zabbix_proxy/taskmanager/taskmanager.c4
-rw-r--r--src/zabbix_server/Makefile.am6
-rw-r--r--src/zabbix_server/actions.c225
-rw-r--r--src/zabbix_server/alerter/alert_manager.c325
-rw-r--r--src/zabbix_server/alerter/alert_syncer.c29
-rw-r--r--src/zabbix_server/alerter/alerter_protocol.c398
-rw-r--r--src/zabbix_server/alerter/alerter_protocol.h31
-rw-r--r--src/zabbix_server/escalator/escalator.c665
-rw-r--r--src/zabbix_server/events.c71
-rw-r--r--src/zabbix_server/httppoller/httptest.c8
-rw-r--r--src/zabbix_server/ipmi/ipmi_manager.c11
-rw-r--r--src/zabbix_server/lld/lld.c8
-rw-r--r--src/zabbix_server/lld/lld.h3
-rw-r--r--src/zabbix_server/lld/lld_item.c1872
-rw-r--r--src/zabbix_server/lld/lld_manager.c83
-rw-r--r--src/zabbix_server/lld/lld_manager.h25
-rw-r--r--src/zabbix_server/lld/lld_protocol.c47
-rw-r--r--src/zabbix_server/lld/lld_protocol.h13
-rw-r--r--src/zabbix_server/lld/lld_worker.c4
-rw-r--r--src/zabbix_server/pinger/pinger.c10
-rw-r--r--src/zabbix_server/poller/checks_aggregate.c2
-rw-r--r--src/zabbix_server/poller/checks_calculated.c3
-rw-r--r--src/zabbix_server/poller/checks_simple_vmware.c8
-rw-r--r--src/zabbix_server/poller/poller.c18
-rw-r--r--src/zabbix_server/preprocessor/preproc_manager.c3
-rw-r--r--src/zabbix_server/preprocessor/preprocessing.c13
-rw-r--r--src/zabbix_server/preprocessor/preprocessing.h1
-rw-r--r--src/zabbix_server/reporter/Makefile.am12
-rw-r--r--src/zabbix_server/reporter/report_manager.c2508
-rw-r--r--src/zabbix_server/reporter/report_manager.h28
-rw-r--r--src/zabbix_server/reporter/report_protocol.c480
-rw-r--r--src/zabbix_server/reporter/report_protocol.h67
-rw-r--r--src/zabbix_server/reporter/report_writer.c517
-rw-r--r--src/zabbix_server/reporter/report_writer.h28
-rw-r--r--src/zabbix_server/scripts/scripts.c354
-rw-r--r--src/zabbix_server/scripts/scripts.h19
-rw-r--r--src/zabbix_server/server.c41
-rw-r--r--src/zabbix_server/snmptrapper/snmptrapper.c4
-rw-r--r--src/zabbix_server/trapper/Makefile.am13
-rw-r--r--src/zabbix_server/trapper/nodecommand.c521
-rw-r--r--src/zabbix_server/trapper/nodecommand.h2
-rw-r--r--src/zabbix_server/trapper/proxydata.c30
-rw-r--r--src/zabbix_server/trapper/trapper.c152
-rw-r--r--src/zabbix_server/trapper/trapper_proxy.c30
-rw-r--r--src/zabbix_server/trapper/trapper_request.h28
-rw-r--r--src/zabbix_server/trapper/trapper_server.c228
-rw-r--r--src/zabbix_server/vmware/vmware.c98
162 files changed, 16570 insertions, 4395 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 8f81421e860..7a9dcb772ce 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -38,7 +38,12 @@ if AGENT2
if !AGENT
AGENT_SUBDIRS = zabbix_agent
endif
-AGENT2_SUBDIRS = \
+GO_SUBDIRS = \
+ go
+endif
+
+if WEBSERVICE
+GO_SUBDIRS = \
go
endif
@@ -84,7 +89,7 @@ endif
SUBDIRS = \
$(COMMON_SUBDIRS) \
$(AGENT_SUBDIRS) \
- $(AGENT2_SUBDIRS) \
+ $(GO_SUBDIRS) \
$(SERVER_SUBDIRS) \
$(PROXY_SUBDIRS) \
$(JAVA_SUBDIRS)
diff --git a/src/go/Makefile.am b/src/go/Makefile.am
index 9d14894757d..aa56de7b895 100644
--- a/src/go/Makefile.am
+++ b/src/go/Makefile.am
@@ -1,34 +1,64 @@
## Process this file with automake to produce Makefile.in
-EXTRA_DIST = .
-
+EXTRA_DIST = .
+
BUILD_TIME=`date +%H:%M:%S`
BUILD_DATE=`date +"%b %_d %Y"`
GOOS=`go env GOOS`
GOARCH=`go env GOARCH`
PKG=zabbix.com/pkg/version
-GOLDFLAGS = -X ${PKG}.titleMessage=zabbix_agent2
-GOLDFLAGS += -X '${PKG}.compileDate=${BUILD_DATE}'
+GOLDFLAGS = -X '${PKG}.compileDate=${BUILD_DATE}'
GOLDFLAGS += -X ${PKG}.compileTime=${BUILD_TIME}
GOLDFLAGS += -X ${PKG}.compileOs=${GOOS}
GOLDFLAGS += -X ${PKG}.compileArch=${GOARCH}
-# GOLDFLAGS += -X ${PKG}.compileMode=daemon
-GOLDFLAGS += -X main.confDefault=${AGENT2_CONFIG_FILE}
+
+AGENT_GOLDFLAGS = ${GOLDFLAGS}
+AGENT_GOLDFLAGS += -X main.confDefault=${AGENT2_CONFIG_FILE}
+AGENT_GOLDFLAGS += -X main.applicationName=zabbix_agent2
+
+WEBSERVICE_GOLDFLAGS = ${GOLDFLAGS}
+WEBSERVICE_GOLDFLAGS += -X main.applicationName=zabbix_web_service
+
+dist_sysconf_DATA =
+TARGETS =
+INSTALL_TARGETS =
+
+if AGENT2
+TARGETS += zabbix.com/cmd/zabbix_agent2
+INSTALL_TARGETS += install-zabbix.com/cmd/zabbix_agent2
+DBGTARGETS = zabbix.com/cmd/mock_server
+dist_sysconf_DATA += conf/zabbix_agent2.conf
+endif
+
+if WEBSERVICE
+TARGETS += zabbix.com/cmd/zabbix_web_service
+INSTALL_TARGETS += install-zabbix.com/cmd/zabbix_web_service
+dist_sysconf_DATA += conf/zabbix_web_service.conf
+endif
all: build
-build:
- CGO_CFLAGS="${CGO_CFLAGS}" CGO_LDFLAGS="${CGO_LDFLAGS}" go build -ldflags="${GOLDFLAGS}" -o bin ./...
+zabbix.com/cmd/zabbix_agent2:
+ CGO_CFLAGS="${CGO_CFLAGS}" CGO_LDFLAGS="${CGO_LDFLAGS}" go build -ldflags="${AGENT_GOLDFLAGS}" -o bin zabbix.com/cmd/zabbix_agent2
+
+zabbix.com/cmd/zabbix_web_service:
+ go build -ldflags="${WEBSERVICE_GOLDFLAGS}" -o bin zabbix.com/cmd/zabbix_web_service
+
+build: ${TARGETS}
clean:
go clean ./...
rm -f bin/zabbix_agent2 bin/mock_server
-install:
+install-zabbix.com/cmd/zabbix_agent2:
CGO_CFLAGS="${CGO_CFLAGS}" CGO_LDFLAGS="${CGO_LDFLAGS}" GOBIN=${GOBIN} \
- go install -ldflags="${GOLDFLAGS}" zabbix.com/cmd/zabbix_agent2
- test -f "$(DESTDIR)@AGENT2_CONFIG_FILE@" || cp "conf/zabbix_agent2.conf" "$(DESTDIR)@AGENT2_CONFIG_FILE@"
+ go install -ldflags="${AGENT_GOLDFLAGS}" ${TARGETS}
+
+install-zabbix.com/cmd/zabbix_web_service:
+ GOBIN=${GOBIN} go install -ldflags="${WEBSERVICE_GOLDFLAGS}" zabbix.com/cmd/zabbix_web_service
+
+install-exec-local: ${INSTALL_TARGETS}
check:
CGO_CFLAGS="${CGO_CFLAGS}" CGO_LDFLAGS="${CGO_LDFLAGS}" go test ./...
diff --git a/src/go/cmd/zabbix_agent2/copyright_extra.go b/src/go/cmd/zabbix_agent2/copyright_extra.go
new file mode 100644
index 00000000000..3fd7482b3c3
--- /dev/null
+++ b/src/go/cmd/zabbix_agent2/copyright_extra.go
@@ -0,0 +1,32 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package main
+
+func copyrightMessageMQTT() string {
+ return "\nWe use the library Eclipse Paho (eclipse/paho.mqtt.golang), which is\n" +
+ "distributed under the terms of the Eclipse Distribution License 1.0 (The 3-Clause BSD License)\n" +
+ "available at https://www.eclipse.org/org/documents/edl-v10.php\n"
+}
+
+func copyrightMessageModbus() string {
+ return "\nWe use the library go-modbus (goburrow/modbus), which is\n" +
+ "distributed under the terms of the 3-Clause BSD License\n" +
+ "available at https://github.com/goburrow/modbus/blob/master/LICENSE\n"
+}
diff --git a/src/go/cmd/zabbix_agent2/service_windows.go b/src/go/cmd/zabbix_agent2/service_windows.go
index 441de2b96c7..e7f21ce59a3 100644
--- a/src/go/cmd/zabbix_agent2/service_windows.go
+++ b/src/go/cmd/zabbix_agent2/service_windows.go
@@ -463,7 +463,7 @@ loop:
closeChan <- true
break loop
default:
- log.Warningf("unsupported windows service command received")
+ log.Debugf("unsupported windows service command '%s' received", getCmdName(c.Cmd))
}
case <-stopChan:
changes <- svc.Status{State: svc.StopPending}
@@ -478,3 +478,38 @@ loop:
return
}
+
+func getCmdName(cmd svc.Cmd) string {
+ switch cmd {
+ case svc.Stop:
+ return "Stop"
+ case svc.Pause:
+ return "Pause"
+ case svc.Continue:
+ return "Continue"
+ case svc.Interrogate:
+ return "Interrogate"
+ case svc.Shutdown:
+ return "Shutdown"
+ case svc.ParamChange:
+ return "ParamChange"
+ case svc.NetBindAdd:
+ return "NetBindAdd"
+ case svc.NetBindRemove:
+ return "NetBindRemove"
+ case svc.NetBindEnable:
+ return "NetBindEnable"
+ case svc.NetBindDisable:
+ return "NetBindDisable"
+ case svc.DeviceEvent:
+ return "DeviceEvent"
+ case svc.HardwareProfileChange:
+ return "HardwareProfileChange"
+ case svc.PowerEvent:
+ return "PowerEvent"
+ case svc.SessionChange:
+ return "SessionChange"
+ default:
+ return "unknown"
+ }
+}
diff --git a/src/go/cmd/zabbix_agent2/zabbix_agent2.go b/src/go/cmd/zabbix_agent2/zabbix_agent2.go
index f4b404d2e64..9cccec9ee17 100644
--- a/src/go/cmd/zabbix_agent2/zabbix_agent2.go
+++ b/src/go/cmd/zabbix_agent2/zabbix_agent2.go
@@ -170,7 +170,8 @@ loop:
}
var (
- confDefault string
+ confDefault string
+ applicationName string
argConfig bool
argTest bool
@@ -180,6 +181,8 @@ var (
)
func main() {
+ version.Init(applicationName, tls.CopyrightMessage(), copyrightMessageMQTT(), copyrightMessageModbus())
+
var confFlag string
const (
confDescription = "Path to the configuration file"
diff --git a/src/go/cmd/zabbix_web_service/config.go b/src/go/cmd/zabbix_web_service/config.go
new file mode 100644
index 00000000000..c745b2a9edf
--- /dev/null
+++ b/src/go/cmd/zabbix_web_service/config.go
@@ -0,0 +1,36 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package main
+
+var options serviceOptions
+
+type serviceOptions struct {
+ ListenPort string `conf:"optional,range=1024:32767,default=10053"`
+ AllowedIP string `conf:"optional"`
+ LogType string `conf:"optional,default=file"`
+ LogFile string `conf:"optional,default=/tmp/zabbix_web_service.log"`
+ LogFileSize int `conf:"optional,range=0:1024,default=1"`
+ Timeout int `conf:"optional,range=1:30,default=3"`
+ DebugLevel int `conf:"range=0:5,default=3"`
+ TLSAccept string `conf:"optional"`
+ TLSCAFile string `conf:"optional"`
+ TLSCertFile string `conf:"optional"`
+ TLSKeyFile string `conf:"optional"`
+}
diff --git a/src/go/cmd/zabbix_web_service/pdf_report_creator.go b/src/go/cmd/zabbix_web_service/pdf_report_creator.go
new file mode 100644
index 00000000000..799e5384ffc
--- /dev/null
+++ b/src/go/cmd/zabbix_web_service/pdf_report_creator.go
@@ -0,0 +1,214 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "strconv"
+ "time"
+
+ "github.com/chromedp/cdproto/emulation"
+ "github.com/chromedp/cdproto/network"
+ "github.com/chromedp/cdproto/page"
+ "github.com/chromedp/chromedp"
+ "zabbix.com/pkg/log"
+ "zabbix.com/pkg/zbxerr"
+)
+
+type requestBody struct {
+ URL string `json:"url"`
+ Header map[string]string `json:"headers"`
+ Parameters map[string]string `json:"parameters"`
+}
+
+func newRequestBody() *requestBody {
+ return &requestBody{"", make(map[string]string), make(map[string]string)}
+}
+
+func logAndWriteError(w http.ResponseWriter, errMsg string, code int) {
+ log.Errf("%s", errMsg)
+ w.Header().Set("Content-Type", "application/problem+json")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ w.WriteHeader(code)
+ json.NewEncoder(w).Encode(map[string]string{"detail": errMsg})
+}
+
+func (h *handler) report(w http.ResponseWriter, r *http.Request) {
+ log.Infof("received report request from %s", r.RemoteAddr)
+
+ host, _, err := net.SplitHostPort(r.RemoteAddr)
+ if err != nil {
+ logAndWriteError(w, fmt.Sprintf("Cannot remove port from host for incoming ip %s.", err.Error()), http.StatusInternalServerError)
+ return
+ }
+
+ if !h.allowedPeers.CheckPeer(net.ParseIP(host)) {
+ logAndWriteError(w, fmt.Sprintf("Cannot accept incoming connection for peer: %s.", r.RemoteAddr), http.StatusInternalServerError)
+ return
+ }
+
+ if r.Method != http.MethodPost {
+ logAndWriteError(w, "Method is not supported.", http.StatusMethodNotAllowed)
+ return
+ }
+
+ if r.Header.Get("Content-Type") != "application/json" {
+ logAndWriteError(w, "Content Type is not application/json.", http.StatusMethodNotAllowed)
+ return
+ }
+
+ b, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ logAndWriteError(w, "Can not read body data.", http.StatusInternalServerError)
+ return
+ }
+
+ req := newRequestBody()
+ if err = json.Unmarshal(b, &req); err != nil {
+ logAndWriteError(w, zbxerr.ErrorCannotUnmarshalJSON.Wrap(err).Error(), http.StatusInternalServerError)
+ return
+ }
+
+ ctx, cancel := chromedp.NewContext(context.Background())
+ defer cancel()
+
+ width, err := strconv.ParseInt(req.Parameters["width"], 10, 64)
+ if err != nil {
+ logAndWriteError(w, fmt.Sprintf("Incorrect parameter width: %s", err.Error()), http.StatusBadRequest)
+ return
+ }
+
+ height, err := strconv.ParseInt(req.Parameters["height"], 10, 64)
+ if err != nil {
+ logAndWriteError(w, fmt.Sprintf("Incorrect parameter height: %s", err.Error()), http.StatusBadRequest)
+ return
+ }
+
+ u, err := parseUrl(req.URL)
+ if err != nil {
+ logAndWriteError(w, fmt.Sprintf("Incorrect request url: %s", err.Error()), http.StatusBadRequest)
+ return
+ }
+
+ log.Tracef(
+ "making chrome headless request with parameters url: %s, width: %s, height: %s for report request from %s",
+ u.String(), req.Parameters["width"], req.Parameters["height"], r.RemoteAddr)
+
+ var buf []byte
+
+ if err = chromedp.Run(ctx, chromedp.Tasks{
+ network.SetExtraHTTPHeaders(network.Headers(map[string]interface{}{"Cookie": req.Header["Cookie"]})),
+ emulation.SetDeviceMetricsOverride(width, height, 1, false),
+ navigateAndWaitFor(u.String(), "networkIdle"),
+ chromedp.ActionFunc(func(ctx context.Context) error {
+ timeoutContext, cancel := context.WithTimeout(ctx, time.Duration(options.Timeout)*time.Second)
+ defer cancel()
+ var err error
+ buf, _, err = page.PrintToPDF().
+ WithPrintBackground(true).
+ WithPreferCSSPageSize(true).
+ WithPaperWidth(pixels2inches(width)).
+ WithPaperHeight(pixels2inches(height)).
+ Do(timeoutContext)
+ return err
+ }),
+ }); err != nil {
+ logAndWriteError(w, zbxerr.ErrorCannotFetchData.Wrap(err).Error(), http.StatusInternalServerError)
+ return
+ }
+
+ log.Infof("writing response for report request from %s", r.RemoteAddr)
+
+ w.Header().Set("Content-type", "application/pdf")
+ w.Write(buf)
+
+ return
+}
+
+func pixels2inches(value int64) float64 {
+ return float64(value) * 0.0104166667
+}
+
+func navigateAndWaitFor(url string, eventName string) chromedp.ActionFunc {
+ return func(ctx context.Context) error {
+ _, _, _, err := page.Navigate(url).Do(ctx)
+ if err != nil {
+ return err
+ }
+
+ return waitFor(ctx, eventName)
+ }
+}
+
+// This comment is taken from the proof of concept example
+//
+// waitFor blocks until eventName is received.
+// Examples of events you can wait for:
+// init, DOMContentLoaded, firstPaint,
+// firstContentfulPaint, firstImagePaint,
+// firstMeaningfulPaintCandidate,
+// load, networkAlmostIdle, firstMeaningfulPaint, networkIdle
+//
+// This is not super reliable, I've already found incidental cases where
+// networkIdle was sent before load. It's probably smart to see how
+// puppeteer implements this exactly.
+func waitFor(ctx context.Context, eventName string) error {
+ ch := make(chan struct{})
+ cctx, cancel := context.WithCancel(ctx)
+ chromedp.ListenTarget(cctx, func(ev interface{}) {
+ switch e := ev.(type) {
+ case *page.EventLifecycleEvent:
+ if e.Name == eventName {
+ cancel()
+ close(ch)
+ }
+ }
+ })
+ select {
+ case <-ch:
+ return nil
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+}
+
+func parseUrl(u string) (*url.URL, error) {
+ if u == "" {
+ return nil, errors.New("url is empty")
+ }
+
+ parsed, err := url.Parse(u)
+ if err != nil {
+ return nil, err
+ }
+
+ if parsed.Scheme == "" {
+ return nil, errors.New("url is missing scheme")
+ }
+
+ return parsed, nil
+}
diff --git a/src/go/cmd/zabbix_web_service/zabbix_web_service.go b/src/go/cmd/zabbix_web_service/zabbix_web_service.go
new file mode 100644
index 00000000000..e7589e49cb4
--- /dev/null
+++ b/src/go/cmd/zabbix_web_service/zabbix_web_service.go
@@ -0,0 +1,229 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package main
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "zabbix.com/pkg/conf"
+ "zabbix.com/pkg/log"
+ "zabbix.com/pkg/version"
+ "zabbix.com/pkg/zbxnet"
+)
+
+var (
+ confDefault string
+ applicationName string
+)
+
+type handler struct {
+ allowedPeers *zbxnet.AllowedPeers
+}
+
+func main() {
+ var confFlag string
+ var helpFlag bool
+ var versionFlag bool
+
+ version.Init(applicationName)
+
+ const (
+ confDescription = "Path to the configuration file"
+ helpDefault = false
+ helpDescription = "Display this help message"
+ versionDefault = false
+ versionDescription = "Print program version and exit"
+ )
+
+ flag.StringVar(&confFlag, "config", confDefault, confDescription)
+ flag.StringVar(&confFlag, "c", confDefault, confDescription+" (shorthand)")
+ flag.BoolVar(&helpFlag, "help", helpDefault, helpDescription)
+ flag.BoolVar(&helpFlag, "h", helpDefault, helpDescription+" (shorthand)")
+ flag.BoolVar(&versionFlag, "version", versionDefault, versionDescription)
+ flag.BoolVar(&versionFlag, "V", versionDefault, versionDescription+" (shorthand)")
+
+ flag.Parse()
+
+ if helpFlag {
+ flag.Usage()
+ os.Exit(0)
+ }
+
+ if versionFlag {
+ version.Display()
+ os.Exit(0)
+ }
+
+ if err := conf.Load(confFlag, &options); err != nil {
+ fatalExit("", err)
+ }
+
+ var logType, logLevel int
+
+ switch options.LogType {
+ case "system":
+ logType = log.System
+ case "console":
+ logType = log.Console
+ case "file":
+ logType = log.File
+ }
+
+ switch options.DebugLevel {
+ case 0:
+ logLevel = log.Info
+ case 1:
+ logLevel = log.Crit
+ case 2:
+ logLevel = log.Err
+ case 3:
+ logLevel = log.Warning
+ case 4:
+ logLevel = log.Debug
+ case 5:
+ logLevel = log.Trace
+ }
+
+ if err := log.Open(logType, logLevel, options.LogFile, options.LogFileSize); err != nil {
+ fatalExit("cannot initialize logger", err)
+ }
+
+ stop := make(chan os.Signal, 1)
+ signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+ log.Infof("starting Zabbix web service")
+
+ go func() {
+ if err := run(); err != nil {
+ fatalExit("failed to start", err)
+ }
+ }()
+
+ <-stop
+
+ farewell := "Zabbix web service stopped."
+ log.Infof(farewell)
+
+ if options.LogType != "console" {
+ fmt.Println(farewell)
+ }
+}
+
+func run() error {
+ var h handler
+
+ var err error
+
+ if h.allowedPeers, err = zbxnet.GetAllowedPeers(options.AllowedIP); err != nil {
+ return err
+ }
+
+ http.HandleFunc("/report", h.report)
+
+ if err := validateTLSFiles(); err != nil {
+ return err
+ }
+
+ switch options.TLSAccept {
+ case "cert":
+ server, err := createTLSServer()
+ if err != nil {
+ return err
+ }
+
+ return server.ListenAndServeTLS(options.TLSCertFile, options.TLSKeyFile)
+ case "", "unencrypted":
+ return http.ListenAndServe(":"+options.ListenPort, nil)
+ }
+
+ return nil
+}
+
+func fatalExit(message string, err error) {
+ if len(message) == 0 {
+ message = err.Error()
+ } else {
+ message = fmt.Sprintf("%s: %s", message, err.Error())
+ }
+
+ if options.LogType == "file" {
+ log.Critf("%s", message)
+ }
+
+ fmt.Fprintf(os.Stderr, "zabbix_web_service [%d]: ERROR: %s\n", os.Getpid(), message)
+ os.Exit(1)
+}
+
+func validateTLSFiles() error {
+ switch options.TLSAccept {
+ case "cert":
+ if options.TLSCAFile == "" {
+ return errors.New("missing TLSCAFile configuration parameter")
+ }
+ if options.TLSCertFile == "" {
+ return errors.New("missing TLSCertFile configuration parameter")
+ }
+ if options.TLSKeyFile == "" {
+ return errors.New("missing TLSKeyFile configuration parameter")
+ }
+ case "", "unencrypted":
+ if options.TLSCAFile != "" {
+ return errors.New("TLSCAFile configuration parameter set without certificates being used")
+ }
+ if options.TLSCertFile != "" {
+ return errors.New("TLSCertFile configuration parameter set without certificates being used")
+ }
+ if options.TLSKeyFile != "" {
+ return errors.New("TLSKeyFile configuration parameter set without certificates being used")
+ }
+ default:
+ return errors.New("invalid TLSAccept configuration parameter")
+ }
+
+ return nil
+}
+
+func createTLSServer() (*http.Server, error) {
+ caCert, err := ioutil.ReadFile(options.TLSCAFile)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read CA cert file: %s", err.Error())
+ }
+
+ caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+
+ tlsConfig := &tls.Config{
+ ClientCAs: caCertPool,
+ ClientAuth: tls.RequireAndVerifyClientCert,
+ }
+
+ tlsConfig.BuildNameToCertificate()
+
+ return &http.Server{Addr: ":" + options.ListenPort, TLSConfig: tlsConfig, ErrorLog: log.DefaultLogger}, nil
+}
diff --git a/src/go/conf/zabbix_agent2.conf b/src/go/conf/zabbix_agent2.conf
index a85be944fb6..14e71a81a7e 100644
--- a/src/go/conf/zabbix_agent2.conf
+++ b/src/go/conf/zabbix_agent2.conf
@@ -25,9 +25,7 @@
#
# Mandatory: yes, if LogType is set to file, otherwise no
# Default:
-# LogFile=
-
-LogFile=/tmp/zabbix_agent2.log
+# LogFile=/tmp/zabbix_agent2.log
### Option: LogFileSize
# Maximum size of log file in MB.
@@ -550,6 +548,72 @@ ControlSocket=/tmp/agent.sock
# Default:
# Plugins.Memcached.Sessions.*.Password=
+### Option: Plugins.Modbus.Timeout
+# The maximum time (in seconds) for connections.
+#
+# Mandatory: no
+# Range: 1-30
+# Default: global timeout
+
+### Option: Plugins.Modbus.Sessions.*.Endpoint
+# Endpoint is a connection string consisting of a protocol scheme, a host address and a port or seral port name and attributes.
+#
+# Mandatory: no
+
+### Option: Plugins.Modbus.Sessions.*.SlaveID
+# Slave ID of modbus devices.
+#
+# Mandatory: no
+
+### Option: Plugins.Modbus.Sessions.*.Timeout
+# The maximum time (in seconds) for connections.
+#
+# Mandatory: no
+# Range: 1-30
+# Default: plugin modbus timeout
+
+### Option: Plugins.Mongo.Timeout
+# Amount of time to wait for a server to respond when first connecting and on
+# follow up operations in the session.
+#
+# Mandatory: no
+# Range: 1-30
+# Default:
+# Plugins.Mongo.Timeout=<Global timeout>
+
+### Option: Plugins.Mongo.KeepAlive
+# Time in seconds for waiting before unused connections will be closed.
+#
+# Mandatory: no
+# Range: 60-900
+# Default:
+# Plugins.Mongo.KeepAlive=300
+
+### Option: Plugins.Mongo.Sessions.*.Uri
+# Uri to connect. "*" should be replaced with a session name.
+#
+# Mandatory: no
+# Range:
+# Must matches the URI format.
+# The only supported schema is "tcp".
+# Embedded credentials will be ignored.
+# Default:
+# Plugins.Mongo.Sessions.*.Uri=
+
+### Option: Plugins.Mongo.Sessions.*.User
+# Username to send to protected MongoDB server. "*" should be replaced with a session name.
+#
+# Mandatory: no
+# Default:
+# Plugins.Mongo.Sessions.*.User=
+
+### Option: Plugins.Mongo.Sessions.*.Password
+# Password to send to protected MongoDB server. "*" should be replaced with a session name.
+#
+# Mandatory: no
+# Default:
+# Plugins.Mongo.Sessions.*.Password=
+
### Option: Plugins.MQTT.Timeout
# The maximum time (in seconds) for connections, disconnections and subscribtions.
#
@@ -776,30 +840,6 @@ ControlSocket=/tmp/agent.sock
# Default:
# Plugins.Redis.Sessions.*.Password=
-### Option: Plugins.Modbus.Timeout
-# The maximum time (in seconds) for connections.
-#
-# Mandatory: no
-# Range: 1-30
-# Default: global timeout
-
-### Option: Plugins.Modbus.Sessions.*.Endpoint
-# Endpoint is a connection string consisting of a protocol scheme, a host address and a port or seral port name and attributes.
-#
-# Mandatory: no
-
-### Option: Plugins.Modbus.Sessions.*.SlaveID
-# Slave ID of modbus devices.
-#
-# Mandatory: no
-
-### Option: Plugins.Modbus.Sessions.*.Timeout
-# The maximum time (in seconds) for connections.
-#
-# Mandatory: no
-# Range: 1-30
-# Default: plugin modbus timeout
-
### Option: Plugins.Smart.Timeout
# The maximum time in seconds for waiting before smartctl execution is terminated.
# The timeout is for a single smartctl command line execution.
diff --git a/src/go/conf/zabbix_agent2.win.conf b/src/go/conf/zabbix_agent2.win.conf
index c6a5190fbe3..b935b109809 100644
--- a/src/go/conf/zabbix_agent2.win.conf
+++ b/src/go/conf/zabbix_agent2.win.conf
@@ -18,9 +18,7 @@
#
# Mandatory: yes, if LogType is set to file, otherwise no
# Default:
-# LogFile=
-
-LogFile=c:\zabbix_agent2.log
+# LogFile=c:\zabbix_agent2.log
### Option: LogFileSize
# Maximum size of log file in MB.
@@ -535,6 +533,48 @@ ControlSocket=\\.\pipe\agent.sock
# Default:
# Plugins.Memcached.Sessions.*.Password=
+### Option: Plugins.Mongo.Timeout
+# Amount of time to wait for a server to respond when first connecting and on
+# follow up operations in the session.
+#
+# Mandatory: no
+# Range: 1-30
+# Default:
+# Plugins.Mongo.Timeout=<Global timeout>
+
+### Option: Plugins.Mongo.KeepAlive
+# Time in seconds for waiting before unused connections will be closed.
+#
+# Mandatory: no
+# Range: 60-900
+# Default:
+# Plugins.Mongo.KeepAlive=300
+
+### Option: Plugins.Mongo.Sessions.*.Uri
+# Uri to connect. "*" should be replaced with a session name.
+#
+# Mandatory: no
+# Range:
+# Must matches the URI format.
+# The only supported schema is "tcp".
+# Embedded credentials will be ignored.
+# Default:
+# Plugins.Mongo.Sessions.*.Uri=
+
+### Option: Plugins.Mongo.Sessions.*.User
+# Username to send to protected MongoDB server. "*" should be replaced with a session name.
+#
+# Mandatory: no
+# Default:
+# Plugins.Mongo.Sessions.*.User=
+
+### Option: Plugins.Mongo.Sessions.*.Password
+# Password to send to protected MongoDB server. "*" should be replaced with a session name.
+#
+# Mandatory: no
+# Default:
+# Plugins.Mongo.Sessions.*.Password=
+
### Option: Plugins.Mysql.CallTimeout
# The maximum time in seconds for waiting when a request has to be done.
#
diff --git a/src/go/conf/zabbix_web_service.conf b/src/go/conf/zabbix_web_service.conf
new file mode 100644
index 00000000000..97a826b62ab
--- /dev/null
+++ b/src/go/conf/zabbix_web_service.conf
@@ -0,0 +1,106 @@
+# This is a configuration file for Zabbix web_service
+# To get more information about Zabbix, visit http://www.zabbix.com
+
+############ GENERAL PARAMETERS #################
+
+### Option: LogType
+# Specifies where log messages are written to:
+# system - syslog
+# file - file specified with LogFile parameter
+# console - standard output
+#
+# Mandatory: no
+# Default:
+# LogType=file
+
+### Option: LogFile
+# Log file name for LogType 'file' parameter.
+#
+# Mandatory: yes, if LogType is set to file, otherwise no
+# Default:
+# LogFile=/tmp/zabbix_web_service.log
+
+### Option: LogFileSize
+# Maximum size of log file in MB.
+# 0 - disable automatic log rotation.
+#
+# Mandatory: no
+# Range: 0-1024
+# Default:
+# LogFileSize=1
+
+### Option: DebugLevel
+# Specifies debug level:
+# 0 - basic information about starting and stopping of Zabbix processes
+# 1 - critical information
+# 2 - error information
+# 3 - warnings
+# 4 - for debugging (produces lots of information)
+# 5 - extended debugging (produces even more information)
+#
+# Mandatory: no
+# Range: 0-5
+# Default:
+# DebugLevel=3
+
+### Option: AllowedIP
+# List of comma delimited IP addresses, optionally in CIDR notation, or DNS names of Zabbix servers and Zabbix proxies.
+# Incoming connections will be accepted only from the hosts listed here.
+# If IPv6 support is enabled then '127.0.0.1', '::127.0.0.1', '::ffff:127.0.0.1' are treated equally
+# and '::/0' will allow any IPv4 or IPv6 address.
+# '0.0.0.0/0' can be used to allow any IPv4 address.
+# Example: AllowedIP=127.0.0.1,192.168.1.0/24,::1,2001:db8::/32,zabbix.example.com
+#
+# Mandatory: yes
+# Default:
+# AllowedIP=
+
+AllowedIP=127.0.0.1,::1
+
+### Option: ListenPort
+# Service will listen on this port for connections from the server.
+#
+# Mandatory: no
+# Range: 1024-32767
+# Default:
+# ListenPort=10053
+
+### Option: Timeout
+# Spend no more than Timeout seconds on processing
+#
+# Mandatory: no
+# Range: 1-30
+# Default:
+# Timeout=3
+
+### Option: TLSAccept
+# What incoming connections to accept.
+# Specifies what type of connection to use:
+# unencrypted - accept connections without encryption
+# cert - accept connections secured with TLS and a certificate
+#
+# Mandatory: no
+# Default:
+# TLSAccept=unencrypted
+
+### Option: TLSCAFile
+# Full pathname of a file containing the top-level CA(s) certificates for
+# peer certificate verification.
+#
+# Mandatory: no
+# Default:
+# TLSCAFile=
+
+### Option: TLSCertFile
+# Full pathname of a file containing the service certificate or certificate chain.
+#
+# Mandatory: no
+# Default:
+# TLSCertFile=
+
+### Option: TLSKeyFile
+# Full pathname of a file containing the service private key.
+#
+# Mandatory: no
+# Default:
+# TLSKeyFile=
diff --git a/src/go/go.mod b/src/go/go.mod
index 24eb35b8554..b6a06d05ea5 100644
--- a/src/go/go.mod
+++ b/src/go/go.mod
@@ -4,6 +4,8 @@ go 1.13
require (
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
+ github.com/chromedp/cdproto v0.0.0-20210104223854-2cc87dae3ee3
+ github.com/chromedp/chromedp v0.6.0
github.com/dustin/gomemcached v0.0.0-20160817010731-a2284a01c143
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/fsnotify/fsnotify v1.4.9
@@ -20,10 +22,11 @@ require (
github.com/memcachier/mc/v3 v3.0.1
github.com/natefinch/npipe v0.0.0-20160621034901-c1b8fa8bdcce
github.com/omeid/go-yarn v0.0.1
- github.com/pkg/errors v0.9.1
- golang.org/x/sys v0.0.0-20200428200454-593003d681fa
+ github.com/pkg/errors v0.9.1 // indirect
+ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
+ gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
)
diff --git a/src/go/go.sum b/src/go/go.sum
index 557c8d15fa7..49862de5099 100644
--- a/src/go/go.sum
+++ b/src/go/go.sum
@@ -1,6 +1,12 @@
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU=
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/chromedp/cdproto v0.0.0-20210104223854-2cc87dae3ee3 h1:XeGYLuu3Yu3/2/FLDXyObe6lBYtUFDTJgjjNPcfcU40=
+github.com/chromedp/cdproto v0.0.0-20210104223854-2cc87dae3ee3/go.mod h1:55pim6Ht4LJKdVLlyFJV/g++HsEA1hQxPbB5JyNdZC0=
+github.com/chromedp/chromedp v0.6.0 h1:jjzHzXW5pNdKt1D9cEDAKZM/yZ2EwL/hLyGbCUFldBI=
+github.com/chromedp/chromedp v0.6.0/go.mod h1:Yay7TUDCNOQBK8EJDUon6AUaQI12VEBOuULcGtY4uDY=
+github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
+github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@@ -28,6 +34,12 @@ github.com/goburrow/modbus v0.1.0 h1:DejRZY73nEM6+bt5JSP6IsFolJ9dVcqxsYbpLbeW/ro
github.com/goburrow/modbus v0.1.0/go.mod h1:Kx552D5rLIS8E7TyUwQ/UdHEqvX5T8tyiGBTlzMcZBg=
github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA=
+github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
+github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
+github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
+github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
+github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godror/godror v0.20.1 h1:s/ehD65nfVzWR2MrZGChDkLvVPlIVxbt+Jpzfwkl1c8=
@@ -94,6 +106,8 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.2-0.20200821025810-91d0159cc97a h1:ec2LCBkfN1pOq0PhLRH/QitjSXr9s2dnh0gOFyohxHM=
github.com/jackc/puddle v1.1.2-0.20200821025810-91d0159cc97a/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
@@ -110,6 +124,8 @@ github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -193,8 +209,8 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200428200454-593003d681fa h1:yMbJOvnfYkO1dSAviTu/ZguZWLBTXx4xE3LYrxUCCiA=
-golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad h1:MCsdmFSdEd4UEa5TKS5JztCRHK/WtvNei1edOj5RSRo=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -223,6 +239,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/src/go/internal/agent/options_windows.go b/src/go/internal/agent/options_windows.go
index fd91c4e9154..99b637dcc24 100644
--- a/src/go/internal/agent/options_windows.go
+++ b/src/go/internal/agent/options_windows.go
@@ -21,7 +21,7 @@ package agent
type AgentOptions struct {
LogType string `conf:"optional,default=file"`
- LogFile string `conf:"optional,default=/tmp/zabbix_agent2.log"`
+ LogFile string `conf:"optional,default=c:\\zabbix_agent2.log"`
LogFileSize int `conf:"optional,range=0:1024,default=1"`
DebugLevel int `conf:"optional,range=0:5,default=3"`
PidFile string `conf:"optional"`
diff --git a/src/go/internal/agent/plugin_userparameter_test.go b/src/go/internal/agent/plugin_userparameter_test.go
index bffc31594ea..720fa29cc12 100644
--- a/src/go/internal/agent/plugin_userparameter_test.go
+++ b/src/go/internal/agent/plugin_userparameter_test.go
@@ -50,7 +50,7 @@ func TestUserParameterPlugin(t *testing.T) {
t.Run(results[i].data[0], func(t *testing.T) {
plugin.Metrics = make(map[string]*plugin.Metric)
- if err := InitUserParameterPlugin(results[i].data, results[i].unsafeUserParameters); err != nil {
+ if err := InitUserParameterPlugin(results[i].data, results[i].unsafeUserParameters, ""); err != nil {
if !results[i].failed {
t.Errorf("Expected success while got error %s", err)
}
@@ -209,7 +209,7 @@ func TestCmd(t *testing.T) {
t.Run(resultsCmd[i].data[0], func(t *testing.T) {
plugin.Metrics = make(map[string]*plugin.Metric)
- if err := InitUserParameterPlugin(resultsCmd[i].data, resultsCmd[i].unsafeUserParameters); err != nil {
+ if err := InitUserParameterPlugin(resultsCmd[i].data, resultsCmd[i].unsafeUserParameters, ""); err != nil {
t.Errorf("Plugin init failed: %s", err)
}
@@ -238,7 +238,7 @@ func TestUnsafeCmd(t *testing.T) {
t.Run("", func(t *testing.T) {
plugin.Metrics = make(map[string]*plugin.Metric)
- if err := InitUserParameterPlugin([]string{"a[*],echo $1"}, 0); err != nil {
+ if err := InitUserParameterPlugin([]string{"a[*],echo $1"}, 0, ""); err != nil {
t.Errorf("Plugin init failed: %s", err)
}
@@ -251,7 +251,7 @@ func TestUnsafeCmd(t *testing.T) {
plugin.Metrics = make(map[string]*plugin.Metric)
- if err := InitUserParameterPlugin([]string{"a[*],echo $1"}, 1); err != nil {
+ if err := InitUserParameterPlugin([]string{"a[*],echo $1"}, 1, ""); err != nil {
t.Errorf("Plugin init failed: %s", err)
}
diff --git a/src/go/internal/agent/serverlistener/serverlistener.go b/src/go/internal/agent/serverlistener/serverlistener.go
index 48f9c1eda2d..fbfd6fd6ccc 100644
--- a/src/go/internal/agent/serverlistener/serverlistener.go
+++ b/src/go/internal/agent/serverlistener/serverlistener.go
@@ -31,6 +31,7 @@ import (
"zabbix.com/pkg/log"
"zabbix.com/pkg/tls"
"zabbix.com/pkg/zbxcomms"
+ "zabbix.com/pkg/zbxnet"
)
type ServerListener struct {
@@ -39,7 +40,7 @@ type ServerListener struct {
scheduler scheduler.Scheduler
options *agent.AgentOptions
tlsConfig *tls.Config
- allowedPeers *AllowedPeers
+ allowedPeers *zbxnet.AllowedPeers
bindIP string
}
@@ -101,7 +102,7 @@ func (sl *ServerListener) Start() (err error) {
if sl.tlsConfig, err = agent.GetTLSConfig(sl.options); err != nil {
return
}
- if sl.allowedPeers, err = GetAllowedPeers(sl.options); err != nil {
+ if sl.allowedPeers, err = zbxnet.GetAllowedPeers(sl.options.Server); err != nil {
return
}
if sl.listener, err = zbxcomms.Listen(fmt.Sprintf("%s:%d", sl.bindIP, sl.options.ListenPort), sl.tlsConfig); err != nil {
diff --git a/src/go/pkg/log/log.go b/src/go/pkg/log/log.go
index 35bcd62a509..9a80eec8bcf 100644
--- a/src/go/pkg/log/log.go
+++ b/src/go/pkg/log/log.go
@@ -47,8 +47,10 @@ const Console = 3
const MB = 1048576
+//DefaultLogger is the default Zabbix agent 2 and Zabbix web service logger.
+var DefaultLogger *log.Logger
+
var logLevel int
-var logger *log.Logger
type LogStat struct {
logType int
@@ -118,13 +120,13 @@ func Open(logType int, level int, filename string, filesize int) error {
return err
}
case Console:
- logger = log.New(os.Stdout, "", log.Lmicroseconds|log.Ldate)
+ DefaultLogger = log.New(os.Stdout, "", log.Lmicroseconds|log.Ldate)
case File:
logStat.f, err = os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
- logger = log.New(logStat.f, "", log.Lmicroseconds|log.Ldate)
+ DefaultLogger = log.New(logStat.f, "", log.Lmicroseconds|log.Ldate)
default:
return errors.New("invalid argument")
}
@@ -178,7 +180,7 @@ func procLog(format string, args []interface{}, level int) {
logAccess.Lock()
defer logAccess.Unlock()
rotateLog()
- logger.Printf(format, args...)
+ DefaultLogger.Printf(format, args...)
}
func rotateLog() {
@@ -210,11 +212,11 @@ func rotateLog() {
panic(errmsg)
}
- logger = log.New(logStat.f, "", log.Lmicroseconds|log.Ldate)
+ DefaultLogger = log.New(logStat.f, "", log.Lmicroseconds|log.Ldate)
if printError != "" {
- logger.Printf("cannot rename log file \"%s\" to \"%s\":%s\n",
+ DefaultLogger.Printf("cannot rename log file \"%s\" to \"%s\":%s\n",
logStat.filename, filenameOld, printError)
- logger.Printf("Logfile \"%s\" size reached configured limit LogFileSize but"+
+ DefaultLogger.Printf("Logfile \"%s\" size reached configured limit LogFileSize but"+
" moving it to \"%s\" failed. The logfile was truncated.",
logStat.filename, filenameOld)
}
diff --git a/src/go/pkg/version/version.go b/src/go/pkg/version/version.go
index daa87695489..e2e6ee9215d 100644
--- a/src/go/pkg/version/version.go
+++ b/src/go/pkg/version/version.go
@@ -23,17 +23,14 @@ package version
import (
"fmt"
"strings"
-
- "zabbix.com/pkg/tls"
)
const (
- APPLICATION_NAME = "Zabbix Agent"
- ZABBIX_REVDATE = "1 March 2021"
+ ZABBIX_REVDATE = "26 April 2021"
ZABBIX_VERSION_MAJOR = 5
ZABBIX_VERSION_MINOR = 4
ZABBIX_VERSION_PATCH = 0
- ZABBIX_VERSION_RC = "beta2"
+ ZABBIX_VERSION_RC = "rc1"
ZABBIX_VERSION_RC_NUM = "{ZABBIX_RC_NUM}"
ZABBIX_VERSION_REVISION = "{ZABBIX_REVISION}"
copyrightMessage = "Copyright (C) 2021 Zabbix SIA\n" +
@@ -43,17 +40,15 @@ const (
)
var (
- titleMessage string = "{undefined}"
- compileDate string = "{undefined}"
- compileTime string = "{undefined}"
- compileOs string = "{undefined}"
- compileArch string = "{undefined}"
- compileMode string
+ titleMessage string = "{undefined}"
+ compileDate string = "{undefined}"
+ compileTime string = "{undefined}"
+ compileOs string = "{undefined}"
+ compileArch string = "{undefined}"
+ compileMode string
+ extraLicenses []string
)
-func ApplicationName() string {
- return APPLICATION_NAME
-}
func RevDate() string {
return ZABBIX_REVDATE
}
@@ -98,28 +93,14 @@ func Revision() string {
return ZABBIX_VERSION_REVISION
}
-func copyrightMessageMQTT() string {
- return "\nWe use the library Eclipse Paho (eclipse/paho.mqtt.golang), which is\n" +
- "distributed under the terms of the Eclipse Distribution License 1.0 (The 3-Clause BSD License)\n" +
- "available at https://www.eclipse.org/org/documents/edl-v10.php\n"
-}
-
-func copyrightMessageModbus() string {
- return "\nWe use the library go-modbus (goburrow/modbus), which is\n" +
- "distributed under the terms of the 3-Clause BSD License\n" +
- "available at https://github.com/goburrow/modbus/blob/master/LICENSE\n"
-}
-
func CopyrightMessage() string {
msg := copyrightMessage
- tlsMsg := tls.CopyrightMessage()
- if tlsMsg == "" {
- msg += "\n"
- } else {
- msg += tlsMsg
+
+ for _, license := range extraLicenses {
+ msg += license
}
- return msg + copyrightMessageModbus() + copyrightMessageMQTT()
+ return msg
}
func CompileDate() string {
@@ -164,3 +145,12 @@ func Display() {
fmt.Printf("Revision %s %s, compilation time: %s %s\n\n", Revision(), RevDate(), CompileDate(), CompileTime())
fmt.Println(CopyrightMessage())
}
+
+func Init(title string, extra ...string) {
+ titleMessage = title
+ extraLicenses = append(extraLicenses, extra...)
+}
+
+func init() {
+ extraLicenses = make([]string, 0)
+}
diff --git a/src/go/pkg/zbxlib/globals_windows.go b/src/go/pkg/zbxlib/globals_windows.go
index c6613a47aea..9d863168324 100644
--- a/src/go/pkg/zbxlib/globals_windows.go
+++ b/src/go/pkg/zbxlib/globals_windows.go
@@ -31,6 +31,7 @@ package zbxlib
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/str.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/file.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/alias.o
+#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/time.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/fatal.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/disk.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/threads.o
diff --git a/src/go/internal/agent/serverlistener/allowedpeers.go b/src/go/pkg/zbxnet/allowedpeers.go
index 292e27ba87a..bf2981002a7 100644
--- a/src/go/internal/agent/serverlistener/allowedpeers.go
+++ b/src/go/pkg/zbxnet/allowedpeers.go
@@ -17,13 +17,11 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-package serverlistener
+package zbxnet
import (
"net"
"strings"
-
- "zabbix.com/internal/agent"
)
// AllowedPeers is preparsed content of field Server
@@ -34,11 +32,11 @@ type AllowedPeers struct {
}
// GetAllowedPeers is parses the Server field
-func GetAllowedPeers(options *agent.AgentOptions) (allowedPeers *AllowedPeers, err error) {
+func GetAllowedPeers(servers string) (allowedPeers *AllowedPeers, err error) {
ap := &AllowedPeers{}
- if options.Server != "" {
- opts := strings.Split(options.Server, ",")
+ if servers != "" {
+ opts := strings.Split(servers, ",")
for _, o := range opts {
peer := strings.Trim(o, " \t")
if _, peerNet, err := net.ParseCIDR(peer); nil == err {
diff --git a/src/go/internal/agent/serverlistener/allowedpeers_test.go b/src/go/pkg/zbxnet/allowedpeers_test.go
index 3e5503a7fab..b0035162371 100644
--- a/src/go/internal/agent/serverlistener/allowedpeers_test.go
+++ b/src/go/pkg/zbxnet/allowedpeers_test.go
@@ -17,7 +17,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-package serverlistener
+package zbxnet
import (
"fmt"
@@ -128,7 +128,7 @@ func (tc *testCase) checkResult() error {
options.Server = tc.allowed
peerip := net.ParseIP(tc.peer)
- if ap, err := GetAllowedPeers(&options); err == nil {
+ if ap, err := GetAllowedPeers(options.Server); err == nil {
allow := ap.CheckPeer(peerip)
if allow != true && tc.fail == false {
diff --git a/src/go/plugins/mongodb/README.md b/src/go/plugins/mongodb/README.md
new file mode 100644
index 00000000000..eba29e9f4cd
--- /dev/null
+++ b/src/go/plugins/mongodb/README.md
@@ -0,0 +1,143 @@
+# MongoDB plugin
+Provides native Zabbix solution for monitoring MongoDB servers and clusters (document-based, distributed database).
+It can monitor several MongoDB instances simultaneously, remotes or locals to the Zabbix Agent.
+The plugin keeps connections in the opened state to reduce network
+congestion, latency, CPU and memory usage. Best for use in conjunction with the official
+[MongoDB template.](https://git.zabbix.com/projects/ZBX/repos/zabbix/browse/templates/app/mongodb)
+You can extend it or create your template for your specific needs.
+
+## Requirements
+* Zabbix Agent 2
+* Go >= 1.13 (required only to build from the source)
+
+## Supported versions
+* MongoDB, versions 4.4, 4.2, 4.0 and 3.6
+
+## Installation
+Depending on your configuration you need to create a local read-only user in the admin database:
+- *STANDALONE*: for each single MongoDB node.
+- *REPLICASET*: create the user on the primary node of the replica set.
+- *SHARDING*: for each shard in your cluster (just create the user on the primary node of the replica set).
+Also, create the same user on a mongos router. It will automatically spread to config servers.
+
+```javascript
+use admin
+
+db.auth("admin", "<ADMIN_PASSWORD>")
+
+db.createUser({
+ "user": "zabbix",
+ "pwd": "<PASSWORD>",
+ "roles": [
+ { role: "readAnyDatabase", db: "admin" },
+ { role: "clusterMonitor", db: "admin" },
+ ]
+})
+```
+
+## Configuration
+The Zabbix Agent's configuration file is used to configure plugins.
+
+**Plugins.Mongo.KeepAlive** — Sets a time for waiting before unused connections will be closed.
+*Default value:* 300 sec.
+*Limits:* 60-900
+
+**Plugins.Mongo.Timeout** — The amount of time to wait for a server to respond when first connecting and on follow up
+operations in the session.
+*Default value:* equals the global Timeout configuration parameter.
+*Limits:* 1-30
+
+### Configuring connection
+A connection can be configured using either keys' parameters or named sessions.
+
+*Notes*:
+* It is not possible to mix configuration using named sessions and keys' parameters simultaneously.
+* You can leave any connection parameter empty, a default hard-coded value will be used in the such case:
+ localhost:27017 without authentication.
+* Embedded URI credentials (userinfo) are forbidden and will be ignored. So, you can't pass the credentials by this:
+
+ mongodb.ping[tcp://user:password@127.0.0.1] — WRONG
+
+ The correct way is:
+
+ mongodb.ping[tcp://127.0.0.1,user,password]
+
+* Currently, only TCP connections supported.
+
+Examples of valid URIs:
+ - tcp://127.0.0.1:27017
+ - tcp://localhost
+ - localhost
+
+#### Using keys' parameters
+The common parameters for all keys are: [ConnString][,User][,Password]
+Where ConnString can be either a URI or session name.
+ConnString will be treated as a URI if no session with the given name found.
+If you use ConnString as a session name, just skip the rest of the connection parameters.
+
+#### Using named sessions
+Named sessions allow you to define specific parameters for each MongoDB instance. Currently, there are only three supported
+parameters: Uri, User and Password. It's a bit more secure way to store credentials compared to item keys or macros.
+
+E.g: suppose you have two MongoDB instances: "Prod" and "Test".
+You should add the following options to the agent configuration file:
+
+ Plugins.Mongo.Sessions.Prod.Uri=tcp://192.168.1.1:27017
+ Plugins.Mongo.Sessions.Prod.User=<UserForProd>
+ Plugins.Mongo.Sessions.Prod.Password=<PasswordForProd>
+
+ Plugins.Mongo.Sessions.Test.Uri=tcp://192.168.0.1:27017
+ Plugins.Mongo.Sessions.Test.User=<UserForTest>
+ Plugins.Mongo.Sessions.Test.Password=<PasswordForTest>
+
+Then you will be able to use these names as the 1st parameter (ConnString) in keys instead of URIs, e.g:
+
+ mongodb.ping[Prod]
+ mongodb.ping[Test]
+
+*Note*: sessions names are case-sensitive.
+
+## Supported keys
+**mongodb.collection.stats[\<commonParams\>[,database],collection]** — Returns a variety of storage statistics for a
+given collection.
+*Parameters:*
+database — database name (default: admin).
+collection (required) — collection name.
+
+**mongodb.cfg.discovery[\<commonParams\>]** — Returns a list of discovered config servers.
+
+**mongodb.collections.discovery[\<commonParams\>]** — Returns a list of discovered collections.
+
+**mongodb.collections.usage[\<commonParams\>]** — Returns usage statistics for collections.
+
+**mongodb.connpool.stats[\<commonParams\>]** — Returns information regarding the open outgoing connections from the
+current database instance to other members of the sharded cluster or replica set.
+
+**mongodb.db.stats[\<commonParams\>[,database]]** — Returns statistics reflecting a given database system’s state.
+*Parameters:*
+database — database name (default: admin).
+
+**mongodb.db.discovery[\<commonParams\>]** — Returns a list of discovered databases.
+
+**mongodb.jumbo_chunks.count[\<commonParams\>]** — Returns count of jumbo chunks.
+
+**mongodb.oplog.stats[\<commonParams\>]** — Returns a status of the replica set, using data polled from the oplog.
+
+**mongodb.ping[\<commonParams\>]** — Tests if a connection is alive or not.
+*Returns:*
+- "1" if a connection is alive.
+- "0" if a connection is broken (if there is any error presented including AUTH and configuration issues).
+
+**mongodb.rs.config[\<commonParams\>]** — Returns a current configuration of the replica set.
+
+**mongodb.rs.status[\<commonParams\>]** — Returns a replica set status from the point of view of the member
+where the method is run.
+
+**mongodb.server.status[\<commonParams\>]** — Returns a database’s state.
+
+**mongodb.sh.discovery[\<commonParams\>]** — Returns a list of discovered shards present in the cluster.
+
+## Troubleshooting
+The plugin uses Zabbix agent's logs. You can increase debugging level of Zabbix Agent if you need more details about
+what is happening.
+Set the DebugLevel configuration option to "5" (extended debugging) in order to turn on verbose log messages for the MGO package.
diff --git a/src/go/plugins/mongodb/config.go b/src/go/plugins/mongodb/config.go
new file mode 100644
index 00000000000..55a3b9c901c
--- /dev/null
+++ b/src/go/plugins/mongodb/config.go
@@ -0,0 +1,57 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "zabbix.com/pkg/conf"
+ "zabbix.com/pkg/plugin"
+)
+
+type PluginOptions struct {
+ // Timeout is the amount of time to wait for a server to respond when
+ // first connecting and on follow up operations in the session.
+ Timeout int `conf:"optional,range=1:30"`
+
+ // KeepAlive is a time to wait before unused connections will be closed.
+ KeepAlive int `conf:"optional,range=60:900,default=300"`
+
+ // Sessions stores pre-defined named sets of connections settings.
+ Sessions map[string]conf.Session `conf:"optional"`
+}
+
+// Configure implements the Configurator interface.
+// Initializes configuration structures.
+func (p *Plugin) Configure(global *plugin.GlobalOptions, options interface{}) {
+ if err := conf.Unmarshal(options, &p.options); err != nil {
+ p.Errf("cannot unmarshal configuration options: %s", err)
+ }
+
+ if p.options.Timeout == 0 {
+ p.options.Timeout = global.Timeout
+ }
+}
+
+// Validate implements the Configurator interface.
+// Returns an error if validation of a plugin's configuration is failed.
+func (p *Plugin) Validate(options interface{}) error {
+ var opts PluginOptions
+
+ return conf.Unmarshal(options, &opts)
+}
diff --git a/src/go/plugins/mongodb/conn.go b/src/go/plugins/mongodb/conn.go
new file mode 100644
index 00000000000..882aae1a63f
--- /dev/null
+++ b/src/go/plugins/mongodb/conn.go
@@ -0,0 +1,293 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "context"
+ "sync"
+ "time"
+
+ "gopkg.in/mgo.v2/bson"
+
+ "gopkg.in/mgo.v2"
+ "zabbix.com/pkg/log"
+ "zabbix.com/pkg/uri"
+)
+
+type MongoConn struct {
+ addr string
+ timeout time.Duration
+ lastTimeAccess time.Time
+ session *mgo.Session
+}
+
+// DB shadows *mgo.DB to returns a Database interface instead of *mgo.Database.
+func (conn *MongoConn) DB(name string) Database {
+ conn.checkConnection()
+
+ return &MongoDatabase{Database: conn.session.DB(name)}
+}
+
+func (conn *MongoConn) DatabaseNames() (names []string, err error) {
+ conn.checkConnection()
+
+ return conn.session.DatabaseNames()
+}
+
+func (conn *MongoConn) Ping() error {
+ return conn.session.DB("admin").Run(&bson.D{
+ bson.DocElem{
+ Name: "ping",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: conn.GetMaxTimeMS(),
+ },
+ }, nil)
+}
+
+func (conn *MongoConn) GetMaxTimeMS() int64 {
+ return conn.timeout.Milliseconds()
+}
+
+// updateAccessTime updates the last time a connection was accessed.
+func (conn *MongoConn) updateAccessTime() {
+ conn.lastTimeAccess = time.Now()
+}
+
+// checkConnection implements db reconnection.
+func (conn *MongoConn) checkConnection() {
+ if err := conn.Ping(); err != nil {
+ conn.session.Refresh()
+ log.Debugf("[%s] Attempt to reconnect: %s", pluginName, conn.addr)
+ }
+}
+
+// Session is an interface to access to the session struct.
+type Session interface {
+ DB(name string) Database
+ DatabaseNames() (names []string, err error)
+ GetMaxTimeMS() int64
+ Ping() error
+}
+
+// Database is an interface to access to the database struct.
+type Database interface {
+ C(name string) Collection
+ CollectionNames() (names []string, err error)
+ Run(cmd, result interface{}) error
+}
+
+// MongoDatabase wraps a mgo.Database to embed methods in models.
+type MongoDatabase struct {
+ *mgo.Database
+}
+
+// C shadows *mgo.DB to returns a Database interface instead of *mgo.Database.
+func (d *MongoDatabase) C(name string) Collection {
+ return &MongoCollection{Collection: d.Database.C(name)}
+}
+
+func (d *MongoDatabase) CollectionNames() (names []string, err error) {
+ return d.Database.CollectionNames()
+}
+
+// Run shadows *mgo.DB to returns a Database interface instead of *mgo.Database.
+func (d *MongoDatabase) Run(cmd, result interface{}) error {
+ return d.Database.Run(cmd, result)
+}
+
+// MongoCollection wraps a mgo.Collection to embed methods in models.
+type MongoCollection struct {
+ *mgo.Collection
+}
+
+// Collection is an interface to access to the collection struct.
+type Collection interface {
+ Find(query interface{}) Query
+}
+
+// Find shadows *mgo.Collection to returns a Query interface instead of *mgo.Query.
+func (c *MongoCollection) Find(query interface{}) Query {
+ return &MongoQuery{Query: c.Collection.Find(query)}
+}
+
+// Query is an interface to access to the query struct
+type Query interface {
+ All(result interface{}) error
+ Count() (n int, err error)
+ Limit(n int) Query
+ One(result interface{}) error
+ SetMaxTime(d time.Duration) Query
+ Sort(fields ...string) Query
+}
+
+// MongoQuery wraps a mgo.Query to embed methods in models.
+type MongoQuery struct {
+ *mgo.Query
+}
+
+func (q *MongoQuery) Limit(n int) Query {
+ q.Query.Limit(n)
+ return q
+}
+
+func (q *MongoQuery) SetMaxTime(d time.Duration) Query {
+ q.Query.SetMaxTime(d)
+ return q
+}
+
+func (q *MongoQuery) Sort(fields ...string) Query {
+ q.Query.Sort(fields...)
+ return q
+}
+
+// ConnManager is thread-safe structure for manage connections.
+type ConnManager struct {
+ sync.Mutex
+ connMutex sync.Mutex
+ connections map[uri.URI]*MongoConn
+ keepAlive time.Duration
+ timeout time.Duration
+ Destroy context.CancelFunc
+}
+
+// NewConnManager initializes connManager structure and runs Go Routine that watches for unused connections.
+func NewConnManager(keepAlive, timeout, hkInterval time.Duration) *ConnManager {
+ ctx, cancel := context.WithCancel(context.Background())
+
+ connMgr := &ConnManager{
+ connections: make(map[uri.URI]*MongoConn),
+ keepAlive: keepAlive,
+ timeout: timeout,
+ Destroy: cancel, // Destroy stops originated goroutines and close connections.
+ }
+
+ go connMgr.housekeeper(ctx, hkInterval)
+
+ return connMgr
+}
+
+// closeUnused closes each connection that has not been accessed at least within the keepalive interval.
+func (c *ConnManager) closeUnused() {
+ c.connMutex.Lock()
+ defer c.connMutex.Unlock()
+
+ for uri, conn := range c.connections {
+ if time.Since(conn.lastTimeAccess) > c.keepAlive {
+ conn.session.Close()
+ delete(c.connections, uri)
+ log.Debugf("[%s] Closed unused connection: %s", pluginName, uri.Addr())
+ }
+ }
+}
+
+// closeAll closes all existed connections.
+func (c *ConnManager) closeAll() {
+ c.connMutex.Lock()
+ for uri, conn := range c.connections {
+ conn.session.Close()
+ delete(c.connections, uri)
+ }
+ c.connMutex.Unlock()
+}
+
+// housekeeper repeatedly checks for unused connections and close them.
+func (c *ConnManager) housekeeper(ctx context.Context, interval time.Duration) {
+ ticker := time.NewTicker(interval)
+
+ for {
+ select {
+ case <-ctx.Done():
+ ticker.Stop()
+ c.closeAll()
+
+ return
+ case <-ticker.C:
+ c.closeUnused()
+ }
+ }
+}
+
+// create creates a new connection with given credentials.
+func (c *ConnManager) create(uri uri.URI) (*MongoConn, error) {
+ c.connMutex.Lock()
+ defer c.connMutex.Unlock()
+
+ if _, ok := c.connections[uri]; ok {
+ // Should never happen.
+ panic("connection already exists")
+ }
+
+ session, err := mgo.DialWithInfo(&mgo.DialInfo{
+ Addrs: []string{uri.Addr()},
+ Direct: true,
+ FailFast: false,
+ Password: uri.Password(),
+ PoolLimit: 1,
+ Timeout: c.timeout,
+ Username: uri.User(),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Read from one of the nearest members, irrespective of it being primary or secondary.
+ session.SetMode(mgo.Nearest, true)
+
+ c.connections[uri] = &MongoConn{
+ addr: uri.Addr(),
+ timeout: c.timeout,
+ lastTimeAccess: time.Now(),
+ session: session,
+ }
+
+ log.Debugf("[%s] Created new connection: %s", pluginName, uri.Addr())
+
+ return c.connections[uri], nil
+}
+
+// get returns a connection with given uri if it exists and also updates lastTimeAccess, otherwise returns nil.
+func (c *ConnManager) get(uri uri.URI) *MongoConn {
+ c.connMutex.Lock()
+ defer c.connMutex.Unlock()
+
+ if conn, ok := c.connections[uri]; ok {
+ conn.updateAccessTime()
+ return conn
+ }
+
+ return nil
+}
+
+// GetConnection returns an existing connection or creates a new one.
+func (c *ConnManager) GetConnection(uri uri.URI) (conn *MongoConn, err error) {
+ c.Lock()
+ defer c.Unlock()
+
+ conn = c.get(uri)
+
+ if conn == nil {
+ conn, err = c.create(uri)
+ }
+
+ return
+}
diff --git a/src/go/plugins/mongodb/handler_collection_stats.go b/src/go/plugins/mongodb/handler_collection_stats.go
new file mode 100644
index 00000000000..1c27dfc7b41
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_collection_stats.go
@@ -0,0 +1,54 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+// collectionStatsHandler
+// https://docs.mongodb.com/manual/reference/command/collStats/index.html
+func collectionStatsHandler(s Session, params map[string]string) (interface{}, error) {
+ colStats := &bson.M{}
+ err := s.DB(params["Database"]).Run(&bson.D{
+ bson.DocElem{
+ Name: "collStats",
+ Value: params["Collection"],
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, colStats)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ jsonRes, err := json.Marshal(colStats)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_collection_stats_test.go b/src/go/plugins/mongodb/handler_collection_stats_test.go
new file mode 100644
index 00000000000..5aeaca5ac3f
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_collection_stats_test.go
@@ -0,0 +1,84 @@
+package mongodb
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "log"
+ "reflect"
+ "strings"
+ "testing"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+func Test_collectionStatsHandler(t *testing.T) {
+ var testData map[string]interface{}
+
+ jsonData, err := ioutil.ReadFile("testdata/collStats.json")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(jsonData, &testData)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ mockSession := NewMockConn()
+ db := mockSession.DB("MyDatabase")
+ db.(*MockMongoDatabase).RunFunc = func(dbName, cmd string) ([]byte, error) {
+ if cmd == "collStats" {
+ return bson.Marshal(testData)
+ }
+
+ return nil, errors.New("no such cmd: " + cmd)
+ }
+
+ type args struct {
+ s Session
+ params map[string]string
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must parse an output of \" + collStats + \"command",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": "MyDatabase", "Collection": "MyCollection"},
+ },
+ want: strings.TrimSpace(string(jsonData)),
+ wantErr: nil,
+ },
+ {
+ name: "Must catch DB.Run() error",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": mustFail},
+ },
+ want: nil,
+ wantErr: zbxerr.ErrorCannotFetchData,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := collectionStatsHandler(tt.args.s, tt.args.params)
+
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("collectionStatsHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("collectionStatsHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_collections_discovery.go b/src/go/plugins/mongodb/handler_collections_discovery.go
new file mode 100644
index 00000000000..5d8f886da91
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_collections_discovery.go
@@ -0,0 +1,69 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+ "sort"
+
+ "zabbix.com/pkg/zbxerr"
+)
+
+type colEntity struct {
+ ColName string `json:"{#COLLECTION}"`
+ DbName string `json:"{#DBNAME}"`
+}
+
+// collectionsDiscoveryHandler
+// https://docs.mongodb.com/manual/reference/command/listDatabases/
+func collectionsDiscoveryHandler(s Session, _ map[string]string) (interface{}, error) {
+ dbs, err := s.DatabaseNames()
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ sort.Strings(dbs)
+
+ lld := make([]colEntity, 0)
+
+ for _, db := range dbs {
+ collections, err := s.DB(db).CollectionNames()
+
+ sort.Strings(collections)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ for _, col := range collections {
+ lld = append(lld, colEntity{
+ ColName: col,
+ DbName: db,
+ })
+ }
+ }
+
+ jsonLLD, err := json.Marshal(lld)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonLLD), nil
+}
diff --git a/src/go/plugins/mongodb/handler_collections_discovery_test.go b/src/go/plugins/mongodb/handler_collections_discovery_test.go
new file mode 100644
index 00000000000..029451ccea5
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_collections_discovery_test.go
@@ -0,0 +1,78 @@
+package mongodb
+
+import (
+ "errors"
+ "reflect"
+ "testing"
+
+ "zabbix.com/pkg/zbxerr"
+)
+
+func Test_collectionsDiscoveryHandler(t *testing.T) {
+ type args struct {
+ s Session
+ dbs map[string][]string
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must return a list of collections",
+ args: args{
+ s: NewMockConn(),
+ dbs: map[string][]string{
+ "testdb": {"col1", "col2"},
+ "local": {"startup_log"},
+ "config": {"system.sessions"},
+ },
+ },
+ want: "[{\"{#COLLECTION}\":\"system.sessions\",\"{#DBNAME}\":\"config\"},{\"{#COLLECTION}\":" +
+ "\"startup_log\",\"{#DBNAME}\":\"local\"},{\"{#COLLECTION}\":\"col1\",\"{#DBNAME}\":\"testdb\"}," +
+ "{\"{#COLLECTION}\":\"col2\",\"{#DBNAME}\":\"testdb\"}]",
+ wantErr: nil,
+ },
+ {
+ name: "Must catch DB.DatabaseNames() error",
+ args: args{
+ s: NewMockConn(),
+ dbs: map[string][]string{mustFail: {}},
+ },
+ want: nil,
+ wantErr: zbxerr.ErrorCannotFetchData,
+ },
+ {
+ name: "Must catch DB.CollectionNames() error",
+ args: args{
+ s: NewMockConn(),
+ dbs: map[string][]string{"MyDatabase": {mustFail}},
+ },
+ want: nil,
+ wantErr: zbxerr.ErrorCannotFetchData,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ for db, cc := range tt.args.dbs {
+ tt.args.s.DB(db)
+ for _, c := range cc {
+ tt.args.s.DB(db).C(c)
+ }
+ }
+
+ got, err := collectionsDiscoveryHandler(tt.args.s, nil)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("collectionsDiscoveryHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("collectionsDiscoveryHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_collections_usage.go b/src/go/plugins/mongodb/handler_collections_usage.go
new file mode 100644
index 00000000000..1ef260472cd
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_collections_usage.go
@@ -0,0 +1,55 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+// collectionsUsageHandler
+// https://docs.mongodb.com/manual/reference/command/top/index.html
+func collectionsUsageHandler(s Session, _ map[string]string) (interface{}, error) {
+ colUsage := &bson.M{}
+
+ err := s.DB("admin").Run(&bson.D{
+ bson.DocElem{
+ Name: "top",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, colUsage)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ jsonRes, err := json.Marshal(colUsage)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_collections_usage_test.go b/src/go/plugins/mongodb/handler_collections_usage_test.go
new file mode 100644
index 00000000000..478141c6a5e
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_collections_usage_test.go
@@ -0,0 +1,70 @@
+package mongodb
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "log"
+ "reflect"
+ "strings"
+ "testing"
+
+ "gopkg.in/mgo.v2/bson"
+)
+
+func Test_collectionsUsageHandler(t *testing.T) {
+ var testData map[string]interface{}
+
+ jsonData, err := ioutil.ReadFile("testdata/top.json")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(jsonData, &testData)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ mockSession := NewMockConn()
+ db := mockSession.DB("admin")
+ db.(*MockMongoDatabase).RunFunc = func(dbName, cmd string) ([]byte, error) {
+ if cmd == "top" {
+ return bson.Marshal(testData)
+ }
+
+ return nil, errors.New("no such cmd: " + cmd)
+ }
+
+ type args struct {
+ s Session
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must parse an output of \" + top + \"command",
+ args: args{
+ s: mockSession,
+ },
+ want: strings.TrimSpace(string(jsonData)),
+ wantErr: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := collectionsUsageHandler(tt.args.s, nil)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("collectionsUsageHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("collectionsUsageHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_config_discovery.go b/src/go/plugins/mongodb/handler_config_discovery.go
new file mode 100644
index 00000000000..9d96bd99e82
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_config_discovery.go
@@ -0,0 +1,96 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+ "strings"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+type lldCfgEntity struct {
+ ReplicaSet string `json:"{#REPLICASET}"`
+ Hostname string `json:"{#HOSTNAME}"`
+ MongodURI string `json:"{#MONGOD_URI}"`
+}
+
+type shardMap struct {
+ Map map[string]string
+}
+
+// configDiscoveryHandler
+// https://docs.mongodb.com/manual/reference/command/getShardMap/#dbcmd.getShardMap
+func configDiscoveryHandler(s Session, params map[string]string) (interface{}, error) {
+ var cfgServers shardMap
+ err := s.DB("admin").Run(&bson.D{
+ bson.DocElem{
+ Name: "getShardMap",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, &cfgServers)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ lld := make([]lldCfgEntity, 0)
+
+ if servers, ok := cfgServers.Map["config"]; ok {
+ var rs string
+
+ hosts := servers
+
+ h := strings.SplitN(hosts, "/", 2)
+ if len(h) > 1 {
+ rs = h[0]
+ hosts = h[1]
+ }
+
+ for _, hostport := range strings.Split(hosts, ",") {
+ host, _, err := net.SplitHostPort(hostport)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotParseResult.Wrap(err)
+ }
+
+ lld = append(lld, lldCfgEntity{
+ Hostname: host,
+ MongodURI: fmt.Sprintf("%s://%s", uriDefaults.Scheme, hostport),
+ ReplicaSet: rs,
+ })
+ }
+ } else {
+ return nil, zbxerr.ErrorCannotParseResult
+ }
+
+ jsonRes, err := json.Marshal(lld)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_connPool_stats.go b/src/go/plugins/mongodb/handler_connPool_stats.go
new file mode 100644
index 00000000000..a4e94142860
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_connPool_stats.go
@@ -0,0 +1,54 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+// connPoolStatsHandler
+//https://docs.mongodb.com/manual/reference/command/connPoolStats/#dbcmd.connPoolStats
+func connPoolStatsHandler(s Session, params map[string]string) (interface{}, error) {
+ connPoolStats := &bson.M{}
+ err := s.DB(params["Database"]).Run(&bson.D{
+ bson.DocElem{
+ Name: "connPoolStats",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, connPoolStats)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ jsonRes, err := json.Marshal(connPoolStats)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_connPool_stats_test.go b/src/go/plugins/mongodb/handler_connPool_stats_test.go
new file mode 100644
index 00000000000..0b819e040de
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_connPool_stats_test.go
@@ -0,0 +1,90 @@
+package mongodb
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "log"
+ "reflect"
+ "strings"
+ "testing"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+func Test_connPoolStatsHandler(t *testing.T) {
+ var testData map[string]interface{}
+
+ jsonData, err := ioutil.ReadFile("testdata/connPoolStats.json")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(jsonData, &testData)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ mockSession := NewMockConn()
+ db := mockSession.DB("testdb")
+ db.(*MockMongoDatabase).RunFunc = func(dbName, cmd string) ([]byte, error) {
+ if cmd == "connPoolStats" {
+ return bson.Marshal(testData)
+ }
+
+ return nil, errors.New("no such cmd: " + cmd)
+ }
+
+ type args struct {
+ s Session
+ params map[string]string
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must parse an output of \" + connPoolStats + \"command",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": "testdb"},
+ },
+ want: strings.TrimSpace(string(jsonData)),
+ wantErr: nil,
+ },
+ {
+ name: "Must not fail on unknown db",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": "not_exists"},
+ },
+ want: "{\"ok\":1}",
+ wantErr: nil,
+ },
+ {
+ name: "Must catch DB.Run() error",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": mustFail},
+ },
+ want: nil,
+ wantErr: zbxerr.ErrorCannotFetchData,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := connPoolStatsHandler(tt.args.s, tt.args.params)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("connPoolStatsHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("connPoolStatsHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_database_stats.go b/src/go/plugins/mongodb/handler_database_stats.go
new file mode 100644
index 00000000000..30861f95b2b
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_database_stats.go
@@ -0,0 +1,54 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+// databaseStatsHandler
+// https://docs.mongodb.com/manual/reference/command/dbStats/index.html
+func databaseStatsHandler(s Session, params map[string]string) (interface{}, error) {
+ dbStats := &bson.M{}
+ err := s.DB(params["Database"]).Run(&bson.D{
+ bson.DocElem{
+ Name: "dbStats",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, dbStats)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ jsonRes, err := json.Marshal(dbStats)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_database_stats_test.go b/src/go/plugins/mongodb/handler_database_stats_test.go
new file mode 100644
index 00000000000..38d752bd35d
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_database_stats_test.go
@@ -0,0 +1,91 @@
+package mongodb
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "log"
+ "reflect"
+ "strings"
+ "testing"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+func Test_databaseStatsHandler(t *testing.T) {
+ var testData map[string]interface{}
+
+ jsonData, err := ioutil.ReadFile("testdata/dbStats.json")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(jsonData, &testData)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ mockSession := NewMockConn()
+ db := mockSession.DB("testdb")
+ db.(*MockMongoDatabase).RunFunc = func(dbName, cmd string) ([]byte, error) {
+ if cmd == "dbStats" {
+ return bson.Marshal(testData)
+ }
+
+ return nil, errors.New("no such cmd: " + cmd)
+ }
+
+ type args struct {
+ s Session
+ params map[string]string
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must parse an output of \" + dbStats + \"command",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": "testdb"},
+ },
+ want: strings.TrimSpace(string(jsonData)),
+ wantErr: nil,
+ },
+ {
+ name: "Must not fail on unknown db",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": "not_exists"},
+ },
+ want: "{\"ok\":1}",
+ wantErr: nil,
+ },
+ {
+ name: "Must catch DB.Run() error",
+ args: args{
+ s: mockSession,
+ params: map[string]string{"Database": mustFail},
+ },
+ want: nil,
+ wantErr: zbxerr.ErrorCannotFetchData,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := databaseStatsHandler(tt.args.s, tt.args.params)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("databaseStatsHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("databaseStatsHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_databases_discovery.go b/src/go/plugins/mongodb/handler_databases_discovery.go
new file mode 100644
index 00000000000..9b3c79261a4
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_databases_discovery.go
@@ -0,0 +1,55 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+ "sort"
+
+ "zabbix.com/pkg/zbxerr"
+)
+
+type dbEntity struct {
+ DBName string `json:"{#DBNAME}"`
+}
+
+// databasesDiscoveryHandler
+// https://docs.mongodb.com/manual/reference/command/listDatabases/
+func databasesDiscoveryHandler(s Session, _ map[string]string) (interface{}, error) {
+ dbs, err := s.DatabaseNames()
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ lld := make([]dbEntity, 0)
+
+ sort.Strings(dbs)
+
+ for _, db := range dbs {
+ lld = append(lld, dbEntity{DBName: db})
+ }
+
+ jsonLLD, err := json.Marshal(lld)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonLLD), nil
+}
diff --git a/src/go/plugins/mongodb/handler_databases_discovery_test.go b/src/go/plugins/mongodb/handler_databases_discovery_test.go
new file mode 100644
index 00000000000..5eed8622bf8
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_databases_discovery_test.go
@@ -0,0 +1,60 @@
+package mongodb
+
+import (
+ "errors"
+ "reflect"
+ "testing"
+
+ "zabbix.com/pkg/zbxerr"
+)
+
+func Test_databasesDiscoveryHandler(t *testing.T) {
+ type args struct {
+ s Session
+ dbs []string
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must return a list of databases",
+ args: args{
+ s: NewMockConn(),
+ dbs: []string{"testdb", "local", "config"},
+ },
+ want: "[{\"{#DBNAME}\":\"config\"},{\"{#DBNAME}\":\"local\"},{\"{#DBNAME}\":\"testdb\"}]",
+ wantErr: nil,
+ },
+ {
+ name: "Must catch DB.DatabaseNames() error",
+ args: args{
+ s: NewMockConn(),
+ dbs: []string{mustFail},
+ },
+ want: nil,
+ wantErr: zbxerr.ErrorCannotFetchData,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ for _, db := range tt.args.dbs {
+ tt.args.s.DB(db)
+ }
+
+ got, err := databasesDiscoveryHandler(tt.args.s, nil)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("databasesDiscoveryHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("databasesDiscoveryHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_jumbo_chunks.go b/src/go/plugins/mongodb/handler_jumbo_chunks.go
new file mode 100644
index 00000000000..a92a93505c2
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_jumbo_chunks.go
@@ -0,0 +1,36 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+// jumboChunksHandler
+// https://docs.mongodb.com/manual/core/sharding-data-partitioning/#indivisible-jumbo-chunks
+func jumboChunksHandler(s Session, _ map[string]string) (interface{}, error) {
+ jumboChunks, err := s.DB("config").C("chunks").Find(bson.M{"jumbo": true}).Count()
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ return jumboChunks, nil
+}
diff --git a/src/go/plugins/mongodb/handler_oplog_stats.go b/src/go/plugins/mongodb/handler_oplog_stats.go
new file mode 100644
index 00000000000..6da5cfe0d95
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_oplog_stats.go
@@ -0,0 +1,92 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+ "time"
+
+ "gopkg.in/mgo.v2"
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+type oplogStats struct {
+ TimeDiff int `json:"timediff"` // in seconds
+}
+
+type oplogEntry struct {
+ Timestamp bson.MongoTimestamp `bson:"ts"`
+}
+
+const (
+ oplogReplicaSet = "oplog.rs" // the capped collection that holds the oplog for Replica Set Members
+ oplogMasterSlave = "oplog.$main" // oplog for the master-slave configuration
+)
+
+const (
+ sortAsc = "$natural"
+ sortDesc = "-$natural"
+)
+
+var oplogQuery = bson.M{"ts": bson.M{"$exists": true}}
+
+// oplogStatsHandler
+// https://docs.mongodb.com/manual/reference/method/db.getReplicationInfo/index.html
+func oplogStatsHandler(s Session, _ map[string]string) (interface{}, error) {
+ var (
+ stats oplogStats
+ opFirst, opLast oplogEntry
+ )
+
+ localDb := s.DB("local")
+
+ for _, collection := range []string{oplogReplicaSet, oplogMasterSlave} {
+ if err := localDb.C(collection).Find(oplogQuery).
+ Sort(sortAsc).Limit(1).
+ SetMaxTime(time.Duration(s.GetMaxTimeMS()) * time.Millisecond).
+ One(&opFirst); err != nil {
+ if err == mgo.ErrNotFound {
+ continue
+ }
+
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ if err := localDb.C(collection).Find(oplogQuery).Sort(sortDesc).Limit(1).One(&opLast); err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ break
+ }
+
+ // BSON has a special timestamp type for internal MongoDB use and is not associated with the regular Date type.
+ // This internal timestamp type is a 64 bit value where:
+ // the most significant 32 bits are a time_t value (seconds since the Unix epoch)
+ // the least significant 32 bits are an incrementing ordinal for operations within a given second.
+ stats.TimeDiff = int(opLast.Timestamp>>32 - opFirst.Timestamp>>32)
+
+ jsonRes, err := json.Marshal(stats)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_oplog_stats_test.go b/src/go/plugins/mongodb/handler_oplog_stats_test.go
new file mode 100644
index 00000000000..07c3a586983
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_oplog_stats_test.go
@@ -0,0 +1,95 @@
+package mongodb
+
+import (
+ "errors"
+ "reflect"
+ "testing"
+
+ "gopkg.in/mgo.v2/bson"
+)
+
+func Test_oplogStatsHandler(t *testing.T) {
+ var (
+ opFirst = map[string]int64{"ts": 6908630134576644097}
+ opLast = map[string]int64{"ts": 6925804549152178177}
+ )
+
+ mockSession := NewMockConn()
+ localDb := mockSession.DB("local")
+
+ dataFunc := func(_ string, _ interface{}, sortFields ...string) ([]byte, error) {
+ if len(sortFields) == 0 {
+ panic("sortFields must be set")
+ }
+
+ switch sortFields[0] {
+ case sortAsc:
+ return bson.Marshal(opFirst)
+
+ case sortDesc:
+ return bson.Marshal(opLast)
+
+ default:
+ panic("unknown sort type")
+ }
+ }
+
+ type args struct {
+ s Session
+ collections []string
+ }
+
+ // WARN: tests order is significant
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must return 0 if neither oplog.rs nor oplog.$main collection found",
+ args: args{
+ s: mockSession,
+ collections: []string{},
+ },
+ want: "{\"timediff\":0}",
+ wantErr: nil,
+ },
+ {
+ name: "Must calculate timediff from oplog.$main collection",
+ args: args{
+ s: mockSession,
+ collections: []string{oplogMasterSlave},
+ },
+ want: "{\"timediff\":3998730}",
+ wantErr: nil,
+ },
+ {
+ name: "Must calculate timediff from oplog.rs collection",
+ args: args{
+ s: mockSession,
+ collections: []string{oplogReplicaSet},
+ },
+ want: "{\"timediff\":3998730}",
+ wantErr: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ for _, col := range tt.args.collections {
+ localDb.C(col).Find(oplogQuery).(*MockMongoQuery).DataFunc = dataFunc
+ }
+
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := oplogStatsHandler(tt.args.s, nil)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("oplogStatsHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("oplogStatsHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_ping.go b/src/go/plugins/mongodb/handler_ping.go
new file mode 100644
index 00000000000..2aceabceb64
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_ping.go
@@ -0,0 +1,35 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+const (
+ pingFailed = 0
+ pingOk = 1
+)
+
+// pingHandler executes 'ping' command and returns pingOk if a connection is alive or pingFailed otherwise.
+// https://docs.mongodb.com/manual/reference/command/ping/index.html
+func pingHandler(s Session, _ map[string]string) (interface{}, error) {
+ if err := s.Ping(); err != nil {
+ return pingFailed, nil
+ }
+
+ return pingOk, nil
+}
diff --git a/src/go/plugins/mongodb/handler_replset_config.go b/src/go/plugins/mongodb/handler_replset_config.go
new file mode 100644
index 00000000000..17130ebc6b4
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_replset_config.go
@@ -0,0 +1,58 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+// replSetConfigHandler
+// https://docs.mongodb.com/manual/reference/command/replSetGetConfig/index.html
+func replSetConfigHandler(s Session, _ map[string]string) (interface{}, error) {
+ replSetGetConfig := &bson.M{}
+ err := s.DB("admin").Run(&bson.D{
+ bson.DocElem{
+ Name: "replSetGetConfig",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "commitmentStatus",
+ Value: true,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, replSetGetConfig)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ jsonRes, err := json.Marshal(replSetGetConfig)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_replset_config_test.go b/src/go/plugins/mongodb/handler_replset_config_test.go
new file mode 100644
index 00000000000..fb9926f8b76
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_replset_config_test.go
@@ -0,0 +1,71 @@
+package mongodb
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "log"
+ "reflect"
+ "strings"
+ "testing"
+
+ "gopkg.in/mgo.v2/bson"
+)
+
+func Test_replSetConfigHandler(t *testing.T) {
+ var testData map[string]interface{}
+
+ jsonData, err := ioutil.ReadFile("testdata/replSetGetConfig.json")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(jsonData, &testData)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ mockSession := NewMockConn()
+ db := mockSession.DB("admin")
+ db.(*MockMongoDatabase).RunFunc = func(dbName, cmd string) ([]byte, error) {
+ if cmd == "replSetGetConfig" {
+ return bson.Marshal(testData)
+ }
+
+ return nil, errors.New("no such cmd: " + cmd)
+ }
+
+ type args struct {
+ s Session
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must parse an output of \" + replSetGetConfig + \"command",
+ args: args{
+ s: mockSession,
+ },
+ want: strings.TrimSpace(string(jsonData)),
+ wantErr: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := replSetConfigHandler(tt.args.s, nil)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("replSetConfigHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("replSetConfigHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_replset_status.go b/src/go/plugins/mongodb/handler_replset_status.go
new file mode 100644
index 00000000000..ef0a7135dfe
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_replset_status.go
@@ -0,0 +1,162 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+const (
+ statePrimary = 1
+ stateSecondary = 2
+)
+
+const nodeHealthy = 1
+
+type Member struct {
+ health int
+ name string
+ optime int
+ ptr interface{}
+ state int
+}
+
+type rawMember = map[string]interface{}
+
+var errUnknownStructure = errors.New("failed to parse the members structure")
+
+func parseMembers(raw []interface{}) (result []Member, err error) {
+ var (
+ members []Member
+ primaryNode Member
+ )
+
+ for _, m := range raw {
+ member := Member{}
+ ok := true
+
+ if v, ok := m.(rawMember)["name"].(string); ok {
+ member.name = v
+ }
+
+ if v, ok := m.(rawMember)["health"].(float64); ok {
+ member.health = int(v)
+ }
+
+ if v, ok := m.(rawMember)["optime"].(map[string]interface{}); ok {
+ if ts, ok := v["ts"].(bson.MongoTimestamp); ok {
+ member.optime = int(ts >> 32)
+ } else {
+ member.optime = int(int64(v["ts"].(float64)) >> 32)
+ }
+ }
+
+ if v, ok := m.(rawMember)["state"].(int); ok {
+ member.state = v
+ }
+
+ if !ok {
+ return nil, errUnknownStructure
+ }
+
+ member.ptr = m
+
+ if member.state == statePrimary {
+ primaryNode = member
+ } else {
+ members = append(members, member)
+ }
+ }
+
+ result = append([]Member{primaryNode}, members...)
+ if len(result) == 0 {
+ return nil, errUnknownStructure
+ }
+
+ return result, nil
+}
+
+func injectExtendedMembersStats(raw []interface{}) error {
+ members, err := parseMembers(raw)
+ if err != nil {
+ return err
+ }
+
+ unhealthyNodes := []string{}
+ unhealthyCount := 0
+ primary := members[0]
+
+ for _, node := range members {
+ node.ptr.(rawMember)["lag"] = primary.optime - node.optime
+
+ if node.state == stateSecondary && node.health != nodeHealthy {
+ unhealthyNodes = append(unhealthyNodes, node.name)
+ unhealthyCount++
+ }
+ }
+
+ primary.ptr.(rawMember)["unhealthyNodes"] = unhealthyNodes
+ primary.ptr.(rawMember)["unhealthyCount"] = unhealthyCount
+ primary.ptr.(rawMember)["totalNodes"] = len(members) - 1
+
+ return nil
+}
+
+// replSetStatusHandler
+// https://docs.mongodb.com/manual/reference/command/replSetGetStatus/index.html
+func replSetStatusHandler(s Session, _ map[string]string) (interface{}, error) {
+ var replSetGetStatus map[string]interface{}
+
+ err := s.DB("admin").Run(&bson.D{
+ bson.DocElem{
+ Name: "replSetGetStatus",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, &replSetGetStatus)
+
+ if err != nil {
+ if strings.Contains(err.Error(), "not running with --replSet") {
+ return "{}", nil
+ }
+
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ err = injectExtendedMembersStats(replSetGetStatus["members"].([]interface{}))
+ if err != nil {
+ return nil, zbxerr.ErrorCannotParseResult.Wrap(err)
+ }
+
+ jsonRes, err := json.Marshal(replSetGetStatus)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_server_status.go b/src/go/plugins/mongodb/handler_server_status.go
new file mode 100644
index 00000000000..c946ed857e6
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_server_status.go
@@ -0,0 +1,58 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+// serverStatusHandler
+// https://docs.mongodb.com/manual/reference/command/serverStatus/#dbcmd.serverStatus
+func serverStatusHandler(s Session, _ map[string]string) (interface{}, error) {
+ serverStatus := &bson.M{}
+ err := s.DB("admin").Run(&bson.D{
+ bson.DocElem{
+ Name: "serverStatus",
+ Value: 1,
+ },
+ bson.DocElem{
+ Name: "recordStats",
+ Value: 0,
+ },
+ bson.DocElem{
+ Name: "maxTimeMS",
+ Value: s.GetMaxTimeMS(),
+ },
+ }, serverStatus)
+
+ if err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ jsonRes, err := json.Marshal(serverStatus)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonRes), nil
+}
diff --git a/src/go/plugins/mongodb/handler_server_status_test.go b/src/go/plugins/mongodb/handler_server_status_test.go
new file mode 100644
index 00000000000..601d7743c3c
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_server_status_test.go
@@ -0,0 +1,71 @@
+package mongodb
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "log"
+ "reflect"
+ "strings"
+ "testing"
+
+ "gopkg.in/mgo.v2/bson"
+)
+
+func Test_serverStatusHandler(t *testing.T) {
+ var testData map[string]interface{}
+
+ jsonData, err := ioutil.ReadFile("testdata/serverStatus.json")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(jsonData, &testData)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ mockSession := NewMockConn()
+ db := mockSession.DB("admin")
+ db.(*MockMongoDatabase).RunFunc = func(dbName, cmd string) ([]byte, error) {
+ if cmd == "serverStatus" {
+ return bson.Marshal(testData)
+ }
+
+ return nil, errors.New("no such cmd: " + cmd)
+ }
+
+ type args struct {
+ s Session
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want interface{}
+ wantErr error
+ }{
+ {
+ name: "Must parse an output of \" + serverStatus + \"command",
+ args: args{
+ s: mockSession,
+ },
+ want: strings.TrimSpace(string(jsonData)),
+ wantErr: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := serverStatusHandler(tt.args.s, nil)
+ if !errors.Is(err, tt.wantErr) {
+ t.Errorf("serverStatusHandler() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("serverStatusHandler() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/mongodb/handler_shards_discovery.go b/src/go/plugins/mongodb/handler_shards_discovery.go
new file mode 100644
index 00000000000..177390aeae1
--- /dev/null
+++ b/src/go/plugins/mongodb/handler_shards_discovery.go
@@ -0,0 +1,87 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+ "strings"
+ "time"
+
+ "zabbix.com/pkg/zbxerr"
+)
+
+type lldShEntity struct {
+ ID string `json:"{#ID}"`
+ Hostname string `json:"{#HOSTNAME}"`
+ MongodURI string `json:"{#MONGOD_URI}"`
+ State string `json:"{#STATE}"`
+}
+
+type shEntry struct {
+ ID string `bson:"_id"`
+ Host string `bson:"host"`
+ State json.Number `bson:"state"`
+}
+
+// shardsDiscoveryHandler
+// https://docs.mongodb.com/manual/reference/method/sh.status/#sh.status
+func shardsDiscoveryHandler(s Session, _ map[string]string) (interface{}, error) {
+ var shards []shEntry
+
+ if err := s.DB("config").C("shards").Find(nil).Sort(sortAsc).
+ SetMaxTime(time.Duration(s.GetMaxTimeMS()) * time.Millisecond).
+ All(&shards); err != nil {
+ return nil, zbxerr.ErrorCannotFetchData.Wrap(err)
+ }
+
+ lld := make([]lldShEntity, 0)
+
+ for _, sh := range shards {
+ hosts := sh.Host
+
+ h := strings.SplitN(sh.Host, "/", 2)
+ if len(h) > 1 {
+ hosts = h[1]
+ }
+
+ for _, hostport := range strings.Split(hosts, ",") {
+ host, _, err := net.SplitHostPort(hostport)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotParseResult.Wrap(err)
+ }
+
+ lld = append(lld, lldShEntity{
+ ID: sh.ID,
+ Hostname: host,
+ MongodURI: fmt.Sprintf("%s://%s", uriDefaults.Scheme, hostport),
+ State: sh.State.String(),
+ })
+ }
+ }
+
+ jsonLLD, err := json.Marshal(lld)
+ if err != nil {
+ return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err)
+ }
+
+ return string(jsonLLD), nil
+}
diff --git a/src/go/plugins/mongodb/metrics.go b/src/go/plugins/mongodb/metrics.go
new file mode 100644
index 00000000000..f1cd553b989
--- /dev/null
+++ b/src/go/plugins/mongodb/metrics.go
@@ -0,0 +1,159 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "zabbix.com/pkg/metric"
+ "zabbix.com/pkg/plugin"
+ "zabbix.com/pkg/uri"
+)
+
+// handlerFunc defines an interface must be implemented by handlers.
+type handlerFunc func(s Session, params map[string]string) (res interface{}, err error)
+
+// getHandlerFunc returns a handlerFunc related to a given key.
+func getHandlerFunc(key string) handlerFunc {
+ switch key {
+ case keyConfigDiscovery:
+ return configDiscoveryHandler
+
+ case keyCollectionStats:
+ return collectionStatsHandler
+
+ case keyCollectionsDiscovery:
+ return collectionsDiscoveryHandler
+
+ case keyCollectionsUsage:
+ return collectionsUsageHandler
+
+ case keyConnPoolStats:
+ return connPoolStatsHandler
+
+ case keyDatabaseStats:
+ return databaseStatsHandler
+
+ case keyDatabasesDiscovery:
+ return databasesDiscoveryHandler
+
+ case keyJumboChunks:
+ return jumboChunksHandler
+
+ case keyOplogStats:
+ return oplogStatsHandler
+
+ case keyPing:
+ return pingHandler
+
+ case keyReplSetConfig:
+ return replSetConfigHandler
+
+ case keyReplSetStatus:
+ return replSetStatusHandler
+
+ case keyServerStatus:
+ return serverStatusHandler
+
+ case keyShardsDiscovery:
+ return shardsDiscoveryHandler
+
+ default:
+ return nil
+ }
+}
+
+const (
+ keyConfigDiscovery = "mongodb.cfg.discovery"
+ keyCollectionStats = "mongodb.collection.stats"
+ keyCollectionsDiscovery = "mongodb.collections.discovery"
+ keyCollectionsUsage = "mongodb.collections.usage"
+ keyConnPoolStats = "mongodb.connpool.stats"
+ keyDatabaseStats = "mongodb.db.stats"
+ keyDatabasesDiscovery = "mongodb.db.discovery"
+ keyJumboChunks = "mongodb.jumbo_chunks.count"
+ keyOplogStats = "mongodb.oplog.stats"
+ keyPing = "mongodb.ping"
+ keyReplSetConfig = "mongodb.rs.config"
+ keyReplSetStatus = "mongodb.rs.status"
+ keyServerStatus = "mongodb.server.status"
+ keyShardsDiscovery = "mongodb.sh.discovery"
+)
+
+var uriDefaults = &uri.Defaults{Scheme: "tcp", Port: "27017"}
+
+// Common params: [URI|Session][,User][,Password]
+var (
+ paramURI = metric.NewConnParam("URI", "URI to connect or session name.").
+ WithDefault(uriDefaults.Scheme + "://localhost:" + uriDefaults.Port).WithSession().
+ WithValidator(uri.URIValidator{Defaults: uriDefaults, AllowedSchemes: []string{"tcp"}})
+ paramUser = metric.NewConnParam("User", "MongoDB user.")
+ paramPassword = metric.NewConnParam("Password", "User's password.")
+ paramDatabase = metric.NewParam("Database", "Database name.").WithDefault("admin")
+ paramCollection = metric.NewParam("Collection", "Collection name.").SetRequired()
+)
+
+var metrics = metric.MetricSet{
+ keyConfigDiscovery: metric.New("Returns a list of discovered config servers.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyCollectionStats: metric.New("Returns a variety of storage statistics for a given collection.",
+ []*metric.Param{paramURI, paramUser, paramPassword, paramDatabase, paramCollection}, false),
+
+ keyCollectionsDiscovery: metric.New("Returns a list of discovered collections.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyCollectionsUsage: metric.New("Returns usage statistics for collections.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyConnPoolStats: metric.New("Returns information regarding the open outgoing connections from the "+
+ "current database instance to other members of the sharded cluster or replica set.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyDatabaseStats: metric.New("Returns statistics reflecting a given database system’s state.",
+ []*metric.Param{paramURI, paramUser, paramPassword, paramDatabase}, false),
+
+ keyDatabasesDiscovery: metric.New("Returns a list of discovered databases.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyJumboChunks: metric.New("Returns count of jumbo chunks.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyOplogStats: metric.New("Returns a status of the replica set, using data polled from the oplog.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyPing: metric.New("Test if connection is alive or not.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyReplSetConfig: metric.New("Returns a current configuration of the replica set.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyReplSetStatus: metric.New("Returns a replica set status from the point of view of the member "+
+ "where the method is run.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyServerStatus: metric.New("Returns a database’s state.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+
+ keyShardsDiscovery: metric.New("Returns a list of discovered shards present in the cluster.",
+ []*metric.Param{paramURI, paramUser, paramPassword}, false),
+}
+
+func init() {
+ plugin.RegisterMetrics(&impl, pluginName, metrics.List()...)
+}
diff --git a/src/go/plugins/mongodb/mockconn.go b/src/go/plugins/mongodb/mockconn.go
new file mode 100644
index 00000000000..aad8053a55f
--- /dev/null
+++ b/src/go/plugins/mongodb/mockconn.go
@@ -0,0 +1,190 @@
+package mongodb
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "gopkg.in/mgo.v2"
+ "gopkg.in/mgo.v2/bson"
+ "zabbix.com/pkg/zbxerr"
+)
+
+const (
+ mustFail = "mustFail"
+)
+
+type MockConn struct {
+ dbs map[string]*MockMongoDatabase
+}
+
+func NewMockConn() *MockConn {
+ return &MockConn{
+ dbs: make(map[string]*MockMongoDatabase),
+ }
+}
+
+func (conn *MockConn) DB(name string) Database {
+ if db, ok := conn.dbs[name]; ok {
+ return db
+ }
+
+ conn.dbs[name] = &MockMongoDatabase{
+ name: name,
+ collections: make(map[string]*MockMongoCollection),
+ }
+
+ return conn.dbs[name]
+}
+
+func (conn *MockConn) DatabaseNames() (names []string, err error) {
+ for _, db := range conn.dbs {
+ if db.name == mustFail {
+ return nil, zbxerr.ErrorCannotFetchData
+ }
+
+ names = append(names, db.name)
+ }
+
+ return
+}
+
+func (conn *MockConn) Ping() error {
+ return nil
+}
+
+func (conn *MockConn) GetMaxTimeMS() int64 {
+ return 3000
+}
+
+type MockSession interface {
+ DB(name string) Database
+ DatabaseNames() (names []string, err error)
+ GetMaxTimeMS() int64
+ Ping() error
+}
+
+type MockMongoDatabase struct {
+ name string
+ collections map[string]*MockMongoCollection
+ RunFunc func(dbName, cmd string) ([]byte, error)
+}
+
+func (d *MockMongoDatabase) C(name string) Collection {
+ if col, ok := d.collections[name]; ok {
+ return col
+ }
+
+ d.collections[name] = &MockMongoCollection{
+ name: name,
+ queries: make(map[interface{}]*MockMongoQuery),
+ }
+
+ return d.collections[name]
+}
+
+func (d *MockMongoDatabase) CollectionNames() (names []string, err error) {
+ for _, col := range d.collections {
+ if col.name == mustFail {
+ return nil, errors.New("fail")
+ }
+
+ names = append(names, col.name)
+ }
+
+ return
+}
+
+func (d *MockMongoDatabase) Run(cmd, result interface{}) error {
+ if d.RunFunc == nil {
+ d.RunFunc = func(dbName, _ string) ([]byte, error) {
+ if dbName == mustFail {
+ return nil, errors.New("fail")
+ }
+
+ return bson.Marshal(map[string]int{"ok": 1})
+ }
+ }
+
+ if result == nil {
+ return nil
+ }
+
+ bsonDcmd := *(cmd.(*bson.D))
+ cmdName := bsonDcmd[0].Name
+
+ data, err := d.RunFunc(d.name, cmdName)
+ if err != nil {
+ return err
+ }
+
+ return bson.Unmarshal(data, result)
+}
+
+type MockMongoCollection struct {
+ name string
+ queries map[interface{}]*MockMongoQuery
+}
+
+func (c *MockMongoCollection) Find(query interface{}) Query {
+ queryHash := fmt.Sprintf("%v", query)
+ if q, ok := c.queries[queryHash]; ok {
+ return q
+ }
+
+ c.queries[queryHash] = &MockMongoQuery{
+ collection: c.name,
+ query: query,
+ }
+
+ return c.queries[queryHash]
+}
+
+type MockMongoQuery struct {
+ collection string
+ query interface{}
+ sortFields []string
+ DataFunc func(collection string, query interface{}, sortFields ...string) ([]byte, error)
+}
+
+func (q *MockMongoQuery) retrieve(result interface{}) error {
+ if q.DataFunc == nil {
+ return mgo.ErrNotFound
+ }
+
+ if result == nil {
+ return nil
+ }
+
+ data, err := q.DataFunc(q.collection, q.query, q.sortFields...)
+ if err != nil {
+ return err
+ }
+
+ return bson.Unmarshal(data, result)
+}
+
+func (q *MockMongoQuery) All(result interface{}) error {
+ return q.retrieve(result)
+}
+
+func (q *MockMongoQuery) Count() (n int, err error) {
+ return 1, nil
+}
+
+func (q *MockMongoQuery) Limit(n int) Query {
+ return q
+}
+
+func (q *MockMongoQuery) One(result interface{}) error {
+ return q.retrieve(result)
+}
+
+func (q *MockMongoQuery) SetMaxTime(_ time.Duration) Query {
+ return q
+}
+
+func (q *MockMongoQuery) Sort(fields ...string) Query {
+ q.sortFields = fields
+ return q
+}
diff --git a/src/go/plugins/mongodb/mongodb.go b/src/go/plugins/mongodb/mongodb.go
new file mode 100644
index 00000000000..ad4e988c6e4
--- /dev/null
+++ b/src/go/plugins/mongodb/mongodb.go
@@ -0,0 +1,114 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package mongodb
+
+import (
+ "time"
+
+ "gopkg.in/mgo.v2"
+
+ "zabbix.com/pkg/uri"
+ "zabbix.com/pkg/zbxerr"
+
+ "zabbix.com/pkg/plugin"
+)
+
+const pluginName = "Mongo"
+
+const hkInterval = 10
+
+// Plugin inherits plugin.Base and store plugin-specific data.
+type Plugin struct {
+ plugin.Base
+ connMgr *ConnManager
+ options PluginOptions
+}
+
+// impl is the pointer to the plugin implementation.
+var impl Plugin
+
+// Export implements the Exporter interface.
+func (p *Plugin) Export(key string, rawParams []string, _ plugin.ContextProvider) (result interface{}, err error) {
+ params, err := metrics[key].EvalParams(rawParams, p.options.Sessions)
+ if err != nil {
+ return nil, err
+ }
+
+ uri, err := uri.NewWithCreds(params["URI"], params["User"], params["Password"], uriDefaults)
+ if err != nil {
+ return nil, err
+ }
+
+ handleMetric := getHandlerFunc(key)
+ if handleMetric == nil {
+ return nil, zbxerr.ErrorUnsupportedMetric
+ }
+
+ conn, err := p.connMgr.GetConnection(*uri)
+ if err != nil {
+ // Special logic of processing connection errors should be used if mongodb.ping is requested
+ // because it must return pingFailed if any error occurred.
+ if key == keyPing {
+ return pingFailed, nil
+ }
+
+ p.Errf(err.Error())
+
+ return nil, err
+ }
+
+ result, err = handleMetric(conn, params)
+ if err != nil {
+ p.Errf(err.Error())
+ }
+
+ return result, err
+}
+
+// Start implements the Runner interface and performs initialization when plugin is activated.
+func (p *Plugin) Start() {
+ p.connMgr = NewConnManager(
+ time.Duration(p.options.KeepAlive)*time.Second,
+ time.Duration(p.options.Timeout)*time.Second,
+ hkInterval*time.Second,
+ )
+}
+
+// Stop implements the Runner interface and frees resources when plugin is deactivated.
+func (p *Plugin) Stop() {
+ p.connMgr.Destroy()
+ p.connMgr = nil
+}
+
+type MongoLogger struct {
+ Debugf func(format string, args ...interface{})
+}
+
+func (l MongoLogger) Output(_ int, msg string) error {
+ l.Debugf(msg)
+ return nil
+}
+
+func init() {
+ logger := MongoLogger{Debugf: impl.Tracef}
+
+ mgo.SetDebug(true)
+ mgo.SetLogger(logger)
+}
diff --git a/src/go/plugins/mongodb/testdata/collStats.json b/src/go/plugins/mongodb/testdata/collStats.json
new file mode 100644
index 00000000000..efb07b16c9a
--- /dev/null
+++ b/src/go/plugins/mongodb/testdata/collStats.json
@@ -0,0 +1 @@
+{"capped":false,"count":0,"indexDetails":{"_id_":{"LSM":{"bloom filter false positives":0,"bloom filter hits":0,"bloom filter misses":0,"bloom filter pages evicted from cache":0,"bloom filter pages read into cache":0,"bloom filters in the LSM tree":0,"chunks in the LSM tree":0,"highest merge generation in the LSM tree":0,"queries that could have benefited from a Bloom filter that did not exist":0,"sleep for LSM checkpoint throttle":0,"sleep for LSM merge throttle":0,"total size of bloom filters":0},"block-manager":{"allocations requiring file extension":0,"blocks allocated":0,"blocks freed":0,"checkpoint size":0,"file allocation unit size":4096,"file bytes available for reuse":0,"file magic number":120897,"file major version number":1,"file size in bytes":4096,"minor version number":0},"btree":{"btree checkpoint generation":70825,"column-store fixed-size leaf pages":0,"column-store internal pages":0,"column-store variable-size RLE encoded values":0,"column-store variable-size deleted values":0,"column-store variable-size leaf pages":0,"fixed-record size":0,"maximum internal page key size":1474,"maximum internal page size":16384,"maximum leaf page key size":1474,"maximum leaf page size":16384,"maximum leaf page value size":7372,"maximum tree depth":0,"number of key/value pairs":0,"overflow pages":0,"pages rewritten by compaction":0,"row-store internal pages":0,"row-store leaf pages":0},"cache":{"bytes currently in the cache":182,"bytes dirty in the cache cumulative":0,"bytes read into cache":0,"bytes written from cache":0,"checkpoint blocked page eviction":0,"data source pages selected for eviction unable to be evicted":0,"eviction walk passes of a file":0,"eviction walk target pages histogram - 0-9":0,"eviction walk target pages histogram - 10-31":0,"eviction walk target pages histogram - 128 and higher":0,"eviction walk target pages histogram - 32-63":0,"eviction walk target pages histogram - 64-128":0,"eviction walks abandoned":0,"eviction walks gave up because they restarted their walk twice":0,"eviction walks gave up because they saw too many pages and found no candidates":0,"eviction walks gave up because they saw too many pages and found too few candidates":0,"eviction walks reached end of tree":0,"eviction walks started from root of tree":0,"eviction walks started from saved location in tree":0,"hazard pointer blocked page eviction":0,"in-memory page passed criteria to be split":0,"in-memory page splits":0,"internal pages evicted":0,"internal pages split during eviction":0,"leaf pages split during eviction":0,"modified pages evicted":0,"overflow pages read into cache":0,"page split during eviction deepened the tree":0,"page written requiring cache overflow records":0,"pages read into cache":0,"pages read into cache after truncate":0,"pages read into cache after truncate in prepare state":0,"pages read into cache requiring cache overflow entries":0,"pages requested from the cache":0,"pages seen by eviction walk":0,"pages written from cache":0,"pages written requiring in-memory restoration":0,"tracked dirty bytes in the cache":0,"unmodified pages evicted":0},"cache_walk":{"Average difference between current eviction generation when the page was last considered":0,"Average on-disk page image size seen":0,"Average time in cache for pages that have been visited by the eviction server":0,"Average time in cache for pages that have not been visited by the eviction server":0,"Clean pages currently in cache":0,"Current eviction generation":0,"Dirty pages currently in cache":0,"Entries in the root page":0,"Internal pages currently in cache":0,"Leaf pages currently in cache":0,"Maximum difference between current eviction generation when the page was last considered":0,"Maximum page size seen":0,"Minimum on-disk page image size seen":0,"Number of pages never visited by eviction server":0,"On-disk page image sizes smaller than a single allocation unit":0,"Pages created in memory and never written":0,"Pages currently queued for eviction":0,"Pages that could not be queued for eviction":0,"Refs skipped during cache traversal":0,"Size of the root page":0,"Total number of pages currently in cache":0},"compression":{"compressed pages read":0,"compressed pages written":0,"page written failed to compress":0,"page written was too small to compress":0},"creationString":"access_pattern_hint=none,allocation_size=4KB,app_metadata=(formatVersion=8,infoObj={ \"v\" : 2, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"MyDatabase.MyCollection\" }),assert=(commit_timestamp=none,read_timestamp=none),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,colgroups=,collator=,columns=,dictionary=0,encryption=(keyid=,name=),exclusive=false,extractor=,format=btree,huffman_key=,huffman_value=,ignore_in_memory_cache_size=false,immutable=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=16k,key_format=u,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=16k,leaf_value_max=0,log=(enabled=false),lsm=(auto_throttle=true,bloom=true,bloom_bit_count=16,bloom_config=,bloom_hash_count=8,bloom_oldest=false,chunk_count_limit=0,chunk_max=5GB,chunk_size=10MB,merge_custom=(prefix=,start_generation=0,suffix=),merge_max=15,merge_min=0),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=true,prefix_compression_min=4,source=,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,type=file,value_format=u","cursor":{"bulk-loaded cursor-insert calls":0,"close calls that result in cache":0,"create calls":0,"cursor operation restarted":0,"cursor-insert key and value bytes inserted":0,"cursor-remove key bytes removed":0,"cursor-update value bytes updated":0,"cursors reused from cache":0,"insert calls":0,"modify calls":0,"next calls":0,"open cursor count":0,"prev calls":0,"remove calls":0,"reserve calls":0,"reset calls":0,"search calls":0,"search near calls":0,"truncate calls":0,"update calls":0},"metadata":{"formatVersion":8,"infoObj":"{ \"v\" : 2, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"MyDatabase.MyCollection\" }"},"reconciliation":{"dictionary matches":0,"fast-path pages deleted":0,"internal page key bytes discarded using suffix compression":0,"internal page multi-block writes":0,"internal-page overflow keys":0,"leaf page key bytes discarded using prefix compression":0,"leaf page multi-block writes":0,"leaf-page overflow keys":0,"maximum blocks required for a page":0,"overflow values written":0,"page checksum matches":0,"page reconciliation calls":0,"page reconciliation calls for eviction":0,"pages deleted":0},"session":{"object compaction":0},"transaction":{"update conflicts":0},"type":"file","uri":"statistics:table:index-28--7017182801192397941"},"supplierId_hashed":{"LSM":{"bloom filter false positives":0,"bloom filter hits":0,"bloom filter misses":0,"bloom filter pages evicted from cache":0,"bloom filter pages read into cache":0,"bloom filters in the LSM tree":0,"chunks in the LSM tree":0,"highest merge generation in the LSM tree":0,"queries that could have benefited from a Bloom filter that did not exist":0,"sleep for LSM checkpoint throttle":0,"sleep for LSM merge throttle":0,"total size of bloom filters":0},"block-manager":{"allocations requiring file extension":0,"blocks allocated":0,"blocks freed":0,"checkpoint size":0,"file allocation unit size":4096,"file bytes available for reuse":0,"file magic number":120897,"file major version number":1,"file size in bytes":4096,"minor version number":0},"btree":{"btree checkpoint generation":70825,"column-store fixed-size leaf pages":0,"column-store internal pages":0,"column-store variable-size RLE encoded values":0,"column-store variable-size deleted values":0,"column-store variable-size leaf pages":0,"fixed-record size":0,"maximum internal page key size":1474,"maximum internal page size":16384,"maximum leaf page key size":1474,"maximum leaf page size":16384,"maximum leaf page value size":7372,"maximum tree depth":0,"number of key/value pairs":0,"overflow pages":0,"pages rewritten by compaction":0,"row-store internal pages":0,"row-store leaf pages":0},"cache":{"bytes currently in the cache":182,"bytes dirty in the cache cumulative":0,"bytes read into cache":0,"bytes written from cache":0,"checkpoint blocked page eviction":0,"data source pages selected for eviction unable to be evicted":0,"eviction walk passes of a file":0,"eviction walk target pages histogram - 0-9":0,"eviction walk target pages histogram - 10-31":0,"eviction walk target pages histogram - 128 and higher":0,"eviction walk target pages histogram - 32-63":0,"eviction walk target pages histogram - 64-128":0,"eviction walks abandoned":0,"eviction walks gave up because they restarted their walk twice":0,"eviction walks gave up because they saw too many pages and found no candidates":0,"eviction walks gave up because they saw too many pages and found too few candidates":0,"eviction walks reached end of tree":0,"eviction walks started from root of tree":0,"eviction walks started from saved location in tree":0,"hazard pointer blocked page eviction":0,"in-memory page passed criteria to be split":0,"in-memory page splits":0,"internal pages evicted":0,"internal pages split during eviction":0,"leaf pages split during eviction":0,"modified pages evicted":0,"overflow pages read into cache":0,"page split during eviction deepened the tree":0,"page written requiring cache overflow records":0,"pages read into cache":0,"pages read into cache after truncate":0,"pages read into cache after truncate in prepare state":0,"pages read into cache requiring cache overflow entries":0,"pages requested from the cache":0,"pages seen by eviction walk":0,"pages written from cache":0,"pages written requiring in-memory restoration":0,"tracked dirty bytes in the cache":0,"unmodified pages evicted":0},"cache_walk":{"Average difference between current eviction generation when the page was last considered":0,"Average on-disk page image size seen":0,"Average time in cache for pages that have been visited by the eviction server":0,"Average time in cache for pages that have not been visited by the eviction server":0,"Clean pages currently in cache":0,"Current eviction generation":0,"Dirty pages currently in cache":0,"Entries in the root page":0,"Internal pages currently in cache":0,"Leaf pages currently in cache":0,"Maximum difference between current eviction generation when the page was last considered":0,"Maximum page size seen":0,"Minimum on-disk page image size seen":0,"Number of pages never visited by eviction server":0,"On-disk page image sizes smaller than a single allocation unit":0,"Pages created in memory and never written":0,"Pages currently queued for eviction":0,"Pages that could not be queued for eviction":0,"Refs skipped during cache traversal":0,"Size of the root page":0,"Total number of pages currently in cache":0},"compression":{"compressed pages read":0,"compressed pages written":0,"page written failed to compress":0,"page written was too small to compress":0},"creationString":"access_pattern_hint=none,allocation_size=4KB,app_metadata=(formatVersion=8,infoObj={ \"v\" : 2, \"key\" : { \"supplierId\" : \"hashed\" }, \"name\" : \"supplierId_hashed\", \"ns\" : \"MyDatabase.MyCollection\" }),assert=(commit_timestamp=none,read_timestamp=none),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,colgroups=,collator=,columns=,dictionary=0,encryption=(keyid=,name=),exclusive=false,extractor=,format=btree,huffman_key=,huffman_value=,ignore_in_memory_cache_size=false,immutable=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=16k,key_format=u,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=16k,leaf_value_max=0,log=(enabled=false),lsm=(auto_throttle=true,bloom=true,bloom_bit_count=16,bloom_config=,bloom_hash_count=8,bloom_oldest=false,chunk_count_limit=0,chunk_max=5GB,chunk_size=10MB,merge_custom=(prefix=,start_generation=0,suffix=),merge_max=15,merge_min=0),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=true,prefix_compression_min=4,source=,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,type=file,value_format=u","cursor":{"bulk-loaded cursor-insert calls":0,"close calls that result in cache":0,"create calls":0,"cursor operation restarted":0,"cursor-insert key and value bytes inserted":0,"cursor-remove key bytes removed":0,"cursor-update value bytes updated":0,"cursors reused from cache":0,"insert calls":0,"modify calls":0,"next calls":0,"open cursor count":0,"prev calls":0,"remove calls":0,"reserve calls":0,"reset calls":0,"search calls":0,"search near calls":0,"truncate calls":0,"update calls":0},"metadata":{"formatVersion":8,"infoObj":"{ \"v\" : 2, \"key\" : { \"supplierId\" : \"hashed\" }, \"name\" : \"supplierId_hashed\", \"ns\" : \"MyDatabase.MyCollection\" }"},"reconciliation":{"dictionary matches":0,"fast-path pages deleted":0,"internal page key bytes discarded using suffix compression":0,"internal page multi-block writes":0,"internal-page overflow keys":0,"leaf page key bytes discarded using prefix compression":0,"leaf page multi-block writes":0,"leaf-page overflow keys":0,"maximum blocks required for a page":0,"overflow values written":0,"page checksum matches":0,"page reconciliation calls":0,"page reconciliation calls for eviction":0,"pages deleted":0},"session":{"object compaction":0},"transaction":{"update conflicts":0},"type":"file","uri":"statistics:table:index-29--7017182801192397941"}},"indexSizes":{"_id_":4096,"supplierId_hashed":4096},"nindexes":2,"ns":"MyDatabase.MyCollection","ok":1,"size":0,"storageSize":4096,"totalIndexSize":8192,"wiredTiger":{"LSM":{"bloom filter false positives":0,"bloom filter hits":0,"bloom filter misses":0,"bloom filter pages evicted from cache":0,"bloom filter pages read into cache":0,"bloom filters in the LSM tree":0,"chunks in the LSM tree":0,"highest merge generation in the LSM tree":0,"queries that could have benefited from a Bloom filter that did not exist":0,"sleep for LSM checkpoint throttle":0,"sleep for LSM merge throttle":0,"total size of bloom filters":0},"block-manager":{"allocations requiring file extension":0,"blocks allocated":0,"blocks freed":0,"checkpoint size":0,"file allocation unit size":4096,"file bytes available for reuse":0,"file magic number":120897,"file major version number":1,"file size in bytes":4096,"minor version number":0},"btree":{"btree checkpoint generation":70825,"column-store fixed-size leaf pages":0,"column-store internal pages":0,"column-store variable-size RLE encoded values":0,"column-store variable-size deleted values":0,"column-store variable-size leaf pages":0,"fixed-record size":0,"maximum internal page key size":368,"maximum internal page size":4096,"maximum leaf page key size":2867,"maximum leaf page size":32768,"maximum leaf page value size":67108864,"maximum tree depth":0,"number of key/value pairs":0,"overflow pages":0,"pages rewritten by compaction":0,"row-store internal pages":0,"row-store leaf pages":0},"cache":{"bytes currently in the cache":182,"bytes dirty in the cache cumulative":0,"bytes read into cache":0,"bytes written from cache":0,"checkpoint blocked page eviction":0,"data source pages selected for eviction unable to be evicted":0,"eviction walk passes of a file":0,"eviction walk target pages histogram - 0-9":0,"eviction walk target pages histogram - 10-31":0,"eviction walk target pages histogram - 128 and higher":0,"eviction walk target pages histogram - 32-63":0,"eviction walk target pages histogram - 64-128":0,"eviction walks abandoned":0,"eviction walks gave up because they restarted their walk twice":0,"eviction walks gave up because they saw too many pages and found no candidates":0,"eviction walks gave up because they saw too many pages and found too few candidates":0,"eviction walks reached end of tree":0,"eviction walks started from root of tree":0,"eviction walks started from saved location in tree":0,"hazard pointer blocked page eviction":0,"in-memory page passed criteria to be split":0,"in-memory page splits":0,"internal pages evicted":0,"internal pages split during eviction":0,"leaf pages split during eviction":0,"modified pages evicted":0,"overflow pages read into cache":0,"page split during eviction deepened the tree":0,"page written requiring cache overflow records":0,"pages read into cache":0,"pages read into cache after truncate":0,"pages read into cache after truncate in prepare state":0,"pages read into cache requiring cache overflow entries":0,"pages requested from the cache":0,"pages seen by eviction walk":0,"pages written from cache":0,"pages written requiring in-memory restoration":0,"tracked dirty bytes in the cache":0,"unmodified pages evicted":0},"cache_walk":{"Average difference between current eviction generation when the page was last considered":0,"Average on-disk page image size seen":0,"Average time in cache for pages that have been visited by the eviction server":0,"Average time in cache for pages that have not been visited by the eviction server":0,"Clean pages currently in cache":0,"Current eviction generation":0,"Dirty pages currently in cache":0,"Entries in the root page":0,"Internal pages currently in cache":0,"Leaf pages currently in cache":0,"Maximum difference between current eviction generation when the page was last considered":0,"Maximum page size seen":0,"Minimum on-disk page image size seen":0,"Number of pages never visited by eviction server":0,"On-disk page image sizes smaller than a single allocation unit":0,"Pages created in memory and never written":0,"Pages currently queued for eviction":0,"Pages that could not be queued for eviction":0,"Refs skipped during cache traversal":0,"Size of the root page":0,"Total number of pages currently in cache":0},"compression":{"compressed pages read":0,"compressed pages written":0,"page written failed to compress":0,"page written was too small to compress":0},"creationString":"access_pattern_hint=none,allocation_size=4KB,app_metadata=(formatVersion=1),assert=(commit_timestamp=none,read_timestamp=none),block_allocation=best,block_compressor=snappy,cache_resident=false,checksum=on,colgroups=,collator=,columns=,dictionary=0,encryption=(keyid=,name=),exclusive=false,extractor=,format=btree,huffman_key=,huffman_value=,ignore_in_memory_cache_size=false,immutable=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=q,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=64MB,log=(enabled=false),lsm=(auto_throttle=true,bloom=true,bloom_bit_count=16,bloom_config=,bloom_hash_count=8,bloom_oldest=false,chunk_count_limit=0,chunk_max=5GB,chunk_size=10MB,merge_custom=(prefix=,start_generation=0,suffix=),merge_max=15,merge_min=0),memory_page_image_max=0,memory_page_max=10m,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,source=,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,type=file,value_format=u","cursor":{"bulk-loaded cursor-insert calls":0,"close calls that result in cache":0,"create calls":0,"cursor operation restarted":0,"cursor-insert key and value bytes inserted":0,"cursor-remove key bytes removed":0,"cursor-update value bytes updated":0,"cursors reused from cache":0,"insert calls":0,"modify calls":0,"next calls":0,"open cursor count":0,"prev calls":0,"remove calls":0,"reserve calls":0,"reset calls":0,"search calls":0,"search near calls":0,"truncate calls":0,"update calls":0},"metadata":{"formatVersion":1},"reconciliation":{"dictionary matches":0,"fast-path pages deleted":0,"internal page key bytes discarded using suffix compression":0,"internal page multi-block writes":0,"internal-page overflow keys":0,"leaf page key bytes discarded using prefix compression":0,"leaf page multi-block writes":0,"leaf-page overflow keys":0,"maximum blocks required for a page":0,"overflow values written":0,"page checksum matches":0,"page reconciliation calls":0,"page reconciliation calls for eviction":0,"pages deleted":0},"session":{"object compaction":0},"transactiozn":{"update conflicts":0},"type":"file","uri":"statistics:table:collection-27--7017182801192397941"}}
diff --git a/src/go/plugins/mongodb/testdata/connPoolStats.json b/src/go/plugins/mongodb/testdata/connPoolStats.json
new file mode 100644
index 00000000000..c96fc3c69f1
--- /dev/null
+++ b/src/go/plugins/mongodb/testdata/connPoolStats.json
@@ -0,0 +1 @@
+{"hosts":{"configsvr01:27017":{"available":2,"created":10,"inUse":0,"refreshing":0},"configsvr02:27017":{"available":2,"created":2,"inUse":0,"refreshing":0},"configsvr03:27017":{"available":2,"created":2,"inUse":0,"refreshing":0},"shard01-a:27017":{"available":2,"created":2,"inUse":0,"refreshing":0},"shard01-b:27017":{"available":2,"created":2,"inUse":0,"refreshing":0},"shard01-c:27017":{"available":2,"created":2,"inUse":0,"refreshing":0},"shard02-a:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard02-b:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard02-c:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard03-a:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard03-b:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard03-c:27017":{"available":1,"created":1,"inUse":0,"refreshing":0}},"lastCommittedOpTime":6926878449889968000,"numAScopedConnections":0,"numClientConnections":12,"ok":1,"operationTime":6926878449889968000,"pools":{"NetworkInterfaceTL-Replication":{"poolAvailable":2,"poolCreated":2,"poolInUse":0,"poolRefreshing":0,"shard01-b:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard01-c:27017":{"available":1,"created":1,"inUse":0,"refreshing":0}},"NetworkInterfaceTL-ShardRegistry":{"configsvr01:27017":{"available":1,"created":9,"inUse":0,"refreshing":0},"configsvr02:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"configsvr03:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"poolAvailable":3,"poolCreated":11,"poolInUse":0,"poolRefreshing":0},"NetworkInterfaceTL-TaskExecutorPool-0":{"poolAvailable":1,"poolCreated":1,"poolInUse":0,"poolRefreshing":0,"shard01-a:27017":{"available":1,"created":1,"inUse":0,"refreshing":0}},"global":{"configsvr01:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"configsvr02:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"configsvr03:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"poolAvailable":12,"poolCreated":12,"poolInUse":0,"poolRefreshing":0,"shard01-a:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard01-b:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard01-c:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard02-a:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard02-b:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard02-c:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard03-a:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard03-b:27017":{"available":1,"created":1,"inUse":0,"refreshing":0},"shard03-c:27017":{"available":1,"created":1,"inUse":0,"refreshing":0}}},"replicaSets":{"rs-config-server":{"hosts":[{"addr":"configsvr01:27017","hidden":false,"ismaster":true,"ok":true,"pingTimeMillis":0,"secondary":false},{"addr":"configsvr02:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true},{"addr":"configsvr03:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true}]},"rs-shard-01":{"hosts":[{"addr":"shard01-a:27017","hidden":false,"ismaster":true,"ok":true,"pingTimeMillis":0,"secondary":false},{"addr":"shard01-b:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true},{"addr":"shard01-c:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true}]},"rs-shard-02":{"hosts":[{"addr":"shard02-a:27017","hidden":false,"ismaster":true,"ok":true,"pingTimeMillis":0,"secondary":false},{"addr":"shard02-b:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true},{"addr":"shard02-c:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true}]},"rs-shard-03":{"hosts":[{"addr":"shard03-a:27017","hidden":false,"ismaster":true,"ok":true,"pingTimeMillis":0,"secondary":false},{"addr":"shard03-b:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true},{"addr":"shard03-c:27017","hidden":false,"ismaster":false,"ok":true,"pingTimeMillis":0,"secondary":true}]}},"totalAvailable":18,"totalCreated":26,"totalInUse":0,"totalRefreshing":0}
diff --git a/src/go/plugins/mongodb/testdata/dbStats.json b/src/go/plugins/mongodb/testdata/dbStats.json
new file mode 100644
index 00000000000..211ddd600f5
--- /dev/null
+++ b/src/go/plugins/mongodb/testdata/dbStats.json
@@ -0,0 +1 @@
+{"avgObjSize":59,"collections":1,"dataSize":59,"db":"admin","fsTotalSize":67371577344,"fsUsedSize":8687353856,"indexSize":32768,"indexes":1,"objects":1,"ok":1,"scaleFactor":1,"storageSize":32768,"totalSize":65536,"views":0}
diff --git a/src/go/plugins/mongodb/testdata/replSetGetConfig.json b/src/go/plugins/mongodb/testdata/replSetGetConfig.json
new file mode 100644
index 00000000000..3224800eea4
--- /dev/null
+++ b/src/go/plugins/mongodb/testdata/replSetGetConfig.json
@@ -0,0 +1 @@
+{"config":{"_id":"rs-shard-01","members":[{"_id":0,"arbiterOnly":false,"buildIndexes":true,"hidden":false,"host":"shard01-a:27017","priority":1,"slaveDelay":0,"tags":{},"votes":1},{"_id":1,"arbiterOnly":false,"buildIndexes":true,"hidden":false,"host":"shard01-b:27017","priority":1,"slaveDelay":0,"tags":{},"votes":1},{"_id":2,"arbiterOnly":false,"buildIndexes":true,"hidden":false,"host":"shard01-c:27017","priority":1,"slaveDelay":0,"tags":{},"votes":1}],"protocolVersion":1,"settings":{"catchUpTakeoverDelayMillis":30000,"catchUpTimeoutMillis":-1,"chainingAllowed":true,"electionTimeoutMillis":10000,"getLastErrorDefaults":{"w":1,"wtimeout":0},"getLastErrorModes":{},"heartbeatIntervalMillis":2000,"heartbeatTimeoutSecs":10,"replicaSetId":"5fe0628084064df4684b5e4d"},"version":1,"writeConcernMajorityJournalDefault":true}}
diff --git a/src/go/plugins/mongodb/testdata/serverStatus.json b/src/go/plugins/mongodb/testdata/serverStatus.json
new file mode 100644
index 00000000000..5a22d85f2eb
--- /dev/null
+++ b/src/go/plugins/mongodb/testdata/serverStatus.json
@@ -0,0 +1 @@
+{"connections":{"active":1,"available":838859,"awaitingTopologyChanges":0,"current":1,"exhaustHello":0,"exhaustIsMaster":0,"totalCreated":8},"electionMetrics":{"averageCatchUpOps":0,"catchUpTakeover":{"called":0,"successful":0},"electionTimeout":{"called":0,"successful":0},"freezeTimeout":{"called":0,"successful":0},"numCatchUps":0,"numCatchUpsAlreadyCaughtUp":0,"numCatchUpsFailedWithError":0,"numCatchUpsFailedWithNewTerm":0,"numCatchUpsFailedWithReplSetAbortPrimaryCatchUpCmd":0,"numCatchUpsSkipped":0,"numCatchUpsSucceeded":0,"numCatchUpsTimedOut":0,"numStepDownsCausedByHigherTerm":0,"priorityTakeover":{"called":0,"successful":0},"stepUpCmd":{"called":0,"successful":0}},"extra_info":{"input_blocks":0,"involuntary_context_switches":8133,"maximum_resident_set_kb":97780,"note":"fields vary by platform","output_blocks":22096,"page_faults":0,"page_reclaims":17360,"system_time_us":18191069,"user_time_us":17360886,"voluntary_context_switches":152109},"flowControl":{"enabled":true,"isLagged":false,"isLaggedCount":0,"isLaggedTimeMicros":0,"locksPerKiloOp":0,"sustainerRate":0,"targetRateLimit":1000000000,"timeAcquiringMicros":4111},"freeMonitoring":{"state":"undecided"},"globalLock":{"activeClients":{"readers":0,"total":0,"writers":0},"currentQueue":{"readers":0,"total":0,"writers":0},"totalTime":4307530000},"host":"1f00645c8ab3","localTime":"2021-02-08T14:45:24.784+02:00","locks":{"Collection":{"acquireCount":{"W":2,"r":151,"w":71}},"Database":{"acquireCount":{"W":4,"r":126,"w":71}},"Global":{"acquireCount":{"W":4,"r":13049,"w":75}},"Mutex":{"acquireCount":{"r":200}},"ParallelBatchWriterMode":{"acquireCount":{"r":159}},"ReplicationStateTransition":{"acquireCount":{"w":13128}}},"logicalSessionRecordCache":{"activeSessionsCount":0,"lastSessionsCollectionJobCursorsClosed":0,"lastSessionsCollectionJobDurationMillis":10,"lastSessionsCollectionJobEntriesEnded":0,"lastSessionsCollectionJobEntriesRefreshed":0,"lastSessionsCollectionJobTimestamp":"2021-02-08T14:43:38.435+02:00","lastTransactionReaperJobDurationMillis":11,"lastTransactionReaperJobEntriesCleanedUp":0,"lastTransactionReaperJobTimestamp":"2021-02-08T14:43:38.435+02:00","sessionCatalogSize":0,"sessionsCollectionJobCount":15,"transactionReaperJobCount":15},"mem":{"bits":64,"resident":93,"supported":true,"virtual":1550},"metrics":{"aggStageCounters":{"$_internalInhibitOptimization":0,"$_internalSplitPipeline":0,"$addFields":0,"$bucket":0,"$bucketAuto":0,"$changeStream":0,"$collStats":0,"$count":0,"$currentOp":0,"$facet":0,"$geoNear":0,"$graphLookup":0,"$group":0,"$indexStats":0,"$limit":0,"$listLocalSessions":0,"$listSessions":0,"$lookup":0,"$match":0,"$merge":0,"$mergeCursors":0,"$out":0,"$planCacheStats":0,"$project":0,"$redact":0,"$replaceRoot":0,"$replaceWith":0,"$sample":0,"$set":0,"$skip":0,"$sort":0,"$sortByCount":0,"$unionWith":0,"$unset":0,"$unwind":0},"commands":{"\u003cUNKNOWN\u003e":0,"_addShard":{"failed":0,"total":0},"_cloneCollectionOptionsFromPrimaryShard":{"failed":0,"total":0},"_configsvrAddShard":{"failed":0,"total":0},"_configsvrAddShardToZone":{"failed":0,"total":0},"_configsvrBalancerCollectionStatus":{"failed":0,"total":0},"_configsvrBalancerStart":{"failed":0,"total":0},"_configsvrBalancerStatus":{"failed":0,"total":0},"_configsvrBalancerStop":{"failed":0,"total":0},"_configsvrClearJumboFlag":{"failed":0,"total":0},"_configsvrCommitChunkMerge":{"failed":0,"total":0},"_configsvrCommitChunkMigration":{"failed":0,"total":0},"_configsvrCommitChunkSplit":{"failed":0,"total":0},"_configsvrCommitMovePrimary":{"failed":0,"total":0},"_configsvrCreateCollection":{"failed":0,"total":0},"_configsvrCreateDatabase":{"failed":0,"total":0},"_configsvrDropCollection":{"failed":0,"total":0},"_configsvrDropDatabase":{"failed":0,"total":0},"_configsvrEnableSharding":{"failed":0,"total":0},"_configsvrEnsureChunkVersionIsGreaterThan":{"failed":0,"total":0},"_configsvrMoveChunk":{"failed":0,"total":0},"_configsvrMovePrimary":{"failed":0,"total":0},"_configsvrRefineCollectionShardKey":{"failed":0,"total":0},"_configsvrRemoveShard":{"failed":0,"total":0},"_configsvrRemoveShardFromZone":{"failed":0,"total":0},"_configsvrShardCollection":{"failed":0,"total":0},"_configsvrUpdateZoneKeyRange":{"failed":0,"total":0},"_flushDatabaseCacheUpdates":{"failed":0,"total":0},"_flushRoutingTableCacheUpdates":{"failed":0,"total":0},"_getNextSessionMods":{"failed":0,"total":0},"_getUserCacheGeneration":{"failed":0,"total":0},"_isSelf":{"failed":0,"total":0},"_killOperations":{"failed":0,"total":0},"_mergeAuthzCollections":{"failed":0,"total":0},"_migrateClone":{"failed":0,"total":0},"_recvChunkAbort":{"failed":0,"total":0},"_recvChunkCommit":{"failed":0,"total":0},"_recvChunkStart":{"failed":0,"total":0},"_recvChunkStatus":{"failed":0,"total":0},"_shardsvrCloneCatalogData":{"failed":0,"total":0},"_shardsvrMovePrimary":{"failed":0,"total":0},"_shardsvrShardCollection":{"failed":0,"total":0},"_transferMods":{"failed":0,"total":0},"abortTransaction":{"failed":0,"total":0},"aggregate":{"failed":0,"total":0},"appendOplogNote":{"failed":0,"total":0},"applyOps":{"failed":0,"total":0},"authenticate":{"failed":0,"total":0},"availableQueryOptions":{"failed":0,"total":0},"buildInfo":{"failed":0,"total":0},"checkShardingIndex":{"failed":0,"total":0},"cleanupOrphaned":{"failed":0,"total":0},"cloneCollectionAsCapped":{"failed":0,"total":0},"collMod":{"failed":0,"total":0},"collStats":{"failed":0,"total":0},"commitTransaction":{"failed":0,"total":0},"compact":{"failed":0,"total":0},"connPoolStats":{"failed":0,"total":0},"connPoolSync":{"failed":0,"total":0},"connectionStatus":{"failed":0,"total":0},"convertToCapped":{"failed":0,"total":0},"coordinateCommitTransaction":{"failed":0,"total":0},"count":{"failed":0,"total":0},"create":{"failed":0,"total":0},"createIndexes":{"failed":0,"total":0},"createRole":{"failed":0,"total":0},"createUser":{"failed":0,"total":0},"currentOp":{"failed":0,"total":0},"dataSize":{"failed":0,"total":0},"dbHash":{"failed":0,"total":0},"dbStats":{"failed":0,"total":0},"delete":{"failed":0,"total":0},"distinct":{"failed":0,"total":0},"driverOIDTest":{"failed":0,"total":0},"drop":{"failed":0,"total":0},"dropAllRolesFromDatabase":{"failed":0,"total":0},"dropAllUsersFromDatabase":{"failed":0,"total":0},"dropConnections":{"failed":0,"total":0},"dropDatabase":{"failed":0,"total":0},"dropIndexes":{"failed":0,"total":0},"dropRole":{"failed":0,"total":0},"dropUser":{"failed":0,"total":0},"endSessions":{"failed":0,"total":0},"explain":{"failed":0,"total":0},"features":{"failed":0,"total":0},"filemd5":{"failed":0,"total":0},"find":{"failed":0,"total":16},"findAndModify":{"arrayFilters":0,"failed":0,"pipeline":0,"total":0},"flushRouterConfig":{"failed":0,"total":0},"fsync":{"failed":0,"total":0},"fsyncUnlock":{"failed":0,"total":0},"geoSearch":{"failed":0,"total":0},"getCmdLineOpts":{"failed":0,"total":0},"getDatabaseVersion":{"failed":0,"total":0},"getDefaultRWConcern":{"failed":0,"total":0},"getDiagnosticData":{"failed":0,"total":0},"getFreeMonitoringStatus":{"failed":0,"total":0},"getLastError":{"failed":0,"total":0},"getLog":{"failed":0,"total":0},"getMore":{"failed":0,"total":0},"getParameter":{"failed":0,"total":0},"getShardMap":{"failed":0,"total":0},"getShardVersion":{"failed":0,"total":0},"getnonce":{"failed":0,"total":8},"grantPrivilegesToRole":{"failed":0,"total":0},"grantRolesToRole":{"failed":0,"total":0},"grantRolesToUser":{"failed":0,"total":0},"hello":{"failed":0,"total":0},"hostInfo":{"failed":0,"total":0},"insert":{"failed":0,"total":0},"internalRenameIfOptionsAndIndexesMatch":{"failed":0,"total":0},"invalidateUserCache":{"failed":0,"total":0},"isMaster":{"failed":0,"total":17},"killAllSessions":{"failed":0,"total":0},"killAllSessionsByPattern":{"failed":0,"total":0},"killCursors":{"failed":0,"total":0},"killOp":{"failed":0,"total":0},"killSessions":{"failed":0,"total":0},"listCollections":{"failed":0,"total":0},"listCommands":{"failed":0,"total":0},"listDatabases":{"failed":0,"total":0},"listIndexes":{"failed":0,"total":30},"lockInfo":{"failed":0,"total":0},"logRotate":{"failed":0,"total":0},"logout":{"failed":0,"total":0},"mapReduce":{"failed":0,"total":0},"mapreduce":{"shardedfinish":{"failed":0,"total":0}},"mergeChunks":{"failed":0,"total":0},"moveChunk":{"failed":0,"total":0},"ping":{"failed":0,"total":39},"planCacheClear":{"failed":0,"total":0},"planCacheClearFilters":{"failed":0,"total":0},"planCacheListFilters":{"failed":0,"total":0},"planCacheSetFilter":{"failed":0,"total":0},"prepareTransaction":{"failed":0,"total":0},"profile":{"failed":0,"total":0},"reIndex":{"failed":0,"total":0},"refreshSessions":{"failed":0,"total":0},"renameCollection":{"failed":0,"total":0},"repairDatabase":{"failed":0,"total":0},"replSetAbortPrimaryCatchUp":{"failed":0,"total":0},"replSetFreeze":{"failed":0,"total":0},"replSetGetConfig":{"failed":0,"total":0},"replSetGetRBID":{"failed":0,"total":0},"replSetGetStatus":{"failed":0,"total":0},"replSetHeartbeat":{"failed":0,"total":0},"replSetInitiate":{"failed":0,"total":0},"replSetMaintenance":{"failed":0,"total":0},"replSetReconfig":{"failed":0,"total":0},"replSetRequestVotes":{"failed":0,"total":0},"replSetResizeOplog":{"failed":0,"total":0},"replSetStepDown":{"failed":0,"total":0},"replSetStepDownWithForce":{"failed":0,"total":0},"replSetStepUp":{"failed":0,"total":0},"replSetSyncFrom":{"failed":0,"total":0},"replSetUpdatePosition":{"failed":0,"total":0},"resetError":{"failed":0,"total":0},"revokePrivilegesFromRole":{"failed":0,"total":0},"revokeRolesFromRole":{"failed":0,"total":0},"revokeRolesFromUser":{"failed":0,"total":0},"rolesInfo":{"failed":0,"total":0},"saslContinue":{"failed":0,"total":0},"saslStart":{"failed":0,"total":0},"serverStatus":{"failed":0,"total":7},"setDefaultRWConcern":{"failed":0,"total":0},"setFeatureCompatibilityVersion":{"failed":0,"total":0},"setFreeMonitoring":{"failed":0,"total":0},"setIndexCommitQuorum":{"failed":0,"total":0},"setParameter":{"failed":0,"total":0},"setShardVersion":{"failed":0,"total":0},"shardConnPoolStats":{"failed":0,"total":0},"shardingState":{"failed":0,"total":0},"shutdown":{"failed":0,"total":0},"splitChunk":{"failed":0,"total":0},"splitVector":{"failed":0,"total":0},"startRecordingTraffic":{"failed":0,"total":0},"startSession":{"failed":0,"total":0},"stopRecordingTraffic":{"failed":0,"total":0},"top":{"failed":0,"total":0},"unsetSharding":{"failed":0,"total":0},"update":{"arrayFilters":0,"failed":0,"pipeline":0,"total":0},"updateRole":{"failed":0,"total":0},"updateUser":{"failed":0,"total":0},"usersInfo":{"failed":0,"total":0},"validate":{"failed":0,"total":0},"voteCommitIndexBuild":{"failed":0,"total":0},"waitForFailPoint":{"failed":0,"total":0},"whatsmyuri":{"failed":0,"total":0}},"cursor":{"open":{"noTimeout":0,"pinned":0,"total":0},"timedOut":0},"document":{"deleted":0,"inserted":0,"returned":0,"updated":0},"getLastError":{"default":{"unsatisfiable":0,"wtimeouts":0},"wtime":{"num":0,"totalMillis":0},"wtimeouts":0},"operation":{"scanAndOrder":0,"writeConflicts":0},"query":{"planCacheTotalSizeEstimateBytes":0,"updateOneOpStyleBroadcastWithExactIDCount":0},"queryExecutor":{"collectionScans":{"nonTailable":0,"total":0},"scanned":0,"scannedObjects":0},"record":{"moves":0},"repl":{"apply":{"attemptsToBecomeSecondary":0,"batchSize":0,"batches":{"num":0,"totalMillis":0},"ops":0},"buffer":{"count":0,"maxSizeBytes":0,"sizeBytes":0},"executor":{"networkInterface":"DEPRECATED: getDiagnosticString is deprecated in NetworkInterfaceTL","pool":{"inProgressCount":0},"queues":{"networkInProgress":0,"sleepers":0},"shuttingDown":false,"unsignaledEvents":0},"initialSync":{"completed":0,"failedAttempts":0,"failures":0},"network":{"bytes":0,"getmores":{"num":0,"numEmptyBatches":0,"totalMillis":0},"notMasterLegacyUnacknowledgedWrites":0,"notMasterUnacknowledgedWrites":0,"oplogGetMoresProcessed":{"num":0,"totalMillis":0},"ops":0,"readersCreated":0,"replSetUpdatePosition":{"num":0}},"stateTransition":{"lastStateTransition":"","userOperationsKilled":0,"userOperationsRunning":0},"syncSource":{"numSelections":0,"numTimesChoseDifferent":0,"numTimesChoseSame":0,"numTimesCouldNotFind":0}},"ttl":{"deletedDocuments":0,"passes":71}},"network":{"bytesIn":4394,"bytesOut":261528,"compression":{"snappy":{"compressor":{"bytesIn":0,"bytesOut":0},"decompressor":{"bytesIn":0,"bytesOut":0}},"zlib":{"compressor":{"bytesIn":0,"bytesOut":0},"decompressor":{"bytesIn":0,"bytesOut":0}},"zstd":{"compressor":{"bytesIn":0,"bytesOut":0},"decompressor":{"bytesIn":0,"bytesOut":0}}},"numRequests":71,"numSlowDNSOperations":0,"numSlowSSLOperations":0,"physicalBytesIn":4394,"physicalBytesOut":261528,"serviceExecutorTaskStats":{"executor":"passthrough","threadsRunning":1},"tcpFastOpen":{"accepted":0,"clientSupported":true,"kernelSetting":1,"serverSupported":true}},"ok":1,"opLatencies":{"commands":{"latency":8911,"ops":70},"reads":{"latency":0,"ops":0},"transactions":{"latency":0,"ops":0},"writes":{"latency":0,"ops":0}},"opReadConcernCounters":{"available":0,"linearizable":0,"local":0,"majority":0,"none":16,"snapshot":0},"opcounters":{"command":101,"delete":0,"getmore":0,"insert":0,"query":16,"update":0},"opcountersRepl":{"command":0,"delete":0,"getmore":0,"insert":0,"query":0,"update":0},"pid":1,"process":"mongod","security":{"authentication":{"mechanisms":{"MONGODB-X509":{"authenticate":{"received":0,"successful":0},"speculativeAuthenticate":{"received":0,"successful":0}},"SCRAM-SHA-1":{"authenticate":{"received":0,"successful":0},"speculativeAuthenticate":{"received":0,"successful":0}},"SCRAM-SHA-256":{"authenticate":{"received":0,"successful":0},"speculativeAuthenticate":{"received":0,"successful":0}}}}},"storageEngine":{"backupCursorOpen":false,"dropPendingIdents":0,"name":"wiredTiger","oldestRequiredTimestampForCrashRecovery":0,"persistent":true,"readOnly":false,"supportsCommittedReads":true,"supportsPendingDrops":true,"supportsSnapshotReadConcern":true,"supportsTwoPhaseIndexBuild":true},"tcmalloc":{"generic":{"current_allocated_bytes":84765480,"heap_size":89341952},"tcmalloc":{"aggressive_memory_decommit":0,"central_cache_free_bytes":224512,"current_total_thread_cache_bytes":729944,"formattedString":"------------------------------------------------\nMALLOC: 84766056 ( 80.8 MiB) Bytes in use by application\nMALLOC: + 3330048 ( 3.2 MiB) Bytes in page heap freelist\nMALLOC: + 224512 ( 0.2 MiB) Bytes in central cache freelist\nMALLOC: + 291968 ( 0.3 MiB) Bytes in transfer cache freelist\nMALLOC: + 729368 ( 0.7 MiB) Bytes in thread cache freelists\nMALLOC: + 2752512 ( 2.6 MiB) Bytes in malloc metadata\nMALLOC: ------------\nMALLOC: = 92094464 ( 87.8 MiB) Actual memory used (physical + swap)\nMALLOC: + 0 ( 0.0 MiB) Bytes released to OS (aka unmapped)\nMALLOC: ------------\nMALLOC: = 92094464 ( 87.8 MiB) Virtual address space used\nMALLOC:\nMALLOC: 674 Spans in use\nMALLOC: 31 Thread heaps in use\nMALLOC: 4096 Tcmalloc page size\n------------------------------------------------\nCall ReleaseFreeMemory() to release freelist memory to the OS (via madvise()).\nBytes released to the OS take up virtual address space but no physical memory.\n","max_total_thread_cache_bytes":260046848,"pageheap_commit_count":62,"pageheap_committed_bytes":89341952,"pageheap_decommit_count":1,"pageheap_free_bytes":3330048,"pageheap_reserve_count":47,"pageheap_scavenge_count":1,"pageheap_total_commit_bytes":92364800,"pageheap_total_decommit_bytes":3022848,"pageheap_total_reserve_bytes":89341952,"pageheap_unmapped_bytes":0,"release_rate":1,"spinlock_total_delay_ns":0,"thread_cache_free_bytes":729944,"total_free_bytes":1246424,"transfer_cache_free_bytes":291968}},"trafficRecording":{"running":false},"transactions":{"currentActive":0,"currentInactive":0,"currentOpen":0,"currentPrepared":0,"retriedCommandsCount":0,"retriedStatementsCount":0,"totalAborted":0,"totalCommitted":0,"totalPrepared":0,"totalPreparedThenAborted":0,"totalPreparedThenCommitted":0,"totalStarted":0,"transactionsCollectionWriteCount":0},"transportSecurity":{"1.0":0,"1.1":0,"1.2":0,"1.3":0,"unknown":0},"twoPhaseCommitCoordinator":{"currentInSteps":{"deletingCoordinatorDoc":0,"waitingForDecisionAcks":0,"waitingForVotes":0,"writingDecision":0,"writingParticipantList":0},"totalAbortedTwoPhaseCommit":0,"totalCommittedTwoPhaseCommit":0,"totalCreated":0,"totalStartedTwoPhaseCommit":0},"uptime":4307,"uptimeEstimate":4307,"uptimeMillis":4307533,"version":"4.4.2","wiredTiger":{"block-manager":{"blocks pre-loaded":7,"blocks read":106,"blocks written":393,"bytes read":454656,"bytes read via memory map API":0,"bytes read via system call API":0,"bytes written":2793472,"bytes written for checkpoint":2793472,"bytes written via memory map API":0,"bytes written via system call API":0,"mapped blocks read":0,"mapped bytes read":0,"number of times the file was remapped because it changed size via fallocate or truncate":0,"number of times the region was remapped via write":0},"cache":{"application threads page read from disk to cache count":6,"application threads page read from disk to cache time (usecs)":63,"application threads page write from cache to disk count":148,"application threads page write from cache to disk time (usecs)":9767,"bytes allocated for updates":23075,"bytes belonging to page images in the cache":76829,"bytes belonging to the history store table in the cache":847,"bytes currently in the cache":106686,"bytes dirty in the cache cumulative":2938265,"bytes not belonging to page images in the cache":29857,"bytes read into cache":71138,"bytes written from cache":1275601,"cache overflow score":0,"checkpoint blocked page eviction":0,"eviction calls to get a page":376,"eviction calls to get a page found queue empty":302,"eviction calls to get a page found queue empty after locking":2,"eviction currently operating in aggressive mode":0,"eviction empty score":0,"eviction passes of a file":0,"eviction server candidate queue empty when topping up":0,"eviction server candidate queue not empty when topping up":0,"eviction server evicting pages":0,"eviction server slept, because we did not make progress with eviction":71,"eviction server unable to reach eviction goal":0,"eviction server waiting for a leaf page":3,"eviction state":64,"eviction walk target pages histogram - 0-9":0,"eviction walk target pages histogram - 10-31":0,"eviction walk target pages histogram - 128 and higher":0,"eviction walk target pages histogram - 32-63":0,"eviction walk target pages histogram - 64-128":0,"eviction walk target strategy both clean and dirty pages":0,"eviction walk target strategy only clean pages":0,"eviction walk target strategy only dirty pages":0,"eviction walks abandoned":0,"eviction walks gave up because they restarted their walk twice":0,"eviction walks gave up because they saw too many pages and found no candidates":0,"eviction walks gave up because they saw too many pages and found too few candidates":0,"eviction walks reached end of tree":0,"eviction walks restarted":0,"eviction walks started from root of tree":0,"eviction walks started from saved location in tree":0,"eviction worker thread active":4,"eviction worker thread created":0,"eviction worker thread evicting pages":71,"eviction worker thread removed":0,"eviction worker thread stable number":0,"files with active eviction walks":0,"files with new eviction walks started":0,"force re-tuning of eviction workers once in a while":0,"forced eviction - history store pages failed to evict while session has history store cursor open":0,"forced eviction - history store pages selected while session has history store cursor open":0,"forced eviction - history store pages successfully evicted while session has history store cursor open":0,"forced eviction - pages evicted that were clean count":0,"forced eviction - pages evicted that were clean time (usecs)":0,"forced eviction - pages evicted that were dirty count":0,"forced eviction - pages evicted that were dirty time (usecs)":0,"forced eviction - pages selected because of too many deleted items count":0,"forced eviction - pages selected count":0,"forced eviction - pages selected unable to be evicted count":0,"forced eviction - pages selected unable to be evicted time":0,"forced eviction - session returned rollback error while force evicting due to being oldest":0,"hazard pointer blocked page eviction":0,"hazard pointer check calls":71,"hazard pointer check entries walked":2,"hazard pointer maximum array length":2,"history store score":0,"history store table insert calls":0,"history store table insert calls that returned restart":0,"history store table max on-disk size":0,"history store table on-disk size":0,"history store table out-of-order resolved updates that lose their durable timestamp":0,"history store table out-of-order updates that were fixed up by moving existing records":0,"history store table out-of-order updates that were fixed up during insertion":0,"history store table reads":0,"history store table reads missed":0,"history store table reads requiring squashed modifies":0,"history store table truncation by rollback to stable to remove an unstable update":0,"history store table truncation by rollback to stable to remove an update":0,"history store table truncation to remove an update":0,"history store table truncation to remove range of updates due to key being removed from the data page during reconciliation":0,"history store table truncation to remove range of updates due to mixed timestamps":0,"history store table writes requiring squashed modifies":0,"in-memory page passed criteria to be split":0,"in-memory page splits":0,"internal pages evicted":0,"internal pages queued for eviction":0,"internal pages seen by eviction walk":0,"internal pages seen by eviction walk that are already queued":0,"internal pages split during eviction":0,"leaf pages split during eviction":0,"maximum bytes configured":505413632,"maximum page size at eviction":352,"modified pages evicted":71,"modified pages evicted by application threads":0,"operations timed out waiting for space in cache":0,"overflow pages read into cache":0,"page split during eviction deepened the tree":0,"page written requiring history store records":0,"pages currently held in the cache":19,"pages evicted by application threads":0,"pages evicted in parallel with checkpoint":71,"pages queued for eviction":0,"pages queued for eviction post lru sorting":0,"pages queued for urgent eviction":71,"pages queued for urgent eviction during walk":0,"pages read into cache":14,"pages read into cache after truncate":72,"pages read into cache after truncate in prepare state":0,"pages requested from the cache":1968,"pages seen by eviction walk":0,"pages seen by eviction walk that are already queued":0,"pages selected for eviction unable to be evicted":0,"pages selected for eviction unable to be evicted as the parent page has overflow items":0,"pages selected for eviction unable to be evicted because of active children on an internal page":0,"pages selected for eviction unable to be evicted because of failure in reconciliation":0,"pages walked for eviction":0,"pages written from cache":156,"pages written requiring in-memory restoration":0,"percentage overhead":8,"tracked bytes belonging to internal pages in the cache":5470,"tracked bytes belonging to leaf pages in the cache":101216,"tracked dirty bytes in the cache":467,"tracked dirty pages in the cache":1,"unmodified pages evicted":0},"capacity":{"background fsync file handles considered":0,"background fsync file handles synced":0,"background fsync time (msecs)":0,"bytes read":77824,"bytes written for checkpoint":1223343,"bytes written for eviction":0,"bytes written for log":1258339200,"bytes written total":1259562543,"threshold to call fsync":0,"time waiting due to total capacity (usecs)":0,"time waiting during checkpoint (usecs)":0,"time waiting during eviction (usecs)":0,"time waiting during logging (usecs)":0,"time waiting during read (usecs)":0},"checkpoint-cleanup":{"pages added for eviction":71,"pages removed":0,"pages skipped during tree walk":0,"pages visited":145},"concurrentTransactions":{"read":{"available":127,"out":1,"totalTickets":128},"write":{"available":128,"out":0,"totalTickets":128}},"connection":{"auto adjusting condition resets":378,"auto adjusting condition wait calls":26641,"auto adjusting condition wait raced to update timeout and skipped updating":0,"detected system time went backwards":0,"files currently open":14,"hash bucket array size for data handles":512,"hash bucket array size general":512,"memory allocations":173468,"memory frees":172614,"memory re-allocations":16623,"pthread mutex condition wait calls":69351,"pthread mutex shared lock read-lock calls":84645,"pthread mutex shared lock write-lock calls":4920,"total fsync I/Os":461,"total read I/Os":1439,"total write I/Os":550},"cursor":{"Total number of entries skipped by cursor next calls":0,"Total number of entries skipped by cursor prev calls":0,"Total number of entries skipped to position the history store cursor":0,"cached cursor count":15,"cursor bulk loaded cursor insert calls":0,"cursor close calls that result in cache":35885,"cursor create calls":70,"cursor insert calls":159,"cursor insert key and value bytes":102523,"cursor modify calls":0,"cursor modify key and value bytes affected":0,"cursor modify value bytes modified":0,"cursor next calls":196,"cursor next calls that skip due to a globally visible history store tombstone":0,"cursor next calls that skip due to a globally visible history store tombstone in rollback to stable":0,"cursor next calls that skip greater than or equal to 100 entries":0,"cursor next calls that skip less than 100 entries":194,"cursor operation restarted":0,"cursor prev calls":81,"cursor prev calls that skip due to a globally visible history store tombstone":0,"cursor prev calls that skip due to a globally visible history store tombstone in rollback to stable":0,"cursor prev calls that skip greater than or equal to 100 entries":0,"cursor prev calls that skip less than 100 entries":81,"cursor remove calls":0,"cursor remove key bytes removed":0,"cursor reserve calls":0,"cursor reset calls":37595,"cursor search calls":839,"cursor search history store calls":0,"cursor search near calls":86,"cursor sweep buckets":6156,"cursor sweep cursors closed":0,"cursor sweep cursors examined":5,"cursor sweeps":1026,"cursor truncate calls":0,"cursor update calls":0,"cursor update key and value bytes":0,"cursor update value size change":0,"cursors reused from cache":35863,"open cursor count":7},"data-handle":{"connection data handle size":432,"connection data handles currently active":19,"connection sweep candidate became referenced":0,"connection sweep dhandles closed":0,"connection sweep dhandles removed from hash list":84,"connection sweep time-of-death sets":681,"connection sweeps":426,"session dhandles swept":139,"session sweep attempts":102},"lock":{"checkpoint lock acquisitions":72,"checkpoint lock application thread wait time (usecs)":0,"checkpoint lock internal thread wait time (usecs)":0,"dhandle lock application thread time waiting (usecs)":0,"dhandle lock internal thread time waiting (usecs)":0,"dhandle read lock acquisitions":17668,"dhandle write lock acquisitions":187,"durable timestamp queue lock application thread time waiting (usecs)":0,"durable timestamp queue lock internal thread time waiting (usecs)":0,"durable timestamp queue read lock acquisitions":0,"durable timestamp queue write lock acquisitions":0,"metadata lock acquisitions":72,"metadata lock application thread wait time (usecs)":100,"metadata lock internal thread wait time (usecs)":0,"read timestamp queue lock application thread time waiting (usecs)":0,"read timestamp queue lock internal thread time waiting (usecs)":0,"read timestamp queue read lock acquisitions":0,"read timestamp queue write lock acquisitions":0,"schema lock acquisitions":85,"schema lock application thread wait time (usecs)":0,"schema lock internal thread wait time (usecs)":0,"table lock application thread time waiting for the table lock (usecs)":0,"table lock internal thread time waiting for the table lock (usecs)":0,"table read lock acquisitions":0,"table write lock acquisitions":9,"txn global lock application thread time waiting (usecs)":0,"txn global lock internal thread time waiting (usecs)":0,"txn global read lock acquisitions":336,"txn global write lock acquisitions":218},"log":{"busy returns attempting to switch slots":0,"force archive time sleeping (usecs)":0,"log bytes of payload data":80873,"log bytes written":112512,"log files manually zero-filled":0,"log flush operations":41972,"log force write operations":46637,"log force write operations skipped":46629,"log records compressed":72,"log records not compressed":0,"log records too small to compress":290,"log release advances write LSN":73,"log scan operations":6,"log scan records requiring two reads":0,"log server thread advances write LSN":8,"log server thread write LSN walk skipped":5236,"log sync operations":81,"log sync time duration (usecs)":150305,"log sync_dir operations":1,"log sync_dir time duration (usecs)":4788,"log write operations":362,"logging bytes consolidated":112000,"maximum log file size":104857600,"number of pre-allocated log files to create":2,"pre-allocated log files not ready and missed":1,"pre-allocated log files prepared":2,"pre-allocated log files used":0,"records processed by log scan":15,"slot close lost race":0,"slot close unbuffered waits":0,"slot closures":81,"slot join atomic update races":0,"slot join calls atomic updates raced":0,"slot join calls did not yield":362,"slot join calls found active slot closed":0,"slot join calls slept":0,"slot join calls yielded":0,"slot join found active slot closed":0,"slot joins yield time (usecs)":0,"slot transitions unable to find free slot":0,"slot unbuffered writes":0,"total in-memory size of compressed records":92785,"total log buffer size":33554432,"total size of compressed records":66331,"written slots coalesced":0,"yields waiting for previous log file close":0},"oplog":{"visibility timestamp":0},"perf":{"file system read latency histogram (bucket 1) - 10-49ms":0,"file system read latency histogram (bucket 2) - 50-99ms":0,"file system read latency histogram (bucket 3) - 100-249ms":0,"file system read latency histogram (bucket 4) - 250-499ms":0,"file system read latency histogram (bucket 5) - 500-999ms":0,"file system read latency histogram (bucket 6) - 1000ms+":0,"file system write latency histogram (bucket 1) - 10-49ms":0,"file system write latency histogram (bucket 2) - 50-99ms":0,"file system write latency histogram (bucket 3) - 100-249ms":0,"file system write latency histogram (bucket 4) - 250-499ms":0,"file system write latency histogram (bucket 5) - 500-999ms":0,"file system write latency histogram (bucket 6) - 1000ms+":0,"operation read latency histogram (bucket 1) - 100-249us":1,"operation read latency histogram (bucket 2) - 250-499us":1,"operation read latency histogram (bucket 3) - 500-999us":0,"operation read latency histogram (bucket 4) - 1000-9999us":0,"operation read latency histogram (bucket 5) - 10000us+":0,"operation write latency histogram (bucket 1) - 100-249us":1,"operation write latency histogram (bucket 2) - 250-499us":0,"operation write latency histogram (bucket 3) - 500-999us":0,"operation write latency histogram (bucket 4) - 1000-9999us":0,"operation write latency histogram (bucket 5) - 10000us+":0},"reconciliation":{"approximate byte size of timestamps in pages written":0,"approximate byte size of transaction IDs in pages written":1184,"fast-path pages deleted":0,"maximum seconds spent in a reconciliation call":0,"page reconciliation calls":374,"page reconciliation calls for eviction":71,"page reconciliation calls that resulted in values with prepared transaction metadata":0,"page reconciliation calls that resulted in values with timestamps":0,"page reconciliation calls that resulted in values with transaction ids":68,"pages deleted":218,"pages written including an aggregated newest start durable timestamp ":0,"pages written including an aggregated newest stop durable timestamp ":0,"pages written including an aggregated newest stop timestamp ":0,"pages written including an aggregated newest stop transaction ID":0,"pages written including an aggregated newest transaction ID ":0,"pages written including an aggregated oldest start timestamp ":0,"pages written including an aggregated prepare":0,"pages written including at least one prepare state":0,"pages written including at least one start durable timestamp":0,"pages written including at least one start timestamp":0,"pages written including at least one start transaction ID":68,"pages written including at least one stop durable timestamp":0,"pages written including at least one stop timestamp":0,"pages written including at least one stop transaction ID":0,"records written including a prepare state":0,"records written including a start durable timestamp":0,"records written including a start timestamp":0,"records written including a start transaction ID":148,"records written including a stop durable timestamp":0,"records written including a stop timestamp":0,"records written including a stop transaction ID":0,"split bytes currently awaiting free":0,"split objects currently awaiting free":0},"session":{"open session count":14,"session query timestamp calls":0,"table alter failed calls":0,"table alter successful calls":0,"table alter unchanged and skipped":0,"table compact failed calls":0,"table compact successful calls":0,"table create failed calls":0,"table create successful calls":1,"table drop failed calls":0,"table drop successful calls":0,"table rename failed calls":0,"table rename successful calls":0,"table salvage failed calls":0,"table salvage successful calls":0,"table truncate failed calls":0,"table truncate successful calls":0,"table verify failed calls":0,"table verify successful calls":0},"snapshot-window-settings":{"cache pressure percentage threshold":95,"current available snapshots window size in seconds":0,"current cache pressure percentage":0,"latest majority snapshot timestamp available":"Jan 1 00:00:00:0","max target available snapshots window size in seconds":5,"oldest majority snapshot timestamp available":"Jan 1 00:00:00:0","target available snapshots window size in seconds":5,"total number of SnapshotTooOld errors":0},"thread-state":{"active filesystem fsync calls":0,"active filesystem read calls":0,"active filesystem write calls":0},"thread-yield":{"application thread time evicting (usecs)":0,"application thread time waiting for cache (usecs)":0,"connection close blocked waiting for transaction state stabilization":0,"connection close yielded for lsm manager shutdown":0,"data handle lock yielded":0,"get reference for page index and slot time sleeping (usecs)":0,"log server sync yielded for log write":0,"page access yielded due to prepare state change":0,"page acquire busy blocked":0,"page acquire eviction blocked":0,"page acquire locked blocked":0,"page acquire read blocked":0,"page acquire time sleeping (usecs)":0,"page delete rollback time sleeping for state change (usecs)":0,"page reconciliation yielded due to child modification":0},"transaction":{"Number of prepared updates":0,"durable timestamp queue entries walked":0,"durable timestamp queue insert to empty":0,"durable timestamp queue inserts to head":0,"durable timestamp queue inserts total":0,"durable timestamp queue length":0,"prepared transactions":0,"prepared transactions committed":0,"prepared transactions currently active":0,"prepared transactions rolled back":0,"query timestamp calls":4301,"race to read prepared update retry":0,"read timestamp queue entries walked":0,"read timestamp queue insert to empty":0,"read timestamp queue inserts to head":0,"read timestamp queue inserts total":0,"read timestamp queue length":0,"rollback to stable calls":0,"rollback to stable hs records with stop timestamps older than newer records":0,"rollback to stable keys removed":0,"rollback to stable keys restored":0,"rollback to stable pages visited":1,"rollback to stable restored tombstones from history store":0,"rollback to stable sweeping history store keys":0,"rollback to stable tree walk skipping pages":0,"rollback to stable updates aborted":0,"rollback to stable updates removed from history store":0,"set timestamp calls":0,"set timestamp durable calls":0,"set timestamp durable updates":0,"set timestamp oldest calls":0,"set timestamp oldest updates":0,"set timestamp stable calls":0,"set timestamp stable updates":0,"transaction begins":180,"transaction checkpoint currently running":0,"transaction checkpoint generation":73,"transaction checkpoint history store file duration (usecs)":2,"transaction checkpoint max time (msecs)":45,"transaction checkpoint min time (msecs)":8,"transaction checkpoint most recent duration for gathering all handles (usecs)":356,"transaction checkpoint most recent duration for gathering applied handles (usecs)":147,"transaction checkpoint most recent duration for gathering skipped handles (usecs)":33,"transaction checkpoint most recent handles applied":1,"transaction checkpoint most recent handles skipped":9,"transaction checkpoint most recent handles walked":20,"transaction checkpoint most recent time (msecs)":12,"transaction checkpoint prepare currently running":0,"transaction checkpoint prepare max time (msecs)":1,"transaction checkpoint prepare min time (msecs)":0,"transaction checkpoint prepare most recent time (msecs)":0,"transaction checkpoint prepare total time (msecs)":1,"transaction checkpoint scrub dirty target":0,"transaction checkpoint scrub time (msecs)":0,"transaction checkpoint total time (msecs)":1373,"transaction checkpoints":72,"transaction checkpoints skipped because database was clean":0,"transaction failures due to history store":0,"transaction fsync calls for checkpoint after allocating the transaction ID":72,"transaction fsync duration for checkpoint after allocating the transaction ID (usecs)":4259,"transaction range of IDs currently pinned":0,"transaction range of IDs currently pinned by a checkpoint":0,"transaction range of timestamps currently pinned":0,"transaction range of timestamps pinned by a checkpoint":0,"transaction range of timestamps pinned by the oldest active read timestamp":0,"transaction range of timestamps pinned by the oldest timestamp":0,"transaction read timestamp of the oldest active reader":0,"transaction sync calls":0,"transactions committed":2,"transactions rolled back":178,"update conflicts":0},"uri":"statistics:"}}
diff --git a/src/go/plugins/mongodb/testdata/top.json b/src/go/plugins/mongodb/testdata/top.json
new file mode 100644
index 00000000000..ec30dbda42f
--- /dev/null
+++ b/src/go/plugins/mongodb/testdata/top.json
@@ -0,0 +1 @@
+{"ok":1,"totals":{"admin.system.version":{"commands":{"count":0,"time":0},"getmore":{"count":0,"time":0},"insert":{"count":0,"time":0},"queries":{"count":0,"time":0},"readLock":{"count":1,"time":162},"remove":{"count":0,"time":0},"total":{"count":1,"time":162},"update":{"count":0,"time":0},"writeLock":{"count":0,"time":0}},"note":"all times in microseconds"}}
diff --git a/src/go/plugins/plugins_darwin.go b/src/go/plugins/plugins_darwin.go
index 32199d66c4d..f546922e7b5 100644
--- a/src/go/plugins/plugins_darwin.go
+++ b/src/go/plugins/plugins_darwin.go
@@ -25,6 +25,7 @@ import (
_ "zabbix.com/plugins/log"
_ "zabbix.com/plugins/memcached"
_ "zabbix.com/plugins/modbus"
+ _ "zabbix.com/plugins/mongodb"
_ "zabbix.com/plugins/mysql"
_ "zabbix.com/plugins/net/tcp"
_ "zabbix.com/plugins/oracle"
diff --git a/src/go/plugins/plugins_linux.go b/src/go/plugins/plugins_linux.go
index ad750bbf5dc..59b6bfa2093 100644
--- a/src/go/plugins/plugins_linux.go
+++ b/src/go/plugins/plugins_linux.go
@@ -26,6 +26,7 @@ import (
_ "zabbix.com/plugins/log"
_ "zabbix.com/plugins/memcached"
_ "zabbix.com/plugins/modbus"
+ _ "zabbix.com/plugins/mongodb"
_ "zabbix.com/plugins/mqtt"
_ "zabbix.com/plugins/mysql"
_ "zabbix.com/plugins/net/netif"
diff --git a/src/go/plugins/plugins_windows.go b/src/go/plugins/plugins_windows.go
index 61d352f6fcd..b8a0143492f 100644
--- a/src/go/plugins/plugins_windows.go
+++ b/src/go/plugins/plugins_windows.go
@@ -24,6 +24,7 @@ import (
_ "zabbix.com/plugins/log"
_ "zabbix.com/plugins/memcached"
_ "zabbix.com/plugins/modbus"
+ _ "zabbix.com/plugins/mongodb"
_ "zabbix.com/plugins/mqtt"
_ "zabbix.com/plugins/mysql"
_ "zabbix.com/plugins/net/netif"
diff --git a/src/go/plugins/smart/smart.go b/src/go/plugins/smart/smart.go
index 67a41f80153..53ff12f0fc3 100644
--- a/src/go/plugins/smart/smart.go
+++ b/src/go/plugins/smart/smart.go
@@ -161,10 +161,10 @@ func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider)
// setDiskFields goes through provided device json map and sets disk_name
// disk_type and returns the devices in a slice.
// It returns an error if there is an issue with unmarshal for the provided input JSON map
-func setDiskFields(deviceJsons map[string]string) (out []interface{}, err error) {
+func setDiskFields(deviceJsons map[string]jsonDevice) (out []interface{}, err error) {
for k, v := range deviceJsons {
b := make(map[string]interface{})
- if err = json.Unmarshal([]byte(v), &b); err != nil {
+ if err = json.Unmarshal([]byte(v.jsonData), &b); err != nil {
return out, zbxerr.ErrorCannotUnmarshalJSON.Wrap(err)
}
diff --git a/src/go/plugins/smart/smart_test.go b/src/go/plugins/smart/smart_test.go
index 4611c634f22..cf54e59d003 100644
--- a/src/go/plugins/smart/smart_test.go
+++ b/src/go/plugins/smart/smart_test.go
@@ -34,7 +34,7 @@ func Test_setDiskFields(t *testing.T) {
}
type args struct {
- deviceJsons map[string]string
+ deviceJsons map[string]jsonDevice
}
tests := []struct {
@@ -43,9 +43,9 @@ func Test_setDiskFields(t *testing.T) {
want []interface{}
wantErr bool
}{
- {"+one_drive", args{map[string]string{"/dev/sda": jsonSdaStr}}, []interface{}{sdaOutStr}, false},
- {"-failed_json", args{map[string]string{"/dev/sda": `{"device":}`}}, nil, true},
- {"-failed_device_data_json", args{map[string]string{"/dev/sda": `{"device": foo,"rotation_rate": 0}`}}, nil, true},
+ {"+one_drive", args{map[string]jsonDevice{"/dev/sda": {jsonData: jsonSdaStr}}}, []interface{}{sdaOutStr}, false},
+ {"-failed_json", args{map[string]jsonDevice{"/dev/sda": {jsonData: `{"device":}`}}}, nil, true},
+ {"-failed_device_data_json", args{map[string]jsonDevice{"/dev/sda": {jsonData: `{"device": foo,"rotation_rate": 0}`}}}, nil, true},
}
for _, tt := range tests {
diff --git a/src/go/plugins/smart/smartfs.go b/src/go/plugins/smart/smartfs.go
index 89a5faade58..722b87c67ec 100644
--- a/src/go/plugins/smart/smartfs.go
+++ b/src/go/plugins/smart/smartfs.go
@@ -53,6 +53,10 @@ type device struct {
Model string `json:"{#MODEL}"`
SerialNumber string `json:"{#SN}"`
}
+type jsonDevice struct {
+ serialNumber string
+ jsonData string
+}
type attribute struct {
Name string `json:"{#NAME}"`
@@ -119,10 +123,8 @@ type runner struct {
done chan struct{}
raidDone chan struct{}
raids chan raidParameters
- devices []deviceParser
- jsonDevices map[string]string
- found map[string]bool
- foundRaid map[string]bool
+ devices map[string]deviceParser
+ jsonDevices map[string]jsonDevice
}
// execute returns the smartctl runner with all devices data returned by smartctl.
@@ -137,17 +139,17 @@ func (p *Plugin) execute(jsonRunner bool) (*runner, error) {
}
r := &runner{
- names: make(chan string, len(basicDev)),
- err: make(chan error, cpuCount),
- done: make(chan struct{}),
- raidDone: make(chan struct{}),
- plugin: p,
- found: make(map[string]bool),
- foundRaid: make(map[string]bool),
+ names: make(chan string, len(basicDev)),
+ err: make(chan error, cpuCount),
+ done: make(chan struct{}),
+ raidDone: make(chan struct{}),
+ plugin: p,
}
if jsonRunner {
- r.jsonDevices = make(map[string]string)
+ r.jsonDevices = make(map[string]jsonDevice)
+ } else {
+ r.devices = make(map[string]deviceParser)
}
r.startBasicRunners(jsonRunner)
@@ -178,6 +180,7 @@ func (p *Plugin) execute(jsonRunner bool) (*runner, error) {
close(r.raids)
r.waitForRaidExecution()
+ r.parseOutput(jsonRunner)
return r, err
}
@@ -326,15 +329,13 @@ func (r *runner) getBasicDevices(jsonRunner bool) {
if dp.SmartStatus != nil {
r.mux.Lock()
- if !r.found[dp.SerialNumber] {
- r.found[dp.SerialNumber] = true
- if jsonRunner {
- r.jsonDevices[name] = string(devices)
- } else {
- r.devices = append(r.devices, dp)
- }
+ if jsonRunner {
+ r.jsonDevices[name] = jsonDevice{dp.SerialNumber, string(devices)}
+ } else {
+ r.devices[name] = dp
}
+
r.mux.Unlock()
}
}
@@ -428,23 +429,60 @@ func (r *runner) setRaidDevices(dp deviceParser, device []byte, raidType string,
r.mux.Lock()
defer r.mux.Unlock()
- if !r.foundRaid[dp.SerialNumber] {
- r.foundRaid[dp.SerialNumber] = true
+ if json {
+ r.jsonDevices[dp.Info.Name] = jsonDevice{dp.SerialNumber, string(device)}
+ } else {
+ r.devices[dp.Info.Name] = dp
+ }
+
+ if raidType == satType {
+ return true
+ }
+
+ return false
+}
+
+func (r *runner) parseOutput(jsonRunner bool) {
+ found := make(map[string]bool)
+ var keys []string
- if json {
- r.jsonDevices[dp.Info.Name] = string(device)
- } else {
- r.devices = append(r.devices, dp)
+ if jsonRunner {
+ tmp := make(map[string]jsonDevice)
+
+ for k := range r.jsonDevices {
+ keys = append(keys, k)
}
- if raidType == satType {
- return true
+ sort.Strings(keys)
+
+ for _, k := range keys {
+ dev := r.jsonDevices[k]
+ if !found[dev.serialNumber] {
+ found[dev.serialNumber] = true
+ tmp[k] = dev
+ }
}
+
+ r.jsonDevices = tmp
} else {
- return true
- }
+ tmp := make(map[string]deviceParser)
- return false
+ for k := range r.devices {
+ keys = append(keys, k)
+ }
+
+ sort.Strings(keys)
+
+ for _, k := range keys {
+ dev := r.devices[k]
+ if !found[dev.SerialNumber] {
+ found[dev.SerialNumber] = true
+ tmp[k] = dev
+ }
+ }
+
+ r.devices = tmp
+ }
}
func (dp *deviceParser) checkErr() (err error) {
diff --git a/src/go/plugins/windows/services/services_windows.go b/src/go/plugins/windows/services/services_windows.go
index 0e14aa9fb20..e179ff08180 100644
--- a/src/go/plugins/windows/services/services_windows.go
+++ b/src/go/plugins/windows/services/services_windows.go
@@ -420,7 +420,7 @@ func (p *Plugin) exportServices(params []string) (result interface{}, err error)
}
stateFilter := stateFlagAll
- if len(params) > 1 && params[1] != "" {
+ if len(params) > 1 && params[1] != "all" && params[1] != "" {
switch params[1] {
case "stopped":
stateFilter = stateFlagStopped
diff --git a/src/libs/zbxalgo/Makefile.am b/src/libs/zbxalgo/Makefile.am
index 05b624ef494..ebadf2c82db 100644
--- a/src/libs/zbxalgo/Makefile.am
+++ b/src/libs/zbxalgo/Makefile.am
@@ -16,8 +16,8 @@ libzbxalgo_a_SOURCES = \
$(EVALUATE_C) \
hashmap.c \
hashset.c \
- linked_list.c \
int128.c \
+ linked_list.c \
prediction.c \
queue.c \
vector.c \
diff --git a/src/libs/zbxalgo/linked_list.c b/src/libs/zbxalgo/linked_list.c
index 2b0c679d4ff..737173eaf91 100644
--- a/src/libs/zbxalgo/linked_list.c
+++ b/src/libs/zbxalgo/linked_list.c
@@ -18,7 +18,6 @@
**/
#include "common.h"
-#include "log.h"
#include "zbxalgo.h"
/******************************************************************************
diff --git a/src/libs/zbxcommon/misc.c b/src/libs/zbxcommon/misc.c
index 29288fe0d24..c1838a19c75 100644
--- a/src/libs/zbxcommon/misc.c
+++ b/src/libs/zbxcommon/misc.c
@@ -3902,3 +3902,75 @@ int zbx_get_agent_item_nextcheck(zbx_uint64_t itemid, const char *delay, int now
return SUCCEED;
}
+/******************************************************************************
+ * *
+ * Function: zbx_get_report_nextcheck *
+ * *
+ * Purpose: calculate report nextcheck *
+ * *
+ * Parameters: now - [IN] the current timestamp *
+ * cycle - [IN] the report cycle *
+ * weekdays - [IN] the week days report should be prepared, *
+ * bitmask (0x01 - Monday, 0x02 - Tuesday...) *
+ * start_time - [IN] the report start time in seconds after *
+ * midnight *
+ * tz - [IN] the report starting timezone *
+ * *
+ * Return value: The timestamp when the report must be prepared or -1 if an *
+ * error occurred. *
+ * *
+ ******************************************************************************/
+int zbx_get_report_nextcheck(int now, unsigned char cycle, unsigned char weekdays, int start_time,
+ const char *tz)
+{
+ struct tm *tm;
+ time_t yesterday = now - SEC_PER_DAY;
+ int nextcheck, tm_hour, tm_min, tm_sec;
+
+ if (NULL == (tm = zbx_localtime(&yesterday, tz)))
+ return -1;
+
+ tm_sec = start_time % 60;
+ start_time /= 60;
+ tm_min = start_time % 60;
+ start_time /= 60;
+ tm_hour = start_time;
+
+ do
+ {
+ /* handle midnight startup times */
+ if (0 == tm->tm_sec && 0 == tm->tm_min && 0 == tm->tm_hour)
+ zbx_tm_add(tm, 1, ZBX_TIME_UNIT_DAY);
+
+ switch (cycle)
+ {
+ case ZBX_REPORT_CYCLE_YEARLY:
+ zbx_tm_round_up(tm, ZBX_TIME_UNIT_YEAR);
+ break;
+ case ZBX_REPORT_CYCLE_MONTHLY:
+ zbx_tm_round_up(tm, ZBX_TIME_UNIT_MONTH);
+ break;
+ case ZBX_REPORT_CYCLE_WEEKLY:
+ if (0 == weekdays)
+ return -1;
+ zbx_tm_round_up(tm, ZBX_TIME_UNIT_DAY);
+
+ while (0 == (weekdays & (1 << (tm->tm_wday + 6) % 7)))
+ zbx_tm_add(tm, 1, ZBX_TIME_UNIT_DAY);
+
+ break;
+ case ZBX_REPORT_CYCLE_DAILY:
+ zbx_tm_round_up(tm, ZBX_TIME_UNIT_DAY);
+ break;
+ }
+
+ tm->tm_sec = tm_sec;
+ tm->tm_min = tm_min;
+ tm->tm_hour = tm_hour;
+
+ nextcheck = (int)mktime(tm);
+ }
+ while (-1 != nextcheck && nextcheck <= now);
+
+ return nextcheck;
+}
diff --git a/src/libs/zbxcommon/str.c b/src/libs/zbxcommon/str.c
index a096d879253..e82be3e1b4c 100644
--- a/src/libs/zbxcommon/str.c
+++ b/src/libs/zbxcommon/str.c
@@ -1357,6 +1357,10 @@ const char *get_process_type_string(unsigned char proc_type)
return "history poller";
case ZBX_PROCESS_TYPE_AVAILMAN:
return "availability manager";
+ case ZBX_PROCESS_TYPE_REPORTMANAGER:
+ return "report manager";
+ case ZBX_PROCESS_TYPE_REPORTWRITER:
+ return "report writer";
}
THIS_SHOULD_NEVER_HAPPEN;
diff --git a/src/libs/zbxcrypto/Makefile.am b/src/libs/zbxcrypto/Makefile.am
index 9247424b8d3..42ff338d829 100644
--- a/src/libs/zbxcrypto/Makefile.am
+++ b/src/libs/zbxcrypto/Makefile.am
@@ -7,6 +7,8 @@ libzbxcrypto_a_SOURCES = \
md5.c \
sha256crypt.c \
sha512crypt.c \
+ aes.c \
+ aes.h \
tls.c \
tls.h \
tls_tcp.h \
diff --git a/src/libs/zbxcrypto/aes.c b/src/libs/zbxcrypto/aes.c
new file mode 100644
index 00000000000..0016d0862df
--- /dev/null
+++ b/src/libs/zbxcrypto/aes.c
@@ -0,0 +1,567 @@
+/*
+ *
+ * This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
+ * Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
+ *
+ * The implementation is verified against the test vectors in:
+ * National Institute of Standards and Technology Special Publication 800-38A 2001 ED
+ *
+ * ECB-AES128
+ * ----------
+ *
+ * plain-text:
+ * 6bc1bee22e409f96e93d7e117393172a
+ * ae2d8a571e03ac9c9eb76fac45af8e51
+ * 30c81c46a35ce411e5fbc1191a0a52ef
+ * f69f2445df4f9b17ad2b417be66c3710
+ *
+ * key:
+ * 2b7e151628aed2a6abf7158809cf4f3c
+ *
+ * resulting cipher
+ * 3ad77bb40d7a3660a89ecaf32466ef97
+ * f5d3d58503b9699de785895a96fdbaaf
+ * 43b1cd7f598ece23881b00e3ed030688
+ * 7b0c785e27e8ad3f8223207104725dd4
+ *
+ *
+ * NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
+ * You should pad the end of the string with zeros if this is not the case.
+ * For AES192/256 the key size is proportionally larger.
+ *
+ */
+
+/*****************************************************************************/
+/* Includes: */
+/*****************************************************************************/
+#include <string.h> // CBC mode, for memset
+#include "aes.h"
+
+/*****************************************************************************/
+/* Defines: */
+/*****************************************************************************/
+// The number of columns comprising a state in AES. This is a constant in AES. Value=4
+#define Nb 4
+
+#if defined(AES256) && (AES256 == 1)
+# define Nk 8
+# define Nr 14
+#elif defined(AES192) && (AES192 == 1)
+# define Nk 6
+# define Nr 12
+#else
+# define Nk 4 // The number of 32 bit words in a key.
+# define Nr 10 // The number of rounds in AES Cipher.
+#endif
+
+// jcallan@github points out that declaring Multiply as a function
+// reduces code size considerably with the Keil ARM compiler.
+// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
+#ifndef MULTIPLY_AS_A_FUNCTION
+# define MULTIPLY_AS_A_FUNCTION 0
+#endif
+
+/*****************************************************************************/
+/* Private variables: */
+/*****************************************************************************/
+// state - array holding the intermediate results during decryption.
+typedef uint8_t state_t[4][4];
+
+// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+// The numbers below can be computed dynamically trading ROM for RAM -
+// This can be useful in (embedded) bootloader applications, where ROM is often limited.
+static const uint8_t sbox[256] =
+{
+ //0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca,
+ 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd,
+ 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23,
+ 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a,
+ 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20,
+ 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d,
+ 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38,
+ 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46,
+ 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3,
+ 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4,
+ 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
+ 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86,
+ 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55,
+ 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb,
+ 0x16 };
+
+#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+static const uint8_t rsbox[256] =
+{
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39,
+ 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
+ 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28,
+ 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68,
+ 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9,
+ 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1,
+ 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2,
+ 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37,
+ 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e,
+ 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78,
+ 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80,
+ 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c,
+ 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+#endif
+
+// The round constant word array, Rcon[i], contains the values given by
+// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+static const uint8_t Rcon[11] =
+{ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
+
+/*
+ * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
+ * that you can remove most of the elements in the Rcon array, because they are unused.
+ *
+ * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
+ *
+ * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed),
+ * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
+ */
+
+/*****************************************************************************/
+/* Private functions: */
+/*****************************************************************************/
+/*
+ * static uint8_t getSBoxValue(uint8_t num)
+ * {
+ * return sbox[num];
+ * }
+ */
+#define getSBoxValue(num) (sbox[(num)])
+
+// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
+static void KeyExpansion(uint8_t *RoundKey, const uint8_t *Key)
+{
+ unsigned i, j, k;
+ uint8_t tempa[4]; // Used for the column/row operations
+
+ // The first round key is the key itself.
+ for (i = 0; i < Nk; ++i)
+ {
+ RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
+ RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
+ RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
+ RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
+ }
+
+ // All other round keys are found from the previous round keys.
+ for (i = Nk; i < Nb * (Nr + 1); ++i)
+ {
+ {
+ k = (i - 1) * 4;
+ tempa[0] = RoundKey[k + 0];
+ tempa[1] = RoundKey[k + 1];
+ tempa[2] = RoundKey[k + 2];
+ tempa[3] = RoundKey[k + 3];
+
+ }
+
+ if (i % Nk == 0)
+ {
+ // This function shifts the 4 bytes in a word to the left once.
+ // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+
+ // Function RotWord()
+ {
+ const uint8_t u8tmp = tempa[0];
+ tempa[0] = tempa[1];
+ tempa[1] = tempa[2];
+ tempa[2] = tempa[3];
+ tempa[3] = u8tmp;
+ }
+
+ // SubWord() is a function that takes a four-byte input word and
+ // applies the S-box to each of the four bytes to produce an output word.
+
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+
+ tempa[0] = tempa[0] ^ Rcon[i / Nk];
+ }
+#if defined(AES256) && (AES256 == 1)
+ if (i % Nk == 4)
+ {
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+ }
+#endif
+ j = i * 4;
+ k = (i - Nk) * 4;
+ RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
+ RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
+ RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
+ RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
+ }
+}
+
+void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key)
+{
+ KeyExpansion(ctx->RoundKey, key);
+}
+#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
+void AES_init_ctx_iv(struct AES_ctx *ctx, const uint8_t *key, const uint8_t *iv)
+{
+ KeyExpansion(ctx->RoundKey, key);
+ memcpy(ctx->Iv, iv, AES_BLOCKLEN);
+}
+void AES_ctx_set_iv(struct AES_ctx *ctx, const uint8_t *iv)
+{
+ memcpy(ctx->Iv, iv, AES_BLOCKLEN);
+}
+#endif
+
+// This function adds the round key to state.
+// The round key is added to the state by an XOR function.
+static void AddRoundKey(uint8_t round, state_t *state, const uint8_t *RoundKey)
+{
+ uint8_t i, j;
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 4; ++j)
+ {
+ (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
+ }
+ }
+}
+
+// The SubBytes Function Substitutes the values in the
+// state matrix with values in an S-box.
+static void SubBytes(state_t *state)
+{
+ uint8_t i, j;
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 4; ++j)
+ {
+ (*state)[j][i] = getSBoxValue((*state)[j][i]);
+ }
+ }
+}
+
+// The ShiftRows() function shifts the rows in the state to the left.
+// Each row is shifted with different offset.
+// Offset = Row number. So the first row is not shifted.
+static void ShiftRows(state_t *state)
+{
+ uint8_t temp;
+
+ // Rotate first row 1 columns to left
+ temp = (*state)[0][1];
+ (*state)[0][1] = (*state)[1][1];
+ (*state)[1][1] = (*state)[2][1];
+ (*state)[2][1] = (*state)[3][1];
+ (*state)[3][1] = temp;
+
+ // Rotate second row 2 columns to left
+ temp = (*state)[0][2];
+ (*state)[0][2] = (*state)[2][2];
+ (*state)[2][2] = temp;
+
+ temp = (*state)[1][2];
+ (*state)[1][2] = (*state)[3][2];
+ (*state)[3][2] = temp;
+
+ // Rotate third row 3 columns to left
+ temp = (*state)[0][3];
+ (*state)[0][3] = (*state)[3][3];
+ (*state)[3][3] = (*state)[2][3];
+ (*state)[2][3] = (*state)[1][3];
+ (*state)[1][3] = temp;
+}
+
+static uint8_t xtime(uint8_t x)
+{
+ return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
+}
+
+// MixColumns function mixes the columns of the state matrix
+static void MixColumns(state_t *state)
+{
+ uint8_t i;
+ uint8_t Tmp, Tm, t;
+ for (i = 0; i < 4; ++i)
+ {
+ t = (*state)[i][0];
+ Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3];
+ Tm = (*state)[i][0] ^ (*state)[i][1];
+ Tm = xtime(Tm);
+ (*state)[i][0] ^= Tm ^ Tmp;
+ Tm = (*state)[i][1] ^ (*state)[i][2];
+ Tm = xtime(Tm);
+ (*state)[i][1] ^= Tm ^ Tmp;
+ Tm = (*state)[i][2] ^ (*state)[i][3];
+ Tm = xtime(Tm);
+ (*state)[i][2] ^= Tm ^ Tmp;
+ Tm = (*state)[i][3] ^ t;
+ Tm = xtime(Tm);
+ (*state)[i][3] ^= Tm ^ Tmp;
+ }
+}
+
+// Multiply is used to multiply numbers in the field GF(2^8)
+// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
+// The compiler seems to be able to vectorize the operation better this way.
+// See https://github.com/kokke/tiny-AES-c/pull/34
+#if MULTIPLY_AS_A_FUNCTION
+static uint8_t Multiply(uint8_t x, uint8_t y)
+{
+ return (((y & 1) * x) ^
+ ((y>>1 & 1) * xtime(x)) ^
+ ((y>>2 & 1) * xtime(xtime(x))) ^
+ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
+ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
+}
+#else
+# define Multiply(x, y) \
+ ( ((y & 1) * x) ^ \
+ ((y>>1 & 1) * xtime(x)) ^ \
+ ((y>>2 & 1) * xtime(xtime(x))) ^ \
+ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
+ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
+
+#endif
+
+#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+/*
+ * static uint8_t getSBoxInvert(uint8_t num)
+ * {
+ * return rsbox[num];
+ * }
+ */
+#define getSBoxInvert(num) (rsbox[(num)])
+
+// MixColumns function mixes the columns of the state matrix.
+// The method used to multiply may be difficult to understand for the inexperienced.
+// Please use the references to gain more information.
+static void InvMixColumns(state_t *state)
+{
+ int i;
+ uint8_t a, b, c, d;
+ for (i = 0; i < 4; ++i)
+ {
+ a = (*state)[i][0];
+ b = (*state)[i][1];
+ c = (*state)[i][2];
+ d = (*state)[i][3];
+
+ (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
+ (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
+ (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
+ (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
+ }
+}
+
+// The SubBytes Function Substitutes the values in the
+// state matrix with values in an S-box.
+static void InvSubBytes(state_t *state)
+{
+ uint8_t i, j;
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 4; ++j)
+ {
+ (*state)[j][i] = getSBoxInvert((*state)[j][i]);
+ }
+ }
+}
+
+static void InvShiftRows(state_t *state)
+{
+ uint8_t temp;
+
+ // Rotate first row 1 columns to right
+ temp = (*state)[3][1];
+ (*state)[3][1] = (*state)[2][1];
+ (*state)[2][1] = (*state)[1][1];
+ (*state)[1][1] = (*state)[0][1];
+ (*state)[0][1] = temp;
+
+ // Rotate second row 2 columns to right
+ temp = (*state)[0][2];
+ (*state)[0][2] = (*state)[2][2];
+ (*state)[2][2] = temp;
+
+ temp = (*state)[1][2];
+ (*state)[1][2] = (*state)[3][2];
+ (*state)[3][2] = temp;
+
+ // Rotate third row 3 columns to right
+ temp = (*state)[0][3];
+ (*state)[0][3] = (*state)[1][3];
+ (*state)[1][3] = (*state)[2][3];
+ (*state)[2][3] = (*state)[3][3];
+ (*state)[3][3] = temp;
+}
+#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+
+// Cipher is the main function that encrypts the PlainText.
+static void Cipher(state_t *state, const uint8_t *RoundKey)
+{
+ uint8_t round = 0;
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(0, state, RoundKey);
+
+ // There will be Nr rounds.
+ // The first Nr-1 rounds are identical.
+ // These Nr rounds are executed in the loop below.
+ // Last one without MixColumns()
+ for (round = 1;; ++round)
+ {
+ SubBytes(state);
+ ShiftRows(state);
+ if (round == Nr)
+ {
+ break;
+ }
+ MixColumns(state);
+ AddRoundKey(round, state, RoundKey);
+ }
+ // Add round key to last round
+ AddRoundKey(Nr, state, RoundKey);
+}
+
+#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+static void InvCipher(state_t *state, const uint8_t *RoundKey)
+{
+ uint8_t round = 0;
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(Nr, state, RoundKey);
+
+ // There will be Nr rounds.
+ // The first Nr-1 rounds are identical.
+ // These Nr rounds are executed in the loop below.
+ // Last one without InvMixColumn()
+ for (round = (Nr - 1);; --round)
+ {
+ InvShiftRows(state);
+ InvSubBytes(state);
+ AddRoundKey(round, state, RoundKey);
+ if (round == 0)
+ {
+ break;
+ }
+ InvMixColumns(state);
+ }
+
+}
+#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+
+/*****************************************************************************/
+/* Public functions: */
+/*****************************************************************************/
+#if defined(ECB) && (ECB == 1)
+
+void AES_ECB_encrypt(const struct AES_ctx *ctx, uint8_t *buf)
+{
+ // The next function call encrypts the PlainText with the Key using AES algorithm.
+ Cipher((state_t*) buf, ctx->RoundKey);
+}
+
+void AES_ECB_decrypt(const struct AES_ctx *ctx, uint8_t *buf)
+{
+ // The next function call decrypts the PlainText with the Key using AES algorithm.
+ InvCipher((state_t*) buf, ctx->RoundKey);
+}
+
+#endif // #if defined(ECB) && (ECB == 1)
+
+#if defined(CBC) && (CBC == 1)
+
+static void XorWithIv(uint8_t *buf, const uint8_t *Iv)
+{
+ uint8_t i;
+ for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
+ {
+ buf[i] ^= Iv[i];
+ }
+}
+
+void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, size_t length)
+{
+ size_t i;
+ uint8_t *Iv = ctx->Iv;
+ for (i = 0; i < length; i += AES_BLOCKLEN)
+ {
+ XorWithIv(buf, Iv);
+ Cipher((state_t*) buf, ctx->RoundKey);
+ Iv = buf;
+ buf += AES_BLOCKLEN;
+ }
+ /* store Iv in ctx for next call */
+ memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
+}
+
+void AES_CBC_decrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, size_t length)
+{
+ size_t i;
+ uint8_t storeNextIv[AES_BLOCKLEN];
+ for (i = 0; i < length; i += AES_BLOCKLEN)
+ {
+ memcpy(storeNextIv, buf, AES_BLOCKLEN);
+ InvCipher((state_t*) buf, ctx->RoundKey);
+ XorWithIv(buf, ctx->Iv);
+ memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
+ buf += AES_BLOCKLEN;
+ }
+
+}
+
+#endif // #if defined(CBC) && (CBC == 1)
+
+#if defined(CTR) && (CTR == 1)
+
+/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
+void AES_CTR_xcrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, size_t length)
+{
+ uint8_t buffer[AES_BLOCKLEN];
+
+ size_t i;
+ int bi;
+ for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
+ {
+ if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
+ {
+
+ memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
+ Cipher((state_t*) buffer, ctx->RoundKey);
+
+ /* Increment Iv and handle overflow */
+ for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
+ {
+ /* inc will overflow */
+ if (ctx->Iv[bi] == 255)
+ {
+ ctx->Iv[bi] = 0;
+ continue;
+ }
+ ctx->Iv[bi] += 1;
+ break;
+ }
+ bi = 0;
+ }
+
+ buf[i] = (buf[i] ^ buffer[bi]);
+ }
+}
+
+#endif // #if defined(CTR) && (CTR == 1)
+
diff --git a/src/libs/zbxcrypto/aes.h b/src/libs/zbxcrypto/aes.h
new file mode 100644
index 00000000000..4527cf415c9
--- /dev/null
+++ b/src/libs/zbxcrypto/aes.h
@@ -0,0 +1,87 @@
+#ifndef _AES_H_
+#define _AES_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+// #define the macros below to 1/0 to enable/disable the mode of operation.
+//
+// CBC enables AES encryption in CBC-mode of operation.
+// CTR enables encryption in counter-mode.
+// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
+
+// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
+#ifndef CBC
+# define CBC 1
+#endif
+
+#ifndef ECB
+# define ECB 1
+#endif
+
+#ifndef CTR
+# define CTR 1
+#endif
+
+//#define AES128 1
+//#define AES192 1
+#define AES256 1
+
+#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
+
+#if defined(AES256) && (AES256 == 1)
+# define AES_KEYLEN 32
+# define AES_keyExpSize 240
+#elif defined(AES192) && (AES192 == 1)
+# define AES_KEYLEN 24
+# define AES_keyExpSize 208
+#else
+# define AES_KEYLEN 16 // Key length in bytes
+# define AES_keyExpSize 176
+#endif
+
+struct AES_ctx
+{
+ uint8_t RoundKey[AES_keyExpSize];
+#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
+ uint8_t Iv[AES_BLOCKLEN];
+#endif
+};
+
+void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key);
+#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
+void AES_init_ctx_iv(struct AES_ctx *ctx, const uint8_t *key, const uint8_t *iv);
+void AES_ctx_set_iv(struct AES_ctx *ctx, const uint8_t *iv);
+#endif
+
+#if defined(ECB) && (ECB == 1)
+// buffer size is exactly AES_BLOCKLEN bytes;
+// you need only AES_init_ctx as IV is not used in ECB
+// NB: ECB is considered insecure for most uses
+void AES_ECB_encrypt(const struct AES_ctx *ctx, uint8_t *buf);
+void AES_ECB_decrypt(const struct AES_ctx *ctx, uint8_t *buf);
+
+#endif // #if defined(ECB) && (ECB == !)
+
+#if defined(CBC) && (CBC == 1)
+// buffer size MUST be mutile of AES_BLOCKLEN;
+// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
+// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
+// no IV should ever be reused with the same key
+void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, size_t length);
+void AES_CBC_decrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, size_t length);
+
+#endif // #if defined(CBC) && (CBC == 1)
+
+#if defined(CTR) && (CTR == 1)
+
+// Same function for encrypting as for decrypting.
+// IV is incremented for every block, and used after encryption as XOR-compliment for output
+// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
+// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
+// no IV should ever be reused with the same key
+void AES_CTR_xcrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, size_t length);
+
+#endif // #if defined(CTR) && (CTR == 1)
+
+#endif // _AES_H_
diff --git a/src/libs/zbxcrypto/sha256crypt.c b/src/libs/zbxcrypto/sha256crypt.c
index bfe49858080..4e7b5202a70 100644
--- a/src/libs/zbxcrypto/sha256crypt.c
+++ b/src/libs/zbxcrypto/sha256crypt.c
@@ -19,7 +19,6 @@ Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>. */
#include <errno.h>
#include <limits.h>
#include <stdint.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/libs/zbxcrypto/sha512crypt.c b/src/libs/zbxcrypto/sha512crypt.c
index 5e7b0b25cb8..f3707b23245 100644
--- a/src/libs/zbxcrypto/sha512crypt.c
+++ b/src/libs/zbxcrypto/sha512crypt.c
@@ -18,7 +18,6 @@ Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>. */
#endif
#include <errno.h>
#include <limits.h>
-#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/src/libs/zbxdbcache/dbcache.c b/src/libs/zbxdbcache/dbcache.c
index 933805ab09a..273f2127d2f 100644
--- a/src/libs/zbxdbcache/dbcache.c
+++ b/src/libs/zbxdbcache/dbcache.c
@@ -40,6 +40,7 @@
#include "zbxavailability.h"
#include "zbxtrends.h"
#include "zbxalgo.h"
+#include "../zbxalgo/vectorimpl.h"
static zbx_mem_info_t *hc_index_mem = NULL;
static zbx_mem_info_t *hc_mem = NULL;
@@ -188,6 +189,9 @@ static int hc_queue_elem_compare_func(const void *d1, const void *d2);
static int hc_queue_get_size(void);
static int hc_get_history_compression_age(void);
+ZBX_PTR_VECTOR_DECL(item_tag, zbx_tag_t)
+ZBX_PTR_VECTOR_IMPL(item_tag, zbx_tag_t)
+
/******************************************************************************
* *
* Function: DCget_stats_all *
@@ -1043,7 +1047,7 @@ typedef struct
zbx_uint64_t itemid;
char *name;
DC_ITEM *item;
- zbx_vector_ptr_t applications;
+ zbx_vector_item_tag_t item_tags;
}
zbx_item_info_t;
@@ -1051,9 +1055,9 @@ zbx_item_info_t;
* *
* Function: db_get_items_info_by_itemid *
* *
- * Purpose: get items name and applications *
+ * Purpose: get items name and item tags *
* *
- * Parameters: items_info - [IN/OUT] output item name and applications *
+ * Parameters: items_info - [IN/OUT] output item name and item tags *
* itemids - [IN] the item identifiers *
* *
******************************************************************************/
@@ -1087,12 +1091,9 @@ static void db_get_items_info_by_itemid(zbx_hashset_t *items_info, const zbx_vec
sql_offset = 0;
zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select i.itemid,a.name"
- " from applications a,items_applications i"
- " where a.applicationid=i.applicationid"
- " and");
+ "select itemid,tag,value from item_tag where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.itemid", itemids->values, itemids->values_num);
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", itemids->values, itemids->values_num);
result = DBselect("%s", sql);
@@ -1100,6 +1101,7 @@ static void db_get_items_info_by_itemid(zbx_hashset_t *items_info, const zbx_vec
{
zbx_uint64_t itemid;
zbx_item_info_t *item_info;
+ zbx_tag_t item_tag;
ZBX_DBROW2UINT64(itemid, row[0]);
@@ -1109,24 +1111,41 @@ static void db_get_items_info_by_itemid(zbx_hashset_t *items_info, const zbx_vec
continue;
}
- zbx_vector_ptr_append(&item_info->applications, zbx_strdup(NULL, row[1]));
+ item_tag.tag = zbx_strdup(NULL, row[1]);
+ item_tag.value = zbx_strdup(NULL, row[2]);
+ zbx_vector_item_tag_append(&item_info->item_tags, item_tag);
}
DBfree_result(result);
}
/******************************************************************************
* *
+ * Function: item_tag_free *
+ * *
+ * Purpose: frees resources allocated to store item tag *
+ * *
+ * Parameters: item_tag - [IN] item tag *
+ * *
+ ******************************************************************************/
+static void item_tag_free(zbx_tag_t item_tag)
+{
+ zbx_free(item_tag.tag);
+ zbx_free(item_tag.value);
+}
+
+/******************************************************************************
+ * *
* Function: zbx_item_info_clean *
* *
- * Purpose: frees resources allocated to store item applications and name *
+ * Purpose: frees resources allocated to store item tags and name *
* *
* Parameters: item_info - [IN] item information *
* *
******************************************************************************/
static void zbx_item_info_clean(zbx_item_info_t *item_info)
{
- zbx_vector_ptr_clear_ext(&item_info->applications, zbx_ptr_free);
- zbx_vector_ptr_destroy(&item_info->applications);
+ zbx_vector_item_tag_clear_ext(&item_info->item_tags, item_tag_free);
+ zbx_vector_item_tag_destroy(&item_info->item_tags);
zbx_free(item_info->name);
}
@@ -1139,7 +1158,7 @@ static void zbx_item_info_clean(zbx_item_info_t *item_info)
* Parameters: trends - [IN] trends from cache *
* trends_num - [IN] number of trends *
* hosts_info - [IN] hosts groups names *
- * items_info - [IN] item names and applications *
+ * items_info - [IN] item names and tags *
* *
******************************************************************************/
static void DCexport_trends(const ZBX_DC_TREND *trends, int trends_num, zbx_hashset_t *hosts_info,
@@ -1184,10 +1203,17 @@ static void DCexport_trends(const ZBX_DC_TREND *trends, int trends_num, zbx_hash
zbx_json_close(&json);
- zbx_json_addarray(&json, ZBX_PROTO_TAG_APPLICATIONS);
+ zbx_json_addarray(&json, ZBX_PROTO_TAG_ITEM_TAGS);
+
+ for (j = 0; j < item_info->item_tags.values_num; j++)
+ {
+ zbx_tag_t item_tag = item_info->item_tags.values[j];
- for (j = 0; j < item_info->applications.values_num; j++)
- zbx_json_addstring(&json, NULL, item_info->applications.values[j], ZBX_JSON_TYPE_STRING);
+ zbx_json_addobject(&json, NULL);
+ zbx_json_addstring(&json, ZBX_PROTO_TAG_TAG, item_tag.tag, ZBX_JSON_TYPE_STRING);
+ zbx_json_addstring(&json, ZBX_PROTO_TAG_VALUE, item_tag.value, ZBX_JSON_TYPE_STRING);
+ zbx_json_close(&json);
+ }
zbx_json_close(&json);
zbx_json_adduint64(&json, ZBX_PROTO_TAG_ITEMID, item->itemid);
@@ -1232,7 +1258,7 @@ static void DCexport_trends(const ZBX_DC_TREND *trends, int trends_num, zbx_hash
* Parameters: history - [IN/OUT] array of history data *
* history_num - [IN] number of history structures *
* hosts_info - [IN] hosts groups names *
- * items_info - [IN] item names and applications *
+ * items_info - [IN] item names and tags *
* *
******************************************************************************/
static void DCexport_history(const ZBX_DC_HISTORY *history, int history_num, zbx_hashset_t *hosts_info,
@@ -1282,10 +1308,17 @@ static void DCexport_history(const ZBX_DC_HISTORY *history, int history_num, zbx
zbx_json_close(&json);
- zbx_json_addarray(&json, ZBX_PROTO_TAG_APPLICATIONS);
+ zbx_json_addarray(&json, ZBX_PROTO_TAG_ITEM_TAGS);
+
+ for (j = 0; j < item_info->item_tags.values_num; j++)
+ {
+ zbx_tag_t item_tag = item_info->item_tags.values[j];
- for (j = 0; j < item_info->applications.values_num; j++)
- zbx_json_addstring(&json, NULL, item_info->applications.values[j], ZBX_JSON_TYPE_STRING);
+ zbx_json_addobject(&json, NULL);
+ zbx_json_addstring(&json, ZBX_PROTO_TAG_TAG, item_tag.tag, ZBX_JSON_TYPE_STRING);
+ zbx_json_addstring(&json, ZBX_PROTO_TAG_VALUE, item_tag.value, ZBX_JSON_TYPE_STRING);
+ zbx_json_close(&json);
+ }
zbx_json_close(&json);
zbx_json_adduint64(&json, ZBX_PROTO_TAG_ITEMID, item->itemid);
@@ -1389,7 +1422,7 @@ static void DCexport_history_and_trends(const ZBX_DC_HISTORY *history, int histo
item_info.itemid = item->itemid;
item_info.name = NULL;
item_info.item = item;
- zbx_vector_ptr_create(&item_info.applications);
+ zbx_vector_item_tag_create(&item_info.item_tags);
zbx_hashset_insert(&items_info, &item_info, sizeof(item_info));
}
@@ -1417,7 +1450,7 @@ static void DCexport_history_and_trends(const ZBX_DC_HISTORY *history, int histo
item_info.itemid = item->itemid;
item_info.name = NULL;
item_info.item = item;
- zbx_vector_ptr_create(&item_info.applications);
+ zbx_vector_item_tag_create(&item_info.item_tags);
zbx_hashset_insert(&items_info, &item_info, sizeof(item_info));
}
}
diff --git a/src/libs/zbxdbcache/dbconfig.c b/src/libs/zbxdbcache/dbconfig.c
index 0294f8a6310..1586d0e78e7 100644
--- a/src/libs/zbxdbcache/dbconfig.c
+++ b/src/libs/zbxdbcache/dbconfig.c
@@ -2785,9 +2785,15 @@ static void DCsync_items(zbx_dbsync_t *sync, int flags)
{
item_hk_local.hostid = item->hostid;
item_hk_local.key = item->key;
- item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk, &item_hk_local);
- if (item == item_hk->item_ptr)
+ if (NULL == (item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk,
+ &item_hk_local)))
+ {
+ /* item keys should be unique for items within a host, otherwise items with */
+ /* same key share index and removal of last added item already cleared index */
+ THIS_SHOULD_NEVER_HAPPEN;
+ }
+ else if (item == item_hk->item_ptr)
{
zbx_strpool_release(item_hk->key);
zbx_hashset_remove_direct(&config->items_hk, item_hk);
@@ -2844,6 +2850,9 @@ static void DCsync_items(zbx_dbsync_t *sync, int flags)
item->poller_type = ZBX_NO_POLLER;
item->queue_priority = ZBX_QUEUE_PRIORITY_NORMAL;
item->schedulable = 1;
+
+ zbx_vector_ptr_create_ext(&item->tags, __config_mem_malloc_func, __config_mem_realloc_func,
+ __config_mem_free_func);
}
else
{
@@ -3482,9 +3491,14 @@ static void DCsync_items(zbx_dbsync_t *sync, int flags)
item_hk_local.hostid = item->hostid;
item_hk_local.key = item->key;
- item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk, &item_hk_local);
- if (item == item_hk->item_ptr)
+ if (NULL == (item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk, &item_hk_local)))
+ {
+ /* item keys should be unique for items within a host, otherwise items with */
+ /* same key share index and removal of last added item already cleared index */
+ THIS_SHOULD_NEVER_HAPPEN;
+ }
+ else if (item == item_hk->item_ptr)
{
zbx_strpool_release(item_hk->key);
zbx_hashset_remove_direct(&config->items_hk, item_hk);
@@ -3500,6 +3514,8 @@ static void DCsync_items(zbx_dbsync_t *sync, int flags)
if (NULL != item->triggers)
config->items.mem_free_func(item->triggers);
+ zbx_vector_ptr_destroy(&item->tags);
+
if (NULL != (preprocitem = (ZBX_DC_PREPROCITEM *)zbx_hashset_search(&config->preprocitems, &item->itemid)))
{
zbx_vector_ptr_destroy(&preprocitem->preproc_ops);
@@ -5069,6 +5085,91 @@ static void DCsync_trigger_tags(zbx_dbsync_t *sync)
/******************************************************************************
* *
+ * Function: DCsync_item_tags *
+ * *
+ * Purpose: Updates item tags in configuration cache *
+ * *
+ * Parameters: sync - [IN] the db synchronization data *
+ * *
+ * Comments: The result contains the following fields: *
+ * 0 - itemtagid *
+ * 1 - itemid *
+ * 2 - tag *
+ * 3 - value *
+ * *
+ ******************************************************************************/
+static void DCsync_item_tags(zbx_dbsync_t *sync)
+{
+ char **row;
+ zbx_uint64_t rowid;
+ unsigned char tag;
+ int found, ret, index;
+ zbx_uint64_t itemid, itemtagid;
+ ZBX_DC_ITEM *item;
+ zbx_dc_item_tag_t *item_tag;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ while (SUCCEED == (ret = zbx_dbsync_next(sync, &rowid, &row, &tag)))
+ {
+ /* removed rows will be always added at the end */
+ if (ZBX_DBSYNC_ROW_REMOVE == tag)
+ break;
+
+ ZBX_STR2UINT64(itemid, row[1]);
+
+ if (NULL == (item = (ZBX_DC_ITEM *)zbx_hashset_search(&config->items, &itemid)))
+ continue;
+
+ ZBX_STR2UINT64(itemtagid, row[0]);
+
+ item_tag = (zbx_dc_item_tag_t *)DCfind_id(&config->item_tags, itemtagid, sizeof(zbx_dc_item_tag_t),
+ &found);
+ DCstrpool_replace(found, &item_tag->tag, row[2]);
+ DCstrpool_replace(found, &item_tag->value, row[3]);
+
+ if (0 == found)
+ {
+ item_tag->itemid = itemid;
+ zbx_vector_ptr_append(&item->tags, item_tag);
+ }
+ }
+
+ /* remove unused item tags */
+
+ for (; SUCCEED == ret; ret = zbx_dbsync_next(sync, &rowid, &row, &tag))
+ {
+ if (NULL == (item_tag = (zbx_dc_item_tag_t *)zbx_hashset_search(&config->item_tags, &rowid)))
+ continue;
+
+ if (NULL != (item = (ZBX_DC_ITEM *)zbx_hashset_search(&config->items, &item_tag->itemid)))
+ {
+ if (FAIL != (index = zbx_vector_ptr_search(&item->tags, item_tag,
+ ZBX_DEFAULT_PTR_COMPARE_FUNC)))
+ {
+ zbx_vector_ptr_remove_noorder(&item->tags, index);
+
+ /* recreate empty tags vector to release used memory */
+ if (0 == item->tags.values_num)
+ {
+ zbx_vector_ptr_destroy(&item->tags);
+ zbx_vector_ptr_create_ext(&item->tags, __config_mem_malloc_func,
+ __config_mem_realloc_func, __config_mem_free_func);
+ }
+ }
+ }
+
+ zbx_strpool_release(item_tag->tag);
+ zbx_strpool_release(item_tag->value);
+
+ zbx_hashset_remove_direct(&config->item_tags, item_tag);
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
* Function: DCsync_host_tags *
* *
* Purpose: Updates host tags in configuration cache *
@@ -5703,13 +5804,14 @@ void DCsync_configuration(unsigned char mode, const struct zbx_json_parse *jp_kv
action_condition_sec2, trigger_tag_sec, trigger_tag_sec2, host_tag_sec, host_tag_sec2,
correlation_sec, correlation_sec2, corr_condition_sec, corr_condition_sec2, corr_operation_sec,
corr_operation_sec2, hgroups_sec, hgroups_sec2, itempp_sec, itempp_sec2, itemscrp_sec,
- itemscrp_sec2, total, total2, update_sec, maintenance_sec, maintenance_sec2;
+ itemscrp_sec2, total, total2, update_sec, maintenance_sec, maintenance_sec2, item_tag_sec,
+ item_tag_sec2;
zbx_dbsync_t config_sync, hosts_sync, hi_sync, htmpl_sync, gmacro_sync, hmacro_sync, if_sync, items_sync,
template_items_sync, prototype_items_sync, triggers_sync, tdep_sync, func_sync, expr_sync,
- action_sync, action_op_sync, action_condition_sync, trigger_tag_sync, host_tag_sync,
- correlation_sync, corr_condition_sync, corr_operation_sync, hgroups_sync, itempp_sync,
- itemscrp_sync, maintenance_sync, maintenance_period_sync, maintenance_tag_sync,
+ action_sync, action_op_sync, action_condition_sync, trigger_tag_sync, item_tag_sync,
+ host_tag_sync, correlation_sync, corr_condition_sync, corr_operation_sync, hgroups_sync,
+ itempp_sync, itemscrp_sync, maintenance_sync, maintenance_period_sync, maintenance_tag_sync,
maintenance_group_sync, maintenance_host_sync, hgroup_host_sync;
double autoreg_csec, autoreg_csec2;
@@ -5761,6 +5863,7 @@ void DCsync_configuration(unsigned char mode, const struct zbx_json_parse *jp_kv
zbx_dbsync_init(&action_condition_sync, mode);
zbx_dbsync_init(&trigger_tag_sync, mode);
+ zbx_dbsync_init(&item_tag_sync, mode);
zbx_dbsync_init(&host_tag_sync, mode);
zbx_dbsync_init(&correlation_sync, mode);
zbx_dbsync_init(&corr_condition_sync, mode);
@@ -5965,6 +6068,12 @@ void DCsync_configuration(unsigned char mode, const struct zbx_json_parse *jp_kv
DCsync_itemscript_param(&itemscrp_sync);
itemscrp_sec2 = zbx_time() - sec;
+ /* relies on items, must be after DCsync_items() */
+ sec = zbx_time();
+ if (FAIL == zbx_dbsync_compare_item_tags(&item_tag_sync))
+ goto out;
+ item_tag_sec = zbx_time() - sec;
+
config->item_sync_ts = time(NULL);
FINISH_SYNC;
@@ -6067,6 +6176,10 @@ void DCsync_configuration(unsigned char mode, const struct zbx_json_parse *jp_kv
trigger_tag_sec2 = zbx_time() - sec;
sec = zbx_time();
+ DCsync_item_tags(&item_tag_sync);
+ item_tag_sec2 = zbx_time() - sec;
+
+ sec = zbx_time();
DCsync_correlations(&correlation_sync);
correlation_sec2 = zbx_time() - sec;
@@ -6115,11 +6228,12 @@ void DCsync_configuration(unsigned char mode, const struct zbx_json_parse *jp_kv
{
total = csec + hsec + hisec + htsec + gmsec + hmsec + ifsec + isec + tsec + dsec + fsec + expr_sec +
action_sec + action_op_sec + action_condition_sec + trigger_tag_sec + correlation_sec +
- corr_condition_sec + corr_operation_sec + hgroups_sec + itempp_sec + maintenance_sec;
+ corr_condition_sec + corr_operation_sec + hgroups_sec + itempp_sec + maintenance_sec +
+ item_tag_sec;
total2 = csec2 + hsec2 + hisec2 + htsec2 + gmsec2 + hmsec2 + ifsec2 + isec2 + tsec2 + dsec2 + fsec2 +
expr_sec2 + action_op_sec2 + action_sec2 + action_condition_sec2 + trigger_tag_sec2 +
correlation_sec2 + corr_condition_sec2 + corr_operation_sec2 + hgroups_sec2 +
- itempp_sec2 + maintenance_sec2 + update_sec;
+ itempp_sec2 + maintenance_sec2 + item_tag_sec2 + update_sec;
zabbix_log(LOG_LEVEL_DEBUG, "%s() config : sql:" ZBX_FS_DBL " sync:" ZBX_FS_DBL " sec ("
ZBX_FS_UI64 "/" ZBX_FS_UI64 "/" ZBX_FS_UI64 ").",
@@ -6185,6 +6299,10 @@ void DCsync_configuration(unsigned char mode, const struct zbx_json_parse *jp_kv
ZBX_FS_UI64 "/" ZBX_FS_UI64 "/" ZBX_FS_UI64 ").",
__func__, host_tag_sec, host_tag_sec2, host_tag_sync.add_num,
host_tag_sync.update_num, host_tag_sync.remove_num);
+ zabbix_log(LOG_LEVEL_DEBUG, "%s() item tags : sql:" ZBX_FS_DBL " sync:" ZBX_FS_DBL " sec ("
+ ZBX_FS_UI64 "/" ZBX_FS_UI64 "/" ZBX_FS_UI64 ").",
+ __func__, item_tag_sec, item_tag_sec2, item_tag_sync.add_num,
+ item_tag_sync.update_num, item_tag_sync.remove_num);
zabbix_log(LOG_LEVEL_DEBUG, "%s() functions : sql:" ZBX_FS_DBL " sync:" ZBX_FS_DBL " sec ("
ZBX_FS_UI64 "/" ZBX_FS_UI64 "/" ZBX_FS_UI64 ").",
__func__, fsec, fsec2, func_sync.add_num, func_sync.update_num,
@@ -6406,6 +6524,7 @@ out:
zbx_dbsync_clear(&hgroups_sync);
zbx_dbsync_clear(&itempp_sync);
zbx_dbsync_clear(&itemscrp_sync);
+ zbx_dbsync_clear(&item_tag_sync);
zbx_dbsync_clear(&maintenance_sync);
zbx_dbsync_clear(&maintenance_period_sync);
zbx_dbsync_clear(&maintenance_tag_sync);
@@ -6810,6 +6929,7 @@ int init_configuration_cache(char **error)
CREATE_HASHSET(config->actions, 0);
CREATE_HASHSET(config->action_conditions, 0);
CREATE_HASHSET(config->trigger_tags, 0);
+ CREATE_HASHSET(config->item_tags, 0);
CREATE_HASHSET(config->host_tags, 0);
CREATE_HASHSET(config->host_tags_index, 0);
CREATE_HASHSET(config->correlations, 0);
@@ -13345,6 +13465,22 @@ void zbx_dc_cleanup_data_sessions(void)
UNLOCK_CACHE;
}
+static void zbx_gather_item_tags(ZBX_DC_ITEM *item, zbx_vector_ptr_t *item_tags)
+{
+ zbx_dc_item_tag_t *dc_tag;
+ zbx_item_tag_t *tag;
+ int i;
+
+ for (i = 0; i < item->tags.values_num; i++)
+ {
+ dc_tag = (zbx_dc_item_tag_t *)item->tags.values[i];
+ tag = (zbx_item_tag_t *) zbx_malloc(NULL, sizeof(zbx_item_tag_t));
+ tag->tag.tag = zbx_strdup(NULL, dc_tag->tag);
+ tag->tag.value = zbx_strdup(NULL, dc_tag->value);
+ zbx_vector_ptr_append(item_tags, tag);
+ }
+}
+
static void zbx_gather_tags_from_host(zbx_uint64_t hostid, zbx_vector_ptr_t *item_tags)
{
zbx_dc_host_tag_index_t *dc_tag_index;
@@ -13390,6 +13526,8 @@ static void zbx_get_item_tags(zbx_uint64_t itemid, zbx_vector_ptr_t *item_tags)
n = item_tags->values_num;
+ zbx_gather_item_tags(item, item_tags);
+
zbx_gather_tags_from_host(item->hostid, item_tags);
if (0 != item->templateid)
@@ -13415,6 +13553,15 @@ static void zbx_get_item_tags(zbx_uint64_t itemid, zbx_vector_ptr_t *item_tags)
}
}
+void zbx_dc_get_item_tags(zbx_uint64_t itemid, zbx_vector_ptr_t *item_tags)
+{
+ RDLOCK_CACHE;
+
+ zbx_get_item_tags(itemid, item_tags);
+
+ UNLOCK_CACHE;
+}
+
void zbx_dc_get_item_tags_by_functionids(const zbx_uint64_t *functionids, size_t functionids_num,
zbx_vector_ptr_t *item_tags)
{
diff --git a/src/libs/zbxdbcache/dbconfig.h b/src/libs/zbxdbcache/dbconfig.h
index b874a3d3e14..1ca6fdaffe7 100644
--- a/src/libs/zbxdbcache/dbconfig.h
+++ b/src/libs/zbxdbcache/dbconfig.h
@@ -104,6 +104,8 @@ typedef struct
unsigned char update_triggers;
zbx_uint64_t templateid;
zbx_uint64_t parent_itemid; /* from joined item_discovery table */
+
+ zbx_vector_ptr_t tags;
}
ZBX_DC_ITEM;
@@ -592,6 +594,15 @@ zbx_dc_trigger_tag_t;
typedef struct
{
+ zbx_uint64_t itemtagid;
+ zbx_uint64_t itemid;
+ const char *tag;
+ const char *value;
+}
+zbx_dc_item_tag_t;
+
+typedef struct
+{
zbx_uint64_t hosttagid;
zbx_uint64_t hostid;
const char *tag;
@@ -825,8 +836,9 @@ typedef struct
zbx_hashset_t actions;
zbx_hashset_t action_conditions;
zbx_hashset_t trigger_tags;
+ zbx_hashset_t item_tags;
zbx_hashset_t host_tags;
- zbx_hashset_t host_tags_index; /* host tag index by hostid */
+ zbx_hashset_t host_tags_index; /* host tag index by hostid */
zbx_hashset_t correlations;
zbx_hashset_t corr_conditions;
zbx_hashset_t corr_operations;
diff --git a/src/libs/zbxdbcache/dbconfig_dump.c b/src/libs/zbxdbcache/dbconfig_dump.c
index b6ab1f8c3ae..8d98bd711e7 100644
--- a/src/libs/zbxdbcache/dbconfig_dump.c
+++ b/src/libs/zbxdbcache/dbconfig_dump.c
@@ -576,6 +576,28 @@ typedef struct
}
zbx_trace_item_t;
+static void DCdump_item_tags(const ZBX_DC_ITEM *item)
+{
+ int i;
+ zbx_vector_ptr_t index;
+
+ zbx_vector_ptr_create(&index);
+
+ zbx_vector_ptr_append_array(&index, item->tags.values, item->tags.values_num);
+ zbx_vector_ptr_sort(&index, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
+
+ zabbix_log(LOG_LEVEL_TRACE, " tags:");
+
+ for (i = 0; i < index.values_num; i++)
+ {
+ zbx_dc_item_tag_t *tag = (zbx_dc_item_tag_t *)index.values[i];
+ zabbix_log(LOG_LEVEL_TRACE, " tagid:" ZBX_FS_UI64 " tag:'%s' value:'%s'",
+ tag->itemtagid, tag->tag, tag->value);
+ }
+
+ zbx_vector_ptr_destroy(&index);
+}
+
static void DCdump_items(void)
{
ZBX_DC_ITEM *item;
@@ -637,6 +659,9 @@ static void DCdump_items(void)
trace_items[j].dump_func(ptr);
}
+ if (0 != item->tags.values_num)
+ DCdump_item_tags(item);
+
if (NULL != item->triggers)
{
ZBX_DC_TRIGGER *trigger;
diff --git a/src/libs/zbxdbcache/dbsync.c b/src/libs/zbxdbcache/dbsync.c
index 4bd4ffb7961..bf0ae011e73 100644
--- a/src/libs/zbxdbcache/dbsync.c
+++ b/src/libs/zbxdbcache/dbsync.c
@@ -2990,6 +2990,110 @@ int zbx_dbsync_compare_trigger_tags(zbx_dbsync_t *sync)
/******************************************************************************
* *
+ * Function: dbsync_compare_item_tag *
+ * *
+ * Purpose: compares item tags table row with cached configuration data *
+ * *
+ * Parameter: tag - [IN] the cached item tag *
+ * dbrow - [IN] the database row *
+ * *
+ * Return value: SUCCEED - the row matches configuration data *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int dbsync_compare_item_tag(const zbx_dc_item_tag_t *tag, const DB_ROW dbrow)
+{
+ if (FAIL == dbsync_compare_uint64(dbrow[1], tag->itemid))
+ return FAIL;
+
+ if (FAIL == dbsync_compare_str(dbrow[2], tag->tag))
+ return FAIL;
+
+ if (FAIL == dbsync_compare_str(dbrow[3], tag->value))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_dbsync_compare_item_tags *
+ * *
+ * Purpose: compares item tags table with cached configuration data *
+ * *
+ * Parameter: sync - [OUT] the changeset *
+ * *
+ * Return value: SUCCEED - the changeset was successfully calculated *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+int zbx_dbsync_compare_item_tags(zbx_dbsync_t *sync)
+{
+ DB_ROW dbrow;
+ DB_RESULT result;
+ zbx_hashset_t ids;
+ zbx_hashset_iter_t iter;
+ zbx_uint64_t rowid;
+ zbx_dc_item_tag_t *item_tag;
+
+ if (NULL == (result = DBselect(
+ "select distinct it.itemtagid,it.itemid,it.tag,it.value"
+ " from item_tag it,items i,hosts h"
+ " where i.itemid=it.itemid"
+ " and i.flags in (%d,%d)"
+ " and h.hostid=i.hostid"
+ " and h.status in (%d,%d)",
+ ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED,
+ HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED)))
+ {
+ return FAIL;
+ }
+
+ dbsync_prepare(sync, 4, NULL);
+
+ if (ZBX_DBSYNC_INIT == sync->mode)
+ {
+ sync->dbresult = result;
+ return SUCCEED;
+ }
+
+ zbx_hashset_create(&ids, dbsync_env.cache->item_tags.num_data, ZBX_DEFAULT_UINT64_HASH_FUNC,
+ ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+
+ while (NULL != (dbrow = DBfetch(result)))
+ {
+ unsigned char tag = ZBX_DBSYNC_ROW_NONE;
+
+ ZBX_STR2UINT64(rowid, dbrow[0]);
+ zbx_hashset_insert(&ids, &rowid, sizeof(rowid));
+
+ if (NULL == (item_tag = (zbx_dc_item_tag_t *)zbx_hashset_search(&dbsync_env.cache->item_tags,
+ &rowid)))
+ {
+ tag = ZBX_DBSYNC_ROW_ADD;
+ }
+ else if (FAIL == dbsync_compare_item_tag(item_tag, dbrow))
+ tag = ZBX_DBSYNC_ROW_UPDATE;
+
+ if (ZBX_DBSYNC_ROW_NONE != tag)
+ dbsync_add_row(sync, rowid, tag, dbrow);
+ }
+
+ zbx_hashset_iter_reset(&dbsync_env.cache->item_tags, &iter);
+ while (NULL != (item_tag = (zbx_dc_item_tag_t *)zbx_hashset_iter_next(&iter)))
+ {
+ if (NULL == zbx_hashset_search(&ids, &item_tag->itemtagid))
+ dbsync_add_row(sync, item_tag->itemtagid, ZBX_DBSYNC_ROW_REMOVE, NULL);
+ }
+
+ zbx_hashset_destroy(&ids);
+ DBfree_result(result);
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
* Function: dbsync_compare_host_tag *
* *
* Purpose: compares host tags table row with cached configuration data *
diff --git a/src/libs/zbxdbcache/dbsync.h b/src/libs/zbxdbcache/dbsync.h
index b82f2889f4b..3d9207881f2 100644
--- a/src/libs/zbxdbcache/dbsync.h
+++ b/src/libs/zbxdbcache/dbsync.h
@@ -134,6 +134,7 @@ int zbx_dbsync_compare_actions(zbx_dbsync_t *sync);
int zbx_dbsync_compare_action_ops(zbx_dbsync_t *sync);
int zbx_dbsync_compare_action_conditions(zbx_dbsync_t *sync);
int zbx_dbsync_compare_trigger_tags(zbx_dbsync_t *sync);
+int zbx_dbsync_compare_item_tags(zbx_dbsync_t *sync);
int zbx_dbsync_compare_host_tags(zbx_dbsync_t *sync);
int zbx_dbsync_compare_correlations(zbx_dbsync_t *sync);
int zbx_dbsync_compare_corr_conditions(zbx_dbsync_t *sync);
diff --git a/src/libs/zbxdbcache/valuecache.c b/src/libs/zbxdbcache/valuecache.c
index 2835fad4d74..701cf0eec0d 100644
--- a/src/libs/zbxdbcache/valuecache.c
+++ b/src/libs/zbxdbcache/valuecache.c
@@ -64,10 +64,7 @@
static zbx_mem_info_t *vc_mem = NULL;
-static zbx_mutex_t vc_lock = ZBX_MUTEX_NULL;
-
-/* flag indicating that the cache was explicitly locked by this process */
-static int vc_locked = 0;
+zbx_rwlock_t vc_lock = ZBX_RWLOCK_NULL;
/* value cache enable/disable flags */
#define ZBX_VC_DISABLED 0
@@ -124,10 +121,6 @@ zbx_vc_chunk_t;
#define ZBX_VC_MAX_CHUNK_RECORDS ((64 * ZBX_KIBIBYTE - sizeof(zbx_vc_chunk_t)) / \
sizeof(zbx_history_record_t) + 1)
-/* the item operational state flags */
-#define ZBX_ITEM_STATE_CLEAN_PENDING 1
-#define ZBX_ITEM_STATE_REMOVE_PENDING 2
-
/* the value cache item data */
typedef struct
{
@@ -137,9 +130,6 @@ typedef struct
/* the item value type */
unsigned char value_type;
- /* the item operational state flags (ZBX_ITEM_STATE_*) */
- unsigned char state;
-
/* the item status flags (ZBX_ITEM_STATUS_*) */
unsigned char status;
@@ -156,10 +146,6 @@ typedef struct
/* in low memory situation. */
int last_accessed;
- /* reference counter indicating number of processes */
- /* accessing item */
- int refcount;
-
/* The range of the largest request in seconds. */
/* Used to determine if data can be removed from cache. */
int active_range;
@@ -243,9 +229,59 @@ zbx_vc_item_weight_t;
ZBX_VECTOR_DECL(vc_itemweight, zbx_vc_item_weight_t)
ZBX_VECTOR_IMPL(vc_itemweight, zbx_vc_item_weight_t)
+typedef enum
+{
+ ZBX_VC_UPDATE_STATS,
+ ZBX_VC_UPDATE_RANGE
+}
+zbx_vc_item_update_type_t;
+
+enum
+{
+ ZBX_VC_UPDATE_STATS_HITS,
+ ZBX_VC_UPDATE_STATS_MISSES
+};
+
+enum
+{
+ ZBX_VC_UPDATE_RANGE_SECONDS,
+ ZBX_VC_UPDATE_RANGE_NOW
+};
+
+typedef struct
+{
+ zbx_uint64_t itemid;
+ zbx_vc_item_update_type_t type;
+ int data[2];
+}
+zbx_vc_item_update_t;
+
+ZBX_VECTOR_DECL(vc_itemupdate, zbx_vc_item_update_t)
+ZBX_VECTOR_IMPL(vc_itemupdate, zbx_vc_item_update_t)
+
+static zbx_vector_vc_itemupdate_t vc_itemupdates;
+
+static void vc_cache_item_update(zbx_uint64_t itemid, zbx_vc_item_update_type_t type, int arg1, int arg2)
+{
+ zbx_vc_item_update_t *update;
+
+ if (vc_itemupdates.values_num == vc_itemupdates.values_alloc)
+ zbx_vector_vc_itemupdate_reserve(&vc_itemupdates, vc_itemupdates.values_alloc * 1.5);
+
+ update = &vc_itemupdates.values[vc_itemupdates.values_num++];
+ update->itemid = itemid;
+ update->type = type;
+ update->data[0] = arg1;
+ update->data[1] = arg2;
+}
+
/* the value cache */
static zbx_vc_cache_t *vc_cache = NULL;
+#define RDLOCK_CACHE zbx_rwlock_rdlock(vc_lock);
+#define WRLOCK_CACHE zbx_rwlock_wrlock(vc_lock);
+#define UNLOCK_CACHE zbx_rwlock_unlock(vc_lock);
+
/* function prototypes */
static void vc_history_record_copy(zbx_history_record_t *dst, const zbx_history_record_t *src, int value_type);
static void vc_history_record_vector_clean(zbx_vector_history_record_t *vector, int value_type);
@@ -255,34 +291,6 @@ static size_t vch_item_free_chunk(zbx_vc_item_t *item, zbx_vc_chunk_t *chunk);
static int vch_item_add_values_at_tail(zbx_vc_item_t *item, const zbx_history_record_t *values, int values_num);
static void vch_item_clean_cache(zbx_vc_item_t *item);
-/******************************************************************************
- * *
- * Function: vc_try_lock *
- * *
- * Purpose: locks the cache unless it was explicitly locked externally with *
- * zbx_vc_lock() call. *
- * *
- ******************************************************************************/
-static void vc_try_lock(void)
-{
- if (ZBX_VC_ENABLED == vc_state && 0 == vc_locked)
- zbx_mutex_lock(vc_lock);
-}
-
-/******************************************************************************
- * *
- * Function: vc_try_unlock *
- * *
- * Purpose: unlocks the cache locked by vc_try_lock() function unless it was *
- * explicitly locked externally with zbx_vc_lock() call. *
- * *
- ******************************************************************************/
-static void vc_try_unlock(void)
-{
- if (ZBX_VC_ENABLED == vc_state && 0 == vc_locked)
- zbx_mutex_unlock(vc_lock);
-}
-
/*********************************************************************************
* *
* Function: vc_db_read_values_by_time *
@@ -655,14 +663,14 @@ static void vc_history_record_vector_clean(zbx_vector_history_record_t *vector,
* added to both - item and cache statistics. *
* *
******************************************************************************/
-static void vc_update_statistics(zbx_vc_item_t *item, int hits, int misses)
+static void vc_update_statistics(zbx_vc_item_t *item, int hits, int misses, int now)
{
if (NULL != item)
{
int hour;
item->hits += hits;
- item->last_accessed = time(NULL);
+ item->last_accessed = now;
hour = item->last_accessed / SEC_PER_HOUR;
if (hour != item->hour)
@@ -805,7 +813,7 @@ static size_t vc_release_unused_items(const zbx_vc_item_t *source_item)
while (NULL != (item = (zbx_vc_item_t *)zbx_hashset_iter_next(&iter)))
{
- if (item->last_accessed < timestamp && 0 == item->refcount && source_item != item)
+ if (0 != item->last_accessed && item->last_accessed < timestamp && source_item != item)
{
freed += vch_item_free_cache(item) + sizeof(zbx_vc_item_t);
zbx_hashset_iter_remove(&iter);
@@ -828,9 +836,12 @@ static size_t vc_release_unused_items(const zbx_vc_item_t *source_item)
******************************************************************************/
void zbx_vc_housekeeping_value_cache(void)
{
- vc_try_lock();
+ if (ZBX_VC_DISABLED == vc_state)
+ return;
+
+ WRLOCK_CACHE;
vc_release_unused_items(NULL);
- vc_try_unlock();
+ UNLOCK_CACHE;
}
/******************************************************************************
@@ -881,7 +892,7 @@ static void vc_release_space(zbx_vc_item_t *source_item, size_t space)
{
/* don't remove the item that requested the space and also keep */
/* items currently being accessed */
- if (0 == item->refcount)
+ if (item != source_item)
{
zbx_vc_item_weight_t weight = {.item = item};
@@ -1213,47 +1224,23 @@ static void vc_remove_item(zbx_vc_item_t *item)
/******************************************************************************
* *
- * Function: vc_item_addref *
- * *
- * Purpose: increment item reference counter *
- * *
- * Parameters: item - [IN] the item *
- * *
- ******************************************************************************/
-static void vc_item_addref(zbx_vc_item_t *item)
-{
- item->refcount++;
-}
-
-/******************************************************************************
- * *
- * Function: vc_item_release *
- * *
- * Purpose: decrement item reference counter *
+ * Function: vc_remove_item_by_id *
* *
- * Parameters: item - [IN] the item *
+ * Purpose: removes item from cache and frees resources allocated for it *
* *
- * Comments: if the resulting reference counter is 0, then this function *
- * processes pending item operations *
+ * Parameters: itemid - [IN] the item identifier *
* *
******************************************************************************/
-static void vc_item_release(zbx_vc_item_t *item)
+static void vc_remove_item_by_id(zbx_uint64_t itemid)
{
- if (0 == (--item->refcount))
- {
- if (0 != (item->state & ZBX_ITEM_STATE_REMOVE_PENDING))
- {
- vc_remove_item(item);
- return;
- }
+ zbx_vc_item_t *item;
- if (0 != (item->state & ZBX_ITEM_STATE_CLEAN_PENDING))
- vch_item_clean_cache(item);
+ if (NULL == (item = (zbx_vc_item_t *)zbx_hashset_search(&vc_cache->items, &itemid)))
+ return;
- item->state = 0;
- }
+ vch_item_free_cache(item);
+ zbx_hashset_remove_direct(&vc_cache->items, item);
}
-
/******************************************************************************
* *
* Function: vc_item_update_db_cached_from *
@@ -1787,7 +1774,7 @@ static void vch_item_remove_values(zbx_vc_item_t *item, int timestamp)
item->status = 0;
/* try to remove chunks with all history values older than the timestamp */
- while (chunk->slots[chunk->first_value].timestamp.sec < timestamp)
+ while (NULL != chunk && chunk->slots[chunk->first_value].timestamp.sec < timestamp)
{
zbx_vc_chunk_t *next;
@@ -1807,15 +1794,6 @@ static void vch_item_remove_values(zbx_vc_item_t *item, int timestamp)
next = chunk->next;
vch_item_remove_chunk(item, chunk);
-
- /* empty items must be removed to avoid situation when a new value is added to cache */
- /* while other values with matching timestamp seconds are not cached */
- if (NULL == next)
- {
- item->state |= ZBX_ITEM_STATE_REMOVE_PENDING;
- break;
- }
-
chunk = next;
}
}
@@ -1840,7 +1818,7 @@ static void vch_item_remove_values(zbx_vc_item_t *item, int timestamp)
static int vch_item_add_value_at_head(zbx_vc_item_t *item, const zbx_history_record_t *value)
{
int ret = FAIL, index, sindex, nslots = 0;
- zbx_vc_chunk_t *head = item->head, *chunk, *schunk;
+ zbx_vc_chunk_t *chunk, *schunk;
if (NULL != item->head &&
0 < zbx_history_record_compare_asc_func(&item->head->slots[item->head->last_value], value))
@@ -1852,6 +1830,11 @@ static int vch_item_add_value_at_head(zbx_vc_item_t *item, const zbx_history_rec
/* values with matching timestamp seconds are kept in cache. */
vch_item_remove_values(item, value->timestamp.sec + 1);
+ /* empty items must be removed to avoid situation when a new value is added to cache */
+ /* while other values with matching timestamp seconds are not cached */
+ if (NULL == item->head)
+ goto out;
+
/* if the value is newer than the database cached from timestamp we must */
/* adjust the cached from timestamp to exclude this value */
if (item->db_cached_from <= value->timestamp.sec)
@@ -1922,10 +1905,6 @@ static int vch_item_add_value_at_head(zbx_vc_item_t *item, const zbx_history_rec
if (SUCCEED != vch_item_copy_value(item, chunk, index, value))
goto out;
- /* try to remove old (unused) chunks if a new chunk was added */
- if (head != item->head)
- item->state |= ZBX_ITEM_STATE_CLEAN_PENDING;
-
ret = SUCCEED;
out:
return ret;
@@ -2011,63 +1990,73 @@ out:
* updates cache from database if necessary. *
* *
******************************************************************************/
-static int vch_item_cache_values_by_time(zbx_vc_item_t *item, int range_start)
+static int vch_item_cache_values_by_time(zbx_vc_item_t **item, int range_start)
{
- int ret = SUCCEED, range_end;
+ int ret, range_end;
+ zbx_vector_history_record_t records;
+ zbx_uint64_t itemid;
+ unsigned char value_type;
- if (ZBX_ITEM_STATUS_CACHED_ALL == item->status)
+ if (ZBX_ITEM_STATUS_CACHED_ALL == (*item)->status)
return SUCCEED;
/* check if the requested period is in the cached range */
- if (0 != item->db_cached_from && range_start >= item->db_cached_from)
+ if (0 != (*item)->db_cached_from && range_start >= (*item)->db_cached_from)
return SUCCEED;
/* find if the cache should be updated to cover the required range */
- if (NULL != item->tail)
+ if (NULL != (*item)->tail)
{
/* we need to get item values before the first cached value, but not including it */
- range_end = item->tail->slots[item->tail->first_value].timestamp.sec - 1;
+ range_end = (*item)->tail->slots[(*item)->tail->first_value].timestamp.sec - 1;
}
else
range_end = ZBX_JAN_2038;
/* update cache if necessary */
- if (range_start < range_end)
- {
- zbx_vector_history_record_t records;
+ if (range_start >= range_end)
+ return SUCCEED;
- zbx_vector_history_record_create(&records);
+ zbx_vector_history_record_create(&records);
+ itemid = (*item)->itemid;
+ value_type = (*item)->value_type;
- vc_try_unlock();
+ UNLOCK_CACHE;
- if (SUCCEED == (ret = vc_db_read_values_by_time(item->itemid, item->value_type, &records,
- range_start, range_end)))
- {
- zbx_vector_history_record_sort(&records,
- (zbx_compare_func_t)zbx_history_record_compare_asc_func);
- }
+ if (SUCCEED == (ret = vc_db_read_values_by_time(itemid, value_type, &records, range_start, range_end)))
+ {
+ zbx_vector_history_record_sort(&records,
+ (zbx_compare_func_t)zbx_history_record_compare_asc_func);
+ }
- vc_try_lock();
+ WRLOCK_CACHE;
- if (SUCCEED == ret)
- {
- if (0 < records.values_num)
- ret = vch_item_add_values_at_tail(item, records.values, records.values_num);
+ if (SUCCEED != ret)
+ goto out;
- /* when updating cache with time based request we can always reset status flags */
- /* flag even if the requested period contains no data */
- item->status = 0;
+ if (NULL == (*item = (zbx_vc_item_t *)zbx_hashset_search(&vc_cache->items, &itemid)))
+ {
+ zbx_vc_item_t new_item = {.itemid = itemid, .value_type = value_type};
- if (SUCCEED == ret)
- {
- ret = records.values_num;
- vc_item_update_db_cached_from(item, range_start);
- }
+ if (NULL == (*item = (zbx_vc_item_t *)zbx_hashset_insert(&vc_cache->items, &new_item, sizeof(new_item))))
+ goto out;
+ }
- }
- zbx_history_record_vector_destroy(&records, item->value_type);
+ /* when updating cache with time based request we can always reset status flags */
+ /* flag even if the requested period contains no data */
+ (*item)->status = 0;
+
+ if (0 < records.values_num)
+ {
+ if (SUCCEED != (ret = vch_item_add_values_at_tail(*item, records.values, records.values_num)))
+ goto out;
}
+ ret = records.values_num;
+ vc_item_update_db_cached_from(*item, range_start);
+out:
+ zbx_history_record_vector_destroy(&records, value_type);
+
return ret;
}
@@ -2090,25 +2079,28 @@ static int vch_item_cache_values_by_time(zbx_vc_item_t *item, int range_start)
* and updates cache from database if necessary. *
* *
******************************************************************************/
-static int vch_item_cache_values_by_time_and_count(zbx_vc_item_t *item, int range_start, int count,
+static int vch_item_cache_values_by_time_and_count(zbx_vc_item_t **item, int range_start, int count,
const zbx_timespec_t *ts)
{
- int ret = SUCCEED, cached_records = 0, range_end;
+ int ret = SUCCEED, cached_records = 0, range_end, records_offset;
+ zbx_vector_history_record_t records;
+ zbx_uint64_t itemid;
+ unsigned char value_type;
- if (ZBX_ITEM_STATUS_CACHED_ALL == item->status)
+ if (ZBX_ITEM_STATUS_CACHED_ALL == (*item)->status)
return SUCCEED;
/* check if the requested period is in the cached range */
- if (0 != item->db_cached_from && range_start >= item->db_cached_from)
+ if (0 != (*item)->db_cached_from && range_start >= (*item)->db_cached_from)
return SUCCEED;
/* find if the cache should be updated to cover the required count */
- if (NULL != item->head)
+ if (NULL != (*item)->head)
{
zbx_vc_chunk_t *chunk;
int index;
- if (SUCCEED == vch_item_get_last_value(item, ts, &chunk, &index))
+ if (SUCCEED == vch_item_get_last_value(*item, ts, &chunk, &index))
{
cached_records = index - chunk->first_value + 1;
@@ -2118,58 +2110,75 @@ static int vch_item_cache_values_by_time_and_count(zbx_vc_item_t *item, int rang
}
/* update cache if necessary */
- if (cached_records < count)
+ if (cached_records >= count)
+ return SUCCEED;
+
+ /* get the end timestamp to which (including) the values should be cached */
+ if (NULL != (*item)->head)
+ range_end = (*item)->tail->slots[(*item)->tail->first_value].timestamp.sec - 1;
+ else
+ range_end = ZBX_JAN_2038;
+
+ itemid = (*item)->itemid;
+ value_type = (*item)->value_type;
+ UNLOCK_CACHE;
+
+ zbx_vector_history_record_create(&records);
+
+ if (range_end > ts->sec)
{
- zbx_vector_history_record_t records;
+ ret = vc_db_read_values_by_time(itemid, value_type, &records, ts->sec + 1, range_end);
+ range_end = ts->sec;
+ }
- /* get the end timestamp to which (including) the values should be cached */
- if (NULL != item->head)
- range_end = item->tail->slots[item->tail->first_value].timestamp.sec - 1;
- else
- range_end = ZBX_JAN_2038;
+ records_offset = records.values_num;
- vc_try_unlock();
+ if (SUCCEED == ret && SUCCEED == (ret = vc_db_read_values_by_time_and_count(itemid, value_type, &records,
+ range_start, count - cached_records, range_end, ts)))
+ {
+ zbx_vector_history_record_sort(&records,
+ (zbx_compare_func_t)zbx_history_record_compare_asc_func);
+ }
- zbx_vector_history_record_create(&records);
+ WRLOCK_CACHE;
- if (range_end > ts->sec)
- {
- ret = vc_db_read_values_by_time(item->itemid, item->value_type, &records, ts->sec + 1,
- range_end);
+ if (SUCCEED != ret)
+ goto out;
- range_end = ts->sec;
- }
+ if (NULL == (*item = (zbx_vc_item_t *)zbx_hashset_search(&vc_cache->items, &itemid)))
+ {
+ zbx_vc_item_t new_item = {.itemid = itemid, .value_type = value_type};
- if (SUCCEED == ret && SUCCEED == (ret = vc_db_read_values_by_time_and_count(item->itemid,
- item->value_type, &records, range_start, count - cached_records, range_end, ts)))
- {
- zbx_vector_history_record_sort(&records,
- (zbx_compare_func_t)zbx_history_record_compare_asc_func);
- }
+ if (NULL == (*item = (zbx_vc_item_t *)zbx_hashset_insert(&vc_cache->items, &new_item, sizeof(new_item))))
+ goto out;
+ }
- vc_try_lock();
+ if (0 < records.values_num)
+ ret = vch_item_add_values_at_tail(*item, records.values, records.values_num);
- if (SUCCEED == ret)
- {
- if (0 < records.values_num)
- ret = vch_item_add_values_at_tail(item, records.values, records.values_num);
+ if (SUCCEED != ret)
+ goto out;
- if (SUCCEED == ret)
- {
- ret = records.values_num;
- if ((count <= records.values_num || 0 == range_start) && 0 != records.values_num)
- {
- vc_item_update_db_cached_from(item,
- item->tail->slots[item->tail->first_value].timestamp.sec);
- }
- else if (0 != range_start)
- vc_item_update_db_cached_from(item, range_start);
- }
- }
+ ret = records.values_num;
- zbx_history_record_vector_destroy(&records, item->value_type);
+ if (0 == range_start && count - cached_records > records.values_num - records_offset)
+ {
+ (*item)->active_range = 0;
+ (*item)->daily_range = 0;
+ (*item)->status = ZBX_ITEM_STATUS_CACHED_ALL;
}
+ if ((count <= records.values_num || 0 == range_start) && 0 != records.values_num)
+ {
+ vc_item_update_db_cached_from(*item,
+ (*item)->tail->slots[(*item)->tail->first_value].timestamp.sec);
+ }
+ else if (0 != range_start)
+ vc_item_update_db_cached_from(*item, range_start);
+
+out:
+ zbx_history_record_vector_destroy(&records, value_type);
+
return ret;
}
@@ -2186,7 +2195,7 @@ static int vch_item_cache_values_by_time_and_count(zbx_vc_item_t *item, int rang
* ts - [IN] the requested period end timestamp *
* *
******************************************************************************/
-static void vch_item_get_values_by_time(zbx_vc_item_t *item, zbx_vector_history_record_t *values, int seconds,
+static void vch_item_get_values_by_time(const zbx_vc_item_t *item, zbx_vector_history_record_t *values, int seconds,
const zbx_timespec_t *ts)
{
int index, now;
@@ -2200,7 +2209,7 @@ static void vch_item_get_values_by_time(zbx_vc_item_t *item, zbx_vector_history_
{
now = time(NULL);
/* add another second to include nanosecond shifts */
- vch_item_update_range(item, seconds + now - ts->sec + 1, now);
+ vc_cache_item_update(item->itemid, ZBX_VC_UPDATE_RANGE, seconds + now - ts->sec + 1, now);
}
if (FAIL == vch_item_get_last_value(item, ts, &chunk, &index))
@@ -2287,13 +2296,8 @@ out:
if (count > values->values_num)
{
if (0 == seconds)
- {
- /* not enough data in db to fulfill a count based request request */
- item->active_range = 0;
- item->daily_range = 0;
- item->status = ZBX_ITEM_STATUS_CACHED_ALL;
return;
- }
+
/* not enough data in the requested period, set the range equal to the period plus */
/* one second to include nanosecond shifts */
range_timestamp = ts->sec - seconds;
@@ -2305,7 +2309,7 @@ out:
}
now = time(NULL);
- vch_item_update_range(item, now - range_timestamp, now);
+ vc_cache_item_update(item->itemid, ZBX_VC_UPDATE_RANGE, now - range_timestamp, now);
}
/******************************************************************************
@@ -2348,7 +2352,7 @@ static int vch_item_get_values(zbx_vc_item_t *item, zbx_vector_history_record_t
if (0 > (range_start = ts->sec - seconds))
range_start = 0;
- if (FAIL == (ret = vch_item_cache_values_by_time(item, range_start)))
+ if (FAIL == (ret = vch_item_cache_values_by_time(&item, range_start)))
goto out;
records_read = ret;
@@ -2362,7 +2366,7 @@ static int vch_item_get_values(zbx_vc_item_t *item, zbx_vector_history_record_t
{
range_start = (0 == seconds ? 0 : ts->sec - seconds);
- if (FAIL == (ret = vch_item_cache_values_by_time_and_count(item, range_start, count, ts)))
+ if (FAIL == (ret = vch_item_cache_values_by_time_and_count(&item, range_start, count, ts)))
goto out;
records_read = ret;
@@ -2376,7 +2380,7 @@ static int vch_item_get_values(zbx_vc_item_t *item, zbx_vector_history_record_t
hits = values->values_num - records_read;
misses = records_read;
- vc_update_statistics(item, hits, misses);
+ vc_cache_item_update(item->itemid, ZBX_VC_UPDATE_STATS, hits, misses);
ret = SUCCEED;
out:
@@ -2437,7 +2441,7 @@ int zbx_vc_init(char **error)
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
- if (SUCCEED != zbx_mutex_create(&vc_lock, ZBX_MUTEX_VALUECACHE, error))
+ if (SUCCEED != (ret = zbx_rwlock_create(&vc_lock, ZBX_RWLOCK_VALUECACHE, error)))
goto out;
size_reserved = zbx_mem_required_size(1, "value cache size", "ValueCacheSize");
@@ -2481,6 +2485,9 @@ int zbx_vc_init(char **error)
if (vc_cache->min_free_request > 128 * ZBX_KIBIBYTE)
vc_cache->min_free_request = 128 * ZBX_KIBIBYTE;
+ zbx_vector_vc_itemupdate_create(&vc_itemupdates);
+ zbx_vector_vc_itemupdate_reserve(&vc_itemupdates, 256);
+
ret = SUCCEED;
out:
zbx_vc_disable();
@@ -2503,7 +2510,9 @@ void zbx_vc_destroy(void)
if (NULL != vc_cache)
{
- zbx_mutex_destroy(&vc_lock);
+ zbx_vector_vc_itemupdate_destroy(&vc_itemupdates);
+
+ zbx_rwlock_destroy(&vc_lock);
zbx_hashset_destroy(&vc_cache->items);
zbx_hashset_destroy(&vc_cache->strpool);
@@ -2534,7 +2543,7 @@ void zbx_vc_reset(void)
zbx_vc_item_t *item;
zbx_hashset_iter_t iter;
- vc_try_lock();
+ WRLOCK_CACHE;
zbx_hashset_iter_reset(&vc_cache->items, &iter);
while (NULL != (item = (zbx_vc_item_t *)zbx_hashset_iter_next(&iter)))
@@ -2550,7 +2559,7 @@ void zbx_vc_reset(void)
vc_cache->mode_time = 0;
vc_cache->last_warning_time = 0;
- vc_try_unlock();
+ UNLOCK_CACHE;
}
zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
@@ -2583,7 +2592,7 @@ int zbx_vc_add_values(zbx_vector_ptr_t *history)
expire_timestamp = time(NULL) - ZBX_VC_ITEM_EXPIRE_PERIOD;
- vc_try_lock();
+ WRLOCK_CACHE;
for (i = 0; i < history->values_num; i++)
{
@@ -2592,30 +2601,29 @@ int zbx_vc_add_values(zbx_vector_ptr_t *history)
if (NULL != (item = (zbx_vc_item_t *)zbx_hashset_search(&vc_cache->items, &h->itemid)))
{
zbx_history_record_t record = {h->ts, h->value};
-
- if (0 == (item->state & ZBX_ITEM_STATE_REMOVE_PENDING))
+ zbx_vc_chunk_t *head = item->head;
+
+ /* If the new value type does not match the item's type in cache remove it, */
+ /* so it's cached with the correct type from correct tables when accessed */
+ /* next time. */
+ /* Also remove item if the value adding failed. In this case we */
+ /* won't have the latest data in cache - so the requests must go directly */
+ /* to the database. */
+ if (item->value_type != h->value_type || item->last_accessed < expire_timestamp ||
+ FAIL == vch_item_add_value_at_head(item, &record))
{
- vc_item_addref(item);
-
- /* If the new value type does not match the item's type in cache we can't */
- /* change the cache because other processes might still be accessing it */
- /* at the same time. The only thing that can be done - mark it for removal */
- /* so it could be added later with new type. */
- /* Also mark it for removal if the value adding failed. In this case we */
- /* won't have the latest data in cache - so the requests must go directly */
- /* to the database. */
- if (item->value_type != h->value_type || item->last_accessed < expire_timestamp ||
- FAIL == vch_item_add_value_at_head(item, &record))
- {
- item->state |= ZBX_ITEM_STATE_REMOVE_PENDING;
- }
-
- vc_item_release(item);
+ vc_remove_item(item);
+ continue;
}
+
+ /* try to remove old (unused) chunks if a new chunk was added */
+ if (head != item->head)
+ vch_item_clean_cache(item);
+
}
}
- vc_try_unlock();
+ UNLOCK_CACHE;
return SUCCEED;
}
@@ -2648,13 +2656,13 @@ int zbx_vc_add_values(zbx_vector_ptr_t *history)
int zbx_vc_get_values(zbx_uint64_t itemid, int value_type, zbx_vector_history_record_t *values, int seconds,
int count, const zbx_timespec_t *ts)
{
- zbx_vc_item_t *item = NULL;
+ zbx_vc_item_t *item, new_item;
int ret = FAIL, cache_used = 1;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() itemid:" ZBX_FS_UI64 " value_type:%d seconds:%d count:%d sec:%d ns:%d",
__func__, itemid, value_type, seconds, count, ts->sec, ts->ns);
- vc_try_lock();
+ RDLOCK_CACHE;
if (ZBX_VC_DISABLED == vc_state)
goto out;
@@ -2664,45 +2672,35 @@ int zbx_vc_get_values(zbx_uint64_t itemid, int value_type, zbx_vector_history_re
if (NULL == (item = (zbx_vc_item_t *)zbx_hashset_search(&vc_cache->items, &itemid)))
{
- if (ZBX_VC_MODE_NORMAL == vc_cache->mode)
- {
- zbx_vc_item_t new_item = {.itemid = itemid, .value_type = value_type};
-
- if (NULL == (item = (zbx_vc_item_t *)zbx_hashset_insert(&vc_cache->items, &new_item, sizeof(zbx_vc_item_t))))
- goto out;
- }
- else
+ if (ZBX_VC_MODE_NORMAL != vc_cache->mode)
goto out;
- }
- vc_item_addref(item);
-
- if (0 != (item->state & ZBX_ITEM_STATE_REMOVE_PENDING) || item->value_type != value_type)
+ memset(&new_item, 0, sizeof(new_item));
+ new_item.itemid = itemid;
+ new_item.value_type = value_type;
+ item = &new_item;
+ }
+ else if (item->value_type != value_type)
goto out;
ret = vch_item_get_values(item, values, seconds, count, ts);
out:
if (FAIL == ret)
{
- if (NULL != item)
- item->state |= ZBX_ITEM_STATE_REMOVE_PENDING;
-
cache_used = 0;
- vc_try_unlock();
-
+ UNLOCK_CACHE;
ret = vc_db_get_values(itemid, value_type, values, seconds, count, ts);
+ WRLOCK_CACHE;
- vc_try_lock();
+ if (ZBX_VC_DISABLED != vc_state)
+ vc_remove_item_by_id(itemid);
if (SUCCEED == ret)
- vc_update_statistics(NULL, 0, values->values_num);
+ vc_update_statistics(NULL, 0, values->values_num, time(NULL));
}
- if (NULL != item)
- vc_item_release(item);
-
- vc_try_unlock();
+ UNLOCK_CACHE;
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s count:%d cached:%d",
__func__, zbx_result_string(ret), values->values_num, cache_used);
@@ -2770,7 +2768,7 @@ int zbx_vc_get_statistics(zbx_vc_stats_t *stats)
if (ZBX_VC_DISABLED == vc_state)
return FAIL;
- vc_try_lock();
+ RDLOCK_CACHE;
stats->hits = vc_cache->hits;
stats->misses = vc_cache->misses;
@@ -2779,46 +2777,13 @@ int zbx_vc_get_statistics(zbx_vc_stats_t *stats)
stats->total_size = vc_mem->total_size;
stats->free_size = vc_mem->free_size;
- vc_try_unlock();
+ UNLOCK_CACHE;
return SUCCEED;
}
/******************************************************************************
* *
- * Function: zbx_vc_lock *
- * *
- * Purpose: locks the cache for batch usage *
- * *
- * Comments: Use zbx_vc_lock()/zbx_vc_unlock to explicitly lock/unlock cache *
- * for batch usage. The cache is automatically locked during every *
- * API call using the cache unless it was explicitly locked with *
- * zbx_vc_lock() function by the same process. *
- * *
- ******************************************************************************/
-void zbx_vc_lock(void)
-{
- zbx_mutex_lock(vc_lock);
- vc_locked = 1;
-}
-
-/******************************************************************************
- * *
- * Function: zbx_vc_unlock *
- * *
- * Purpose: unlocks cache after it has been locked with zbx_vc_lock() *
- * *
- * Comments: See zbx_vc_lock() function. *
- * *
- ******************************************************************************/
-void zbx_vc_unlock(void)
-{
- vc_locked = 0;
- zbx_mutex_unlock(vc_lock);
-}
-
-/******************************************************************************
- * *
* Function: zbx_vc_enable *
* *
* Purpose: enables value caching for current process *
@@ -2863,7 +2828,7 @@ void zbx_vc_get_diag_stats(zbx_uint64_t *items_num, zbx_uint64_t *values_num, in
return;
}
- vc_try_lock();
+ RDLOCK_CACHE;
*items_num = vc_cache->items.num_data;
*mode = vc_cache->mode;
@@ -2872,7 +2837,7 @@ void zbx_vc_get_diag_stats(zbx_uint64_t *items_num, zbx_uint64_t *values_num, in
while (NULL != (item = (zbx_vc_item_t *)zbx_hashset_iter_next(&iter)))
*values_num += item->values_total;
- vc_try_unlock();
+ UNLOCK_CACHE;
}
/******************************************************************************
@@ -2890,9 +2855,9 @@ void zbx_vc_get_mem_stats(zbx_mem_stats_t *mem)
return;
}
- vc_try_lock();
+ RDLOCK_CACHE;
zbx_mem_get_stats(vc_mem, mem);
- vc_try_unlock();
+ UNLOCK_CACHE;
}
/******************************************************************************
@@ -2911,7 +2876,7 @@ void zbx_vc_get_item_stats(zbx_vector_ptr_t *stats)
if (ZBX_VC_DISABLED == vc_state)
return;
- vc_try_lock();
+ RDLOCK_CACHE;
zbx_vector_ptr_reserve(stats, vc_cache->items.num_data);
@@ -2925,7 +2890,60 @@ void zbx_vc_get_item_stats(zbx_vector_ptr_t *stats)
zbx_vector_ptr_append(stats, item_stats);
}
- vc_try_unlock();
+ UNLOCK_CACHE;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_vc_flush_stats *
+ * *
+ * Purpose: flush locally cached statistics *
+ * *
+ ******************************************************************************/
+void zbx_vc_flush_stats(void)
+{
+ int i, now;
+ zbx_vc_item_t *item = NULL;
+ zbx_uint64_t itemid = 0;
+
+ if (ZBX_VC_DISABLED == vc_state || 0 == vc_itemupdates.values_num)
+ return;
+
+ zbx_vector_vc_itemupdate_sort(&vc_itemupdates, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+
+ now = time(NULL);
+
+ WRLOCK_CACHE;
+
+ for (i = 0; i < vc_itemupdates.values_num; i++)
+ {
+ zbx_vc_item_update_t *update = &vc_itemupdates.values[i];
+
+ if (itemid != update->itemid)
+ {
+ itemid = update->itemid;
+ item = (zbx_vc_item_t *)zbx_hashset_search(&vc_cache->items, &itemid);
+ }
+
+ if (NULL == item)
+ continue;
+
+ switch (update->type)
+ {
+ case ZBX_VC_UPDATE_RANGE:
+ vch_item_update_range(item, update->data[ZBX_VC_UPDATE_RANGE_SECONDS],
+ update->data[ZBX_VC_UPDATE_RANGE_NOW]);
+ break;
+ case ZBX_VC_UPDATE_STATS:
+ vc_update_statistics(item, update->data[ZBX_VC_UPDATE_STATS_HITS],
+ update->data[ZBX_VC_UPDATE_STATS_MISSES], now);
+ break;
+ }
+ }
+
+ UNLOCK_CACHE;
+
+ zbx_vector_vc_itemupdate_clear(&vc_itemupdates);
}
#ifdef HAVE_TESTS
diff --git a/src/libs/zbxdbcache/valuecache.h b/src/libs/zbxdbcache/valuecache.h
index d8045a93f10..d94b9185f2c 100644
--- a/src/libs/zbxdbcache/valuecache.h
+++ b/src/libs/zbxdbcache/valuecache.h
@@ -98,10 +98,6 @@ void zbx_vc_destroy(void);
void zbx_vc_reset(void);
-void zbx_vc_lock(void);
-
-void zbx_vc_unlock(void);
-
void zbx_vc_enable(void);
void zbx_vc_disable(void);
@@ -120,5 +116,6 @@ void zbx_vc_housekeeping_value_cache(void);
void zbx_vc_get_diag_stats(zbx_uint64_t *items_num, zbx_uint64_t *values_num, int *mode);
void zbx_vc_get_mem_stats(zbx_mem_stats_t *mem);
void zbx_vc_get_item_stats(zbx_vector_ptr_t *stats);
+void zbx_vc_flush_stats(void);
#endif /* ZABBIX_VALUECACHE_H */
diff --git a/src/libs/zbxdbhigh/Makefile.am b/src/libs/zbxdbhigh/Makefile.am
index da708b2353c..471744ef400 100644
--- a/src/libs/zbxdbhigh/Makefile.am
+++ b/src/libs/zbxdbhigh/Makefile.am
@@ -23,7 +23,8 @@ libzbxdbhigh_a_SOURCES = \
template.h \
trigger.c \
tag.c \
- lld_override.c
+ lld_override.c \
+ mediatype.c
libzbxdbhigh_a_CFLAGS = \
-I$(top_srcdir)/src/zabbix_server/
diff --git a/src/libs/zbxdbhigh/event.c b/src/libs/zbxdbhigh/event.c
index 4a3bfa131c8..5f93ca4a8f0 100644
--- a/src/libs/zbxdbhigh/event.c
+++ b/src/libs/zbxdbhigh/event.c
@@ -24,7 +24,7 @@
/******************************************************************************
* *
- * Function: zbx_get_events_by_eventids *
+ * Function: zbx_db_get_events_by_eventids *
* *
* Purpose: get events and flags that indicate what was filled in DB_EVENT *
* structure *
@@ -32,7 +32,7 @@
* Parameters: eventids - [IN] requested event ids *
* events - [OUT] the array of events *
* *
- * Comments: use 'free_db_event' function to release allocated memory *
+ * Comments: use 'zbx_db_free_event' function to release allocated memory *
* *
******************************************************************************/
void zbx_db_get_events_by_eventids(zbx_vector_uint64_t *eventids, zbx_vector_ptr_t *events)
@@ -41,10 +41,10 @@ void zbx_db_get_events_by_eventids(zbx_vector_uint64_t *eventids, zbx_vector_ptr
DB_ROW row;
char *sql = NULL;
size_t sql_alloc = 0, sql_offset = 0;
- zbx_vector_uint64_t trigger_eventids, triggerids;
+ zbx_vector_uint64_t tagged_eventids, triggerids;
int i, index;
- zbx_vector_uint64_create(&trigger_eventids);
+ zbx_vector_uint64_create(&tagged_eventids);
zbx_vector_uint64_create(&triggerids);
zbx_vector_uint64_sort(eventids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
@@ -80,10 +80,10 @@ void zbx_db_get_events_by_eventids(zbx_vector_uint64_t *eventids, zbx_vector_ptr
event->trigger.triggerid = 0;
- if (EVENT_SOURCE_TRIGGERS == event->source)
+ if (EVENT_SOURCE_TRIGGERS == event->source || EVENT_SOURCE_INTERNAL == event->source)
{
zbx_vector_ptr_create(&event->tags);
- zbx_vector_uint64_append(&trigger_eventids, event->eventid);
+ zbx_vector_uint64_append(&tagged_eventids, event->eventid);
}
if (EVENT_OBJECT_TRIGGER == event->object)
@@ -118,13 +118,13 @@ void zbx_db_get_events_by_eventids(zbx_vector_uint64_t *eventids, zbx_vector_ptr
}
DBfree_result(result);
- if (0 != trigger_eventids.values_num) /* EVENT_SOURCE_TRIGGERS */
+ if (0 != tagged_eventids.values_num) /* EVENT_SOURCE_TRIGGERS || EVENT_SOURCE_INTERNAL */
{
DB_EVENT *event = NULL;
sql_offset = 0;
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "eventid", trigger_eventids.values,
- trigger_eventids.values_num);
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "eventid", tagged_eventids.values,
+ tagged_eventids.values_num);
result = DBselect("select eventid,tag,value from event_tag where%s order by eventid", sql);
@@ -206,7 +206,7 @@ void zbx_db_get_events_by_eventids(zbx_vector_uint64_t *eventids, zbx_vector_ptr
zbx_free(sql);
- zbx_vector_uint64_destroy(&trigger_eventids);
+ zbx_vector_uint64_destroy(&tagged_eventids);
zbx_vector_uint64_destroy(&triggerids);
}
@@ -232,16 +232,16 @@ void zbx_db_trigger_clean(DB_TRIGGER *trigger)
/******************************************************************************
* *
- * Function: zbx_free_event *
+ * Function: zbx_db_free_event *
* *
- * Purpose: deallocate memory allocated in function 'get_db_events_info' *
+ * Purpose: deallocate memory allocated in zbx_db_get_events_by_eventids() *
* *
* Parameters: event - [IN] event data *
* *
******************************************************************************/
void zbx_db_free_event(DB_EVENT *event)
{
- if (EVENT_SOURCE_TRIGGERS == event->source)
+ if (EVENT_SOURCE_TRIGGERS == event->source || EVENT_SOURCE_INTERNAL == event->source)
{
zbx_vector_ptr_clear_ext(&event->tags, (zbx_clean_func_t)zbx_free_tag);
zbx_vector_ptr_destroy(&event->tags);
diff --git a/src/libs/zbxdbhigh/host.c b/src/libs/zbxdbhigh/host.c
index 20bb9904611..a3af4b1925d 100644
--- a/src/libs/zbxdbhigh/host.c
+++ b/src/libs/zbxdbhigh/host.c
@@ -59,38 +59,6 @@ static char *get_template_names(const zbx_vector_uint64_t *templateids)
/******************************************************************************
* *
- * Function: DBget_screenitems_by_resource_types_ids *
- * *
- * Description: gets a vector of screen item identifiers used with the *
- * specified resource types and identifiers *
- * *
- * Parameters: screen_itemids - [OUT] the screen item identifiers *
- * types - [IN] an array of resource types *
- * types_num - [IN] the number of values in types array *
- * resourceids - [IN] the resource identifiers *
- * *
- ******************************************************************************/
-static void DBget_screenitems_by_resource_types_ids(zbx_vector_uint64_t *screen_itemids, const zbx_uint64_t *types,
- int types_num, const zbx_vector_uint64_t *resourceids)
-{
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
-
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "select distinct screenitemid from screens_items where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "resourcetype", types, types_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " and");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "resourceid", resourceids->values,
- resourceids->values_num);
-
- DBselect_uint64(sql, screen_itemids);
-
- zbx_free(sql);
-
- zbx_vector_uint64_sort(screen_itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
-}
-
-/******************************************************************************
- * *
* Function: DBget_profiles_by_source_idxs_values *
* *
* Description: gets a vector of profile identifiers used with the specified *
@@ -1173,8 +1141,7 @@ void DBdelete_graphs(zbx_vector_uint64_t *graphids)
{
char *sql = NULL;
size_t sql_alloc = 256, sql_offset = 0;
- zbx_vector_uint64_t profileids, screen_itemids;
- zbx_uint64_t resource_type = SCREEN_RESOURCE_GRAPH;
+ zbx_vector_uint64_t profileids;
const char *profile_idx = "web.favorite.graphids";
zabbix_log(LOG_LEVEL_DEBUG, "In %s() values_num:%d", __func__, graphids->values_num);
@@ -1185,20 +1152,9 @@ void DBdelete_graphs(zbx_vector_uint64_t *graphids)
sql = (char *)zbx_malloc(sql, sql_alloc);
zbx_vector_uint64_create(&profileids);
- zbx_vector_uint64_create(&screen_itemids);
DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
- /* delete from screens_items */
- DBget_screenitems_by_resource_types_ids(&screen_itemids, &resource_type, 1, graphids);
- if (0 != screen_itemids.values_num)
- {
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from screens_items where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "screenitemid", screen_itemids.values,
- screen_itemids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
- }
-
/* delete from profiles */
DBget_profiles_by_source_idxs_values(&profileids, "graphid", &profile_idx, 1, graphids);
if (0 != profileids.values_num)
@@ -1218,7 +1174,6 @@ void DBdelete_graphs(zbx_vector_uint64_t *graphids)
DBexecute("%s", sql);
- zbx_vector_uint64_destroy(&screen_itemids);
zbx_vector_uint64_destroy(&profileids);
zbx_free(sql);
@@ -1340,9 +1295,8 @@ void DBdelete_items(zbx_vector_uint64_t *itemids)
{
char *sql = NULL;
size_t sql_alloc = 256, sql_offset;
- zbx_vector_uint64_t screen_itemids, profileids;
+ zbx_vector_uint64_t profileids;
int num;
- zbx_uint64_t resource_types[] = {SCREEN_RESOURCE_PLAIN_TEXT, SCREEN_RESOURCE_SIMPLE_GRAPH};
const char *history_tables[] = {"history", "history_str", "history_uint", "history_log",
"history_text", "trends", "trends_uint"};
const char *event_tables[] = {"events"};
@@ -1354,7 +1308,6 @@ void DBdelete_items(zbx_vector_uint64_t *itemids)
goto out;
sql = (char *)zbx_malloc(sql, sql_alloc);
- zbx_vector_uint64_create(&screen_itemids);
zbx_vector_uint64_create(&profileids);
do /* add child items (auto-created and prototypes) */
@@ -1382,16 +1335,6 @@ void DBdelete_items(zbx_vector_uint64_t *itemids)
sql_offset = 0;
DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
- /* delete from screens_items */
- DBget_screenitems_by_resource_types_ids(&screen_itemids, resource_types, ARRSIZE(resource_types), itemids);
- if (0 != screen_itemids.values_num)
- {
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from screens_items where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "screenitemid", screen_itemids.values,
- screen_itemids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
- }
-
/* delete from profiles */
DBget_profiles_by_source_idxs_values(&profileids, "itemid", &profile_idx, 1, itemids);
if (0 != profileids.values_num)
@@ -1412,7 +1355,6 @@ void DBdelete_items(zbx_vector_uint64_t *itemids)
DBexecute("%s", sql);
zbx_vector_uint64_destroy(&profileids);
- zbx_vector_uint64_destroy(&screen_itemids);
zbx_free(sql);
out:
@@ -1481,91 +1423,6 @@ out:
/******************************************************************************
* *
- * Function: DBdelete_application *
- * *
- * Purpose: delete application *
- * *
- * Parameters: applicationid - [IN] application identificator from database *
- * *
- * Author: Eugene Grigorjev, Alexander Vladishev *
- * *
- * Comments: !!! Don't forget to sync the code with PHP !!! *
- * *
- ******************************************************************************/
-static void DBdelete_applications(zbx_vector_uint64_t *applicationids)
-{
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_uint64_t applicationid;
- int index;
-
- if (0 == applicationids->values_num)
- goto out;
-
- /* don't delete applications used in web scenarios */
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
- "select distinct applicationid"
- " from httptest"
- " where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "applicationid", applicationids->values,
- applicationids->values_num);
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- ZBX_STR2UINT64(applicationid, row[0]);
-
- index = zbx_vector_uint64_bsearch(applicationids, applicationid, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
- zbx_vector_uint64_remove(applicationids, index);
- }
- DBfree_result(result);
-
- if (0 == applicationids->values_num)
- goto out;
-
- /* don't delete applications with items assigned to them */
- sql_offset = 0;
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
- "select distinct applicationid"
- " from items_applications"
- " where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "applicationid", applicationids->values,
- applicationids->values_num);
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- ZBX_STR2UINT64(applicationid, row[0]);
-
- index = zbx_vector_uint64_bsearch(applicationids, applicationid, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
- zbx_vector_uint64_remove(applicationids, index);
- }
- DBfree_result(result);
-
- if (0 == applicationids->values_num)
- goto out;
-
- sql_offset = 0;
- DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
-
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from applications where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset,
- "applicationid", applicationids->values, applicationids->values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
-
- DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
-
- DBexecute("%s", sql);
-out:
- zbx_free(sql);
-}
-
-/******************************************************************************
- * *
* Function: DBgroup_prototypes_delete *
* *
* Parameters: del_group_prototypeids - [IN] list of group_prototypeids which *
@@ -1876,189 +1733,6 @@ static void DBdelete_template_items(zbx_uint64_t hostid, const zbx_vector_uint64
/******************************************************************************
* *
- * Function: DBdelete_template_applications *
- * *
- * Purpose: delete host applications that belong to an unlinked template *
- * *
- * Parameters: hostid - [IN] host identificator from database *
- * templateids - [IN] array of template IDs *
- * *
- * Author: Eugene Grigorjev *
- * *
- * Comments: !!! Don't forget to sync the code with PHP !!! *
- * *
- * This function does not remove applications discovered by *
- * application prototypes. *
- * Use DBdelete_template_discovered_applications() for that. *
- * *
- ******************************************************************************/
-static void DBdelete_template_applications(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids)
-{
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_uint64_t id;
- zbx_vector_uint64_t applicationids, apptemplateids;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_vector_uint64_create(&applicationids);
- zbx_vector_uint64_create(&apptemplateids);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select t.application_templateid,t.applicationid"
- " from application_template t,applications a,applications ta"
- " where t.applicationid=a.applicationid"
- " and t.templateid=ta.applicationid"
- " and a.hostid=" ZBX_FS_UI64
- " and a.flags=%d"
- " and",
- hostid, ZBX_FLAG_DISCOVERY_NORMAL);
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "ta.hostid", templateids->values, templateids->values_num);
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- ZBX_STR2UINT64(id, row[0]);
- zbx_vector_uint64_append(&apptemplateids, id);
-
- ZBX_STR2UINT64(id, row[1]);
- zbx_vector_uint64_append(&applicationids, id);
- }
- DBfree_result(result);
-
- if (0 != apptemplateids.values_num)
- {
- zbx_vector_uint64_sort(&apptemplateids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
-
- zbx_vector_uint64_sort(&applicationids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
- zbx_vector_uint64_uniq(&applicationids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
-
- sql_offset = 0;
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from application_template where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "application_templateid",
- apptemplateids.values, apptemplateids.values_num);
-
- DBexecute("%s", sql);
-
- DBdelete_applications(&applicationids);
- }
-
- zbx_vector_uint64_destroy(&apptemplateids);
- zbx_vector_uint64_destroy(&applicationids);
- zbx_free(sql);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-/******************************************************************************
- * *
- * Function: DBdelete_template_discovered_applications *
- * *
- * Purpose: delete host applications that belong to an unlinked template *
- * *
- * Parameters: hostid - [IN] host identificator from database *
- * templateids - [IN] array of template IDs *
- * *
- * Comments: !!! Don't forget to sync the code with PHP !!! *
- * *
- ******************************************************************************/
-static void DBdelete_template_discovered_applications(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids)
-{
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_uint64_t id;
- zbx_vector_uint64_t applicationids, lld_ruleids;
- int index;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_vector_uint64_create(&applicationids);
- zbx_vector_uint64_create(&lld_ruleids);
-
- /* get the discovery rules */
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select i.itemid from items i"
- " left join items ti"
- " on i.templateid=ti.itemid"
- " where i.hostid=" ZBX_FS_UI64
- " and i.flags=%d"
- " and",
- hostid, ZBX_FLAG_DISCOVERY_RULE);
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "ti.hostid", templateids->values, templateids->values_num);
-
- DBselect_uint64(sql, &lld_ruleids);
-
- if (0 == lld_ruleids.values_num)
- goto out;
-
- /* get the applications discovered by those rules */
-
- sql_offset = 0;
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select ad.applicationid"
- " from application_discovery ad"
- " left join application_prototype ap"
- " on ap.application_prototypeid=ad.application_prototypeid"
- " where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "ap.itemid", lld_ruleids.values, lld_ruleids.values_num);
-
- zbx_vector_uint64_clear(&applicationids);
- DBselect_uint64(sql, &applicationids);
-
- if (0 == applicationids.values_num)
- goto out;
-
- /* check if the applications are not discovered by other discovery rules */
-
- sql_offset = 0;
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
- "select ad.applicationid"
- " from application_discovery ad"
- " left join application_prototype ap"
- " on ad.application_prototypeid=ap.application_prototypeid"
- " where not");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "ap.itemid", lld_ruleids.values, lld_ruleids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " and");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "ad.applicationid", applicationids.values,
- applicationids.values_num);
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- ZBX_STR2UINT64(id, row[0]);
-
- if (FAIL != (index = zbx_vector_uint64_bsearch(&applicationids, id, ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
- zbx_vector_uint64_remove(&applicationids, index);
- }
- DBfree_result(result);
-
- if (0 == applicationids.values_num)
- goto out;
-
- /* discovered applications must be always removed, that's why we are */
- /* doing it directly instead of using DBdelete_applications() */
- sql_offset = 0;
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from applications where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "applicationid", applicationids.values
- , applicationids.values_num);
- DBexecute("%s", sql);
-out:
- zbx_vector_uint64_destroy(&lld_ruleids);
- zbx_vector_uint64_destroy(&applicationids);
- zbx_free(sql);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-/******************************************************************************
- * *
* Function: DBcopy_trigger_to_host *
* *
* Purpose: copy specified trigger to host *
@@ -2623,16 +2297,9 @@ int DBdelete_template_elements(zbx_uint64_t hostid, zbx_vector_uint64_t *del_tem
DBdelete_template_triggers(hostid, del_templateids);
DBdelete_template_host_prototypes(hostid, del_templateids);
- /* Removing items will remove discovery rules and all application discovery records */
- /* related to them. Because of that discovered applications must be removed before */
- /* removing items. */
- DBdelete_template_discovered_applications(hostid, del_templateids);
+ /* removing items will remove discovery rules related to them */
DBdelete_template_items(hostid, del_templateids);
- /* normal applications must be removed after items are removed to cleanup */
- /* unlinked applications */
- DBdelete_template_applications(hostid, del_templateids);
-
sql = (char *)zbx_malloc(sql, sql_alloc);
zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
@@ -2655,291 +2322,6 @@ clean:
typedef struct
{
- zbx_uint64_t applicationid;
- char *name;
- zbx_vector_uint64_t templateids;
-}
-zbx_application_t;
-
-static void zbx_application_clean(zbx_application_t *application)
-{
- zbx_vector_uint64_destroy(&application->templateids);
- zbx_free(application->name);
- zbx_free(application);
-}
-
-/******************************************************************************
- * *
- * Function: DBcopy_template_application_prototypes *
- * *
- * Purpose: copy application prototypes from templates to host *
- * *
- * Parameters: hostid - [IN] host id *
- * templateids - [IN] array of template IDs *
- * *
- * Comments: The host items must be already copied. *
- * *
- ******************************************************************************/
-static void DBcopy_template_application_prototypes(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids)
-{
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_db_insert_t db_insert;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select ap.application_prototypeid,ap.name,i_t.itemid"
- " from application_prototype ap"
- " left join items i"
- " on ap.itemid=i.itemid"
- " left join items i_t"
- " on i_t.templateid=i.itemid"
- " where i.flags=%d"
- " and i_t.hostid=" ZBX_FS_UI64
- " and",
- ZBX_FLAG_DISCOVERY_RULE, hostid);
-
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.hostid", templateids->values, templateids->values_num);
-
- result = DBselect("%s", sql);
-
- zbx_free(sql);
-
- if (NULL == (row = DBfetch(result)))
- goto out;
-
- zbx_db_insert_prepare(&db_insert, "application_prototype", "application_prototypeid", "itemid", "templateid",
- "name", NULL);
- do
- {
- zbx_uint64_t application_prototypeid, lld_ruleid;
-
- ZBX_STR2UINT64(application_prototypeid, row[0]);
- ZBX_STR2UINT64(lld_ruleid, row[2]);
-
- zbx_db_insert_add_values(&db_insert, __UINT64_C(0), lld_ruleid, application_prototypeid, row[1]);
- }
- while (NULL != (row = DBfetch(result)));
-
- zbx_db_insert_autoincrement(&db_insert, "application_prototypeid");
- zbx_db_insert_execute(&db_insert);
- zbx_db_insert_clean(&db_insert);
-out:
- DBfree_result(result);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-/******************************************************************************
- * *
- * Function: DBcopy_template_item_application_prototypes *
- * *
- * Purpose: copy application prototypes from templates to host *
- * *
- * Parameters: hostid - [IN] host id *
- * templateids - [IN] array of template IDs *
- * *
- * Comments: The host items and application prototypes must be already copied.*
- * *
- ******************************************************************************/
-static void DBcopy_template_item_application_prototypes(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids)
-{
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_db_insert_t db_insert;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select ap.application_prototypeid,i.itemid"
- " from items i_ap,item_application_prototype iap"
- " left join application_prototype ap"
- " on ap.templateid=iap.application_prototypeid"
- " left join items i_t"
- " on i_t.itemid=iap.itemid"
- " left join items i"
- " on i.templateid=i_t.itemid"
- " where i.hostid=" ZBX_FS_UI64
- " and i_ap.itemid=ap.itemid"
- " and i_ap.hostid=" ZBX_FS_UI64
- " and",
- hostid, hostid);
-
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "i_t.hostid", templateids->values,
- templateids->values_num);
-
- result = DBselect("%s", sql);
-
- zbx_free(sql);
-
- if (NULL == (row = DBfetch(result)))
- goto out;
-
- zbx_db_insert_prepare(&db_insert, "item_application_prototype", "item_application_prototypeid",
- "application_prototypeid", "itemid", NULL);
-
- do
- {
- zbx_uint64_t application_prototypeid, itemid;
-
- ZBX_STR2UINT64(application_prototypeid, row[0]);
- ZBX_STR2UINT64(itemid, row[1]);
-
- zbx_db_insert_add_values(&db_insert, __UINT64_C(0), application_prototypeid, itemid);
- }
- while (NULL != (row = DBfetch(result)));
-
- zbx_db_insert_autoincrement(&db_insert, "item_application_prototypeid");
- zbx_db_insert_execute(&db_insert);
- zbx_db_insert_clean(&db_insert);
-out:
- DBfree_result(result);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-/******************************************************************************
- * *
- * Function: DBcopy_template_applications *
- * *
- * Purpose: copy applications from templates to host *
- * *
- * Parameters: hostid - [IN] host id *
- * templateids - [IN] array of template IDs *
- * *
- ******************************************************************************/
-static void DBcopy_template_applications(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids)
-{
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = ZBX_KIBIBYTE, sql_offset = 0;
- zbx_application_t *application = NULL;
- zbx_vector_ptr_t applications;
- int i, j, new_applications = 0, new_application_templates = 0;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_vector_ptr_create(&applications);
-
- sql = (char *)zbx_malloc(sql, sql_alloc);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select applicationid,hostid,name"
- " from applications"
- " where hostid=" ZBX_FS_UI64
- " or", hostid);
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "hostid", templateids->values, templateids->values_num);
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- zbx_uint64_t db_applicationid, db_hostid;
-
- ZBX_STR2UINT64(db_applicationid, row[0]);
- ZBX_STR2UINT64(db_hostid, row[1]);
-
- for (i = 0; i < applications.values_num; i++)
- {
- application = (zbx_application_t *)applications.values[i];
-
- if (0 == strcmp(application->name, row[2]))
- break;
- }
-
- if (i == applications.values_num)
- {
- application = (zbx_application_t *)zbx_malloc(NULL, sizeof(zbx_application_t));
-
- application->applicationid = 0;
- application->name = zbx_strdup(NULL, row[2]);
- zbx_vector_uint64_create(&application->templateids);
-
- zbx_vector_ptr_append(&applications, application);
- }
-
- if (db_hostid == hostid)
- application->applicationid = db_applicationid;
- else
- zbx_vector_uint64_append(&application->templateids, db_applicationid);
- }
- DBfree_result(result);
-
- for (i = 0; i < applications.values_num; i++)
- {
- application = (zbx_application_t *)applications.values[i];
-
- if (0 == application->applicationid)
- new_applications++;
-
- new_application_templates += application->templateids.values_num;
- }
-
- if (0 != new_applications)
- {
- zbx_uint64_t applicationid;
- zbx_db_insert_t db_insert;
-
- applicationid = DBget_maxid_num("applications", new_applications);
-
- zbx_db_insert_prepare(&db_insert, "applications", "applicationid", "hostid", "name", NULL);
-
- for (i = 0; i < applications.values_num; i++)
- {
- application = (zbx_application_t *)applications.values[i];
-
- if (0 != application->applicationid)
- continue;
-
- zbx_db_insert_add_values(&db_insert, applicationid, hostid, application->name);
-
- application->applicationid = applicationid++;
- }
-
- zbx_db_insert_execute(&db_insert);
- zbx_db_insert_clean(&db_insert);
- }
-
- if (0 != new_application_templates)
- {
- zbx_uint64_t application_templateid;
- zbx_db_insert_t db_insert;
-
- application_templateid = DBget_maxid_num("application_template", new_application_templates);
-
- zbx_db_insert_prepare(&db_insert,"application_template", "application_templateid", "applicationid",
- "templateid", NULL);
-
- for (i = 0; i < applications.values_num; i++)
- {
- application = (zbx_application_t *)applications.values[i];
-
- for (j = 0; j < application->templateids.values_num; j++)
- {
- zbx_db_insert_add_values(&db_insert, application_templateid++,
- application->applicationid, application->templateids.values[j]);
- }
- }
-
- zbx_db_insert_execute(&db_insert);
- zbx_db_insert_clean(&db_insert);
- }
-
- zbx_vector_ptr_clear_ext(&applications, (zbx_clean_func_t)zbx_application_clean);
- zbx_vector_ptr_destroy(&applications);
- zbx_free(sql);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-typedef struct
-{
zbx_uint64_t group_prototypeid;
zbx_uint64_t groupid;
zbx_uint64_t templateid; /* reference to parent group_prototypeid */
@@ -5277,6 +4659,14 @@ httpstep_t;
typedef struct
{
+ zbx_uint64_t httptesttagid;
+ char *tag;
+ char *value;
+}
+httptesttag_t;
+
+typedef struct
+{
zbx_uint64_t t_itemid;
zbx_uint64_t h_itemid;
unsigned char type;
@@ -5287,8 +4677,6 @@ typedef struct
{
zbx_uint64_t templateid;
zbx_uint64_t httptestid;
- zbx_uint64_t t_applicationid;
- zbx_uint64_t h_applicationid;
char *name;
char *delay;
zbx_vector_ptr_t fields;
@@ -5298,6 +4686,7 @@ typedef struct
char *http_proxy;
zbx_vector_ptr_t httpsteps;
zbx_vector_ptr_t httptestitems;
+ zbx_vector_ptr_t httptesttags;
int retries;
unsigned char status;
unsigned char authentication;
@@ -5326,25 +4715,24 @@ static void DBget_httptests(zbx_uint64_t hostid, const zbx_vector_uint64_t *temp
DB_ROW row;
httptest_t *httptest;
httpstep_t *httpstep;
+ httptesttag_t *httptesttag;
httpfield_t *httpfield;
httptestitem_t *httptestitem;
httpstepitem_t *httpstepitem;
zbx_vector_uint64_t httptestids; /* the list of web scenarios which should be added to a host */
- zbx_vector_uint64_t applications;
zbx_vector_uint64_t items;
- zbx_uint64_t httptestid, httpstepid, applicationid, itemid;
+ zbx_uint64_t httptestid, httpstepid, itemid;
int i, j, k;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
zbx_vector_uint64_create(&httptestids);
- zbx_vector_uint64_create(&applications);
zbx_vector_uint64_create(&items);
sql = (char *)zbx_malloc(sql, sql_alloc);
zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select t.httptestid,t.name,t.applicationid,t.delay,t.status,t.agent,t.authentication,"
+ "select t.httptestid,t.name,t.delay,t.status,t.agent,t.authentication,"
"t.http_user,t.http_password,t.http_proxy,t.retries,h.httptestid"
" from httptest t"
" left join httptest h"
@@ -5365,26 +4753,23 @@ static void DBget_httptests(zbx_uint64_t hostid, const zbx_vector_uint64_t *temp
zbx_vector_ptr_create(&httptest->httpsteps);
zbx_vector_ptr_create(&httptest->httptestitems);
zbx_vector_ptr_create(&httptest->fields);
+ zbx_vector_ptr_create(&httptest->httptesttags);
zbx_vector_ptr_append(httptests, httptest);
if (0 == httptest->httptestid)
{
httptest->name = zbx_strdup(NULL, row[1]);
- ZBX_DBROW2UINT64(httptest->t_applicationid, row[2]);
- httptest->delay = zbx_strdup(NULL, row[3]);
- httptest->status = (unsigned char)atoi(row[4]);
- httptest->agent = zbx_strdup(NULL, row[5]);
- httptest->authentication = (unsigned char)atoi(row[6]);
- httptest->http_user = zbx_strdup(NULL, row[7]);
- httptest->http_password = zbx_strdup(NULL, row[8]);
- httptest->http_proxy = zbx_strdup(NULL, row[9]);
- httptest->retries = atoi(row[10]);
+ httptest->delay = zbx_strdup(NULL, row[2]);
+ httptest->status = (unsigned char)atoi(row[3]);
+ httptest->agent = zbx_strdup(NULL, row[4]);
+ httptest->authentication = (unsigned char)atoi(row[5]);
+ httptest->http_user = zbx_strdup(NULL, row[6]);
+ httptest->http_password = zbx_strdup(NULL, row[7]);
+ httptest->http_proxy = zbx_strdup(NULL, row[8]);
+ httptest->retries = atoi(row[9]);
zbx_vector_uint64_append(&httptestids, httptest->templateid);
-
- if (0 != httptest->t_applicationid)
- zbx_vector_uint64_append(&applications, httptest->t_applicationid);
}
}
DBfree_result(result);
@@ -5544,40 +4929,52 @@ static void DBget_httptests(zbx_uint64_t hostid, const zbx_vector_uint64_t *temp
zbx_vector_ptr_append(&httpstep->fields, httpfield);
}
DBfree_result(result);
- }
- /* applications */
- if (0 != applications.values_num)
- {
- zbx_vector_uint64_sort(&applications, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
- zbx_vector_uint64_uniq(&applications, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ /* web scenario tags */
+ httptest = NULL;
sql_offset = 0;
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select t.applicationid,h.applicationid"
- " from applications t"
- " join applications h"
- " on t.name=h.name"
- " and h.hostid=" ZBX_FS_UI64
- " where", hostid);
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "t.applicationid",
- applications.values, applications.values_num);
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
+ "select httptesttagid,httptestid,tag,value"
+ " from httptest_tag"
+ " where");
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "httptestid",
+ httptestids.values, httptestids.values_num);
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " order by httptestid");
result = DBselect("%s", sql);
while (NULL != (row = DBfetch(result)))
{
- ZBX_STR2UINT64(applicationid, row[0]);
+ ZBX_STR2UINT64(httptestid, row[1]);
- for (i = 0; i < httptests->values_num; i++)
+ if (NULL == httptest || httptest->templateid != httptestid)
{
- httptest = (httptest_t *)httptests->values[i];
+ if (FAIL == (i = zbx_vector_ptr_bsearch(httptests, &httptestid,
+ ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ continue;
+ }
- if (httptest->t_applicationid == applicationid)
- ZBX_STR2UINT64(httptest->h_applicationid, row[1]);
+ httptest = (httptest_t *)httptests->values[i];
}
+
+ httptesttag = (httptesttag_t *)zbx_malloc(NULL, sizeof(httptesttag_t));
+
+ ZBX_STR2UINT64(httptesttag->httptesttagid, row[0]);
+ httptesttag->tag = zbx_strdup(NULL, row[2]);
+ httptesttag->value = zbx_strdup(NULL, row[3]);
+
+ zbx_vector_ptr_append(&httptest->httptesttags, httptesttag);
}
DBfree_result(result);
+
+ for (i = 0; i < httptests->values_num; i++)
+ {
+ httptest = (httptest_t *)httptests->values[i];
+ zbx_vector_ptr_sort(&httptest->httptesttags, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
+ }
}
/* web scenario items */
@@ -5739,7 +5136,6 @@ static void DBget_httptests(zbx_uint64_t hostid, const zbx_vector_uint64_t *temp
zbx_free(sql);
zbx_vector_uint64_destroy(&items);
- zbx_vector_uint64_destroy(&applications);
zbx_vector_uint64_destroy(&httptestids);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
@@ -5760,12 +5156,13 @@ static void DBsave_httptests(zbx_uint64_t hostid, zbx_vector_ptr_t *httptests)
httpstep_t *httpstep;
httptestitem_t *httptestitem;
httpstepitem_t *httpstepitem;
+ httptesttag_t *httptesttag;
zbx_uint64_t httptestid = 0, httpstepid = 0, httptestitemid = 0, httpstepitemid = 0, httptestfieldid = 0,
- httpstepfieldid = 0;
+ httpstepfieldid = 0, httptesttagid = 0;
int i, j, k, num_httptests = 0, num_httpsteps = 0, num_httptestitems = 0, num_httpstepitems = 0,
- num_httptestfields = 0, num_httpstepfields = 0;
+ num_httptestfields = 0, num_httpstepfields = 0, num_httptesttags = 0;
zbx_db_insert_t db_insert_htest, db_insert_hstep, db_insert_htitem, db_insert_hsitem, db_insert_tfield,
- db_insert_sfield;
+ db_insert_sfield, db_insert_httag;
if (0 == httptests->values_num)
return;
@@ -5780,6 +5177,7 @@ static void DBsave_httptests(zbx_uint64_t hostid, zbx_vector_ptr_t *httptests)
num_httpsteps += httptest->httpsteps.values_num;
num_httptestitems += httptest->httptestitems.values_num;
num_httptestfields += httptest->fields.values_num;
+ num_httptesttags += httptest->httptesttags.values_num;
for (j = 0; j < httptest->httpsteps.values_num; j++)
{
@@ -5795,9 +5193,9 @@ static void DBsave_httptests(zbx_uint64_t hostid, zbx_vector_ptr_t *httptests)
{
httptestid = DBget_maxid_num("httptest", num_httptests);
- zbx_db_insert_prepare(&db_insert_htest, "httptest", "httptestid", "name", "applicationid", "delay",
- "status", "agent", "authentication", "http_user", "http_password", "http_proxy",
- "retries", "hostid", "templateid", NULL);
+ zbx_db_insert_prepare(&db_insert_htest, "httptest", "httptestid", "name", "delay", "status", "agent",
+ "authentication", "http_user", "http_password", "http_proxy", "retries", "hostid",
+ "templateid", NULL);
}
if (httptests->values_num != num_httptests)
@@ -5844,6 +5242,14 @@ static void DBsave_httptests(zbx_uint64_t hostid, zbx_vector_ptr_t *httptests)
"name", "value", NULL);
}
+ if (0 != num_httptesttags)
+ {
+ httptesttagid = DBget_maxid_num("httptest_tag", num_httptesttags);
+
+ zbx_db_insert_prepare(&db_insert_httag, "httptest_tag", "httptesttagid", "httptestid", "tag", "value",
+ NULL);
+ }
+
DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
for (i = 0; i < httptests->values_num; i++)
@@ -5855,10 +5261,9 @@ static void DBsave_httptests(zbx_uint64_t hostid, zbx_vector_ptr_t *httptests)
httptest->httptestid = httptestid++;
zbx_db_insert_add_values(&db_insert_htest, httptest->httptestid, httptest->name,
- httptest->h_applicationid, httptest->delay, (int)httptest->status,
- httptest->agent, (int)httptest->authentication, httptest->http_user,
- httptest->http_password, httptest->http_proxy, httptest->retries, hostid,
- httptest->templateid);
+ httptest->delay, (int)httptest->status, httptest->agent,
+ (int)httptest->authentication, httptest->http_user, httptest->http_password,
+ httptest->http_proxy, httptest->retries, hostid, httptest->templateid);
for (j = 0; j < httptest->fields.values_num; j++)
{
@@ -5912,6 +5317,16 @@ static void DBsave_httptests(zbx_uint64_t hostid, zbx_vector_ptr_t *httptests)
httptestitemid++;
}
+
+ for (j = 0; j < httptest->httptesttags.values_num; j++)
+ {
+ httptesttag = (httptesttag_t *)httptest->httptesttags.values[j];
+
+ zbx_db_insert_add_values(&db_insert_httag, httptesttagid, httptest->httptestid,
+ httptesttag->tag, httptesttag->value);
+
+ httptesttagid++;
+ }
}
else
{
@@ -5959,6 +5374,12 @@ static void DBsave_httptests(zbx_uint64_t hostid, zbx_vector_ptr_t *httptests)
zbx_db_insert_clean(&db_insert_sfield);
}
+ if (0 != num_httptesttags)
+ {
+ zbx_db_insert_execute(&db_insert_httag);
+ zbx_db_insert_clean(&db_insert_httag);
+ }
+
DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
if (16 < sql_offset)
@@ -5978,6 +5399,7 @@ static void clean_httptests(zbx_vector_ptr_t *httptests)
httptest_t *httptest;
httpfield_t *httpfield;
httpstep_t *httpstep;
+ httptesttag_t *httptesttag;
int i, j, k;
for (i = 0; i < httptests->values_num; i++)
@@ -6003,6 +5425,18 @@ static void clean_httptests(zbx_vector_ptr_t *httptests)
zbx_vector_ptr_destroy(&httptest->fields);
+ for (j = 0; j < httptest->httptesttags.values_num; j++)
+ {
+ httptesttag = (httptesttag_t *)httptest->httptesttags.values[j];
+
+ zbx_free(httptesttag->tag);
+ zbx_free(httptesttag->value);
+
+ zbx_free(httptesttag);
+ }
+
+ zbx_vector_ptr_destroy(&httptest->httptesttags);
+
for (j = 0; j < httptest->httpsteps.values_num; j++)
{
httpstep = (httpstep_t *)httptest->httpsteps.values[j];
@@ -6144,10 +5578,7 @@ int DBcopy_template_elements(zbx_uint64_t hostid, zbx_vector_uint64_t *lnk_templ
hosttemplateid++, hostid, lnk_templateids->values[i]);
}
- DBcopy_template_applications(hostid, lnk_templateids);
DBcopy_template_items(hostid, lnk_templateids);
- DBcopy_template_application_prototypes(hostid, lnk_templateids);
- DBcopy_template_item_application_prototypes(hostid, lnk_templateids);
DBcopy_template_host_prototypes(hostid, lnk_templateids);
if (SUCCEED == (res = DBcopy_template_triggers(hostid, lnk_templateids)))
{
@@ -6700,11 +6131,7 @@ void DBdelete_groups(zbx_vector_uint64_t *groupids)
char *sql = NULL;
size_t sql_alloc = 256, sql_offset = 0;
int i;
- zbx_vector_uint64_t screen_itemids, selementids;
- zbx_uint64_t resource_types_delete[] = {SCREEN_RESOURCE_DATA_OVERVIEW,
- SCREEN_RESOURCE_TRIGGER_OVERVIEW};
- zbx_uint64_t resource_types_update[] = {SCREEN_RESOURCE_HOST_INFO, SCREEN_RESOURCE_TRIGGER_INFO,
- SCREEN_RESOURCE_HOSTGROUP_TRIGGERS, SCREEN_RESOURCE_HOST_TRIGGERS};
+ zbx_vector_uint64_t selementids;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() values_num:%d", __func__, groupids->values_num);
@@ -6718,7 +6145,6 @@ void DBdelete_groups(zbx_vector_uint64_t *groupids)
sql = (char *)zbx_malloc(sql, sql_alloc);
- zbx_vector_uint64_create(&screen_itemids);
zbx_vector_uint64_create(&selementids);
DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
@@ -6733,30 +6159,6 @@ void DBdelete_groups(zbx_vector_uint64_t *groupids)
zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
}
- /* delete screens_items (host group is mandatory for this elements) */
- DBget_screenitems_by_resource_types_ids(&screen_itemids, resource_types_delete, ARRSIZE(resource_types_delete),
- groupids);
- if (0 != screen_itemids.values_num)
- {
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from screens_items where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "screenitemid", screen_itemids.values,
- screen_itemids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
- }
-
- /* update screens_items (host group isn't mandatory for this elements) */
- zbx_vector_uint64_clear(&screen_itemids);
- DBget_screenitems_by_resource_types_ids(&screen_itemids, resource_types_update, ARRSIZE(resource_types_update),
- groupids);
-
- if (0 != screen_itemids.values_num)
- {
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update screens_items set resourceid=0 where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "screenitemid", screen_itemids.values,
- screen_itemids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
- }
-
/* groups */
zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from hstgrp where");
DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "groupid", groupids->values, groupids->values_num);
@@ -6767,7 +6169,6 @@ void DBdelete_groups(zbx_vector_uint64_t *groupids)
DBexecute("%s", sql);
zbx_vector_uint64_destroy(&selementids);
- zbx_vector_uint64_destroy(&screen_itemids);
zbx_free(sql);
out:
diff --git a/src/libs/zbxdbhigh/lld_override.c b/src/libs/zbxdbhigh/lld_override.c
index 6f7bc17231c..18db5168063 100644
--- a/src/libs/zbxdbhigh/lld_override.c
+++ b/src/libs/zbxdbhigh/lld_override.c
@@ -66,8 +66,9 @@ static void lld_override_operations_load_tags(const zbx_vector_uint64_t *overrid
" and");
DBadd_condition_alloc(sql, sql_alloc, &sql_offset, "o.lld_overrideid", overrideids->values,
overrideids->values_num);
- zbx_snprintf_alloc(sql, sql_alloc, &sql_offset, " and o.operationobject in (%d,%d)",
- ZBX_LLD_OVERRIDE_OP_OBJECT_TRIGGER, ZBX_LLD_OVERRIDE_OP_OBJECT_HOST);
+ zbx_snprintf_alloc(sql, sql_alloc, &sql_offset, " and o.operationobject in (%d,%d,%d)",
+ ZBX_LLD_OVERRIDE_OP_OBJECT_TRIGGER, ZBX_LLD_OVERRIDE_OP_OBJECT_HOST,
+ ZBX_LLD_OVERRIDE_OP_OBJECT_ITEM);
zbx_strcpy_alloc(sql, sql_alloc, &sql_offset, " order by o.lld_override_operationid");
result = DBselect("%s", *sql);
@@ -254,8 +255,11 @@ void zbx_load_lld_override_operations(const zbx_vector_uint64_t *overrideids, ch
}
DBfree_result(result);
- if (0 != (object_mask & ((1 << ZBX_LLD_OVERRIDE_OP_OBJECT_HOST) | (1 << ZBX_LLD_OVERRIDE_OP_OBJECT_TRIGGER))))
+ if (0 != (object_mask & ((1 << ZBX_LLD_OVERRIDE_OP_OBJECT_HOST) | (1 << ZBX_LLD_OVERRIDE_OP_OBJECT_TRIGGER) |
+ (1 << ZBX_LLD_OVERRIDE_OP_OBJECT_ITEM))))
+ {
lld_override_operations_load_tags(overrideids, sql, sql_alloc, ops);
+ }
if (0 != (object_mask & (1 << ZBX_LLD_OVERRIDE_OP_OBJECT_HOST)))
lld_override_operations_load_templates(overrideids, sql, sql_alloc, ops);
diff --git a/src/libs/zbxdbhigh/mediatype.c b/src/libs/zbxdbhigh/mediatype.c
new file mode 100644
index 00000000000..e79231ef479
--- /dev/null
+++ b/src/libs/zbxdbhigh/mediatype.c
@@ -0,0 +1,131 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "zbxserialize.h"
+#include "db.h"
+
+void zbx_db_mediatype_clean(DB_MEDIATYPE *mt)
+{
+ zbx_free(mt->smtp_server);
+ zbx_free(mt->smtp_helo);
+ zbx_free(mt->smtp_email);
+ zbx_free(mt->exec_path);
+ zbx_free(mt->gsm_modem);
+ zbx_free(mt->username);
+ zbx_free(mt->passwd);
+ zbx_free(mt->exec_params);
+ zbx_free(mt->attempt_interval);
+ zbx_free(mt->script);
+ zbx_free(mt->timeout);
+}
+
+void zbx_serialize_mediatype(unsigned char **data, zbx_uint32_t *data_alloc, zbx_uint32_t *data_offset,
+ const DB_MEDIATYPE *mt)
+{
+ zbx_uint32_t data_len = 0, smtp_server_len, smtp_helo_len, smtp_email_len, exec_path_len, gsm_modem_len,
+ username_len, passwd_len, exec_params_len, script_len, attempt_interval_len, timeout_len;
+ unsigned char *ptr, type = mt->type;
+
+ zbx_serialize_prepare_value(data_len, mt->mediatypeid);
+ zbx_serialize_prepare_value(data_len, type);
+ zbx_serialize_prepare_str_len(data_len, mt->smtp_server, smtp_server_len);
+ zbx_serialize_prepare_str_len(data_len, mt->smtp_helo, smtp_helo_len);
+ zbx_serialize_prepare_str_len(data_len, mt->smtp_email, smtp_email_len);
+ zbx_serialize_prepare_str_len(data_len, mt->exec_path, exec_path_len);
+ zbx_serialize_prepare_str_len(data_len, mt->gsm_modem, gsm_modem_len);
+ zbx_serialize_prepare_str_len(data_len, mt->username, username_len);
+ zbx_serialize_prepare_str_len(data_len, mt->passwd, passwd_len);
+ zbx_serialize_prepare_value(data_len, mt->smtp_port);
+ zbx_serialize_prepare_value(data_len, mt->smtp_security);
+ zbx_serialize_prepare_value(data_len, mt->smtp_verify_peer);
+ zbx_serialize_prepare_value(data_len, mt->smtp_verify_host);
+ zbx_serialize_prepare_value(data_len, mt->smtp_authentication);
+ zbx_serialize_prepare_str_len(data_len, mt->exec_params, exec_params_len);
+ zbx_serialize_prepare_value(data_len, mt->maxsessions);
+ zbx_serialize_prepare_value(data_len, mt->maxattempts);
+ zbx_serialize_prepare_str_len(data_len, mt->attempt_interval, attempt_interval_len);
+ zbx_serialize_prepare_value(data_len, mt->content_type);
+ zbx_serialize_prepare_str_len(data_len, mt->script, script_len);
+ zbx_serialize_prepare_str_len(data_len, mt->timeout, timeout_len);
+
+ while (data_len > *data_alloc - *data_offset)
+ {
+ *data_alloc *= 2;
+ *data = (unsigned char *)zbx_realloc(*data, *data_alloc);
+ }
+
+ ptr = *data + *data_offset;
+ ptr += zbx_serialize_value(ptr, mt->mediatypeid);
+ ptr += zbx_serialize_value(ptr, type);
+ ptr += zbx_serialize_str(ptr, mt->smtp_server, smtp_server_len);
+ ptr += zbx_serialize_str(ptr, mt->smtp_helo, smtp_helo_len);
+ ptr += zbx_serialize_str(ptr, mt->smtp_email, smtp_email_len);
+ ptr += zbx_serialize_str(ptr, mt->exec_path, exec_path_len);
+ ptr += zbx_serialize_str(ptr, mt->gsm_modem, gsm_modem_len);
+ ptr += zbx_serialize_str(ptr, mt->username, username_len);
+ ptr += zbx_serialize_str(ptr, mt->passwd, passwd_len);
+ ptr += zbx_serialize_value(ptr, mt->smtp_port);
+ ptr += zbx_serialize_value(ptr, mt->smtp_security);
+ ptr += zbx_serialize_value(ptr, mt->smtp_verify_peer);
+ ptr += zbx_serialize_value(ptr, mt->smtp_verify_host);
+ ptr += zbx_serialize_value(ptr, mt->smtp_authentication);
+ ptr += zbx_serialize_str(ptr, mt->exec_params, exec_params_len);
+ ptr += zbx_serialize_value(ptr, mt->maxsessions);
+ ptr += zbx_serialize_value(ptr, mt->maxattempts);
+ ptr += zbx_serialize_str(ptr, mt->attempt_interval, attempt_interval_len);
+ ptr += zbx_serialize_value(ptr, mt->content_type);
+ ptr += zbx_serialize_str(ptr, mt->script, script_len);
+ (void)zbx_serialize_str(ptr, mt->timeout, timeout_len);
+
+ *data_offset += data_len;
+}
+
+zbx_uint32_t zbx_deserialize_mediatype(const unsigned char *data, DB_MEDIATYPE *mt)
+{
+ zbx_uint32_t len;
+ const unsigned char *start = data;
+ unsigned char type;
+
+ data += zbx_deserialize_value(data, &mt->mediatypeid);
+ data += zbx_deserialize_value(data, &type);
+ data += zbx_deserialize_str(data, &mt->smtp_server, len);
+ data += zbx_deserialize_str(data, &mt->smtp_helo, len);
+ data += zbx_deserialize_str(data, &mt->smtp_email, len);
+ data += zbx_deserialize_str(data, &mt->exec_path, len);
+ data += zbx_deserialize_str(data, &mt->gsm_modem, len);
+ data += zbx_deserialize_str(data, &mt->username, len);
+ data += zbx_deserialize_str(data, &mt->passwd, len);
+ data += zbx_deserialize_value(data, &mt->smtp_port);
+ data += zbx_deserialize_value(data, &mt->smtp_security);
+ data += zbx_deserialize_value(data, &mt->smtp_verify_peer);
+ data += zbx_deserialize_value(data, &mt->smtp_verify_host);
+ data += zbx_deserialize_value(data, &mt->smtp_authentication);
+ data += zbx_deserialize_str(data, &mt->exec_params, len);
+ data += zbx_deserialize_value(data, &mt->maxsessions);
+ data += zbx_deserialize_value(data, &mt->maxattempts);
+ data += zbx_deserialize_str(data, &mt->attempt_interval, len);
+ data += zbx_deserialize_value(data, &mt->content_type);
+ data += zbx_deserialize_str(data, &mt->script, len);
+ data += zbx_deserialize_str(data, &mt->timeout, len);
+
+ mt->type = type;
+
+ return (zbx_uint32_t)(data - start);
+}
diff --git a/src/libs/zbxdbhigh/proxy.c b/src/libs/zbxdbhigh/proxy.c
index 4663dfe8391..c3e4391c1a3 100644
--- a/src/libs/zbxdbhigh/proxy.c
+++ b/src/libs/zbxdbhigh/proxy.c
@@ -2909,14 +2909,15 @@ static void process_item_value(const DC_ITEM *item, AGENT_RESULT *result, zbx_ti
{
if (0 == item->host.proxy_hostid)
{
- zbx_preprocess_item_value(item->itemid, item->value_type, item->flags, result, ts, item->state, error);
+ zbx_preprocess_item_value(item->itemid, item->host.hostid, item->value_type, item->flags, result, ts,
+ item->state, error);
*h_num = 0;
}
else
{
if (0 != (ZBX_FLAG_DISCOVERY_RULE & item->flags))
{
- zbx_lld_process_agent_result(item->itemid, result, ts, error);
+ zbx_lld_process_agent_result(item->itemid, item->host.hostid, result, ts, error);
*h_num = 0;
}
else
diff --git a/src/libs/zbxdbhigh/template_item.c b/src/libs/zbxdbhigh/template_item.c
index 86a02fe7d59..c95e624bb7c 100644
--- a/src/libs/zbxdbhigh/template_item.c
+++ b/src/libs/zbxdbhigh/template_item.c
@@ -938,97 +938,6 @@ static void save_template_lld_rules(zbx_vector_ptr_t *items, zbx_vector_ptr_t *r
/******************************************************************************
* *
- * Function: save_template_item_applications *
- * *
- * Purpose: saves new item applications links in database *
- * *
- * Parameters: items - [IN] the template items *
- * *
- ******************************************************************************/
-static void save_template_item_applications(zbx_vector_ptr_t *items)
-{
- typedef struct
- {
- zbx_uint64_t itemid;
- zbx_uint64_t applicationid;
- }
- zbx_itemapp_t;
-
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_vector_uint64_t itemids;
- zbx_vector_ptr_t itemapps;
- zbx_itemapp_t *itemapp;
- int i;
- zbx_db_insert_t db_insert;
-
- zbx_vector_ptr_create(&itemapps);
- zbx_vector_uint64_create(&itemids);
-
- for (i = 0; i < items->values_num; i++)
- {
- zbx_template_item_t *item = (zbx_template_item_t *)items->values[i];
-
- zbx_vector_uint64_append(&itemids, item->itemid);
- }
-
- zbx_vector_uint64_sort(&itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
-
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
- "select hi.itemid,ha.applicationid"
- " from items_applications tia"
- " join items hi on hi.templateid=tia.itemid"
- " and");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "hi.itemid", itemids.values, itemids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
- " join application_template hat on hat.templateid=tia.applicationid"
- " join applications ha on ha.applicationid=hat.applicationid"
- " and ha.hostid=hi.hostid"
- " left join items_applications hia on hia.applicationid=ha.applicationid"
- " and hia.itemid=hi.itemid"
- " where hia.itemappid is null");
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- itemapp = (zbx_itemapp_t *)zbx_malloc(NULL, sizeof(zbx_itemapp_t));
-
- ZBX_STR2UINT64(itemapp->itemid, row[0]);
- ZBX_STR2UINT64(itemapp->applicationid, row[1]);
-
- zbx_vector_ptr_append(&itemapps, itemapp);
- }
- DBfree_result(result);
-
- if (0 == itemapps.values_num)
- goto out;
-
- zbx_db_insert_prepare(&db_insert, "items_applications", "itemappid", "itemid", "applicationid", NULL);
-
- for (i = 0; i < itemapps.values_num; i++)
- {
- itemapp = (zbx_itemapp_t *)itemapps.values[i];
-
- zbx_db_insert_add_values(&db_insert, __UINT64_C(0), itemapp->itemid, itemapp->applicationid);
- }
-
- zbx_db_insert_autoincrement(&db_insert, "itemappid");
- zbx_db_insert_execute(&db_insert);
- zbx_db_insert_clean(&db_insert);
-out:
- zbx_free(sql);
-
- zbx_vector_uint64_destroy(&itemids);
-
- zbx_vector_ptr_clear_ext(&itemapps, zbx_ptr_free);
- zbx_vector_ptr_destroy(&itemapps);
-}
-
-/******************************************************************************
- * *
* Function: save_template_discovery_prototypes *
* *
* Purpose: saves host item prototypes in database *
@@ -1310,6 +1219,92 @@ static void copy_template_items_preproc(const zbx_vector_uint64_t *templateids,
/******************************************************************************
* *
+ * Function: copy_template_item_tags *
+ * *
+ * Purpose: copy template item tags *
+ * *
+ * Parameters: templateids - [IN] array of template IDs *
+ * items - [IN] array of new/updated items *
+ * *
+ ******************************************************************************/
+static void copy_template_item_tags(const zbx_vector_uint64_t *templateids, const zbx_vector_ptr_t *items)
+{
+ zbx_vector_uint64_t itemids;
+ zbx_hashset_t items_t;
+ int i;
+ const zbx_template_item_t *item, **pitem;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_db_insert_t db_insert;
+
+ if (0 == items->values_num)
+ return;
+
+ zbx_vector_uint64_create(&itemids);
+ zbx_hashset_create(&items_t, items->values_num, template_item_hash_func, template_item_compare_func);
+
+ /* remove old item tags */
+
+ for (i = 0; i < items->values_num; i++)
+ {
+ item = (const zbx_template_item_t *)items->values[i];
+
+ if (NULL == item->key)
+ zbx_vector_uint64_append(&itemids, item->itemid);
+
+ zbx_hashset_insert(&items_t, &item, sizeof(zbx_template_item_t *));
+ }
+
+ if (0 != itemids.values_num)
+ {
+ zbx_vector_uint64_sort(&itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from item_tag where");
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", itemids.values, itemids.values_num);
+ DBexecute("%s", sql);
+ sql_offset = 0;
+ }
+
+ zbx_db_insert_prepare(&db_insert, "item_tag", "itemtagid", "itemid", "tag", "value", NULL);
+
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
+ "select it.itemid,it.tag,it.value"
+ " from item_tag it,items i"
+ " where it.itemid=i.itemid"
+ " and");
+
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.hostid", templateids->values, templateids->values_num);
+
+ result = DBselect("%s", sql);
+ while (NULL != (row = DBfetch(result)))
+ {
+ zbx_template_item_t item_local, *pitem_local = &item_local;
+
+ ZBX_STR2UINT64(item_local.templateid, row[0]);
+ if (NULL == (pitem = (const zbx_template_item_t **)zbx_hashset_search(&items_t, &pitem_local)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ continue;
+ }
+
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), (*pitem)->itemid, row[1], row[2]);
+
+ }
+ DBfree_result(result);
+
+ zbx_db_insert_autoincrement(&db_insert, "itemtagid");
+ zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ zbx_free(sql);
+ zbx_hashset_destroy(&items_t);
+ zbx_vector_uint64_destroy(&itemids);
+}
+
+/******************************************************************************
+ * *
* Function: copy_template_item_script_params *
* *
* Purpose: copy template item script parameters *
@@ -1955,10 +1950,10 @@ void DBcopy_template_items(zbx_uint64_t hostid, const zbx_vector_uint64_t *templ
link_template_dependent_items(&items);
save_template_items(hostid, &items);
save_template_lld_rules(&items, &lld_rules, new_conditions);
- save_template_item_applications(&items);
save_template_discovery_prototypes(hostid, &items);
copy_template_items_preproc(templateids, &items);
copy_template_item_script_params(templateids, &items);
+ copy_template_item_tags(templateids, &items);
zbx_vector_uint64_create(&lld_itemids);
zbx_hashset_create(&lld_items, items.values_num, template_item_hash_func, template_item_compare_func);
diff --git a/src/libs/zbxdbhigh/trigger.c b/src/libs/zbxdbhigh/trigger.c
index 772b85e9f3d..a881e01b69c 100644
--- a/src/libs/zbxdbhigh/trigger.c
+++ b/src/libs/zbxdbhigh/trigger.c
@@ -128,7 +128,8 @@ static int zbx_process_trigger(struct _DC_TRIGGER *trigger, zbx_vector_ptr_t *di
if (0 != (event_flags & ZBX_FLAGS_TRIGGER_CREATE_INTERNAL_EVENT))
{
zbx_add_event(EVENT_SOURCE_INTERNAL, EVENT_OBJECT_TRIGGER, trigger->triggerid,
- &trigger->timespec, new_state, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, NULL,
+ &trigger->timespec, new_state, NULL, trigger->expression_orig,
+ trigger->recovery_expression_orig, 0, 0, &trigger->tags, 0, NULL, 0, NULL, NULL,
new_error);
}
diff --git a/src/libs/zbxdbupgrade/Makefile.am b/src/libs/zbxdbupgrade/Makefile.am
index be8bcad1b98..7c390ef7dbe 100644
--- a/src/libs/zbxdbupgrade/Makefile.am
+++ b/src/libs/zbxdbupgrade/Makefile.am
@@ -25,4 +25,6 @@ libzbxdbupgrade_a_SOURCES = \
dbupgrade_5010.c \
dbupgrade_5020.c \
dbupgrade_5030.c \
+ dbupgrade_macros.c \
+ dbupgrade_macros.h \
dbupgrade.h
diff --git a/src/libs/zbxdbupgrade/dbupgrade_4000.c b/src/libs/zbxdbupgrade/dbupgrade_4000.c
index 51f7de6727a..7a2ad0ef2b1 100644
--- a/src/libs/zbxdbupgrade/dbupgrade_4000.c
+++ b/src/libs/zbxdbupgrade/dbupgrade_4000.c
@@ -20,6 +20,7 @@
#include "common.h"
#include "db.h"
#include "dbupgrade.h"
+#include "dbupgrade_macros.h"
/*
* 4.0 maintenance database patches
@@ -34,132 +35,6 @@ static int DBpatch_4000000(void)
return SUCCEED;
}
-/******************************************************************************
- * *
- * Function: str_rename_macro *
- * *
- * Purpose: rename macros in the string *
- * *
- * Parameters: in - [IN] the input string *
- * oldmacro - [IN] the macro to rename *
- * newmacro - [IN] the new macro name *
- * out - [IN/OUT] the string with renamed macros *
- * out_alloc - [IN/OUT] the output buffer size *
- * *
- * Return value: SUCCEED - macros were found and renamed *
- * FAIL - no target macros were found *
- * *
- * Comments: If the oldmacro is found in input string then all occurrences of *
- * it are replaced with the new macro in the output string. *
- * Otherwise the output string is not changed. *
- * *
- ******************************************************************************/
-static int str_rename_macro(const char *in, const char *oldmacro, const char *newmacro, char **out,
- size_t *out_alloc)
-{
- zbx_token_t token;
- int pos = 0, ret = FAIL;
- size_t out_offset = 0, newmacro_len;
-
- newmacro_len = strlen(newmacro);
- zbx_strcpy_alloc(out, out_alloc, &out_offset, in);
- out_offset++;
-
- for (; SUCCEED == zbx_token_find(*out, pos, &token, ZBX_TOKEN_SEARCH_BASIC); pos++)
- {
- switch (token.type)
- {
- case ZBX_TOKEN_MACRO:
- pos = token.loc.r;
- if (0 == strncmp(*out + token.loc.l, oldmacro, token.loc.r - token.loc.l + 1))
- {
- pos += zbx_replace_mem_dyn(out, out_alloc, &out_offset, token.loc.l,
- token.loc.r - token.loc.l + 1, newmacro, newmacro_len);
- ret = SUCCEED;
- }
- break;
-
- case ZBX_TOKEN_USER_MACRO:
- case ZBX_TOKEN_SIMPLE_MACRO:
- pos = token.loc.r;
- break;
- }
- }
-
- return ret;
-}
-
-/******************************************************************************
- * *
- * Function: db_rename_macro *
- * *
- * Purpose: rename macro in the specified database fields *
- * *
- * Parameters: result - [IN] database query with fields to replace. First *
- * field is table id field, following with *
- * the target fields listed in fields parameter *
- * table - [IN] the target table name *
- * pkey - [IN] the primary key field name *
- * fields - [IN] the table fields to check for macros and *
- * rename if found *
- * fields_num - [IN] the number of fields to check *
- * oldmacro - [IN] the macro to rename *
- * newmacro - [IN] the new macro name *
- * *
- * Return value: SUCCEED - macros were renamed successfully *
- * FAIL - database error occurred *
- * *
- ******************************************************************************/
-static int db_rename_macro(DB_RESULT result, const char *table, const char *pkey, const char **fields,
- int fields_num, const char *oldmacro, const char *newmacro)
-{
- DB_ROW row;
- char *sql = 0, *field = NULL, *field_esc;
- size_t sql_alloc = 4096, sql_offset = 0, field_alloc = 0, old_offset;
- int i, ret = SUCCEED;
-
- sql = zbx_malloc(NULL, sql_alloc);
-
- DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
-
- while (NULL != (row = DBfetch(result)))
- {
- old_offset = sql_offset;
-
- for (i = 0; i < fields_num; i++)
- {
- if (SUCCEED == str_rename_macro(row[i + 1], oldmacro, newmacro, &field, &field_alloc))
- {
- if (old_offset == sql_offset)
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update %s set ", table);
- else
- zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, ',');
-
- field_esc = DBdyn_escape_string(field);
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%s='%s'", fields[i], field_esc);
- zbx_free(field_esc);
- }
- }
-
- if (old_offset != sql_offset)
- {
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where %s=%s;\n", pkey, row[0]);
- if (SUCCEED != (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
- goto out;
- }
- }
-
- DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
-
- if (16 < sql_offset && ZBX_DB_OK > DBexecute("%s", sql))
- ret = FAIL;
-out:
- zbx_free(field);
- zbx_free(sql);
-
- return ret;
-}
-
static int DBpatch_4000001(void)
{
DB_RESULT result;
diff --git a/src/libs/zbxdbupgrade/dbupgrade_5010.c b/src/libs/zbxdbupgrade/dbupgrade_5010.c
index 8b7b6738d10..a888d746217 100644
--- a/src/libs/zbxdbupgrade/dbupgrade_5010.c
+++ b/src/libs/zbxdbupgrade/dbupgrade_5010.c
@@ -476,10 +476,10 @@ zbx_db_widget_t;
#define ZBX_WIDGET_FIELD_TYPE_GRAPH (6)
#define ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE (7)
-#define ZBX_WIDGET_FIELD_RESOURCE_GRAPH (0)
-#define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH (1)
-#define ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE (2)
-#define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE (3)
+/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH (0) */
+/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH (1) */
+/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE (2) */
+/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE (3) */
#define ZBX_WIDGET_TYPE_CLOCK ("clock")
#define ZBX_WIDGET_TYPE_GRAPH_CLASSIC ("graph")
@@ -490,10 +490,10 @@ zbx_db_widget_t;
#define POS_EMPTY (127)
#define POS_TAKEN (1)
-ZBX_VECTOR_DECL(scitem_dim, zbx_screen_item_dim_t);
-ZBX_VECTOR_IMPL(scitem_dim, zbx_screen_item_dim_t);
-ZBX_VECTOR_DECL(char, char);
-ZBX_VECTOR_IMPL(char, char);
+ZBX_VECTOR_DECL(scitem_dim, zbx_screen_item_dim_t)
+ZBX_VECTOR_IMPL(scitem_dim, zbx_screen_item_dim_t)
+ZBX_VECTOR_DECL(char, char)
+ZBX_VECTOR_IMPL(char, char)
#define SKIP_EMPTY(vector,index) if (POS_EMPTY == vector->values[index]) continue
@@ -568,7 +568,7 @@ static void DBpatch_normalize_screen_items_pos(zbx_vector_ptr_t *scr_items)
#define COMPRESS_SCREEN_ITEMS(axis, span, a_size) \
\
do { \
- for (x = DBpatch_array_max_used_index(keep_ ## axis, a_size); x >= 0; x--) \
+ for (x = (int)DBpatch_array_max_used_index(keep_ ## axis, a_size); x >= 0; x--) \
{ \
if (0 != keep_ ## axis[x] && 0 != used_ ## axis[x]) \
continue; \
@@ -639,7 +639,7 @@ static char *lw_array_to_str(zbx_vector_char_t *v)
int i, max = MAX_STRING_LEN, len;
ptr = str;
- len = zbx_snprintf(ptr, max, "[ ");
+ len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
ptr += len;
max -= len;
@@ -647,7 +647,7 @@ static char *lw_array_to_str(zbx_vector_char_t *v)
{
if (POS_EMPTY != v->values[i])
{
- len = zbx_snprintf(ptr, max, "%d:%d ", i, (int)v->values[i]);
+ len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, (int)v->values[i]);
ptr += len;
max -= len;
}
@@ -671,7 +671,7 @@ static void int_array_debug(char *pfx, int *a, int alen, int emptyval)
int i, max = MAX_STRING_LEN, len;
ptr = str;
- len = zbx_snprintf(ptr, max, "[ ");
+ len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
ptr += len;
max -= len;
@@ -679,7 +679,7 @@ static void int_array_debug(char *pfx, int *a, int alen, int emptyval)
{
if (emptyval != a[i])
{
- len = zbx_snprintf(ptr, max, "%d:%d ", i, a[i]);
+ len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, a[i]);
ptr += len;
max -= len;
}
@@ -723,7 +723,7 @@ static zbx_vector_char_t *lw_array_create_fill(int start, size_t num)
v = lw_array_create();
- for (i = start; i < start + num && i < (size_t)v->values_num; i++)
+ for (i = (size_t)start; i < (size_t)start + num && i < (size_t)v->values_num; i++)
v->values[i] = POS_TAKEN;
return v;
@@ -834,7 +834,7 @@ static zbx_vector_char_t *DBpatch_get_axis_dimensions(zbx_vector_scitem_dim_t *s
for (i = 0; i < scitems->values_num; i++)
{
block = (sciitem_block_t *)malloc(sizeof(sciitem_block_t));
- block->r_block = lw_array_create_fill(scitems->values[i].position, scitems->values[i].span);
+ block->r_block = lw_array_create_fill(scitems->values[i].position, (size_t)scitems->values[i].span);
block->index = i;
zbx_vector_ptr_append(&blocks, (void *)block);
}
@@ -863,7 +863,7 @@ static zbx_vector_char_t *DBpatch_get_axis_dimensions(zbx_vector_scitem_dim_t *s
for (n = 0; n < block_unsized->values_num; n++)
{
SKIP_EMPTY(block_unsized, n);
- dimensions->values[n] = MAX(1, size_overflow / block_unsized_count);
+ dimensions->values[n] = (char)MAX(1, size_overflow / block_unsized_count);
size_overflow -= dimensions->values[n];
block_unsized_count--;
}
@@ -880,7 +880,7 @@ static zbx_vector_char_t *DBpatch_get_axis_dimensions(zbx_vector_scitem_dim_t *s
new_dimension = (int)round(factor * dimensions->values[n]);
block_dimensions_sum -= dimensions->values[n];
size_overflow -= new_dimension - dimensions->values[n];
- dimensions->values[n] = new_dimension;
+ dimensions->values[n] = (char)new_dimension;
}
}
@@ -928,8 +928,11 @@ static void DBpatch_adjust_axis_dimensions(zbx_vector_char_t *d, zbx_vector_char
}
}
- zabbix_log(LOG_LEVEL_TRACE, "dim_sum:%d pot_idx/val:%d/%.2lf", dimensions_sum,
- potential_index, potential_value);
+ if (0 <= potential_index)
+ {
+ zabbix_log(LOG_LEVEL_TRACE, "dim_sum:%d pot_idx/val:%d/%.2lf", dimensions_sum,
+ potential_index, potential_value);
+ }
if (dimensions_sum > target && d->values[potential_index] == d_min->values[potential_index])
break;
@@ -1472,10 +1475,10 @@ static int DBpatch_5010044(void)
#undef ZBX_WIDGET_FIELD_TYPE_GRAPH
#undef ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE
-#undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH
-#undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH
-#undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE
-#undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH */
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH */
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE */
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE */
#undef ZBX_WIDGET_TYPE_CLOCK
#undef ZBX_WIDGET_TYPE_GRAPH_CLASSIC
diff --git a/src/libs/zbxdbupgrade/dbupgrade_5030.c b/src/libs/zbxdbupgrade/dbupgrade_5030.c
index 001e52ccb90..5c481aca995 100644
--- a/src/libs/zbxdbupgrade/dbupgrade_5030.c
+++ b/src/libs/zbxdbupgrade/dbupgrade_5030.c
@@ -21,7 +21,10 @@
#include "log.h"
#include "db.h"
#include "dbupgrade.h"
-#include "log.h"
+#include "dbupgrade_macros.h"
+#include "zbxalgo.h"
+#include "zbxjson.h"
+#include "../zbxalgo/vectorimpl.h"
/*
* 5.4 development database patches
@@ -949,12 +952,3714 @@ static int DBpatch_5030056(void)
return SUCCEED;
}
+/* Patches and helper functions for ZBXNEXT-6368 */
+
+static int is_valid_opcommand_type(const char *type_str, const char *scriptid)
+{
+#define ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT 4 /* not used after upgrade */
+ unsigned int type;
+
+ if (SUCCEED != is_uint31(type_str, &type))
+ return FAIL;
+
+ switch (type)
+ {
+ case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
+ case ZBX_SCRIPT_TYPE_IPMI:
+ case ZBX_SCRIPT_TYPE_SSH:
+ case ZBX_SCRIPT_TYPE_TELNET:
+ if (SUCCEED == DBis_null(scriptid))
+ return SUCCEED;
+ else
+ return FAIL;
+ case ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT:
+ if (FAIL == DBis_null(scriptid))
+ return SUCCEED;
+ else
+ return FAIL;
+ default:
+ return FAIL;
+ }
+#undef ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT
+}
+
+static int validate_types_in_opcommand(void)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ int ret = SUCCEED;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return ret;
+
+ if (NULL == (result = DBselect("select operationid,type,scriptid from opcommand")))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'opcommand'", __func__);
+ return FAIL;
+ }
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ if (SUCCEED != is_valid_opcommand_type(row[1], row[2]))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "%s(): invalid record in table \"opcommand\": operationid: %s"
+ " type: %s scriptid: %s", __func__, row[0], row[1],
+ (SUCCEED == DBis_null(row[2])) ? "value is NULL" : row[2]);
+ ret = FAIL;
+ break;
+ }
+ }
+
+ DBfree_result(result);
+
+ return ret;
+}
+
static int DBpatch_5030057(void)
{
+ return validate_types_in_opcommand();
+}
+
+static int DBpatch_5030058(void)
+{
+ const ZBX_FIELD field = {"scope", "1", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+static int DBpatch_5030059(void)
+{
+ const ZBX_FIELD field = {"port", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+static int DBpatch_5030060(void)
+{
+ const ZBX_FIELD field = {"authtype", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+static int DBpatch_5030061(void)
+{
+ const ZBX_FIELD field = {"username", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+static int DBpatch_5030062(void)
+{
+ const ZBX_FIELD field = {"password", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+static int DBpatch_5030063(void)
+{
+ const ZBX_FIELD field = {"publickey", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+static int DBpatch_5030064(void)
+{
+ const ZBX_FIELD field = {"privatekey", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+static int DBpatch_5030065(void)
+{
+ const ZBX_FIELD field = {"menu_path", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("scripts", &field);
+}
+
+/******************************************************************************
+ * *
+ * Function: DBpatch_5030066 (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: set value for 'scripts' table column 'scope' for existing global *
+ * scripts *
+ * *
+ * Return value: SUCCEED or FAIL *
+ * *
+ * Comments: 'scope' is set only for scripts which are NOT used in any action *
+ * operation. Otherwise the 'scope' default value is used, no need *
+ * to modify it. *
+ * *
+ ******************************************************************************/
+static int DBpatch_5030066(void)
+{
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute("update scripts set scope=%d"
+ " where scriptid not in ("
+ "select distinct scriptid"
+ " from opcommand"
+ " where scriptid is not null)", ZBX_SCRIPT_SCOPE_HOST))
+ {
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+static char *zbx_rename_host_macros(const char *command)
+{
+ char *p1, *p2, *p3, *p4, *p5, *p6, *p7;
+
+ p1 = string_replace(command, "{HOST.CONN}", "{HOST.TARGET.CONN}");
+ p2 = string_replace(p1, "{HOST.DNS}", "{HOST.TARGET.DNS}");
+ p3 = string_replace(p2, "{HOST.HOST}", "{HOST.TARGET.HOST}");
+ p4 = string_replace(p3, "{HOST.IP}", "{HOST.TARGET.IP}");
+ p5 = string_replace(p4, "{HOST.NAME}", "{HOST.TARGET.NAME}");
+ p6 = string_replace(p5, "{HOSTNAME}", "{HOST.TARGET.NAME}");
+ p7 = string_replace(p6, "{IPADDRESS}", "{HOST.TARGET.IP}");
+
+ zbx_free(p1);
+ zbx_free(p2);
+ zbx_free(p3);
+ zbx_free(p4);
+ zbx_free(p5);
+ zbx_free(p6);
+
+ return p7;
+}
+
+/******************************************************************************
+ * *
+ * Function: DBpatch_5030067 (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: rename some {HOST.*} macros to {HOST.TARGET.*} in existing global *
+ * scripts which are used in actions *
+ * *
+ * Return value: SUCCEED or FAIL *
+ * *
+ ******************************************************************************/
+static int DBpatch_5030067(void)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ int ret = SUCCEED;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return ret;
+
+ if (NULL == (result = DBselect("select scriptid,command"
+ " from scripts"
+ " where scriptid in (select distinct scriptid from opcommand where scriptid is not null)")))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'scripts'", __func__);
+ return FAIL;
+ }
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ char *command, *command_esc;
+ int rc;
+
+ command_esc = DBdyn_escape_field("scripts", "command", (command = zbx_rename_host_macros(row[1])));
+
+ zbx_free(command);
+
+ rc = DBexecute("update scripts set command='%s' where scriptid=%s", command_esc, row[0]);
+
+ zbx_free(command_esc);
+
+ if (ZBX_DB_OK > rc)
+ {
+ ret = FAIL;
+ break;
+ }
+ }
+ DBfree_result(result);
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_split_name (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: helper function to split script name into menu_path and name *
+ * *
+ * Parameters: *
+ * name - [IN] old name *
+ * menu_path - [OUT] menu path part, must be deallocated by caller *
+ * name_without_path - [OUT] name, DO NOT deallocate in caller *
+ * *
+ ******************************************************************************/
+static void zbx_split_name(const char *name, char **menu_path, const char **name_without_path)
+{
+ char *p;
+
+ if (NULL == (p = strrchr(name, '/')))
+ return;
+
+ /* do not split if '/' is found at the beginning or at the end */
+ if (name == p || '\0' == *(p + 1))
+ return;
+
+ *menu_path = zbx_strdup(*menu_path, name);
+
+ p = *menu_path + (p - name);
+ *p = '\0';
+ *name_without_path = p + 1;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_make_script_name_unique (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: helper function to assist in making unique script names *
+ * *
+ * Parameters: *
+ * name - [IN] proposed name, to be tried first *
+ * suffix - [IN/OUT] numeric suffix to start from *
+ * unique_name - [OUT] unique name, must be deallocated by caller *
+ * *
+ * Return value: SUCCEED - unique name found, FAIL - DB error *
+ * *
+ * Comments: pass initial suffix=0 to get "script ABC", "script ABC 2", *
+ * "script ABC 3", ... . *
+ * Pass initial suffix=1 to get "script ABC 1", "script ABC 2", *
+ * "script ABC 3", ... . *
+ * *
+ ******************************************************************************/
+static int zbx_make_script_name_unique(const char *name, int *suffix, char **unique_name)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ char *sql, *try_name = NULL, *try_name_esc = NULL;
+
+ while (1)
+ {
+ if (0 == *suffix)
+ {
+ try_name = zbx_strdup(NULL, name);
+ (*suffix)++;
+ }
+ else
+ try_name = zbx_dsprintf(try_name, "%s %d", name, *suffix);
+
+ (*suffix)++;
+
+ try_name_esc = DBdyn_escape_string(try_name);
+
+ sql = zbx_dsprintf(NULL, "select scriptid from scripts where name='%s'", try_name_esc);
+
+ zbx_free(try_name_esc);
+
+ if (NULL == (result = DBselectN(sql, 1)))
+ {
+ zbx_free(try_name);
+ zbx_free(sql);
+ zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'scripts'", __func__);
+ return FAIL;
+ }
+
+ zbx_free(sql);
+
+ if (NULL == (row = DBfetch(result)))
+ {
+ *unique_name = try_name;
+ DBfree_result(result);
+ return SUCCEED;
+ }
+
+ DBfree_result(result);
+ }
+}
+
+/******************************************************************************
+ * *
+ * Function: DBpatch_5030068 (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: split script name between 'menu_path' and 'name' columns for *
+ * existing global scripts *
+ * *
+ * Return value: SUCCEED or FAIL *
+ * *
+ ******************************************************************************/
+static int DBpatch_5030068(void)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ int ret = SUCCEED;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return ret;
+
+ if (NULL == (result = DBselect("select scriptid,name"
+ " from scripts")))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'scripts'", __func__);
+ return FAIL;
+ }
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ const char *scriptid = row[0];
+ const char *name = row[1];
+ const char *name_without_path;
+ char *menu_path = NULL, *menu_path_esc = NULL;
+ char *name_without_path_unique = NULL, *name_esc = NULL;
+ int rc, suffix = 0;
+
+ zbx_split_name(name, &menu_path, &name_without_path);
+
+ if (NULL == menu_path)
+ continue;
+
+ if (SUCCEED != zbx_make_script_name_unique(name_without_path, &suffix, &name_without_path_unique))
+ {
+ zbx_free(menu_path);
+ ret = FAIL;
+ break;
+ }
+
+ menu_path_esc = DBdyn_escape_string(menu_path);
+ name_esc = DBdyn_escape_string(name_without_path_unique);
+
+ rc = DBexecute("update scripts set menu_path='%s',name='%s' where scriptid=%s",
+ menu_path_esc, name_esc, scriptid);
+
+ zbx_free(name_esc);
+ zbx_free(menu_path_esc);
+ zbx_free(name_without_path_unique);
+ zbx_free(menu_path);
+
+ if (ZBX_DB_OK > rc)
+ {
+ ret = FAIL;
+ break;
+ }
+ }
+
+ DBfree_result(result);
+
+ return ret;
+}
+
+typedef struct
+{
+ char *command;
+ char *username;
+ char *password;
+ char *publickey;
+ char *privatekey;
+ char *type;
+ char *execute_on;
+ char *port;
+ char *authtype;
+}
+zbx_opcommand_parts_t;
+
+typedef struct
+{
+ size_t size;
+ char *record;
+ zbx_uint64_t scriptid;
+}
+zbx_opcommand_rec_t;
+
+ZBX_VECTOR_DECL(opcommands, zbx_opcommand_rec_t)
+ZBX_VECTOR_IMPL(opcommands, zbx_opcommand_rec_t)
+
+/******************************************************************************
+ * *
+ * Function: zbx_pack_record (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: helper function, packs parts of remote command into one memory *
+ * chunk for efficient storing and comparing *
+ * *
+ * Parameters: *
+ * parts - [IN] structure with all remote command components *
+ * packed_record - [OUT] memory chunk with packed data. Must be deallocated *
+ * by caller. *
+ * *
+ * Return value: size of memory chunk with the packed remote command *
+ * *
+ ******************************************************************************/
+static size_t zbx_pack_record(const zbx_opcommand_parts_t *parts, char **packed_record)
+{
+ size_t size;
+ char *p, *p_end;
+
+ size = strlen(parts->command) + strlen(parts->username) + strlen(parts->password) + strlen(parts->publickey) +
+ strlen(parts->privatekey) + strlen(parts->type) + strlen(parts->execute_on) +
+ strlen(parts->port) + strlen(parts->authtype) + 9; /* 9 terminating '\0' bytes for 9 parts */
+
+ *packed_record = (char *)zbx_malloc(*packed_record, size);
+ p = *packed_record;
+ p_end = *packed_record + size;
+
+ p += zbx_strlcpy(p, parts->command, size) + 1;
+ p += zbx_strlcpy(p, parts->username, (size_t)(p_end - p)) + 1;
+ p += zbx_strlcpy(p, parts->password, (size_t)(p_end - p)) + 1;
+ p += zbx_strlcpy(p, parts->publickey, (size_t)(p_end - p)) + 1;
+ p += zbx_strlcpy(p, parts->privatekey, (size_t)(p_end - p)) + 1;
+ p += zbx_strlcpy(p, parts->type, (size_t)(p_end - p)) + 1;
+ p += zbx_strlcpy(p, parts->execute_on, (size_t)(p_end - p)) + 1;
+ p += zbx_strlcpy(p, parts->port, (size_t)(p_end - p)) + 1;
+ p += zbx_strlcpy(p, parts->authtype, (size_t)(p_end - p)) + 1;
+
+ return size;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_check_duplicate (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: checking if this remote command is a new one or a duplicate one *
+ * and storing the assigned new global script id *
+ * *
+ * Parameters: *
+ * opcommands - [IN] vector used for checking duplicates *
+ * parts - [IN] structure with all remote command components *
+ * index - [OUT] index of vector element used to store information *
+ * about the remote command (either a new one or *
+ * an existing one) *
+ * *
+ * Return value: IS_NEW for new elements, IS_DUPLICATE for elements already *
+ * seen *
+ * *
+ ******************************************************************************/
+#define IS_NEW 0
+#define IS_DUPLICATE 1
+
+static int zbx_check_duplicate(zbx_vector_opcommands_t *opcommands,
+ const zbx_opcommand_parts_t *parts, int *index)
+{
+ char *packed_record = NULL;
+ size_t size;
+ zbx_opcommand_rec_t elem;
+ int i;
+
+ size = zbx_pack_record(parts, &packed_record);
+
+ for (i = 0; i < opcommands->values_num; i++)
+ {
+ if (size == opcommands->values[i].size &&
+ 0 == memcmp(opcommands->values[i].record, packed_record, size))
+ {
+ zbx_free(packed_record);
+ *index = i;
+ return IS_DUPLICATE;
+ }
+ }
+
+ elem.size = size;
+ elem.record = packed_record;
+ elem.scriptid = 0;
+ zbx_vector_opcommands_append(opcommands, elem);
+ *index = opcommands->values_num - 1;
+
+ return IS_NEW;
+}
+
+/******************************************************************************
+ * *
+ * Function: DBpatch_5030069 (part of ZBXNEXT-6368) *
+ * *
+ * Purpose: migrate remote commands from table 'opcommand' to table 'scripts' *
+ * and convert them into global scripts *
+ * *
+ ******************************************************************************/
+static int DBpatch_5030069(void)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ int ret = SUCCEED, i, suffix = 1;
+ zbx_vector_opcommands_t opcommands;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return ret;
+
+ zbx_vector_opcommands_create(&opcommands);
+
+ if (NULL == (result = DBselect("select command,username,password,publickey,privatekey,type,execute_on,port,"
+ "authtype,operationid"
+ " from opcommand"
+ " where scriptid is null"
+ " order by command,username,password,publickey,privatekey,type,execute_on,port,authtype")))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'opcommand'", __func__);
+ zbx_vector_opcommands_destroy(&opcommands);
+
+ return FAIL;
+ }
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ char *operationid;
+ int index;
+ zbx_opcommand_parts_t parts;
+
+ parts.command = row[0];
+ parts.username = row[1];
+ parts.password = row[2];
+ parts.publickey = row[3];
+ parts.privatekey = row[4];
+ parts.type = row[5];
+ parts.execute_on = row[6];
+ parts.port = row[7];
+ parts.authtype = row[8];
+ operationid = row[9];
+
+ if (IS_NEW == zbx_check_duplicate(&opcommands, &parts, &index))
+ {
+ char *script_name = NULL, *script_name_esc;
+ char *command_esc, *port_esc, *username_esc;
+ char *password_esc, *publickey_esc, *privatekey_esc;
+ zbx_uint64_t scriptid, type, execute_on, authtype, operationid_num;
+ int rc;
+
+ if (SUCCEED != zbx_make_script_name_unique("Script", &suffix, &script_name))
+ {
+ ret = FAIL;
+ break;
+ }
+
+ scriptid = DBget_maxid("scripts");
+
+ ZBX_DBROW2UINT64(type, parts.type);
+ ZBX_DBROW2UINT64(execute_on, parts.execute_on);
+ ZBX_DBROW2UINT64(authtype, parts.authtype);
+ ZBX_DBROW2UINT64(operationid_num, operationid);
+
+ script_name_esc = DBdyn_escape_string(script_name);
+ command_esc = DBdyn_escape_string(parts.command);
+ port_esc = DBdyn_escape_string(parts.port);
+ username_esc = DBdyn_escape_string(parts.username);
+ password_esc = DBdyn_escape_string(parts.password);
+ publickey_esc = DBdyn_escape_string(parts.publickey);
+ privatekey_esc = DBdyn_escape_string(parts.privatekey);
+
+ zbx_free(script_name);
+
+ rc = DBexecute("insert into scripts (scriptid,name,command,description,type,execute_on,scope,"
+ "port,authtype,username,password,publickey,privatekey) values ("
+ ZBX_FS_UI64 ",'%s','%s',''," ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,'%s',"
+ ZBX_FS_UI64 ",'%s','%s','%s','%s')",
+ scriptid, script_name_esc, command_esc, type, execute_on,
+ ZBX_SCRIPT_SCOPE_ACTION, port_esc, authtype,
+ username_esc, password_esc, publickey_esc, privatekey_esc);
+
+ zbx_free(privatekey_esc);
+ zbx_free(publickey_esc);
+ zbx_free(password_esc);
+ zbx_free(username_esc);
+ zbx_free(port_esc);
+ zbx_free(command_esc);
+ zbx_free(script_name_esc);
+
+ if (ZBX_DB_OK > rc || ZBX_DB_OK > DBexecute("update opcommand set scriptid=" ZBX_FS_UI64
+ " where operationid=" ZBX_FS_UI64, scriptid, operationid_num))
+ {
+ ret = FAIL;
+ break;
+ }
+
+ opcommands.values[index].scriptid = scriptid;
+ }
+ else /* IS_DUPLICATE */
+ {
+ zbx_uint64_t scriptid;
+
+ /* link to a previously migrated script */
+ scriptid = opcommands.values[index].scriptid;
+
+ if (ZBX_DB_OK > DBexecute("update opcommand set scriptid=" ZBX_FS_UI64
+ " where operationid=%s", scriptid, operationid))
+ {
+ ret = FAIL;
+ break;
+ }
+ }
+ }
+
+ DBfree_result(result);
+
+ for (i = 0; i < opcommands.values_num; i++)
+ zbx_free(opcommands.values[i].record);
+
+ zbx_vector_opcommands_destroy(&opcommands);
+
+ return ret;
+}
+#undef IS_NEW
+#undef IS_DUPLICATE
+
+static int DBpatch_5030070(void)
+{
+ const ZBX_FIELD field = {"scriptid", NULL, "scripts","scriptid", 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0};
+
+ return DBset_not_null("opcommand", &field);
+}
+
+static int DBpatch_5030071(void)
+{
+ return DBdrop_field("opcommand", "execute_on");
+}
+
+static int DBpatch_5030072(void)
+{
+ return DBdrop_field("opcommand", "port");
+}
+
+static int DBpatch_5030073(void)
+{
+ return DBdrop_field("opcommand", "authtype");
+}
+
+static int DBpatch_5030074(void)
+{
+ return DBdrop_field("opcommand", "username");
+}
+
+static int DBpatch_5030075(void)
+{
+ return DBdrop_field("opcommand", "password");
+}
+
+static int DBpatch_5030076(void)
+{
+ return DBdrop_field("opcommand", "publickey");
+}
+
+static int DBpatch_5030077(void)
+{
+ return DBdrop_field("opcommand", "privatekey");
+}
+
+static int DBpatch_5030078(void)
+{
+ return DBdrop_field("opcommand", "command");
+}
+
+static int DBpatch_5030079(void)
+{
+ return DBdrop_field("opcommand", "type");
+}
+
+static int DBpatch_5030080(void)
+{
+ const ZBX_FIELD old_field = {"command", "", NULL, NULL, 0, ZBX_TYPE_SHORTTEXT, ZBX_NOTNULL, 0};
+ const ZBX_FIELD field = {"command", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
+
+ return DBmodify_field_type("task_remote_command", &field, &old_field);
+}
+/* end of ZBXNEXT-6368 patches */
+
+static int DBpatch_5030081(void)
+{
+ const ZBX_FIELD field = {"display_period", "30", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};
+
+ return DBadd_field("dashboard", &field);
+}
+
+static int DBpatch_5030082(void)
+{
+ const ZBX_FIELD field = {"auto_start", "1", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};
+
+ return DBadd_field("dashboard", &field);
+}
+
+static int DBpatch_5030083(void)
+{
+ const ZBX_TABLE table =
+ {"dashboard_page", "dashboard_pageid", 0,
+ {
+ {"dashboard_pageid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"dashboardid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"display_period", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"sortorder", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030084(void)
+{
+ const ZBX_FIELD field = {"dashboardid", NULL, "dashboard", "dashboardid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("dashboard_page", 1, &field);
+}
+
+static int DBpatch_5030085(void)
+{
+ return DBcreate_index("dashboard_page", "dashboard_page_1", "dashboardid", 0);
+}
+
+static int DBpatch_5030086(void)
+{
+ if (0 == (ZBX_PROGRAM_TYPE_SERVER & program_type))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute(
+ "insert into dashboard_page (dashboard_pageid,dashboardid)"
+ " select dashboardid,dashboardid from dashboard"))
+ {
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030087(void)
+{
+ const ZBX_FIELD field = {"dashboard_pageid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0};
+
+ if (SUCCEED != DBadd_field("widget", &field))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030088(void)
+{
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute("update widget set dashboard_pageid=dashboardid"))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030089(void)
+{
+ const ZBX_FIELD field = {"dashboard_pageid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0};
+
+ return DBset_not_null("widget", &field);
+}
+
+static int DBpatch_5030090(void)
+{
+ return DBdrop_foreign_key("widget", 1);
+}
+
+static int DBpatch_5030091(void)
+{
+ return DBdrop_field("widget", "dashboardid");
+}
+
+static int DBpatch_5030092(void)
+{
+ return DBcreate_index("widget", "widget_1", "dashboard_pageid", 0);
+}
+
+static int DBpatch_5030093(void)
+{
+ const ZBX_FIELD field = {"dashboard_pageid", NULL, "dashboard_page", "dashboard_pageid", 0, 0, 0,
+ ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("widget", 1, &field);
+}
+
+typedef struct
+{
+ uint64_t screenitemid;
+ uint64_t screenid;
+ int resourcetype;
+ uint64_t resourceid;
+ int width;
+ int height;
+ int x;
+ int y;
+ int colspan;
+ int rowspan;
+ int elements;
+ int style;
+ char *url;
+ int sort_triggers;
+ char *application;
+ int dynamic;
+}
+zbx_db_screen_item_t;
+
+typedef struct
+{
+ uint64_t widget_fieldid;
+ int type;
+ char *name;
+ int value_int;
+ char *value_str;
+ uint64_t value_itemid;
+ uint64_t value_graphid;
+ uint64_t value_groupid;
+ uint64_t value_hostid;
+ uint64_t value_sysmapid;
+}
+zbx_db_widget_field_t;
+
+typedef struct
+{
+ int position;
+ int span;
+ int size;
+}
+zbx_screen_item_dim_t;
+
+typedef struct
+{
+ uint64_t dashboardid;
+ char *name;
+ uint64_t userid;
+ int private;
+ int display_period;
+}
+zbx_db_dashboard_t;
+
+typedef struct
+{
+ uint64_t dashboard_pageid;
+ uint64_t dashboardid;
+ char *name;
+}
+zbx_db_dashboard_page_t;
+
+typedef struct
+{
+ uint64_t widgetid;
+ uint64_t dashboardid;
+ char *type;
+ char *name;
+ int x;
+ int y;
+ int width;
+ int height;
+ int view_mode;
+}
+zbx_db_widget_t;
+
+#define DASHBOARD_MAX_COLS (24)
+#define DASHBOARD_MAX_ROWS (64)
+#define DASHBOARD_WIDGET_MIN_ROWS (2)
+#define DASHBOARD_WIDGET_MAX_ROWS (32)
+#define SCREEN_MAX_ROWS (100)
+#define SCREEN_MAX_COLS (100)
+
+#undef SCREEN_RESOURCE_CLOCK
+#define SCREEN_RESOURCE_CLOCK (7)
+#undef SCREEN_RESOURCE_GRAPH
+#define SCREEN_RESOURCE_GRAPH (0)
+#undef SCREEN_RESOURCE_SIMPLE_GRAPH
+#define SCREEN_RESOURCE_SIMPLE_GRAPH (1)
+#undef SCREEN_RESOURCE_LLD_GRAPH
+#define SCREEN_RESOURCE_LLD_GRAPH (20)
+#undef SCREEN_RESOURCE_LLD_SIMPLE_GRAPH
+#define SCREEN_RESOURCE_LLD_SIMPLE_GRAPH (19)
+#undef SCREEN_RESOURCE_PLAIN_TEXT
+#define SCREEN_RESOURCE_PLAIN_TEXT (3)
+#undef SCREEN_RESOURCE_URL
+#define SCREEN_RESOURCE_URL (11)
+
+#undef SCREEN_RESOURCE_MAP
+#define SCREEN_RESOURCE_MAP (2)
+#undef SCREEN_RESOURCE_HOST_INFO
+#define SCREEN_RESOURCE_HOST_INFO (4)
+#undef SCREEN_RESOURCE_TRIGGER_INFO
+#define SCREEN_RESOURCE_TRIGGER_INFO (5)
+#undef SCREEN_RESOURCE_SERVER_INFO
+#define SCREEN_RESOURCE_SERVER_INFO (6)
+#undef SCREEN_RESOURCE_TRIGGER_OVERVIEW
+#define SCREEN_RESOURCE_TRIGGER_OVERVIEW (9)
+#undef SCREEN_RESOURCE_DATA_OVERVIEW
+#define SCREEN_RESOURCE_DATA_OVERVIEW (10)
+#undef SCREEN_RESOURCE_ACTIONS
+#define SCREEN_RESOURCE_ACTIONS (12)
+#undef SCREEN_RESOURCE_EVENTS
+#define SCREEN_RESOURCE_EVENTS (13)
+#undef SCREEN_RESOURCE_HOSTGROUP_TRIGGERS
+#define SCREEN_RESOURCE_HOSTGROUP_TRIGGERS (14)
+#undef SCREEN_RESOURCE_SYSTEM_STATUS
+#define SCREEN_RESOURCE_SYSTEM_STATUS (15)
+#undef SCREEN_RESOURCE_HOST_TRIGGERS
+#define SCREEN_RESOURCE_HOST_TRIGGERS (16)
+
+#define ZBX_WIDGET_FIELD_TYPE_INT32 (0)
+#define ZBX_WIDGET_FIELD_TYPE_STR (1)
+#define ZBX_WIDGET_FIELD_TYPE_GROUP (2)
+#define ZBX_WIDGET_FIELD_TYPE_HOST (3)
+#define ZBX_WIDGET_FIELD_TYPE_ITEM (4)
+#define ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE (5)
+#define ZBX_WIDGET_FIELD_TYPE_GRAPH (6)
+#define ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE (7)
+#define ZBX_WIDGET_FIELD_TYPE_MAP (8)
+
+/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH (0) */
+/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH (1) */
+/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE (2) */
+/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE (3) */
+
+#define ZBX_WIDGET_TYPE_CLOCK ("clock")
+#define ZBX_WIDGET_TYPE_GRAPH_CLASSIC ("graph")
+#define ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE ("graphprototype")
+#define ZBX_WIDGET_TYPE_PLAIN_TEXT ("plaintext")
+#define ZBX_WIDGET_TYPE_URL ("url")
+#define ZBX_WIDGET_TYPE_ACTIONS ("actionlog")
+#define ZBX_WIDGET_TYPE_DATA_OVERVIEW ("dataover")
+#define ZBX_WIDGET_TYPE_PROBLEMS ("problems")
+#define ZBX_WIDGET_TYPE_HOST_INFO ("hostavail")
+#define ZBX_WIDGET_TYPE_MAP ("map")
+#define ZBX_WIDGET_TYPE_SYSTEM_STATUS ("problemsbysv")
+#define ZBX_WIDGET_TYPE_SERVER_INFO ("systeminfo")
+#define ZBX_WIDGET_TYPE_TRIGGER_OVERVIEW ("trigover")
+
+#define POS_EMPTY (127)
+#define POS_TAKEN (1)
+
+ZBX_VECTOR_DECL(scitem_dim2, zbx_screen_item_dim_t)
+ZBX_VECTOR_IMPL(scitem_dim2, zbx_screen_item_dim_t)
+ZBX_VECTOR_DECL(char2, char)
+ZBX_VECTOR_IMPL(char2, char)
+
+#define SKIP_EMPTY(vector,index) if (POS_EMPTY == vector->values[index]) continue
+
+#define COLLISIONS_MAX_NUMBER (100)
+#define REFERENCE_MAX_LEN (5)
+#define DASHBOARD_NAME_LEN (255)
+
+static int DBpatch_dashboard_name(char *name, char **new_name)
+{
+ int affix = 0, ret = FAIL, trim;
+ char *affix_string = NULL;
+ DB_RESULT result = NULL;
+ DB_ROW row;
+
+ *new_name = zbx_strdup(*new_name, name);
+
+ do
+ {
+ DBfree_result(result);
+
+ result = DBselect("select count(*)"
+ " from dashboard"
+ " where name='%s' and templateid is null",
+ *new_name);
+
+ if (NULL == result || NULL == (row = DBfetch(result)))
+ {
+ zbx_free(*new_name);
+ break;
+ }
+
+ if (0 == strcmp("0", row[0]))
+ {
+ ret = SUCCEED;
+ break;
+ }
+
+ affix_string = zbx_dsprintf(affix_string, " (%d)", affix + 1);
+ trim = (int)strlen(name) + (int)strlen(affix_string) - DASHBOARD_NAME_LEN;
+ if (0 < trim )
+ name[(int)strlen(name) - trim] = '\0';
+
+ *new_name = zbx_dsprintf(*new_name, "%s%s", name, affix_string);
+ } while (COLLISIONS_MAX_NUMBER > affix++);
+
+ DBfree_result(result);
+ zbx_free(affix_string);
+
+ return ret;
+}
+
+static int DBpatch_reference_name(char **ref_name)
+{
+ int i = 0, j, ret = FAIL;
+ char name[REFERENCE_MAX_LEN + 1];
+ const char *pattern = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ DB_RESULT result = NULL;
+ DB_ROW row;
+
+ name[REFERENCE_MAX_LEN] = '\0';
+
+ do
+ {
+ for (j = 0; j < REFERENCE_MAX_LEN; j++)
+ name[j] = pattern[rand() % (int)strlen(pattern)];
+
+ DBfree_result(result);
+
+ result = DBselect("select count(*)"
+ " from widget_field"
+ " where value_str='%s' and name='reference'",
+ name);
+
+ if (NULL == result || NULL == (row = DBfetch(result)))
+ break;
+
+ if (0 == strcmp("0", row[0]))
+ {
+ ret = SUCCEED;
+ *ref_name = zbx_strdup(NULL, name);
+ break;
+ }
+
+ } while (COLLISIONS_MAX_NUMBER > i++);
+
+ DBfree_result(result);
+
+ return ret;
+}
+
+static int DBpatch_init_dashboard(zbx_db_dashboard_t *dashboard, char *name, uint64_t userid,
+ int private)
+{
+ int ret = SUCCEED;
+
+ memset((void *)dashboard, 0, sizeof(zbx_db_dashboard_t));
+
+ dashboard->userid = userid;
+ dashboard->private = private;
+ dashboard->display_period = 30;
+ ret = DBpatch_dashboard_name(name, &dashboard->name);
+
+ return ret;
+}
+
+static void DBpatch_widget_field_free(zbx_db_widget_field_t *field)
+{
+ zbx_free(field->name);
+ zbx_free(field->value_str);
+ zbx_free(field);
+}
+
+static void DBpatch_screen_item_free(zbx_db_screen_item_t *si)
+{
+ zbx_free(si->url);
+ zbx_free(si->application);
+ zbx_free(si);
+}
+
+static size_t DBpatch_array_max_used_index(char *array, size_t arr_size)
+{
+ size_t i, m = 0;
+
+ for (i = 0; i < arr_size; i++)
+ {
+ if (0 != array[i])
+ m = i;
+ }
+
+ return m;
+}
+
+static void DBpatch_normalize_screen_items_pos(zbx_vector_ptr_t *scr_items)
+{
+ char used_x[SCREEN_MAX_COLS], used_y[SCREEN_MAX_ROWS];
+ char keep_x[SCREEN_MAX_COLS], keep_y[SCREEN_MAX_ROWS];
+ int i, n, x;
+
+ memset((void *)used_x, 0, sizeof(used_x));
+ memset((void *)used_y, 0, sizeof(used_y));
+ memset((void *)keep_x, 0, sizeof(keep_x));
+ memset((void *)keep_y, 0, sizeof(keep_y));
+
+ for (i = 0; i < scr_items->values_num; i++)
+ {
+ zbx_db_screen_item_t *c = (zbx_db_screen_item_t *)scr_items->values[i];
+
+ for (n = c->x; n < c->x + c->colspan && n < SCREEN_MAX_COLS; n++)
+ used_x[n] = 1;
+ for (n = c->y; n < c->y + c->rowspan && n < SCREEN_MAX_ROWS; n++)
+ used_y[n] = 1;
+
+ keep_x[c->x] = 1;
+ if (c->x + c->colspan < SCREEN_MAX_COLS)
+ keep_x[c->x + c->colspan] = 1;
+ keep_y[c->y] = 1;
+ if (c->y + c->rowspan < SCREEN_MAX_ROWS)
+ keep_y[c->y + c->rowspan] = 1;
+ }
+
+#define COMPRESS_SCREEN_ITEMS(axis, span, a_size) \
+ \
+do { \
+ for (x = (int)DBpatch_array_max_used_index(keep_ ## axis, a_size); x >= 0; x--) \
+ { \
+ if (0 != keep_ ## axis[x] && 0 != used_ ## axis[x]) \
+ continue; \
+ \
+ for (i = 0; i < scr_items->values_num; i++) \
+ { \
+ zbx_db_screen_item_t *c = (zbx_db_screen_item_t *)scr_items->values[i]; \
+ \
+ if (x < c->axis) \
+ c->axis--; \
+ \
+ if (x > c->axis && x < c->axis + c->span) \
+ c->span--; \
+ } \
+ } \
+} while (0)
+
+ COMPRESS_SCREEN_ITEMS(x, colspan, SCREEN_MAX_COLS);
+ COMPRESS_SCREEN_ITEMS(y, rowspan, SCREEN_MAX_ROWS);
+
+#undef COMPRESS_SCREEN_ITEMS
+}
+
+static void DBpatch_get_preferred_widget_size(zbx_db_screen_item_t *item, int *w, int *h)
+{
+ *w = item->width;
+ *h = item->height;
+
+ if (SCREEN_RESOURCE_LLD_GRAPH == item->resourcetype || SCREEN_RESOURCE_LLD_SIMPLE_GRAPH == item->resourcetype ||
+ SCREEN_RESOURCE_GRAPH == item->resourcetype ||
+ SCREEN_RESOURCE_SIMPLE_GRAPH == item->resourcetype)
+ {
+ *h += 215; /* SCREEN_LEGEND_HEIGHT */
+ }
+
+ if (SCREEN_RESOURCE_PLAIN_TEXT == item->resourcetype || SCREEN_RESOURCE_HOST_INFO == item->resourcetype ||
+ SCREEN_RESOURCE_TRIGGER_INFO == item->resourcetype ||
+ SCREEN_RESOURCE_SERVER_INFO == item->resourcetype ||
+ SCREEN_RESOURCE_ACTIONS == item->resourcetype ||
+ SCREEN_RESOURCE_EVENTS == item->resourcetype ||
+ SCREEN_RESOURCE_HOSTGROUP_TRIGGERS == item->resourcetype ||
+ SCREEN_RESOURCE_SYSTEM_STATUS == item->resourcetype ||
+ SCREEN_RESOURCE_HOST_TRIGGERS== item->resourcetype)
+ {
+ *h = 2 + 2 * MIN(25, item->elements) / 5;
+ }
+ else
+ *h = (int)round((double)*h / 70); /* WIDGET_ROW_HEIGHT */
+
+ *w = (int)round((double)*w / 1920 * DASHBOARD_MAX_COLS); /* DISPLAY_WIDTH */
+
+ *w = MIN(DASHBOARD_MAX_COLS, MAX(1, *w));
+ *h = MIN(DASHBOARD_WIDGET_MAX_ROWS, MAX(DASHBOARD_WIDGET_MIN_ROWS, *h));
+}
+
+static void DBpatch_get_min_widget_size(zbx_db_screen_item_t *item, int *w, int *h)
+{
+ switch (item->resourcetype)
+ {
+ case SCREEN_RESOURCE_CLOCK:
+ *w = 1; *h = 2;
+ break;
+ case SCREEN_RESOURCE_GRAPH:
+ case SCREEN_RESOURCE_SIMPLE_GRAPH:
+ case SCREEN_RESOURCE_LLD_GRAPH:
+ case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
+ case SCREEN_RESOURCE_MAP:
+ *w = 4; *h = 4;
+ break;
+ case SCREEN_RESOURCE_PLAIN_TEXT:
+ case SCREEN_RESOURCE_URL:
+ case SCREEN_RESOURCE_TRIGGER_INFO:
+ case SCREEN_RESOURCE_ACTIONS:
+ case SCREEN_RESOURCE_EVENTS:
+ case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS:
+ case SCREEN_RESOURCE_HOST_TRIGGERS:
+ *w = 4; *h = 2;
+ break;
+ case SCREEN_RESOURCE_HOST_INFO:
+ *w = 4; *h = 3;
+ break;
+ case SCREEN_RESOURCE_SERVER_INFO:
+ case SCREEN_RESOURCE_SYSTEM_STATUS:
+ *w = 4; *h = 4;
+ break;
+ case SCREEN_RESOURCE_TRIGGER_OVERVIEW:
+ *w = 4; *h = 7;
+ break;
+ case SCREEN_RESOURCE_DATA_OVERVIEW:
+ *w = 4; *h = 5;
+ break;
+ default:
+ zabbix_log(LOG_LEVEL_WARNING, "%s: unknown resource type %d", __func__, item->resourcetype);
+ }
+}
+
+static char *lw_array_to_str(zbx_vector_char2_t *v)
+{
+ static char str[MAX_STRING_LEN];
+ char *ptr;
+ int i, max = MAX_STRING_LEN, len;
+
+ ptr = str;
+ len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
+ ptr += len;
+ max -= len;
+
+ for (i = 0; 0 < max && i < v->values_num; i++)
+ {
+ if (POS_EMPTY != v->values[i])
+ {
+ len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, (int)v->values[i]);
+ ptr += len;
+ max -= len;
+ }
+ }
+
+ if (max > 1)
+ strcat(ptr, "]");
+
+ return str;
+}
+
+static void lw_array_debug(char *pfx, zbx_vector_char2_t *v)
+{
+ zabbix_log(LOG_LEVEL_TRACE, "%s: %s", pfx, lw_array_to_str(v));
+}
+
+static void int_array_debug(char *pfx, int *a, int alen, int emptyval)
+{
+ static char str[MAX_STRING_LEN];
+ char *ptr;
+ int i, max = MAX_STRING_LEN, len;
+
+ ptr = str;
+ len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
+ ptr += len;
+ max -= len;
+
+ for (i = 0; 0 < max && i < alen; i++)
+ {
+ if (emptyval != a[i])
+ {
+ len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, a[i]);
+ ptr += len;
+ max -= len;
+ }
+ }
+
+ if (max > 1)
+ strcat(ptr, "]");
+
+ zabbix_log(LOG_LEVEL_TRACE, "%s: %s", pfx, str);
+}
+
+static zbx_vector_char2_t *lw_array_create(void)
+{
+ zbx_vector_char2_t *v;
+ static char fill[SCREEN_MAX_ROWS];
+
+ if (0 == fill[0])
+ memset(fill, POS_EMPTY, SCREEN_MAX_ROWS);
+
+ v = (zbx_vector_char2_t *)malloc(sizeof(zbx_vector_char2_t));
+
+ zbx_vector_char2_create(v);
+ zbx_vector_char2_append_array(v, fill, SCREEN_MAX_ROWS);
+
+ return v;
+}
+
+static void lw_array_free(zbx_vector_char2_t *v)
+{
+ if (NULL != v)
+ {
+ zbx_vector_char2_destroy(v);
+ zbx_free(v);
+ }
+}
+
+static zbx_vector_char2_t *lw_array_create_fill(int start, size_t num)
+{
+ size_t i;
+ zbx_vector_char2_t *v;
+
+ v = lw_array_create();
+
+ for (i = (size_t)start; i < (size_t)start + num && i < (size_t)v->values_num; i++)
+ v->values[i] = POS_TAKEN;
+
+ return v;
+}
+
+static zbx_vector_char2_t *lw_array_diff(zbx_vector_char2_t *a, zbx_vector_char2_t *b)
+{
+ int i;
+ zbx_vector_char2_t *v;
+
+ v = lw_array_create();
+
+ for (i = 0; i < a->values_num; i++)
+ {
+ SKIP_EMPTY(a, i);
+ if (POS_EMPTY == b->values[i])
+ v->values[i] = a->values[i];
+ }
+
+ return v;
+}
+
+static zbx_vector_char2_t *lw_array_intersect(zbx_vector_char2_t *a, zbx_vector_char2_t *b)
+{
+ int i;
+ zbx_vector_char2_t *v;
+
+ v = lw_array_create();
+
+ for (i = 0; i < a->values_num; i++)
+ {
+ SKIP_EMPTY(a, i);
+ if (POS_EMPTY != b->values[i])
+ v->values[i] = a->values[i];
+ }
+
+ return v;
+}
+
+static int lw_array_count(zbx_vector_char2_t *v)
+{
+ int i, c = 0;
+
+ for (i = 0; i < v->values_num; i++)
+ {
+ if (POS_EMPTY != v->values[i])
+ c++;
+ }
+
+ return c;
+}
+
+static int lw_array_sum(zbx_vector_char2_t *v)
+{
+ int i, c = 0;
+
+ for (i = 0; i < v->values_num; i++)
+ {
+ if (POS_EMPTY != v->values[i])
+ c += v->values[i];
+ }
+
+ return c;
+}
+
+typedef struct
+{
+ int index; /* index for zbx_vector_scitem_dim2_t */
+ zbx_vector_char2_t *r_block;
+}
+sciitem_block_t;
+
+static zbx_vector_char2_t *sort_dimensions;
+
+static int DBpatch_block_compare_func(const void *d1, const void *d2)
+{
+ const sciitem_block_t *i1 = *(const sciitem_block_t **)d1;
+ const sciitem_block_t *i2 = *(const sciitem_block_t **)d2;
+ zbx_vector_char2_t *diff1, *diff2;
+ int unsized_a, unsized_b;
+
+ diff1 = lw_array_diff(i1->r_block, sort_dimensions);
+ diff2 = lw_array_diff(i2->r_block, sort_dimensions);
+
+ unsized_a = lw_array_count(diff1);
+ unsized_b = lw_array_count(diff2);
+
+ lw_array_free(diff1);
+ lw_array_free(diff2);
+
+ ZBX_RETURN_IF_NOT_EQUAL(unsized_a, unsized_b);
+
+ return 0;
+}
+
+static zbx_vector_char2_t *DBpatch_get_axis_dimensions(zbx_vector_scitem_dim2_t *scitems)
+{
+ int i;
+ zbx_vector_ptr_t blocks;
+ sciitem_block_t *block;
+ zbx_vector_char2_t *dimensions;
+
+ zabbix_log(LOG_LEVEL_TRACE, "In %s()", __func__);
+
+ zbx_vector_ptr_create(&blocks);
+ dimensions = lw_array_create();
+
+ for (i = 0; i < scitems->values_num; i++)
+ {
+ block = (sciitem_block_t *)malloc(sizeof(sciitem_block_t));
+ block->r_block = lw_array_create_fill(scitems->values[i].position, (size_t)scitems->values[i].span);
+ block->index = i;
+ zbx_vector_ptr_append(&blocks, (void *)block);
+ }
+
+ sort_dimensions = dimensions;
+
+ while (0 < blocks.values_num)
+ {
+ zbx_vector_char2_t *block_dimensions, *block_unsized, *r_block;
+ int block_dimensions_sum, block_unsized_count, size_overflow, n;
+
+ zbx_vector_ptr_sort(&blocks, DBpatch_block_compare_func);
+ block = blocks.values[0];
+ r_block = block->r_block;
+
+ block_dimensions = lw_array_intersect(dimensions, r_block);
+ block_dimensions_sum = lw_array_sum(block_dimensions);
+ lw_array_free(block_dimensions);
+
+ block_unsized = lw_array_diff(r_block, dimensions);
+ block_unsized_count = lw_array_count(block_unsized);
+ size_overflow = scitems->values[block->index].size - block_dimensions_sum;
+
+ if (0 < block_unsized_count)
+ {
+ for (n = 0; n < block_unsized->values_num; n++)
+ {
+ SKIP_EMPTY(block_unsized, n);
+ dimensions->values[n] = (char)MAX(1, size_overflow / block_unsized_count);
+ size_overflow -= dimensions->values[n];
+ block_unsized_count--;
+ }
+ }
+ else if (0 < size_overflow)
+ {
+ for (n = 0; n < r_block->values_num; n++)
+ {
+ double factor;
+ int new_dimension;
+
+ SKIP_EMPTY(r_block, n);
+ factor = (double)(size_overflow + block_dimensions_sum) / block_dimensions_sum;
+ new_dimension = (int)round(factor * dimensions->values[n]);
+ block_dimensions_sum -= dimensions->values[n];
+ size_overflow -= new_dimension - dimensions->values[n];
+ dimensions->values[n] = (char)new_dimension;
+ }
+ }
+
+ lw_array_free(block->r_block);
+ zbx_free(block);
+ lw_array_free(block_unsized);
+ zbx_vector_ptr_remove(&blocks, 0);
+ }
+
+ zbx_vector_ptr_destroy(&blocks);
+
+ zabbix_log(LOG_LEVEL_TRACE, "End of %s(): dim:%s", __func__, lw_array_to_str(dimensions));
+
+ return dimensions;
+}
+
+/* modifies widget units in first argument */
+static void DBpatch_adjust_axis_dimensions(zbx_vector_char2_t *d, zbx_vector_char2_t *d_min, int target)
+{
+ int dimensions_sum, i;
+
+ zabbix_log(LOG_LEVEL_TRACE, "In %s(): d:%s", __func__, lw_array_to_str(d));
+ zabbix_log(LOG_LEVEL_TRACE, " d_min:%s", lw_array_to_str(d_min));
+
+ dimensions_sum = lw_array_sum(d);
+
+ while (dimensions_sum != target)
+ {
+ int potential_index = -1;
+ double potential_value;
+
+ for (i = 0; i < d->values_num; i++)
+ {
+ double value;
+
+ SKIP_EMPTY(d, i);
+ value = (double)d->values[i] / d_min->values[i];
+
+ if (0 > potential_index ||
+ (dimensions_sum > target && value > potential_value) ||
+ (dimensions_sum < target && value < potential_value))
+ {
+ potential_index = i;
+ potential_value = value;
+ }
+ }
+
+ if (0 <= potential_index)
+ {
+ zabbix_log(LOG_LEVEL_TRACE, "dim_sum:%d pot_idx/val:%d/%.2lf", dimensions_sum,
+ potential_index, potential_value);
+ }
+
+ if (dimensions_sum > target && d->values[potential_index] == d_min->values[potential_index])
+ break;
+
+ if (dimensions_sum > target)
+ {
+ d->values[potential_index]--;
+ dimensions_sum--;
+ }
+ else
+ {
+ d->values[potential_index]++;
+ dimensions_sum++;
+ }
+ }
+
+ zabbix_log(LOG_LEVEL_TRACE, "End of %s(): d:%s", __func__, lw_array_to_str(d));
+}
+
+static void DBpatch_get_dashboard_dimensions(zbx_vector_ptr_t *scr_items, zbx_vector_char2_t **x,
+ zbx_vector_char2_t **y)
+{
+ zbx_vector_char2_t *dim_x_pref, *dim_x_min;
+ zbx_vector_char2_t *dim_y_pref, *dim_y_min;
+ zbx_vector_scitem_dim2_t items_x_pref, items_y_pref;
+ zbx_vector_scitem_dim2_t items_x_min, items_y_min;
+ int i;
+
+ zabbix_log(LOG_LEVEL_TRACE, "In %s()", __func__);
+
+ zbx_vector_scitem_dim2_create(&items_x_pref);
+ zbx_vector_scitem_dim2_create(&items_y_pref);
+ zbx_vector_scitem_dim2_create(&items_x_min);
+ zbx_vector_scitem_dim2_create(&items_y_min);
+
+ for (i = 0; i < scr_items->values_num; i++)
+ {
+ int pref_size_w, pref_size_h;
+ int min_size_w, min_size_h;
+ zbx_screen_item_dim_t item;
+ zbx_db_screen_item_t *si;
+
+ si = scr_items->values[i];
+ DBpatch_get_preferred_widget_size(si, &pref_size_w, &pref_size_h);
+ DBpatch_get_min_widget_size(si, &min_size_w, &min_size_h);
+
+ item.position = si->x;
+ item.span = si->colspan;
+ item.size = MAX(pref_size_w, min_size_w);
+ zbx_vector_scitem_dim2_append(&items_x_pref, item);
+
+ item.position = si->y;
+ item.span = si->rowspan;
+ item.size = MAX(pref_size_h, min_size_h);
+ zbx_vector_scitem_dim2_append(&items_y_pref, item);
+
+ item.position = si->x;
+ item.span = si->colspan;
+ item.size = min_size_w;
+ zbx_vector_scitem_dim2_append(&items_x_min, item);
+
+ item.position = si->y;
+ item.span = si->rowspan;
+ item.size = min_size_h;
+ zbx_vector_scitem_dim2_append(&items_y_min, item);
+ }
+
+ dim_x_pref = DBpatch_get_axis_dimensions(&items_x_pref);
+ dim_x_min = DBpatch_get_axis_dimensions(&items_x_min);
+
+ zabbix_log(LOG_LEVEL_TRACE, "%s: dim_x_pref:%s", __func__, lw_array_to_str(dim_x_pref));
+ zabbix_log(LOG_LEVEL_TRACE, " dim_x_min:%s", lw_array_to_str(dim_x_min));
+
+ DBpatch_adjust_axis_dimensions(dim_x_pref, dim_x_min, DASHBOARD_MAX_COLS);
+
+ dim_y_pref = DBpatch_get_axis_dimensions(&items_y_pref);
+ dim_y_min = DBpatch_get_axis_dimensions(&items_y_min);
+
+ if (DASHBOARD_MAX_ROWS < lw_array_sum(dim_y_pref))
+ DBpatch_adjust_axis_dimensions(dim_y_pref, dim_y_min, DASHBOARD_MAX_ROWS);
+
+ lw_array_free(dim_x_min);
+ lw_array_free(dim_y_min);
+ zbx_vector_scitem_dim2_destroy(&items_x_pref);
+ zbx_vector_scitem_dim2_destroy(&items_y_pref);
+ zbx_vector_scitem_dim2_destroy(&items_x_min);
+ zbx_vector_scitem_dim2_destroy(&items_y_min);
+
+ *x = dim_x_pref;
+ *y = dim_y_pref;
+
+ zabbix_log(LOG_LEVEL_TRACE, "End of %s(): x:%s y:%s", __func__, lw_array_to_str(*x), lw_array_to_str(*y));
+}
+
+static zbx_db_widget_field_t *DBpatch_make_widget_field(int type, char *name, void *value)
+{
+ zbx_db_widget_field_t *wf;
+
+ wf = (zbx_db_widget_field_t *)zbx_calloc(NULL, 1, sizeof(zbx_db_widget_field_t));
+ wf->name = zbx_strdup(NULL, name);
+ wf->type = type;
+
+ switch (type)
+ {
+ case ZBX_WIDGET_FIELD_TYPE_INT32:
+ wf->value_int = *((int *)value);
+ break;
+ case ZBX_WIDGET_FIELD_TYPE_STR:
+ wf->value_str = zbx_strdup(NULL, (char *)value);
+ break;
+ case ZBX_WIDGET_FIELD_TYPE_GROUP:
+ wf->value_groupid = *((uint64_t *)value);
+ break;
+ case ZBX_WIDGET_FIELD_TYPE_HOST:
+ wf->value_hostid = *((uint64_t *)value);
+ break;
+ case ZBX_WIDGET_FIELD_TYPE_ITEM:
+ case ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE:
+ wf->value_itemid = *((uint64_t *)value);
+ break;
+ case ZBX_WIDGET_FIELD_TYPE_GRAPH:
+ case ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE:
+ wf->value_graphid = *((uint64_t *)value);
+ break;
+ case ZBX_WIDGET_FIELD_TYPE_MAP:
+ wf->value_sysmapid = *((uint64_t *)value);
+ break;
+ default:
+ zabbix_log(LOG_LEVEL_WARNING, "%s: unknown field type: %d", __func__, type);
+ }
+
+ if (NULL == wf->value_str)
+ wf->value_str = zbx_strdup(NULL, "");
+
+ return wf;
+}
+
+static void DBpatch_widget_from_screen_item(zbx_db_screen_item_t *si, zbx_db_widget_t *w, zbx_vector_ptr_t *fields)
+{
+ int tmp;
+ char *reference = NULL;
+ zbx_db_widget_field_t *f;
+
+ w->name = zbx_strdup(NULL, "");
+ w->view_mode = 0; /* ZBX_WIDGET_VIEW_MODE_NORMAL */
+
+#define ADD_FIELD(a, b, c) \
+ \
+do { \
+ f = DBpatch_make_widget_field(a, b, c); \
+ zbx_vector_ptr_append(fields, (void *)f); \
+} while (0)
+
+ switch (si->resourcetype)
+ {
+ case SCREEN_RESOURCE_CLOCK:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_CLOCK);
+
+ /* here are below in this switch we add only those fields that are not */
+ /* considered default by frontend API */
+
+ if (0 != si->style) /* style 0 is default, don't add */
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "time_type", (void *)&si->style);
+ if (2 == si->style) /* TIME_TYPE_HOST */
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemid", (void *)&si->resourceid);
+ break;
+ case SCREEN_RESOURCE_GRAPH:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_CLASSIC);
+ /* source_type = ZBX_WIDGET_FIELD_RESOURCE_GRAPH (0); don't add because it's default */
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GRAPH, "graphid", (void *)&si->resourceid);
+ tmp = 1;
+ if (1 == si->dynamic)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_SIMPLE_GRAPH:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_CLASSIC);
+ tmp = 1; /* source_type = ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH */
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "source_type", (void *)&tmp);
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemid", (void *)&si->resourceid);
+ tmp = 1;
+ if (1 == si->dynamic)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_LLD_GRAPH:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE);
+ /* source_type = ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE (2); don't add because it's default */
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE, "graphid", (void *)&si->resourceid);
+ /* add field "columns" because the default value is 2 */
+ tmp = 1;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "columns", (void *)&tmp);
+ tmp = 1;
+ if (1 == si->dynamic)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
+ /* don't add field "rows" because 1 is default */
+ break;
+ case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE);
+ tmp = 3; /* source_type = ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE */
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "source_type", (void *)&tmp);
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE, "itemid", (void *)&si->resourceid);
+ tmp = 1;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "columns", (void *)&tmp);
+ tmp = 1;
+ if (1 == si->dynamic)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
+ /* don't add field "rows" because 1 is default */
+ break;
+ case SCREEN_RESOURCE_PLAIN_TEXT:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PLAIN_TEXT);
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemids", (void *)&si->resourceid);
+ if (0 != si->style)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_as_html", (void *)&si->style);
+ if (25 != si->elements)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
+ tmp = 1;
+ if (1 == si->dynamic)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_URL:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_URL);
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "url", (void *)si->url);
+ tmp = 1;
+ if (1 == si->dynamic)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_ACTIONS:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_ACTIONS);
+ if (4 != si->sort_triggers)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "sort_triggers", (void *)&si->sort_triggers);
+ if (25 != si->elements)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
+ break;
+ case SCREEN_RESOURCE_DATA_OVERVIEW:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_DATA_OVERVIEW);
+ tmp = 1;
+ if (1 == si->style)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "style", (void *)&tmp);
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
+ if ('\0' != *si->application)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "application", (void *)si->application);
+ break;
+ case SCREEN_RESOURCE_EVENTS:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PROBLEMS);
+ if (25 != si->elements)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
+ tmp = 2;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PROBLEMS);
+ if (0 != si->sort_triggers)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "sort_triggers", (void *)&si->sort_triggers);
+ if (25 != si->elements)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
+ if (0 != si->resourceid)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
+ tmp = 3;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
+ tmp = 0;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_timeline", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_HOST_INFO:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_HOST_INFO);
+ tmp = 1;
+ if (1 == si->style)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "layout", (void *)&tmp);
+ if (0 != si->resourceid)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
+ break;
+ case SCREEN_RESOURCE_HOST_TRIGGERS:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PROBLEMS);
+ if (0 != si->sort_triggers)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "sort_triggers", (void *)&si->sort_triggers);
+ if (25 != si->elements)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
+ if (0 != si->resourceid)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_HOST, "hostids", (void *)&si->resourceid);
+ tmp = 3;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
+ tmp = 0;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_timeline", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_MAP:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_MAP);
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_MAP, "sysmapid", (void *)&si->resourceid);
+ if (SUCCEED == DBpatch_reference_name(&reference))
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "reference", (void *)reference);
+ zbx_free(reference);
+ break;
+ case SCREEN_RESOURCE_SYSTEM_STATUS:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_SYSTEM_STATUS);
+ break;
+ case SCREEN_RESOURCE_SERVER_INFO:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_SERVER_INFO);
+ break;
+ case SCREEN_RESOURCE_TRIGGER_OVERVIEW:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_TRIGGER_OVERVIEW);
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
+ if ('\0' != *si->application)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "application", (void *)si->application);
+ tmp = 1;
+ if (1 == si->style)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "style", (void *)&tmp);
+ tmp = 2;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
+ break;
+ case SCREEN_RESOURCE_TRIGGER_INFO:
+ w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_SYSTEM_STATUS);
+ if (0 != si->resourceid)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
+ tmp = 1;
+ if (1 == si->style)
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "layout", (void *)&tmp);
+ tmp = 1;
+ ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_type", (void *)&tmp);
+ break;
+ default:
+ zabbix_log(LOG_LEVEL_WARNING, "%s: unknown screen resource type: %d", __func__,
+ si->resourcetype);
+ }
+#undef ADD_FIELD
+}
+
+static char *DBpatch_resourcetype_str(int rtype)
+{
+ switch (rtype)
+ {
+ case SCREEN_RESOURCE_CLOCK:
+ return "clock";
+ case SCREEN_RESOURCE_GRAPH:
+ return "graph";
+ case SCREEN_RESOURCE_SIMPLE_GRAPH:
+ return "simplegraph";
+ case SCREEN_RESOURCE_LLD_GRAPH:
+ return "lldgraph";
+ case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
+ return "lldsimplegraph";
+ case SCREEN_RESOURCE_PLAIN_TEXT:
+ return "plaintext";
+ case SCREEN_RESOURCE_URL:
+ return "url";
+ /* additional types */
+ case SCREEN_RESOURCE_MAP:
+ return "map";
+ case SCREEN_RESOURCE_HOST_INFO:
+ return "host info";
+ case SCREEN_RESOURCE_TRIGGER_INFO:
+ return "trigger info";
+ case SCREEN_RESOURCE_SERVER_INFO:
+ return "server info";
+ case SCREEN_RESOURCE_TRIGGER_OVERVIEW:
+ return "trigger overview";
+ case SCREEN_RESOURCE_DATA_OVERVIEW:
+ return "data overview";
+ case SCREEN_RESOURCE_ACTIONS:
+ return "action";
+ case SCREEN_RESOURCE_EVENTS:
+ return "events";
+ case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS:
+ return "hostgroup triggers";
+ case SCREEN_RESOURCE_SYSTEM_STATUS:
+ return "system status";
+ case SCREEN_RESOURCE_HOST_TRIGGERS:
+ return "host triggers";
+ }
+
+ return "*unknown*";
+}
+
+static void DBpatch_trace_screen_item(zbx_db_screen_item_t *item)
+{
+ zabbix_log(LOG_LEVEL_TRACE, " screenitemid:" ZBX_FS_UI64 " screenid:" ZBX_FS_UI64,
+ item->screenitemid, item->screenid);
+ zabbix_log(LOG_LEVEL_TRACE, " resourcetype: %s resourceid:" ZBX_FS_UI64,
+ DBpatch_resourcetype_str(item->resourcetype), item->resourceid);
+ zabbix_log(LOG_LEVEL_TRACE, " w/h: %dx%d (x,y): (%d,%d) (c,rspan): (%d,%d)",
+ item->width, item->height, item->x, item->y, item->colspan, item->rowspan);
+}
+
+static void DBpatch_trace_widget(zbx_db_widget_t *w)
+{
+ zabbix_log(LOG_LEVEL_TRACE, " widgetid:" ZBX_FS_UI64 " dbid:" ZBX_FS_UI64 " type:%s",
+ w->widgetid, w->dashboardid, w->type);
+ zabbix_log(LOG_LEVEL_TRACE, " widget type: %s w/h: %dx%d (x,y): (%d,%d)",
+ w->type, w->width, w->height, w->x, w->y);
+}
+
+/* adds new dashboard to the DB, sets new dashboardid in the struct */
+static int DBpatch_add_dashboard(zbx_db_dashboard_t *dashboard)
+{
+ char *name_esc;
+ int res;
+
+ dashboard->dashboardid = DBget_maxid("dashboard");
+ name_esc = DBdyn_escape_string(dashboard->name);
+
+ zabbix_log(LOG_LEVEL_TRACE, "adding dashboard id:" ZBX_FS_UI64, dashboard->dashboardid);
+
+ res = DBexecute("insert into dashboard (dashboardid,name,userid,private,display_period) values "
+ "("ZBX_FS_UI64 ",'%s',"ZBX_FS_UI64 ",%d,%d)",
+ dashboard->dashboardid, name_esc, dashboard->userid, dashboard->private,
+ dashboard->display_period);
+
+ zbx_free(name_esc);
+
+ return ZBX_DB_OK > res ? FAIL : SUCCEED;
+}
+
+/* adds new dashboard page to the DB, sets new dashboard_pageid in the struct */
+static int DBpatch_add_dashboard_page(zbx_db_dashboard_page_t *dashboard_page, uint64_t dashboardid, char *name,
+ int display_period, int sortorder)
+{
+ int res;
+
+ dashboard_page->dashboard_pageid = DBget_maxid("dashboard_page");
+ dashboard_page->dashboardid = dashboardid;
+
+ zabbix_log(LOG_LEVEL_TRACE, "adding dashboard_page id:" ZBX_FS_UI64, dashboard_page->dashboard_pageid);
+
+ res = DBexecute("insert into dashboard_page (dashboard_pageid,dashboardid,name,display_period,sortorder)"
+ " values ("ZBX_FS_UI64 ","ZBX_FS_UI64 ",'%s',%d,%d)",
+ dashboard_page->dashboard_pageid, dashboardid, name, display_period, sortorder);
+
+ return ZBX_DB_OK > res ? FAIL : SUCCEED;
+}
+
+/* adds new widget and widget fields to the DB */
+static int DBpatch_add_widget(uint64_t dashboardid, zbx_db_widget_t *widget, zbx_vector_ptr_t *fields)
+{
+ uint64_t new_fieldid;
+ int i, ret = SUCCEED;
+ char *name_esc;
+
+ widget->widgetid = DBget_maxid("widget");
+ widget->dashboardid = dashboardid;
+ name_esc = DBdyn_escape_string(widget->name);
+
+ zabbix_log(LOG_LEVEL_TRACE, "adding widget id: " ZBX_FS_UI64 ", type: %s", widget->widgetid, widget->type);
+
+
+ if (ZBX_DB_OK > DBexecute("insert into widget (widgetid,dashboard_pageid,type,name,x,y,width,height,view_mode) "
+ "values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",'%s','%s',%d,%d,%d,%d,%d)",
+ widget->widgetid, widget->dashboardid, widget->type, name_esc,
+ widget->x, widget->y, widget->width, widget->height, widget->view_mode))
+ {
+ ret = FAIL;
+ }
+
+ zbx_free(name_esc);
+
+ if (SUCCEED == ret && 0 < fields->values_num)
+ new_fieldid = DBget_maxid_num("widget_field", fields->values_num);
+
+ for (i = 0; SUCCEED == ret && i < fields->values_num; i++)
+ {
+ char s1[ZBX_MAX_UINT64_LEN + 1], s2[ZBX_MAX_UINT64_LEN + 1],
+ s3[ZBX_MAX_UINT64_LEN + 1], s4[ZBX_MAX_UINT64_LEN + 1],
+ s5[ZBX_MAX_UINT64_LEN + 1], *url_esc;
+ zbx_db_widget_field_t *f;
+
+ f = (zbx_db_widget_field_t *)fields->values[i];
+ url_esc = DBdyn_escape_string(f->value_str);
+
+ if (0 != f->value_itemid)
+ zbx_snprintf(s1, ZBX_MAX_UINT64_LEN + 1, ZBX_FS_UI64, f->value_itemid);
+ else
+ zbx_snprintf(s1, ZBX_MAX_UINT64_LEN + 1, "null");
+
+ if (0 != f->value_graphid)
+ zbx_snprintf(s2, ZBX_MAX_UINT64_LEN + 1, ZBX_FS_UI64, f->value_graphid);
+ else
+ zbx_snprintf(s2, ZBX_MAX_UINT64_LEN + 1, "null");
+
+ if (0 != f->value_groupid)
+ zbx_snprintf(s3, ZBX_MAX_UINT64_LEN + 1, ZBX_FS_UI64, f->value_groupid);
+ else
+ zbx_snprintf(s3, ZBX_MAX_UINT64_LEN + 1, "null");
+
+ if (0 != f->value_hostid)
+ zbx_snprintf(s4, ZBX_MAX_UINT64_LEN + 1, ZBX_FS_UI64, f->value_hostid);
+ else
+ zbx_snprintf(s4, ZBX_MAX_UINT64_LEN + 1, "null");
+
+ if (0 != f->value_sysmapid)
+ zbx_snprintf(s5, ZBX_MAX_UINT64_LEN + 1, ZBX_FS_UI64, f->value_sysmapid);
+ else
+ zbx_snprintf(s5, ZBX_MAX_UINT64_LEN + 1, "null");
+
+ if (ZBX_DB_OK > DBexecute("insert into widget_field (widget_fieldid,widgetid,type,name,value_int,"
+ "value_str,value_itemid,value_graphid,value_groupid,value_hostid,value_sysmapid)"
+ " values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,'%s',%d,'%s',%s,%s,%s,%s,%s)",
+ new_fieldid++, widget->widgetid, f->type, f->name, f->value_int, url_esc,
+ s1, s2, s3, s4, s5))
+ {
+ ret = FAIL;
+ }
+
+ zbx_free(url_esc);
+ }
+
+ return ret;
+}
+
+static int DBpatch_set_permissions_screen(uint64_t dashboardid, uint64_t screenid)
+{
+ int ret = SUCCEED, permission;
+ uint64_t userid, usrgrpid, dashboard_userid, dashboard_usrgrpid;
+ DB_RESULT result;
+ DB_ROW row;
+
+ dashboard_userid = DBget_maxid("dashboard_user");
+
+ result = DBselect("select userid, permission from screen_user where screenid=" ZBX_FS_UI64, screenid);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_STR2UINT64(userid, row[0]);
+ permission = atoi(row[1]);
+
+ if (ZBX_DB_OK > DBexecute("insert into dashboard_user (dashboard_userid,dashboardid,userid,permission)"
+ " values ("ZBX_FS_UI64 ","ZBX_FS_UI64 ","ZBX_FS_UI64 ", %d)",
+ dashboard_userid++, dashboardid, userid, permission))
+ {
+ ret = FAIL;
+ goto out;
+ }
+ }
+
+ DBfree_result(result);
+
+ dashboard_usrgrpid = DBget_maxid("dashboard_usrgrp");
+
+ result = DBselect("select usrgrpid,permission from screen_usrgrp where screenid=" ZBX_FS_UI64, screenid);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_STR2UINT64(usrgrpid, row[0]);
+ permission = atoi(row[1]);
+
+ if (ZBX_DB_OK > DBexecute("insert into dashboard_usrgrp (dashboard_usrgrpid,dashboardid,usrgrpid,permission)"
+ " values ("ZBX_FS_UI64 ","ZBX_FS_UI64 ","ZBX_FS_UI64 ", %d)",
+ dashboard_usrgrpid++, dashboardid, usrgrpid, permission))
+ {
+ ret = FAIL;
+ goto out;
+ }
+ }
+out:
+ DBfree_result(result);
+
+ return ret;
+}
+
+static int DBpatch_set_permissions_slideshow(uint64_t dashboardid, uint64_t slideshowid)
+{
+ int ret = SUCCEED, permission;
+ uint64_t userid, usrgrpid, dashboard_userid, dashboard_usrgrpid;
+ DB_RESULT result;
+ DB_ROW row;
+
+ dashboard_userid = DBget_maxid("dashboard_user");
+
+ result = DBselect("select userid,permission from slideshow_user where slideshowid=" ZBX_FS_UI64, slideshowid);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_STR2UINT64(userid, row[0]);
+ permission = atoi(row[1]);
+
+ if (ZBX_DB_OK > DBexecute("insert into dashboard_user (dashboard_userid,dashboardid,userid,permission)"
+ " values ("ZBX_FS_UI64 ","ZBX_FS_UI64 ","ZBX_FS_UI64 ", %d)",
+ dashboard_userid++, dashboardid, userid, permission))
+ {
+ ret = FAIL;
+ goto out;
+ }
+ }
+
+ DBfree_result(result);
+
+ dashboard_usrgrpid = DBget_maxid("dashboard_usrgrp");
+
+ result = DBselect("select usrgrpid,permission from slideshow_usrgrp where slideshowid=" ZBX_FS_UI64,
+ slideshowid);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_STR2UINT64(usrgrpid, row[0]);
+ permission = atoi(row[1]);
+
+ if (ZBX_DB_OK > DBexecute("insert into dashboard_usrgrp (dashboard_usrgrpid,dashboardid,usrgrpid,permission)"
+ " values ("ZBX_FS_UI64 ","ZBX_FS_UI64 ","ZBX_FS_UI64 ", %d)",
+ dashboard_usrgrpid++, dashboardid, usrgrpid, permission))
+ {
+ ret = FAIL;
+ goto out;
+ }
+ }
+out:
+ DBfree_result(result);
+
+ return ret;
+}
+
+
+static int DBpatch_delete_screen(uint64_t screenid)
+{
+ if (ZBX_DB_OK > DBexecute("delete from screens_items where screenid=" ZBX_FS_UI64, screenid))
+ return FAIL;
+
+ if (ZBX_DB_OK > DBexecute("delete from screens where screenid=" ZBX_FS_UI64, screenid))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+#define OFFSET_ARRAY_SIZE (SCREEN_MAX_ROWS + 1)
+
+static int DBpatch_convert_screen_items(DB_RESULT result, uint64_t id)
+{
+ DB_ROW row;
+ int i, ret = SUCCEED;
+ zbx_db_screen_item_t *scr_item;
+ zbx_vector_ptr_t screen_items;
+ zbx_vector_char2_t *dim_x, *dim_y;
+ int offsets_x[OFFSET_ARRAY_SIZE], offsets_y[OFFSET_ARRAY_SIZE];
+
+ zbx_vector_ptr_create(&screen_items);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ scr_item = (zbx_db_screen_item_t*)zbx_calloc(NULL, 1, sizeof(zbx_db_screen_item_t));
+
+ ZBX_DBROW2UINT64(scr_item->screenitemid, row[0]);
+ ZBX_DBROW2UINT64(scr_item->screenid, row[1]);
+ scr_item->resourcetype = atoi(row[2]);
+ ZBX_DBROW2UINT64(scr_item->resourceid, row[3]);
+ scr_item->width = atoi(row[4]);
+ scr_item->height = atoi(row[5]);
+ scr_item->x = atoi(row[6]);
+ scr_item->y = atoi(row[7]);
+ scr_item->colspan = atoi(row[8]);
+ scr_item->rowspan = atoi(row[9]);
+ scr_item->elements = atoi(row[10]);
+ scr_item->style = atoi(row[11]);
+ scr_item->url = zbx_strdup(NULL, row[12]);
+ scr_item->sort_triggers = atoi(row[13]);
+ scr_item->application = zbx_strdup(NULL, row[14]);
+ scr_item->dynamic = atoi(row[15]);
+
+ DBpatch_trace_screen_item(scr_item);
+
+ zbx_vector_ptr_append(&screen_items, (void *)scr_item);
+ }
+
+ if (screen_items.values_num > 0)
+ {
+ zabbix_log(LOG_LEVEL_TRACE, "total %d screen items", screen_items.values_num);
+
+ DBpatch_normalize_screen_items_pos(&screen_items);
+ DBpatch_get_dashboard_dimensions(&screen_items, &dim_x, &dim_y);
+
+ lw_array_debug("dim_x", dim_x);
+ lw_array_debug("dim_y", dim_y);
+
+ offsets_x[0] = 0;
+ offsets_y[0] = 0;
+ for (i = 1; i < OFFSET_ARRAY_SIZE; i++)
+ {
+ offsets_x[i] = -1;
+ offsets_y[i] = -1;
+ }
+
+ for (i = 0; i < dim_x->values_num; i++)
+ {
+ if (POS_EMPTY != dim_x->values[i])
+ offsets_x[i + 1] = i == 0 ? dim_x->values[i] : offsets_x[i] + dim_x->values[i];
+ if (POS_EMPTY != dim_y->values[i])
+ offsets_y[i + 1] = i == 0 ? dim_y->values[i] : offsets_y[i] + dim_y->values[i];
+ }
+
+ int_array_debug("offsets_x", offsets_x, OFFSET_ARRAY_SIZE, -1);
+ int_array_debug("offsets_y", offsets_y, OFFSET_ARRAY_SIZE, -1);
+ }
+
+
+ for (i = 0; SUCCEED == ret && i < screen_items.values_num; i++)
+ {
+ int offset_idx_x, offset_idx_y;
+ zbx_db_widget_t w;
+ zbx_vector_ptr_t widget_fields;
+ zbx_db_screen_item_t *si;
+
+ si = screen_items.values[i];
+
+ offset_idx_x = si->x + si->colspan;
+ if (offset_idx_x > OFFSET_ARRAY_SIZE - 1)
+ {
+ offset_idx_x = OFFSET_ARRAY_SIZE - 1;
+ zabbix_log(LOG_LEVEL_WARNING, "config error, x screen size overflow for item " ZBX_FS_UI64,
+ si->screenitemid);
+ }
+
+ offset_idx_y = si->y + si->rowspan;
+ if (offset_idx_y > OFFSET_ARRAY_SIZE - 1)
+ {
+ offset_idx_y = OFFSET_ARRAY_SIZE - 1;
+ zabbix_log(LOG_LEVEL_WARNING, "config error, y screen size overflow for item " ZBX_FS_UI64,
+ si->screenitemid);
+ }
+
+ memset((void *)&w, 0, sizeof(zbx_db_widget_t));
+ w.x = offsets_x[si->x];
+ w.y = offsets_y[si->y];
+ w.width = offsets_x[offset_idx_x] - offsets_x[si->x];
+ w.height = offsets_y[offset_idx_y] - offsets_y[si->y];
+
+ /* skip screen items not fitting on the dashboard */
+ if (w.x + w.width > DASHBOARD_MAX_COLS || w.y + w.height > DASHBOARD_MAX_ROWS)
+ {
+ zabbix_log(LOG_LEVEL_WARNING, "skipping screenitemid " ZBX_FS_UI64
+ " (too wide, tall or offscreen)", si->screenitemid);
+ continue;
+ }
+
+ zbx_vector_ptr_create(&widget_fields);
+
+ DBpatch_widget_from_screen_item(si, &w, &widget_fields);
+
+ ret = DBpatch_add_widget(id, &w, &widget_fields);
+
+ DBpatch_trace_widget(&w);
+
+ zbx_vector_ptr_clear_ext(&widget_fields, (zbx_clean_func_t)DBpatch_widget_field_free);
+ zbx_vector_ptr_destroy(&widget_fields);
+ zbx_free(w.name);
+ zbx_free(w.type);
+ }
+
+ if (screen_items.values_num > 0)
+ {
+ lw_array_free(dim_x);
+ lw_array_free(dim_y);
+ }
+
+ zbx_vector_ptr_clear_ext(&screen_items, (zbx_clean_func_t)DBpatch_screen_item_free);
+ zbx_vector_ptr_destroy(&screen_items);
+
+ return ret;
+}
+
+static int DBpatch_convert_screen(uint64_t screenid, char *name, uint64_t userid, int private)
+{
+ DB_RESULT result;
+ int ret;
+ zbx_db_dashboard_t dashboard;
+ zbx_db_dashboard_page_t dashboard_page;
+
+ result = DBselect(
+ "select screenitemid,screenid,resourcetype,resourceid,width,height,x,y,colspan,rowspan"
+ ",elements,style,url,sort_triggers,application,dynamic from screens_items"
+ " where screenid=" ZBX_FS_UI64, screenid);
+
+ if (NULL == result)
+ return FAIL;
+
+ if (SUCCEED != DBpatch_init_dashboard(&dashboard, name, userid, private))
+ {
+ zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen '%s'due to name collision.", name);
+ ret = FAIL;
+ goto out;
+ }
+
+ ret = DBpatch_add_dashboard(&dashboard);
+
+ if (SUCCEED == ret)
+ ret = DBpatch_add_dashboard_page(&dashboard_page, dashboard.dashboardid, "", 0, 0);
+
+ if (SUCCEED == ret)
+ ret = DBpatch_convert_screen_items(result, dashboard_page.dashboard_pageid);
+
+ if (SUCCEED == ret)
+ ret = DBpatch_set_permissions_screen(dashboard.dashboardid, screenid);
+
+ zbx_free(dashboard.name);
+out:
+ DBfree_result(result);
+
+ return ret;
+}
+
+static int DBpatch_delay_routine(const char *screen_delay, int *dashboard_delay)
+{
+ int delays[] = {10, 30, 60, 120, 600, 1800, 3600};
+ int i, imax, tmp;
+
+ if (FAIL == is_time_suffix(screen_delay, &tmp, ZBX_LENGTH_UNLIMITED))
+ return FAIL;
+
+ imax = (int)ARRSIZE(delays);
+
+ if (0 >= tmp)
+ {
+ tmp = 0;
+ }
+ else if (tmp <= delays[0])
+ {
+ tmp = delays[0];
+ }
+ else if (tmp >= delays[imax - 1])
+ {
+ tmp = delays[imax - 1];
+ }
+ else
+ {
+ for (i = 0; i < imax - 1; i++)
+ {
+ if (tmp >= delays[i] && tmp <= delays[i + 1])
+ tmp = ((tmp - delays[i]) >= (delays[i + 1] - tmp)) ? delays[i + 1] : delays[i];
+ }
+ }
+
+ *dashboard_delay = tmp;
+
+ return SUCCEED;
+}
+
+static int DBpatch_convert_slideshow(uint64_t slideshowid, char *name, int delay, uint64_t userid, int private)
+{
+ int ret;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+ zbx_db_dashboard_t dashboard;
+ DB_RESULT result;
+ DB_ROW row;
+
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
+ "select slideid,screenid,step,delay"
+ " from slides"
+ " where slideshowid=" ZBX_FS_UI64
+ " order by step asc", slideshowid);
+
+ result = DBselectN(sql, 50);
+ zbx_free(sql);
+
+ if (NULL == result)
+ return FAIL;
+
+ if (SUCCEED != DBpatch_init_dashboard(&dashboard, name, userid, private))
+ {
+ zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen '%s'due to name collision.", name);
+ ret = FAIL;
+ goto exit;
+ }
+
+ dashboard.display_period = delay;
+
+ if (SUCCEED != (ret = DBpatch_add_dashboard(&dashboard)))
+ goto out;
+
+ while (SUCCEED == ret && NULL != (row = DBfetch(result)))
+ {
+ int step, page_delay;
+ zbx_db_dashboard_page_t dashboard_page;
+ DB_RESULT result2, result3;
+ DB_ROW row2;
+ uint64_t screenid;
+
+ step = atoi(row[2]);
+ page_delay = atoi(row[3]);
+ ZBX_DBROW2UINT64(screenid, row[1]);
+
+ result2 = DBselect("select name from screens where screenid=" ZBX_FS_UI64, screenid);
+
+ if (NULL == result2 || NULL == (row2 = DBfetch(result2)))
+ {
+ zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen " ZBX_FS_UI64, screenid);
+ DBfree_result(result2);
+ continue;
+ }
+
+ if (SUCCEED != (ret = DBpatch_add_dashboard_page(&dashboard_page, dashboard.dashboardid,
+ row2[0], page_delay, step)))
+ {
+ zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen " ZBX_FS_UI64, screenid);
+ DBfree_result(result2);
+ continue;
+ }
+
+ result3 = DBselect(
+ "select screenitemid,screenid,resourcetype,resourceid,width,height,x,y,colspan,rowspan"
+ ",elements,style,url,sort_triggers,application,dynamic from screens_items"
+ " where screenid=" ZBX_FS_UI64, screenid);
+
+ if (NULL != result3)
+ DBpatch_convert_screen_items(result3, dashboard_page.dashboard_pageid);
+
+ DBfree_result(result2);
+ DBfree_result(result3);
+ }
+
+out:
+ ret = DBpatch_set_permissions_slideshow(dashboard.dashboardid, slideshowid);
+
+ zbx_free(dashboard.name);
+exit:
+ DBfree_result(result);
+
+ return ret;
+}
+
+#undef OFFSET_ARRAY_SIZE
+
+static int DBpatch_5030094(void)
+{
+ int ret = SUCCEED;
+ DB_RESULT result;
+ DB_ROW row;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return ret;
+
+ result = DBselect("select slideshowid,name,delay,userid,private from slideshows");
+
+ while (SUCCEED == ret && NULL != (row = DBfetch(result)))
+ {
+ uint64_t slideshowid, userid;
+ int private, delay;
+
+ ZBX_DBROW2UINT64(slideshowid, row[0]);
+ ZBX_DBROW2UINT64(userid, row[3]);
+ private = atoi(row[4]);
+
+ if (FAIL == (ret = DBpatch_delay_routine(row[2], &delay)))
+ break;
+
+ ret = DBpatch_convert_slideshow(slideshowid, row[1], delay, userid, private);
+ }
+
+ DBfree_result(result);
+
+ return ret;
+}
+
+static int DBpatch_5030095(void)
+{
+ int ret = SUCCEED;
+ DB_RESULT result;
+ DB_ROW row;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return ret;
+
+ result = DBselect("select screenid,name,userid,private from screens");
+
+ while (SUCCEED == ret && NULL != (row = DBfetch(result)))
+ {
+ uint64_t screenid, userid;
+ int private;
+
+ ZBX_DBROW2UINT64(screenid, row[0]);
+ ZBX_DBROW2UINT64(userid, row[2]);
+ private = atoi(row[3]);
+
+ if (SUCCEED == (ret = DBpatch_convert_screen(screenid, row[1], userid, private)))
+ ret = DBpatch_delete_screen(screenid);
+ }
+
+ DBfree_result(result);
+
+ return ret;
+}
+
+#undef DASHBOARD_MAX_COLS
+#undef DASHBOARD_MAX_ROWS
+#undef DASHBOARD_WIDGET_MIN_ROWS
+#undef DASHBOARD_WIDGET_MAX_ROWS
+
+#undef SCREEN_MAX_ROWS
+#undef SCREEN_MAX_COLS
+#undef SCREEN_RESOURCE_CLOCK
+#undef SCREEN_RESOURCE_GRAPH
+#undef SCREEN_RESOURCE_SIMPLE_GRAPH
+#undef SCREEN_RESOURCE_LLD_GRAPH
+#undef SCREEN_RESOURCE_LLD_SIMPLE_GRAPH
+#undef SCREEN_RESOURCE_PLAIN_TEXT
+#undef SCREEN_RESOURCE_URL
+
+#undef SCREEN_RESOURCE_MAP
+#undef SCREEN_RESOURCE_HOST_INFO
+#undef SCREEN_RESOURCE_TRIGGER_INFO
+#undef SCREEN_RESOURCE_SERVER_INFO
+#undef SCREEN_RESOURCE_TRIGGER_OVERVIEW
+#undef SCREEN_RESOURCE_DATA_OVERVIEW
+#undef SCREEN_RESOURCE_ACTIONS
+#undef SCREEN_RESOURCE_EVENTS
+#undef SCREEN_RESOURCE_HOSTGROUP_TRIGGERS
+#undef SCREEN_RESOURCE_SYSTEM_STATUS
+#undef SCREEN_RESOURCE_HOST_TRIGGERS
+
+#undef ZBX_WIDGET_FIELD_TYPE_INT32
+#undef ZBX_WIDGET_FIELD_TYPE_STR
+#undef ZBX_WIDGET_FIELD_TYPE_ITEM
+#undef ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE
+#undef ZBX_WIDGET_FIELD_TYPE_GRAPH
+#undef ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE
+
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH */
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH */
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE */
+/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE */
+
+#undef ZBX_WIDGET_TYPE_CLOCK
+#undef ZBX_WIDGET_TYPE_GRAPH_CLASSIC
+#undef ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE
+#undef ZBX_WIDGET_TYPE_PLAIN_TEXT
+#undef ZBX_WIDGET_TYPE_URL
+#undef POS_EMPTY
+#undef POS_TAKEN
+#undef SKIP_EMPTY
+
+static int DBpatch_5030096(void)
+{
+ return DBdrop_table("slides");
+}
+
+static int DBpatch_5030097(void)
+{
+ return DBdrop_table("slideshow_user");
+}
+
+static int DBpatch_5030098(void)
+{
+ return DBdrop_table("slideshow_usrgrp");
+}
+
+static int DBpatch_5030099(void)
+{
+ return DBdrop_table("slideshows");
+
+}
+
+static int DBpatch_5030100(void)
+{
+ return DBdrop_table("screen_usrgrp");
+}
+
+static int DBpatch_5030101(void)
+{
+ return DBdrop_table("screens_items");
+}
+
+static int DBpatch_5030102(void)
+{
+ return DBdrop_table("screen_user");
+}
+
+static int DBpatch_5030103(void)
+{
+ return DBdrop_table("screens");
+}
+
+static int DBpatch_5030104(void)
+{
+ if (ZBX_DB_OK > DBexecute("delete from widget where type='favscreens'"))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030105(void)
+{
+ if (ZBX_DB_OK > DBexecute("delete from profiles where idx in ("
+ "'web.favorite.screenids', "
+ "'web.screenconf.filter.active', "
+ "'web.screenconf.filter_name', "
+ "'web.screenconf.php.sort', "
+ "'web.screenconf.php.sortorder', "
+ "'web.screens.elementid', "
+ "'web.screens.filter.active', "
+ "'web.screens.filter.from', "
+ "'web.screens.filter.to', "
+ "'web.screens.hostid', "
+ "'web.screens.tr_groupid', "
+ "'web.screens.tr_hostid', "
+ "'web.slideconf.filter.active', "
+ "'web.slideconf.filter_name', "
+ "'web.slideconf.php.sort', "
+ "'web.slideconf.php.sortorder', "
+ "'web.slides.elementid', "
+ "'web.slides.filter.active', "
+ "'web.slides.filter.from', "
+ "'web.slides.filter.to', "
+ "'web.slides.hostid', "
+ "'web.slides.rf_rate.hat_slides', "
+ "'web.favorite.screenids')"))
+ {
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030106(void)
+{
+ if (ZBX_DB_OK > DBexecute("delete from role_rule"
+ " where name like 'api.method.%%'"
+ " and (value_str like 'screen.%%' or value_str like 'screenitem.%%')"))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030107(void)
+{
+ if (ZBX_DB_OK > DBexecute("delete from role_rule where name='ui.monitoring.screens'"))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030108(void)
+{
+ int i;
+ const char *values[] = {
+ "web.dashbrd.dashboardid", "web.dashboard.dashboardid",
+ "web.dashbrd.hostid", "web.dashboard.hostid",
+ "web.dashbrd.list.sort", "web.dashboard.list.sort",
+ "web.dashbrd.list.sortorder", "web.dashboard.list.sortorder",
+ "web.dashbrd.list_was_opened", "web.dashboard.list_was_opened",
+ "web.dashbrd.filter", "web.dashboard.filter",
+ "web.dashbrd.filter.active", "web.dashboard.filter.active",
+ "web.dashbrd.filter.from", "web.dashboard.filter.from",
+ "web.dashbrd.filter.to", "web.dashboard.filter.to",
+ "web.dashbrd.filter_name", "web.dashboard.filter_name",
+ "web.dashbrd.filter_show", "web.dashboard.filter_show",
+ "web.dashbrd.last_widget_type", "web.dashboard.last_widget_type",
+ "web.dashbrd.navtree.item.selected", "web.dashboard.widget.navtree.item.selected",
+ "web.dashbrd.widget.rf_rate", "web.dashboard.widget.rf_rate",
+ "web.templates.dashbrd.list.sort", "web.templates.dashboard.list.sort",
+ "web.templates.dashbrd.list.sortorder", "web.templates.dashboard.list.sortorder"
+ };
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ for (i = 0; i < (int)ARRSIZE(values); i += 2)
+ {
+ if (ZBX_DB_OK > DBexecute("update profiles set idx='%s' where idx='%s'", values[i + 1], values[i]))
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030109(void)
+{
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute("update profiles set idx=CONCAT('web.dashboard.widget.navtree.item-', SUBSTR(idx, 21))"
+ " where idx like 'web.dashbrd.navtree-%%.toggle'"))
+ {
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030110(void)
+{
+ const ZBX_TABLE table =
+ {"item_tag", "itemtagid", 0,
+ {
+ {"itemtagid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"itemid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"tag", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"value", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030111(void)
+{
+ return DBcreate_index("item_tag", "item_tag_1", "itemid", 0);
+}
+
+static int DBpatch_5030112(void)
+{
+ const ZBX_FIELD field = {"itemid", NULL, "items", "itemid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("item_tag", 1, &field);
+}
+
+static int DBpatch_5030113(void)
+{
+ const ZBX_TABLE table =
+ {"httptest_tag", "httptesttagid", 0,
+ {
+ {"httptesttagid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"httptestid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"tag", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"value", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030114(void)
+{
+ return DBcreate_index("httptest_tag", "httptest_tag_1", "httptestid", 0);
+}
+
+static int DBpatch_5030115(void)
+{
+ const ZBX_FIELD field = {"httptestid", NULL, "httptest", "httptestid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("httptest_tag", 1, &field);
+}
+
+static int DBpatch_5030116(void)
+{
+ const ZBX_TABLE table =
+ {"sysmaps_element_tag", "selementtagid", 0,
+ {
+ {"selementtagid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"selementid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"tag", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"value", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"operator", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030117(void)
+{
+ return DBcreate_index("sysmaps_element_tag", "sysmaps_element_tag_1", "selementid", 0);
+}
+
+static int DBpatch_5030118(void)
+{
+ const ZBX_FIELD field = {"selementid", NULL, "sysmaps_elements", "selementid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("sysmaps_element_tag", 1, &field);
+}
+
+static int DBpatch_5030119(void)
+{
+ const ZBX_FIELD field = {"evaltype", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};
+
+ return DBadd_field("sysmaps_elements", &field);
+}
+
+static int DBpatch_5030120(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_uint64_t itemid, itemtagid = 1;
+ int ret;
+ char *value;
+ zbx_db_insert_t db_insert;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "item_tag", "itemtagid", "itemid", "tag", "value", NULL);
+ result = DBselect(
+ "select i.itemid,a.name from items i"
+ " join items_applications ip on i.itemid=ip.itemid"
+ " join applications a on ip.applicationid=a.applicationid");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_DBROW2UINT64(itemid, row[0]);
+ value = DBdyn_escape_string(row[1]);
+ zbx_db_insert_add_values(&db_insert, itemtagid++, itemid, "Application", value);
+ zbx_free(value);
+ }
+ DBfree_result(result);
+
+ ret = zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
+static int DBpatch_5030121(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_uint64_t itemid;
+ int ret;
+ char *value;
+ zbx_db_insert_t db_insert;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "item_tag", "itemtagid", "itemid", "tag", "value", NULL);
+
+ result = DBselect(
+ "select i.itemid,ap.name from items i"
+ " join item_application_prototype ip on i.itemid=ip.itemid"
+ " join application_prototype ap on ip.application_prototypeid=ap.application_prototypeid");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_DBROW2UINT64(itemid, row[0]);
+ value = DBdyn_escape_string(row[1]);
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), itemid, "Application", value);
+ zbx_free(value);
+ }
+ DBfree_result(result);
+
+ zbx_db_insert_autoincrement(&db_insert, "itemtagid");
+ ret = zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
+static int DBpatch_5030122(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_uint64_t httptestid, httptesttagid = 1;
+ int ret;
+ char *value;
+ zbx_db_insert_t db_insert;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "httptest_tag", "httptesttagid", "httptestid", "tag", "value", NULL);
+ result = DBselect(
+ "select h.httptestid,a.name from httptest h"
+ " join applications a on h.applicationid=a.applicationid");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_DBROW2UINT64(httptestid, row[0]);
+ value = DBdyn_escape_string(row[1]);
+ zbx_db_insert_add_values(&db_insert, httptesttagid++, httptestid, "Application", value);
+ zbx_free(value);
+ }
+ DBfree_result(result);
+
+ ret = zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
+static int DBpatch_5030123(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_uint64_t selementid, selementtagid = 1;
+ int ret;
+ char *value;
+ zbx_db_insert_t db_insert;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "sysmaps_element_tag", "selementtagid", "selementid", "tag", "value", NULL);
+ result = DBselect(
+ "select selementid,application from sysmaps_elements"
+ " where elementtype in (0,3) and application<>''");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_DBROW2UINT64(selementid, row[0]);
+ value = DBdyn_escape_string(row[1]);
+ zbx_db_insert_add_values(&db_insert, selementtagid++, selementid, "Application", value);
+ zbx_free(value);
+ }
+ DBfree_result(result);
+
+ ret = zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
+static int DBpatch_5030124(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_db_insert_t db_insert;
+ int ret;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "event_tag", "eventtagid", "eventid", "tag", "value", NULL);
+
+ result = DBselect(
+ "select distinct e.eventid,it.tag,it.value from events e"
+ " join triggers t on e.objectid=t.triggerid"
+ " join functions f on t.triggerid=f.triggerid"
+ " join items i on i.itemid=f.itemid"
+ " join item_tag it on i.itemid=it.itemid"
+ " where e.source in (%d,%d) and e.object=%d and t.flags in (%d,%d) order by e.eventid",
+ EVENT_SOURCE_TRIGGERS, EVENT_SOURCE_INTERNAL, EVENT_OBJECT_TRIGGER, ZBX_FLAG_DISCOVERY_NORMAL,
+ ZBX_FLAG_DISCOVERY_CREATED);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ DB_ROW rowN;
+ DB_RESULT resultN;
+ zbx_uint64_t eventid;
+ char *tag, *value, tmp[MAX_STRING_LEN];
+
+ ZBX_DBROW2UINT64(eventid, row[0]);
+ tag = DBdyn_escape_string(row[1]);
+ value = DBdyn_escape_string(row[2]);
+ zbx_snprintf(tmp, sizeof(tmp),
+ "select null from event_tag where eventid=" ZBX_FS_UI64 " and tag='%s' and value='%s'",
+ eventid, tag, value);
+
+ resultN = DBselectN(tmp, 1);
+
+ if (NULL == (rowN = DBfetch(resultN)))
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), eventid, tag, value);
+
+ DBfree_result(resultN);
+ zbx_free(tag);
+ zbx_free(value);
+ }
+ DBfree_result(result);
+
+ zbx_db_insert_autoincrement(&db_insert, "eventtagid");
+ ret = zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
+static int DBpatch_5030125(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_db_insert_t db_insert;
+ int ret;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "event_tag", "eventtagid", "eventid", "tag", "value", NULL);
+
+ result = DBselect(
+ "select distinct e.eventid,it.tag,it.value from events e"
+ " join items i on i.itemid=e.objectid"
+ " join item_tag it on i.itemid=it.itemid"
+ " where e.source=%d and e.object=%d and i.flags in (%d,%d)",
+ EVENT_SOURCE_INTERNAL, EVENT_OBJECT_ITEM, ZBX_FLAG_DISCOVERY_NORMAL,
+ ZBX_FLAG_DISCOVERY_CREATED);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ DB_ROW rowN;
+ DB_RESULT resultN;
+ zbx_uint64_t eventid;
+ char *tag, *value, tmp[MAX_STRING_LEN];
+
+ ZBX_DBROW2UINT64(eventid, row[0]);
+ tag = DBdyn_escape_string(row[1]);
+ value = DBdyn_escape_string(row[2]);
+ zbx_snprintf(tmp, sizeof(tmp),
+ "select null from event_tag where eventid=" ZBX_FS_UI64 " and tag='%s' and value='%s'",
+ eventid, tag, value);
+
+ resultN = DBselectN(tmp, 1);
+
+ if (NULL == (rowN = DBfetch(resultN)))
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), eventid, tag, value);
+
+ DBfree_result(resultN);
+ zbx_free(tag);
+ zbx_free(value);
+ }
+ DBfree_result(result);
+
+ zbx_db_insert_autoincrement(&db_insert, "eventtagid");
+ ret = zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
+static int DBpatch_5030126(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_db_insert_t db_insert;
+ int ret;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "problem_tag", "problemtagid", "eventid", "tag", "value", NULL);
+
+ result = DBselect(
+ "select distinct e.eventid,e.tag,e.value from event_tag e"
+ " join problem p on e.eventid=p.eventid");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ DB_ROW rowN;
+ DB_RESULT resultN;
+ zbx_uint64_t eventid;
+ char *tag, *value, tmp[MAX_STRING_LEN];
+
+ ZBX_DBROW2UINT64(eventid, row[0]);
+ tag = DBdyn_escape_string(row[1]);
+ value = DBdyn_escape_string(row[2]);
+ zbx_snprintf(tmp, sizeof(tmp),
+ "select null from problem_tag where eventid=" ZBX_FS_UI64 " and tag='%s'"
+ " and value='%s'", eventid, tag, value);
+
+ resultN = DBselectN(tmp, 1);
+
+ if (NULL == (rowN = DBfetch(resultN)))
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), eventid, tag, value);
+
+ DBfree_result(resultN);
+ zbx_free(tag);
+ zbx_free(value);
+ }
+ DBfree_result(result);
+
+ zbx_db_insert_autoincrement(&db_insert, "problemtagid");
+ ret = zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
+static int DBpatch_5030127(void)
+{
+#define CONDITION_TYPE_APPLICATION 15
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute("update conditions set conditiontype=%d,value2='Application' where conditiontype=%d",
+ CONDITION_TYPE_EVENT_TAG_VALUE, CONDITION_TYPE_APPLICATION))
+ {
+ return FAIL;
+ }
+
+ return SUCCEED;
+#undef CONDITION_TYPE_APPLICATION
+}
+
+static int DBpatch_5030128(void)
+{
+#define AUDIT_RESOURCE_APPLICATION 12
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute("delete from auditlog where resourcetype=%d", AUDIT_RESOURCE_APPLICATION))
+ return FAIL;
+
+ return SUCCEED;
+#undef AUDIT_RESOURCE_APPLICATION
+}
+
+static int DBpatch_5030129(void)
+{
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute("delete from profiles where idx in ("
+ "'web.applications.filter','web.latest.filter.application',"
+ "'web.overview.filter.application','web.applications.filter.active',"
+ "'web.applications.filter_groups','web.applications.filter_hostids',"
+ "'web.applications.php.sort','web.applications.php.sortorder',"
+ "'web.hosts.items.subfilter_apps','web.templates.items.subfilter_apps',"
+ "'web.latest.toggle','web.latest.toggle_other','web.items.filter_application')"))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+typedef struct
+{
+ char *tag;
+ char *op;
+ char *value;
+}
+patch_filtertag_t;
+
+ZBX_PTR_VECTOR_DECL(patch_filtertag, patch_filtertag_t)
+ZBX_PTR_VECTOR_IMPL(patch_filtertag, patch_filtertag_t)
+
+static void patch_filtertag_free(patch_filtertag_t tag)
+{
+ zbx_free(tag.tag);
+ zbx_free(tag.op);
+ zbx_free(tag.value);
+}
+
+static int DBpatch_parse_tags_json(struct zbx_json_parse *jp, zbx_vector_patch_filtertag_t *tags)
+{
+ const char *p = NULL;
+ struct zbx_json_parse jp_data;
+ patch_filtertag_t tag;
+ size_t tag_alloc, op_alloc, val_alloc;
+
+ while (NULL != (p = zbx_json_next(jp, p)))
+ {
+ if (SUCCEED != zbx_json_brackets_open(p, &jp_data))
+ return FAIL;
+
+ tag.tag = NULL;
+ tag.op = NULL;
+ tag.value = NULL;
+
+ tag_alloc = 0;
+ op_alloc = 0;
+ val_alloc = 0;
+
+ if (SUCCEED != zbx_json_value_by_name_dyn(&jp_data, "tag", &tag.tag, &tag_alloc, NULL) ||
+ SUCCEED != zbx_json_value_by_name_dyn(&jp_data, "operator", &tag.op, &op_alloc, NULL) ||
+ SUCCEED != zbx_json_value_by_name_dyn(&jp_data, "value", &tag.value, &val_alloc, NULL))
+ {
+ patch_filtertag_free(tag);
+ return FAIL;
+ }
+
+ zbx_vector_patch_filtertag_append(tags, tag);
+ }
+
+ return SUCCEED;
+}
+
+static int DBpatch_parse_applications_json(struct zbx_json_parse *jp, struct zbx_json *json_dest,
+ zbx_vector_patch_filtertag_t *tags, char **app, int depth)
+{
+ struct zbx_json_parse jp_sub;
+ const char *p = NULL, *prev = NULL;
+ char name[MAX_STRING_LEN], value[MAX_STRING_LEN];
+ zbx_json_type_t type;
+
+ do
+ {
+ if (NULL != (p = zbx_json_pair_next(jp, p, name, sizeof(name))))
+ {
+ if (NULL == zbx_json_decodevalue(p, value, sizeof(value), NULL))
+ {
+ type = zbx_json_valuetype(p);
+
+ if (type == ZBX_JSON_TYPE_ARRAY)
+ {
+ if (0 == depth && 0 == strcmp(name, "tags"))
+ {
+ if (SUCCEED != zbx_json_brackets_open(p, &jp_sub) ||
+ SUCCEED != DBpatch_parse_tags_json(&jp_sub, tags))
+ {
+ return FAIL;
+ }
+
+ continue;
+ }
+
+ zbx_json_addarray(json_dest, name);
+ }
+ else if (type == ZBX_JSON_TYPE_OBJECT)
+ {
+ zbx_json_addobject(json_dest, name);
+ }
+ }
+ else
+ {
+ if (0 == depth && 0 == strcmp(name, "application"))
+ *app = zbx_strdup(*app, value);
+ else
+ zbx_json_addstring(json_dest, name, value, ZBX_JSON_TYPE_STRING);
+ }
+ }
+ else
+ {
+ p = prev;
+
+ if (NULL == (p = zbx_json_next_value(jp, p, value, sizeof(value), NULL)))
+ {
+ p = prev;
+
+ if (NULL != (p = zbx_json_next(jp, p)))
+ {
+ type = zbx_json_valuetype(p);
+
+ if (type == ZBX_JSON_TYPE_OBJECT)
+ zbx_json_addobject(json_dest, NULL);
+ else if (type == ZBX_JSON_TYPE_ARRAY)
+ zbx_json_addarray(json_dest, NULL);
+ }
+ else
+ {
+ if (0 == depth)
+ {
+ if (NULL != *app)
+ {
+ patch_filtertag_t tag;
+
+ tag.tag = zbx_strdup(NULL, "Application");
+ tag.op = zbx_strdup(NULL, "0");
+ tag.value = zbx_strdup(NULL, *app);
+
+ zbx_vector_patch_filtertag_append(tags, tag);
+ }
+
+ if (0 < tags->values_num)
+ {
+ int i;
+
+ zbx_json_addarray(json_dest, "tags");
+
+ for (i = 0; i < tags->values_num; i++)
+ {
+ zbx_json_addobject(json_dest, NULL);
+ zbx_json_addstring(json_dest, "tag",
+ tags->values[i].tag,
+ ZBX_JSON_TYPE_STRING);
+ zbx_json_addstring(json_dest, "operator",
+ tags->values[i].op,
+ ZBX_JSON_TYPE_STRING);
+ zbx_json_addstring(json_dest, "value",
+ tags->values[i].value,
+ ZBX_JSON_TYPE_STRING);
+ zbx_json_close(json_dest);
+ }
+ }
+
+ zbx_json_close(json_dest);
+ }
+
+ zbx_json_close(json_dest);
+ }
+ }
+ else
+ zbx_json_addstring(json_dest, NULL, value, ZBX_JSON_TYPE_STRING);
+ }
+
+ if (NULL != p && SUCCEED == zbx_json_brackets_open(p, &jp_sub))
+ {
+ if (SUCCEED != DBpatch_parse_applications_json(&jp_sub, json_dest, tags, app, depth + 1))
+ return FAIL;
+ }
+
+ prev = p;
+ } while (NULL != p);
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030130(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+ int ret = SUCCEED;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ result = DBselect("select profileid,value_str from profiles"
+ " where idx='web.monitoring.problem.properties'");
+
+ while (NULL != (row = DBfetch(result)) && SUCCEED == ret)
+ {
+ struct zbx_json_parse jp;
+ struct zbx_json json;
+ zbx_vector_patch_filtertag_t tags;
+ char *app, *value_str;
+ zbx_uint64_t profileid;
+
+ app = NULL;
+ zbx_vector_patch_filtertag_create(&tags);
+ zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);
+
+ if (SUCCEED == (ret = zbx_json_open(row[1], &jp)) &&
+ SUCCEED == (ret = DBpatch_parse_applications_json(&jp, &json, &tags, &app, 0)))
+ {
+ ZBX_DBROW2UINT64(profileid, row[0]);
+
+ value_str = DBdyn_escape_string(json.buffer);
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
+ "update profiles set value_str='%s' where profileid=" ZBX_FS_UI64 ";\n",
+ value_str, profileid);
+ zbx_free(value_str);
+
+ ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
+ }
+ else
+ zabbix_log(LOG_LEVEL_ERR, "failed to parse web.monitoring.problem.properties JSON");
+
+ zbx_vector_patch_filtertag_clear_ext(&tags, patch_filtertag_free);
+ zbx_vector_patch_filtertag_destroy(&tags);
+ zbx_free(app);
+ zbx_json_free(&json);
+ }
+ DBfree_result(result);
+
+ DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ if (16 < sql_offset && ZBX_DB_OK > DBexecute("%s", sql))
+ ret = FAIL;
+
+ zbx_free(sql);
+
+ return ret;
+}
+
+static int DBpatch_5030131(void)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_db_insert_t db_insert;
+ zbx_vector_uint64_t widget_fieldids;
+ int ret;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "widget_field", "widget_fieldid", "widgetid", "type", "name", "value_int",
+ "value_str", NULL);
+
+ zbx_vector_uint64_create(&widget_fieldids);
+
+ result = DBselect(
+ "select w.widgetid,wf.value_str,wf.widget_fieldid from widget w"
+ " join widget_field wf on wf.widgetid=w.widgetid"
+ " where w.type in ('dataover','trigover') and wf.type=1 and wf.name='application'");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ zbx_uint64_t widgetid, widget_fieldid;
+ char *val;
+
+ ZBX_DBROW2UINT64(widgetid, row[0]);
+ val = DBdyn_escape_string(row[1]);
+ ZBX_DBROW2UINT64(widget_fieldid, row[2]);
+
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), widgetid, 0, "tags.operator.0", 0, "");
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), widgetid, 1, "tags.tag.0", 0, "Application");
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), widgetid, 1, "tags.value.0", 0, val);
+
+ zbx_vector_uint64_append(&widget_fieldids, widget_fieldid);
+
+ zbx_free(val);
+ }
+ DBfree_result(result);
+
+ zbx_db_insert_autoincrement(&db_insert, "widget_fieldid");
+
+ if (SUCCEED != (ret = zbx_db_insert_execute(&db_insert)))
+ goto out;
+
+ if (0 < widget_fieldids.values_num)
+ {
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "delete from widget_field where");
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "widget_fieldid", widget_fieldids.values,
+ widget_fieldids.values_num);
+
+ if (ZBX_DB_OK > DBexecute("%s", sql))
+ ret = FAIL;
+
+ zbx_free(sql);
+ }
+out:
+ zbx_db_insert_clean(&db_insert);
+ zbx_vector_uint64_destroy(&widget_fieldids);
+
+ return ret;
+}
+
+static int DBpatch_5030132(void)
+{
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ if (ZBX_DB_OK > DBexecute("delete from role_rule where name like 'api.method.%%'"
+ " and value_str like 'application.%%'"))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+static int DBpatch_5030133(void)
+{
+ return DBdrop_foreign_key("httptest", 1);
+}
+
+static int DBpatch_5030134(void)
+{
+ return DBdrop_index("httptest", "httptest_1");
+}
+
+static int DBpatch_5030135(void)
+{
+ return DBdrop_field("httptest", "applicationid");
+}
+
+static int DBpatch_5030136(void)
+{
+ return DBdrop_field("sysmaps_elements", "application");
+}
+
+static int DBpatch_5030137(void)
+{
+ return DBdrop_table("application_discovery");
+}
+
+static int DBpatch_5030138(void)
+{
+ return DBdrop_table("item_application_prototype");
+}
+
+static int DBpatch_5030139(void)
+{
+ return DBdrop_table("application_prototype");
+}
+
+static int DBpatch_5030140(void)
+{
+ return DBdrop_table("application_template");
+}
+
+static int DBpatch_5030141(void)
+{
+ return DBdrop_table("items_applications");
+}
+
+static int DBpatch_5030142(void)
+{
+ return DBdrop_table("applications");
+}
+
+static int DBpatch_5030143(void)
+{
+ DB_RESULT result;
+ int ret;
+ const char *fields[] = {"subject", "message"};
+
+ result = DBselect("select om.operationid,om.subject,om.message"
+ " from opmessage om,operations o,actions a"
+ " where om.operationid=o.operationid"
+ " and o.actionid=a.actionid"
+ " and a.eventsource=0 and o.operationtype=11");
+
+ ret = db_rename_macro(result, "opmessage", "operationid", fields, ARRSIZE(fields), "{EVENT.NAME}",
+ "{EVENT.RECOVERY.NAME}");
+
+ DBfree_result(result);
+
+ return ret;
+}
+
+static int DBpatch_5030144(void)
+{
+ DB_RESULT result;
+ int ret;
+ const char *fields[] = {"subject", "message"};
+
+ result = DBselect("select mediatype_messageid,subject,message from media_type_message where recovery=1");
+
+ ret = db_rename_macro(result, "media_type_message", "mediatype_messageid", fields, ARRSIZE(fields),
+ "{EVENT.NAME}", "{EVENT.RECOVERY.NAME}");
+
+ DBfree_result(result);
+
+ return ret;
+}
+
+static int DBpatch_5030145(void)
+{
+ const ZBX_TABLE table =
+ {"report", "reportid", 0,
+ {
+ {"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"description", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"status", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"dashboardid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"period", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"cycle", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"weekdays", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"start_time", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"active_since", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"active_till", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"state", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"lastsent", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"info", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030146(void)
+{
+ return DBcreate_index("report", "report_1", "name", 1);
+}
+
+static int DBpatch_5030147(void)
+{
+ const ZBX_FIELD field = {"userid", NULL, "users", "userid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("report", 1, &field);
+}
+
+static int DBpatch_5030148(void)
+{
+ const ZBX_FIELD field = {"dashboardid", NULL, "dashboard", "dashboardid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("report", 2, &field);
+}
+
+static int DBpatch_5030149(void)
+{
+ const ZBX_TABLE table =
+ {"report_param", "reportparamid", 0,
+ {
+ {"reportparamid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
+ {"value", "", NULL, NULL, 0, ZBX_TYPE_SHORTTEXT, ZBX_NOTNULL, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030150(void)
+{
+ return DBcreate_index("report_param", "report_param_1", "reportid", 0);
+}
+
+static int DBpatch_5030151(void)
+{
+ const ZBX_FIELD field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("report_param", 1, &field);
+}
+
+static int DBpatch_5030152(void)
+{
+ const ZBX_TABLE table =
+ {"report_user", "reportuserid", 0,
+ {
+ {"reportuserid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"exclude", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
+ {"access_userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030153(void)
+{
+ return DBcreate_index("report_user", "report_user_1", "reportid", 0);
+}
+
+static int DBpatch_5030154(void)
+{
+ const ZBX_FIELD field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("report_user", 1, &field);
+}
+
+static int DBpatch_5030155(void)
+{
+ const ZBX_FIELD field = {"userid", NULL, "users", "userid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("report_user", 2, &field);
+}
+
+static int DBpatch_5030156(void)
+{
+ const ZBX_FIELD field = {"access_userid", NULL, "users", "userid", 0, 0, 0, 0};
+
+ return DBadd_foreign_key("report_user", 3, &field);
+}
+
+static int DBpatch_5030157(void)
+{
+ const ZBX_TABLE table =
+ {"report_usrgrp", "reportusrgrpid", 0,
+ {
+ {"reportusrgrpid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"usrgrpid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
+ {"access_userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0},
+ {0}
+ },
+ NULL
+ };
+
+ return DBcreate_table(&table);
+}
+
+static int DBpatch_5030158(void)
+{
+ return DBcreate_index("report_usrgrp", "report_usrgrp_1", "reportid", 0);
+}
+
+static int DBpatch_5030159(void)
+{
+ const ZBX_FIELD field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("report_usrgrp", 1, &field);
+}
+
+static int DBpatch_5030160(void)
+{
+ const ZBX_FIELD field = {"usrgrpid", NULL, "usrgrp", "usrgrpid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};
+
+ return DBadd_foreign_key("report_usrgrp", 2, &field);
+}
+
+static int DBpatch_5030161(void)
+{
+ const ZBX_FIELD field = {"access_userid", NULL, "users", "userid", 0, 0, 0, 0};
+
+ return DBadd_foreign_key("report_usrgrp", 3, &field);
+}
+
+static int DBpatch_5030162(void)
+{
+ const ZBX_FIELD field = {"url", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("config", &field);
+}
+
+static int DBpatch_5030163(void)
+{
+ const ZBX_FIELD field = {"report_test_timeout", "60s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
+
+ return DBadd_field("config", &field);
+}
+
+static int DBpatch_5030164(void)
+{
const ZBX_FIELD field = {"dbversion_status", "", NULL, NULL, 1024, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
return DBadd_field("config", &field);
}
+
#endif
DBPATCH_START(5030)
@@ -1019,5 +4724,112 @@ DBPATCH_ADD(5030054, 0, 1)
DBPATCH_ADD(5030055, 0, 1)
DBPATCH_ADD(5030056, 0, 1)
DBPATCH_ADD(5030057, 0, 1)
+DBPATCH_ADD(5030058, 0, 1)
+DBPATCH_ADD(5030059, 0, 1)
+DBPATCH_ADD(5030060, 0, 1)
+DBPATCH_ADD(5030061, 0, 1)
+DBPATCH_ADD(5030062, 0, 1)
+DBPATCH_ADD(5030063, 0, 1)
+DBPATCH_ADD(5030064, 0, 1)
+DBPATCH_ADD(5030065, 0, 1)
+DBPATCH_ADD(5030066, 0, 1)
+DBPATCH_ADD(5030067, 0, 1)
+DBPATCH_ADD(5030068, 0, 1)
+DBPATCH_ADD(5030069, 0, 1)
+DBPATCH_ADD(5030070, 0, 1)
+DBPATCH_ADD(5030071, 0, 1)
+DBPATCH_ADD(5030072, 0, 1)
+DBPATCH_ADD(5030073, 0, 1)
+DBPATCH_ADD(5030074, 0, 1)
+DBPATCH_ADD(5030075, 0, 1)
+DBPATCH_ADD(5030076, 0, 1)
+DBPATCH_ADD(5030077, 0, 1)
+DBPATCH_ADD(5030078, 0, 1)
+DBPATCH_ADD(5030079, 0, 1)
+DBPATCH_ADD(5030080, 0, 1)
+DBPATCH_ADD(5030081, 0, 1)
+DBPATCH_ADD(5030082, 0, 1)
+DBPATCH_ADD(5030083, 0, 1)
+DBPATCH_ADD(5030084, 0, 1)
+DBPATCH_ADD(5030085, 0, 1)
+DBPATCH_ADD(5030086, 0, 1)
+DBPATCH_ADD(5030087, 0, 1)
+DBPATCH_ADD(5030088, 0, 1)
+DBPATCH_ADD(5030089, 0, 1)
+DBPATCH_ADD(5030090, 0, 1)
+DBPATCH_ADD(5030091, 0, 1)
+DBPATCH_ADD(5030092, 0, 1)
+DBPATCH_ADD(5030093, 0, 1)
+DBPATCH_ADD(5030094, 0, 1)
+DBPATCH_ADD(5030095, 0, 1)
+DBPATCH_ADD(5030096, 0, 1)
+DBPATCH_ADD(5030097, 0, 1)
+DBPATCH_ADD(5030098, 0, 1)
+DBPATCH_ADD(5030099, 0, 1)
+DBPATCH_ADD(5030100, 0, 1)
+DBPATCH_ADD(5030101, 0, 1)
+DBPATCH_ADD(5030102, 0, 1)
+DBPATCH_ADD(5030103, 0, 1)
+DBPATCH_ADD(5030104, 0, 1)
+DBPATCH_ADD(5030105, 0, 1)
+DBPATCH_ADD(5030106, 0, 1)
+DBPATCH_ADD(5030107, 0, 1)
+DBPATCH_ADD(5030108, 0, 1)
+DBPATCH_ADD(5030109, 0, 1)
+DBPATCH_ADD(5030110, 0, 1)
+DBPATCH_ADD(5030111, 0, 1)
+DBPATCH_ADD(5030112, 0, 1)
+DBPATCH_ADD(5030113, 0, 1)
+DBPATCH_ADD(5030114, 0, 1)
+DBPATCH_ADD(5030115, 0, 1)
+DBPATCH_ADD(5030116, 0, 1)
+DBPATCH_ADD(5030117, 0, 1)
+DBPATCH_ADD(5030118, 0, 1)
+DBPATCH_ADD(5030119, 0, 1)
+DBPATCH_ADD(5030120, 0, 1)
+DBPATCH_ADD(5030121, 0, 1)
+DBPATCH_ADD(5030122, 0, 1)
+DBPATCH_ADD(5030123, 0, 1)
+DBPATCH_ADD(5030124, 0, 1)
+DBPATCH_ADD(5030125, 0, 1)
+DBPATCH_ADD(5030126, 0, 1)
+DBPATCH_ADD(5030127, 0, 1)
+DBPATCH_ADD(5030128, 0, 1)
+DBPATCH_ADD(5030129, 0, 1)
+DBPATCH_ADD(5030130, 0, 1)
+DBPATCH_ADD(5030131, 0, 1)
+DBPATCH_ADD(5030132, 0, 1)
+DBPATCH_ADD(5030133, 0, 1)
+DBPATCH_ADD(5030134, 0, 1)
+DBPATCH_ADD(5030135, 0, 1)
+DBPATCH_ADD(5030136, 0, 1)
+DBPATCH_ADD(5030137, 0, 1)
+DBPATCH_ADD(5030138, 0, 1)
+DBPATCH_ADD(5030139, 0, 1)
+DBPATCH_ADD(5030140, 0, 1)
+DBPATCH_ADD(5030141, 0, 1)
+DBPATCH_ADD(5030142, 0, 1)
+DBPATCH_ADD(5030143, 0, 1)
+DBPATCH_ADD(5030144, 0, 1)
+DBPATCH_ADD(5030145, 0, 1)
+DBPATCH_ADD(5030146, 0, 1)
+DBPATCH_ADD(5030147, 0, 1)
+DBPATCH_ADD(5030148, 0, 1)
+DBPATCH_ADD(5030149, 0, 1)
+DBPATCH_ADD(5030150, 0, 1)
+DBPATCH_ADD(5030151, 0, 1)
+DBPATCH_ADD(5030152, 0, 1)
+DBPATCH_ADD(5030153, 0, 1)
+DBPATCH_ADD(5030154, 0, 1)
+DBPATCH_ADD(5030155, 0, 1)
+DBPATCH_ADD(5030156, 0, 1)
+DBPATCH_ADD(5030157, 0, 1)
+DBPATCH_ADD(5030158, 0, 1)
+DBPATCH_ADD(5030159, 0, 1)
+DBPATCH_ADD(5030160, 0, 1)
+DBPATCH_ADD(5030161, 0, 1)
+DBPATCH_ADD(5030162, 0, 1)
+DBPATCH_ADD(5030163, 0, 1)
+DBPATCH_ADD(5030164, 0, 1)
DBPATCH_END()
diff --git a/src/libs/zbxdbupgrade/dbupgrade_macros.c b/src/libs/zbxdbupgrade/dbupgrade_macros.c
new file mode 100644
index 00000000000..1df8f6b09aa
--- /dev/null
+++ b/src/libs/zbxdbupgrade/dbupgrade_macros.c
@@ -0,0 +1,149 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "db.h"
+#include "dbupgrade.h"
+#include "dbupgrade_macros.h"
+
+/******************************************************************************
+ * *
+ * Function: str_rename_macro *
+ * *
+ * Purpose: rename macros in the string *
+ * *
+ * Parameters: in - [IN] the input string *
+ * oldmacro - [IN] the macro to rename *
+ * newmacro - [IN] the new macro name *
+ * out - [IN/OUT] the string with renamed macros *
+ * out_alloc - [IN/OUT] the output buffer size *
+ * *
+ * Return value: SUCCEED - macros were found and renamed *
+ * FAIL - no target macros were found *
+ * *
+ * Comments: If the oldmacro is found in input string then all occurrences of *
+ * it are replaced with the new macro in the output string. *
+ * Otherwise the output string is not changed. *
+ * *
+ ******************************************************************************/
+static int str_rename_macro(const char *in, const char *oldmacro, const char *newmacro, char **out,
+ size_t *out_alloc)
+{
+ zbx_token_t token;
+ int pos = 0, ret = FAIL;
+ size_t out_offset = 0, newmacro_len;
+
+ newmacro_len = strlen(newmacro);
+ zbx_strcpy_alloc(out, out_alloc, &out_offset, in);
+ out_offset++;
+
+ for (; SUCCEED == zbx_token_find(*out, pos, &token, ZBX_TOKEN_SEARCH_BASIC); pos++)
+ {
+ switch (token.type)
+ {
+ case ZBX_TOKEN_MACRO:
+ pos = token.loc.r;
+ if (0 == strncmp(*out + token.loc.l, oldmacro, token.loc.r - token.loc.l + 1))
+ {
+ pos += zbx_replace_mem_dyn(out, out_alloc, &out_offset, token.loc.l,
+ token.loc.r - token.loc.l + 1, newmacro, newmacro_len);
+ ret = SUCCEED;
+ }
+ break;
+
+ case ZBX_TOKEN_USER_MACRO:
+ case ZBX_TOKEN_SIMPLE_MACRO:
+ pos = token.loc.r;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: db_rename_macro *
+ * *
+ * Purpose: rename macro in the specified database fields *
+ * *
+ * Parameters: result - [IN] database query with fields to replace. First *
+ * field is table id field, following with *
+ * the target fields listed in fields parameter *
+ * table - [IN] the target table name *
+ * pkey - [IN] the primary key field name *
+ * fields - [IN] the table fields to check for macros and *
+ * rename if found *
+ * fields_num - [IN] the number of fields to check *
+ * oldmacro - [IN] the macro to rename *
+ * newmacro - [IN] the new macro name *
+ * *
+ * Return value: SUCCEED - macros were renamed successfully *
+ * FAIL - database error occurred *
+ * *
+ ******************************************************************************/
+int db_rename_macro(DB_RESULT result, const char *table, const char *pkey, const char **fields, int fields_num,
+ const char *oldmacro, const char *newmacro)
+{
+ DB_ROW row;
+ char *sql = 0, *field = NULL, *field_esc;
+ size_t sql_alloc = 4096, sql_offset = 0, field_alloc = 0, old_offset;
+ int i, ret = SUCCEED;
+
+ sql = zbx_malloc(NULL, sql_alloc);
+
+ DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ old_offset = sql_offset;
+
+ for (i = 0; i < fields_num; i++)
+ {
+ if (SUCCEED == str_rename_macro(row[i + 1], oldmacro, newmacro, &field, &field_alloc))
+ {
+ if (old_offset == sql_offset)
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update %s set ", table);
+ else
+ zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, ',');
+
+ field_esc = DBdyn_escape_string(field);
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%s='%s'", fields[i], field_esc);
+ zbx_free(field_esc);
+ }
+ }
+
+ if (old_offset != sql_offset)
+ {
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where %s=%s;\n", pkey, row[0]);
+ if (SUCCEED != (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
+ goto out;
+ }
+ }
+
+ DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ if (16 < sql_offset && ZBX_DB_OK > DBexecute("%s", sql))
+ ret = FAIL;
+out:
+ zbx_free(field);
+ zbx_free(sql);
+
+ return ret;
+}
diff --git a/src/libs/zbxdbupgrade/dbupgrade_macros.h b/src/libs/zbxdbupgrade/dbupgrade_macros.h
new file mode 100644
index 00000000000..9752ccc0bee
--- /dev/null
+++ b/src/libs/zbxdbupgrade/dbupgrade_macros.h
@@ -0,0 +1,26 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_DBUPGRADE_MACROS_H
+#define ZABBIX_DBUPGRADE_MACROS_H
+
+int db_rename_macro(DB_RESULT result, const char *table, const char *pkey, const char **fields, int fields_num,
+ const char *oldmacro, const char *newmacro);
+
+#endif
diff --git a/src/libs/zbxdiag/diag.c b/src/libs/zbxdiag/diag.c
index 49017cfb97b..61789bffe82 100644
--- a/src/libs/zbxdiag/diag.c
+++ b/src/libs/zbxdiag/diag.c
@@ -522,6 +522,9 @@ void diag_add_locks_info(struct zbx_json *json)
zbx_json_addobject(json, NULL);
zbx_json_addhex(json, "ZBX_RWLOCK_CONFIG", (zbx_uint64_t)zbx_rwlock_addr_get(ZBX_RWLOCK_CONFIG));
zbx_json_close(json);
+ zbx_json_addobject(json, NULL);
+ zbx_json_addhex(json, "ZBX_RWLOCK_VALUECACHE", (zbx_uint64_t)zbx_rwlock_addr_get(ZBX_RWLOCK_VALUECACHE));
+ zbx_json_close(json);
zbx_json_close(json);
}
diff --git a/src/libs/zbxipcservice/ipcservice.c b/src/libs/zbxipcservice/ipcservice.c
index 62c1c4521df..351e4130853 100644
--- a/src/libs/zbxipcservice/ipcservice.c
+++ b/src/libs/zbxipcservice/ipcservice.c
@@ -48,6 +48,8 @@ struct zbx_ipc_client
zbx_uint64_t id;
unsigned char state;
+ void *userdata;
+
zbx_uint32_t refcount;
};
@@ -1806,6 +1808,16 @@ zbx_uint64_t zbx_ipc_client_id(const zbx_ipc_client_t *client)
return client->id;
}
+void zbx_ipc_client_set_userdata(zbx_ipc_client_t *client, void *userdata)
+{
+ client->userdata = userdata;
+}
+
+void *zbx_ipc_client_get_userdata(zbx_ipc_client_t *client)
+{
+ return client->userdata;
+}
+
/******************************************************************************
* *
* Function: zbx_ipc_async_socket_open *
@@ -1879,6 +1891,7 @@ void zbx_ipc_async_socket_close(zbx_ipc_async_socket_t *asocket)
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
ipc_client_free(asocket->client);
+ asocket->client = NULL;
event_free(asocket->ev_timer);
event_base_free(asocket->ev);
@@ -2063,9 +2076,9 @@ out:
* *
* Function: zbx_ipc_async_socket_check_unsent *
* *
- * Purpose: checks if there are data to be sent *
+ * Purpose: check if there are data to be sent *
* *
- * Parameters: client - [IN] the IPC service client *
+ * Parameters: asocket - [IN] the asynchronous IPC service socket *
* *
* Return value: SUCCEED - there are messages queued to be sent *
* FAIL - all data has been sent *
@@ -2078,6 +2091,26 @@ int zbx_ipc_async_socket_check_unsent(zbx_ipc_async_socket_t *asocket)
/******************************************************************************
* *
+ * Function: zbx_ipc_async_socket_connected *
+ * *
+ * Purpose: check if socket is connected *
+ * *
+ * Parameters: asocket - [IN] the asynchronous IPC service socket *
+ * *
+ * Return value: SUCCEED - socket is connected *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+int zbx_ipc_async_socket_connected(zbx_ipc_async_socket_t *asocket)
+{
+ if (NULL == asocket->client)
+ return FAIL;
+
+ return zbx_ipc_client_connected(asocket->client);
+}
+
+/******************************************************************************
+ * *
* Function: zbx_ipc_async_exchange *
* *
* Purpose: connect, send message and receive response in a given timeout *
diff --git a/src/libs/zbxmedia/email.c b/src/libs/zbxmedia/email.c
index 322293340b1..81df3b361dc 100644
--- a/src/libs/zbxmedia/email.c
+++ b/src/libs/zbxmedia/email.c
@@ -34,6 +34,9 @@
/* multiple 'encoded-word's should be separated by <CR><LF><SPACE> */
#define ZBX_EMAIL_ENCODED_WORD_SEPARATOR "\r\n "
+/* separator for multipart mixed messages */
+#define ZBX_MULTIPART_MIXED_BOUNDARY "MULTIPART-MIXED-BOUNDARY"
+
/******************************************************************************
* *
* Function: str_base64_encode_rfc2047 *
@@ -272,16 +275,27 @@ out:
return ret;
}
+static char *email_encode_part(const char *data, size_t data_size)
+{
+ char *base64 = NULL, *part;
+
+ str_base64_encode_dyn(data, &base64, data_size);
+ part = str_linefeed(base64, ZBX_EMAIL_B64_MAXLINE, "\r\n");
+ zbx_free(base64);
+
+ return part;
+}
+
static char *smtp_prepare_payload(zbx_vector_ptr_t *from_mails, zbx_vector_ptr_t *to_mails, const char *inreplyto,
const char *mailsubject, const char *mailbody, unsigned char content_type)
{
- char *tmp = NULL, *base64 = NULL, *base64_lf;
+ char *tmp = NULL, *base64 = NULL;
char *localsubject = NULL, *localbody = NULL, *from = NULL, *to = NULL;
char str_time[MAX_STRING_LEN];
struct tm *local_time;
time_t email_time;
int i;
- size_t from_alloc = 0, from_offset = 0, to_alloc = 0, to_offset = 0;
+ size_t from_alloc = 0, from_offset = 0, to_alloc = 0, to_offset = 0, tmp_alloc = 0, tmp_offset = 0;
/* prepare subject */
@@ -301,20 +315,18 @@ static char *smtp_prepare_payload(zbx_vector_ptr_t *from_mails, zbx_vector_ptr_t
/* prepare body */
- tmp = string_replace(mailbody, "\r\n", "\n");
- localbody = string_replace(tmp, "\n", "\r\n");
- zbx_free(tmp);
-
- str_base64_encode_dyn(localbody, &base64, strlen(localbody));
-
- /* wrap base64 encoded data with linefeeds */
- base64_lf = str_linefeed(base64, ZBX_EMAIL_B64_MAXLINE, "\r\n");
- zbx_free(base64);
- base64 = base64_lf;
+ if (ZBX_MEDIA_CONTENT_TYPE_MULTI != content_type)
+ {
+ char *tmp_body;
- zbx_free(localbody);
- localbody = base64;
- base64 = NULL;
+ tmp = string_replace(mailbody, "\r\n", "\n");
+ tmp_body = string_replace(tmp, "\n", "\r\n");
+ localbody = email_encode_part(tmp_body, strlen(tmp_body));
+ zbx_free(tmp_body);
+ zbx_free(tmp);
+ }
+ else
+ localbody = (char *)mailbody;
/* prepare date */
@@ -345,24 +357,36 @@ static char *smtp_prepare_payload(zbx_vector_ptr_t *from_mails, zbx_vector_ptr_t
/* e-mails are sent in 'SMTP/MIME e-mail' format because UTF-8 is used both in mailsubject and mailbody */
/* =?charset?encoding?encoded text?= format must be used for subject field */
- tmp = zbx_dsprintf(tmp,
+ zbx_snprintf_alloc(&tmp, &tmp_alloc, &tmp_offset,
"From: %s\r\n"
"To: %s\r\n"
"In-Reply-To: %s\r\n"
"Date: %s\r\n"
"Subject: %s\r\n"
- "MIME-Version: 1.0\r\n"
- "Content-Type: %s; charset=\"UTF-8\"\r\n"
- "Content-Transfer-Encoding: base64\r\n"
+ "MIME-Version: 1.0\r\n",
+ from, to, inreplyto, str_time, localsubject);
+
+ if (ZBX_MEDIA_CONTENT_TYPE_MULTI == content_type)
+ {
+ zbx_strcpy_alloc(&tmp, &tmp_alloc, &tmp_offset,
+ "Content-Type: multipart/mixed; boundary=" ZBX_MULTIPART_MIXED_BOUNDARY "\r\n");
+ }
+ else
+ {
+ zbx_snprintf_alloc(&tmp, &tmp_alloc, &tmp_offset,
+ "Content-Type: %s\r\n"
+ "Content-Transfer-Encoding: base64\r\n",
+ ZBX_MEDIA_CONTENT_TYPE_HTML == content_type ? "text/html" : "text/plain");
+ }
+
+ zbx_snprintf_alloc(&tmp, &tmp_alloc, &tmp_offset,
"\r\n"
"%s",
- from, to, inreplyto,
- str_time, localsubject,
- ZBX_MEDIA_CONTENT_TYPE_HTML == content_type ? "text/html" : "text/plain",
localbody);
zbx_free(localsubject);
- zbx_free(localbody);
+ if (localbody != mailbody)
+ zbx_free(localbody);
zbx_free(from);
zbx_free(to);
@@ -834,3 +858,45 @@ clean:
return ret;
}
+
+char *zbx_email_make_body(const char *message, unsigned char content_type, const char *attachment_name,
+ const char *attachment_type, const char *attachment, size_t attachment_size)
+{
+ size_t body_alloc = 0, body_offset = 0;
+ char *body = NULL, *localbody, *tmp, *tmp_body, *localattachment;
+
+ tmp = string_replace(message, "\r\n", "\n");
+ tmp_body = string_replace(tmp, "\n", "\r\n");
+ localbody = email_encode_part(tmp_body, strlen(tmp_body));
+ zbx_free(tmp_body);
+ zbx_free(tmp);
+
+ zbx_snprintf_alloc(&body, &body_alloc, &body_offset,
+ "--" ZBX_MULTIPART_MIXED_BOUNDARY "\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "\r\n"
+ "%s\r\n"
+ "\r\n",
+ ZBX_MEDIA_CONTENT_TYPE_HTML == content_type ? "text/html" : "text/plain",
+ localbody);
+
+ zbx_free(localbody);
+
+ localattachment = email_encode_part(attachment, attachment_size);
+
+ zbx_snprintf_alloc(&body, &body_alloc, &body_offset,
+ "--" ZBX_MULTIPART_MIXED_BOUNDARY "\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "Content-Disposition: attachment; filename=\"%s\"\r\n"
+ "\r\n"
+ "%s\r\n"
+ "\r\n"
+ "--" ZBX_MULTIPART_MIXED_BOUNDARY "--\r\n",
+ attachment_type, attachment_name, localattachment);
+
+ zbx_free(localattachment);
+
+ return body;
+}
diff --git a/src/libs/zbxself/Makefile.am b/src/libs/zbxself/Makefile.am
index 18920c70d28..5a311514310 100644
--- a/src/libs/zbxself/Makefile.am
+++ b/src/libs/zbxself/Makefile.am
@@ -1,6 +1,15 @@
## Process this file with automake to produce Makefile.in
-noinst_LIBRARIES = libzbxself.a
+noinst_LIBRARIES = libzbxself.a libzbxself_server.a libzbxself_proxy.a
libzbxself_a_SOURCES = \
- selfmon.c
+ selfmon.c \
+ selfmon.h
+
+libzbxself_server_a_SOURCES = \
+ selfmon_server.c \
+ selfmon.h
+
+libzbxself_proxy_a_SOURCES = \
+ selfmon_proxy.c \
+ selfmon.h
diff --git a/src/libs/zbxself/selfmon.c b/src/libs/zbxself/selfmon.c
index a3ffc0da49f..bbc57d72c38 100644
--- a/src/libs/zbxself/selfmon.c
+++ b/src/libs/zbxself/selfmon.c
@@ -19,6 +19,7 @@
#include "zbxself.h"
#include "common.h"
+#include "selfmon.h"
#ifndef _WINDOWS
# include "mutexs.h"
@@ -209,8 +210,7 @@ int get_process_type_forks(unsigned char proc_type)
return CONFIG_AVAILMAN_FORKS;
}
- THIS_SHOULD_NEVER_HAPPEN;
- exit(EXIT_FAILURE);
+ return get_component_process_type_forks(proc_type);
}
#ifndef _WINDOWS
diff --git a/src/libs/zbxself/selfmon.h b/src/libs/zbxself/selfmon.h
new file mode 100644
index 00000000000..6a75c086aa9
--- /dev/null
+++ b/src/libs/zbxself/selfmon.h
@@ -0,0 +1,25 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_SELFMON_H
+#define ZABBIX_SELFMON_H
+
+int get_component_process_type_forks(unsigned char proc_type);
+
+#endif
diff --git a/src/libs/zbxself/selfmon_proxy.c b/src/libs/zbxself/selfmon_proxy.c
new file mode 100644
index 00000000000..c80089301a3
--- /dev/null
+++ b/src/libs/zbxself/selfmon_proxy.c
@@ -0,0 +1,39 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "zbxself.h"
+#include "common.h"
+#include "selfmon.h"
+
+/******************************************************************************
+ * *
+ * Function: get_component_process_type_forks *
+ * *
+ * Purpose: Returns number of processes depending on process type *
+ * *
+ * Parameters: proc_type - [IN] process type; ZBX_PROCESS_TYPE_* *
+ * *
+ * Return value: number of processes *
+ * *
+ ******************************************************************************/
+int get_component_process_type_forks(unsigned char proc_type)
+{
+ ZBX_UNUSED(proc_type);
+ return 0;
+}
diff --git a/src/libs/zbxself/selfmon_server.c b/src/libs/zbxself/selfmon_server.c
new file mode 100644
index 00000000000..f1852c9a4d1
--- /dev/null
+++ b/src/libs/zbxself/selfmon_server.c
@@ -0,0 +1,49 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "zbxself.h"
+#include "common.h"
+#include "selfmon.h"
+
+extern int CONFIG_REPORTMANAGER_FORKS;
+extern int CONFIG_REPORTWRITER_FORKS;
+
+/******************************************************************************
+ * *
+ * Function: get_component_process_type_forks *
+ * *
+ * Purpose: Returns number of processes depending on process type *
+ * *
+ * Parameters: proc_type - [IN] process type; ZBX_PROCESS_TYPE_* *
+ * *
+ * Return value: number of processes *
+ * *
+ ******************************************************************************/
+int get_component_process_type_forks(unsigned char proc_type)
+{
+ switch (proc_type)
+ {
+ case ZBX_PROCESS_TYPE_REPORTMANAGER:
+ return CONFIG_REPORTMANAGER_FORKS;
+ case ZBX_PROCESS_TYPE_REPORTWRITER:
+ return CONFIG_REPORTWRITER_FORKS;
+ }
+
+ return 0;
+}
diff --git a/src/libs/zbxserver/Makefile.am b/src/libs/zbxserver/Makefile.am
index c96d0ef0a36..c7fda611927 100644
--- a/src/libs/zbxserver/Makefile.am
+++ b/src/libs/zbxserver/Makefile.am
@@ -9,7 +9,11 @@ libzbxserver_a_SOURCES = \
macrofunc.c \
macrofunc.h \
zabbix_stats.c \
- zabbix_stats.h
+ zabbix_stats.h \
+ get_host_from_event.c \
+ get_host_from_event.h \
+ zabbix_users.c \
+ zabbix_users.h
libzbxserver_server_a_SOURCES = \
zabbix_stats.h \
diff --git a/src/libs/zbxserver/expression.c b/src/libs/zbxserver/expression.c
index 1c8f2563d95..1b15a0fcb99 100644
--- a/src/libs/zbxserver/expression.c
+++ b/src/libs/zbxserver/expression.c
@@ -71,10 +71,10 @@ zbx_libxml_error_t;
#define ZBX_REQUEST_ITEM_LOG_NSEVERITY 206
#define ZBX_REQUEST_ITEM_LOG_EVENTID 207
-static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT *event, const DB_EVENT *r_event,
- zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host, const DC_ITEM *dc_item,
- DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data, int macro_type, char *error,
- int maxerrlen);
+static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_EVENT *event,
+ const DB_EVENT *r_event, const zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host,
+ const DC_ITEM *dc_item, const DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data,
+ int macro_type, char *error, int maxerrlen);
static int substitute_key_macros_impl(char **data, zbx_uint64_t *hostid, DC_ITEM *dc_item,
const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, int macro_type,
@@ -1365,6 +1365,8 @@ static int DBget_history_log_value(zbx_uint64_t itemid, char **replace_to, int r
if (SUCCEED != zbx_vc_get_value(itemid, item.value_type, &ts, &value))
goto out;
+ zbx_vc_flush_stats();
+
switch (request)
{
case ZBX_REQUEST_ITEM_LOG_DATE:
@@ -1452,6 +1454,7 @@ static int DBitem_get_value(zbx_uint64_t itemid, char **lastvalue, int raw, zbx_
{
char tmp[MAX_BUFFER_LEN];
+ zbx_vc_flush_stats();
zbx_history_value_print(tmp, sizeof(tmp), &vc_value.value, value_type);
zbx_history_record_clear(&vc_value, value_type);
@@ -1845,6 +1848,11 @@ static int get_autoreg_value_by_event(const DB_EVENT *event, char **replace_to,
#define MVAR_HOSTNAME "{HOSTNAME}" /* deprecated */
#define MVAR_HOST_DESCRIPTION "{HOST.DESCRIPTION}"
#define MVAR_HOST_PORT "{HOST.PORT}"
+#define MVAR_HOST_TARGET_DNS "{HOST.TARGET.DNS}"
+#define MVAR_HOST_TARGET_CONN "{HOST.TARGET.CONN}"
+#define MVAR_HOST_TARGET_HOST "{HOST.TARGET.HOST}"
+#define MVAR_HOST_TARGET_IP "{HOST.TARGET.IP}"
+#define MVAR_HOST_TARGET_NAME "{HOST.TARGET.NAME}"
#define MVAR_TIME "{TIME}"
#define MVAR_ITEM_LASTVALUE "{ITEM.LASTVALUE}"
#define MVAR_ITEM_VALUE "{ITEM.VALUE}"
@@ -2946,9 +2954,6 @@ static const char *zbx_dobject_status2str(int st)
* *
* Purpose: resolve {EVENT.OPDATA} macro *
* *
- * Return value: upon successful completion return SUCCEED *
- * otherwise FAIL *
- * *
******************************************************************************/
static void resolve_opdata(const DB_EVENT *event, char **replace_to, const char *tz, char *error, int maxerrlen)
{
@@ -3014,6 +3019,86 @@ static void resolve_opdata(const DB_EVENT *event, char **replace_to, const char
/******************************************************************************
* *
+ * Function: resolve_user_macros *
+ * *
+ * Purpose: resolve {USER.*} macros *
+ * *
+ ******************************************************************************/
+static void resolve_user_macros(zbx_uint64_t userid, const char *m, char **user_username, char **user_name,
+ char **user_surname, int *user_names_found, char **replace_to)
+{
+ /* use only one DB request for all occurrences of 5 macros */
+ if (0 == *user_names_found)
+ {
+ if (SUCCEED == DBget_user_names(userid, user_username, user_name, user_surname))
+ *user_names_found = 1;
+ else
+ return;
+ }
+
+ if (0 == strcmp(m, MVAR_USER_USERNAME) || 0 == strcmp(m, MVAR_USER_ALIAS))
+ {
+ *replace_to = zbx_strdup(*replace_to, *user_username);
+ }
+ else if (0 == strcmp(m, MVAR_USER_NAME))
+ {
+ *replace_to = zbx_strdup(*replace_to, *user_name);
+ }
+ else if (0 == strcmp(m, MVAR_USER_SURNAME))
+ {
+ *replace_to = zbx_strdup(*replace_to, *user_surname);
+ }
+ else if (0 == strcmp(m, MVAR_USER_FULLNAME))
+ {
+ zbx_free(*replace_to);
+ *replace_to = format_user_fullname(*user_name, *user_surname, *user_username);
+ }
+}
+
+static int resolve_host_target_macros(const char *m, const DC_HOST *dc_host, DC_INTERFACE *interface,
+ int *require_address, char **replace_to)
+{
+ int ret = SUCCEED;
+
+ if (NULL == dc_host)
+ return SUCCEED;
+
+ if (0 == strcmp(m, MVAR_HOST_TARGET_DNS))
+ {
+ if (SUCCEED == (ret = DCconfig_get_interface(interface, dc_host->hostid, 0)))
+ *replace_to = zbx_strdup(*replace_to, interface->dns_orig);
+
+ *require_address = 1;
+ }
+ else if (0 == strcmp(m, MVAR_HOST_TARGET_CONN))
+ {
+ if (SUCCEED == (ret = DCconfig_get_interface(interface, dc_host->hostid, 0)))
+ *replace_to = zbx_strdup(*replace_to, interface->addr);
+
+ *require_address = 1;
+
+ }
+ else if (0 == strcmp(m, MVAR_HOST_TARGET_HOST))
+ {
+ *replace_to = zbx_strdup(*replace_to, dc_host->host);
+ }
+ else if (0 == strcmp(m, MVAR_HOST_TARGET_IP))
+ {
+ if (SUCCEED == (ret = DCconfig_get_interface(interface, dc_host->hostid, 0)))
+ *replace_to = zbx_strdup(*replace_to, interface->ip_orig);
+
+ *require_address = 1;
+ }
+ else if (0 == strcmp(m, MVAR_HOST_TARGET_NAME))
+ {
+ *replace_to = zbx_strdup(*replace_to, dc_host->name);
+ }
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
* Function: substitute_simple_macros_impl *
* *
* Purpose: substitute simple macros in data string with real values *
@@ -3021,9 +3106,10 @@ static void resolve_opdata(const DB_EVENT *event, char **replace_to, const char
* Author: Eugene Grigorjev *
* *
******************************************************************************/
-static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT *event, const DB_EVENT *r_event,
- zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host, const DC_ITEM *dc_item,
- DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data, int macro_type, char *error, int maxerrlen)
+static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_EVENT *event,
+ const DB_EVENT *r_event, const zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host,
+ const DC_ITEM *dc_item, const DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data,
+ int macro_type, char *error, int maxerrlen)
{
char c, *replace_to = NULL, sql[64];
const char *m;
@@ -3110,7 +3196,8 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
break;
case ZBX_TOKEN_SIMPLE_MACRO:
if (0 == (macro_type & (MACRO_TYPE_MESSAGE_NORMAL | MACRO_TYPE_MESSAGE_RECOVERY |
- MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_EXPRESSION)) ||
+ MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_EXPRESSION |
+ MACRO_TYPE_SCRIPT_NORMAL | MACRO_TYPE_SCRIPT_RECOVERY)) ||
EVENT_SOURCE_TRIGGERS != ((NULL != r_event) ? r_event : event)->source)
{
pos++;
@@ -3135,7 +3222,11 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
ret = SUCCEED;
if (0 != (macro_type & (MACRO_TYPE_MESSAGE_NORMAL | MACRO_TYPE_MESSAGE_RECOVERY |
- MACRO_TYPE_MESSAGE_ACK)))
+ MACRO_TYPE_MESSAGE_ACK |
+ MACRO_TYPE_SCRIPT_NORMAL | MACRO_TYPE_SCRIPT_RECOVERY)))
+ /* MACRO_TYPE_SCRIPT_NORMAL and MACRO_TYPE_SCRIPT_RECOVERY behave pretty similar to */
+ /* MACRO_TYPE_MESSAGE_NORMAL and MACRO_TYPE_MESSAGE_RECOVERY. Therefore the code is not duplicated */
+ /* but few conditions are added below where behavior differs. */
{
const DB_EVENT *c_event;
@@ -3145,9 +3236,15 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
if (ZBX_TOKEN_USER_MACRO == token.type)
{
- cache_trigger_hostids(&hostids, c_event->trigger.expression,
- c_event->trigger.recovery_expression);
- DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to);
+ if (NULL == dc_host)
+ {
+ cache_trigger_hostids(&hostids, c_event->trigger.expression,
+ c_event->trigger.recovery_expression);
+ DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to);
+ }
+ else
+ DCget_user_macro(&dc_host->hostid, 1, m, &replace_to);
+
pos = token.loc.r;
}
else if (ZBX_TOKEN_SIMPLE_MACRO == token.type)
@@ -3155,7 +3252,8 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
ret = get_trigger_function_value(c_event->trigger.expression, &replace_to,
*data, &token.data.simple_macro, ZBX_FORMAT_HUMAN);
}
- else if (0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
+ else if (NULL != actionid &&
+ 0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
{
ret = get_action_value(m, *actionid, &replace_to);
}
@@ -3163,7 +3261,7 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
replace_to = zbx_strdup(replace_to, zbx_date2str(time(NULL), tz));
}
- else if (0 == strcmp(m, MVAR_ESC_HISTORY))
+ else if (NULL != actionid && 0 == strcmp(m, MVAR_ESC_HISTORY))
{
get_escalation_history(*actionid, event, r_event, &replace_to, userid, tz);
}
@@ -3421,19 +3519,17 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
replace_to = zbx_dsprintf(replace_to, "%d", c_event->trigger.value);
}
- else if (0 == strcmp(m, MVAR_USER_FULLNAME))
+ else if (0 != (macro_type & MACRO_TYPE_MESSAGE_ACK) && NULL != ack &&
+ 0 == strcmp(m, MVAR_USER_FULLNAME))
{
- if (0 != (macro_type & MACRO_TYPE_MESSAGE_ACK) && NULL != ack)
- {
- const char *user_name1;
+ const char *user_name1;
- if (SUCCEED == zbx_check_user_permissions(&ack->userid, userid))
- user_name1 = zbx_user_string(ack->userid);
- else
- user_name1 = "Inaccessible user";
+ if (SUCCEED == zbx_check_user_permissions(&ack->userid, userid))
+ user_name1 = zbx_user_string(ack->userid);
+ else
+ user_name1 = "Inaccessible user";
- replace_to = zbx_strdup(replace_to, user_name1);
- }
+ replace_to = zbx_strdup(replace_to, user_name1);
}
else if (0 == strcmp(m, MVAR_ALERT_SENDTO))
{
@@ -3450,6 +3546,19 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
if (NULL != alert)
replace_to = zbx_strdup(replace_to, alert->message);
}
+ else if (0 != (macro_type & (MACRO_TYPE_SCRIPT_NORMAL | MACRO_TYPE_SCRIPT_RECOVERY)) &&
+ NULL != userid && (0 == strcmp(m, MVAR_USER_USERNAME) ||
+ 0 == strcmp(m, MVAR_USER_NAME) || 0 == strcmp(m, MVAR_USER_SURNAME) ||
+ 0 == strcmp(m, MVAR_USER_FULLNAME) || 0 == strcmp(m, MVAR_USER_ALIAS)))
+ {
+ resolve_user_macros(*userid, m, &user_username, &user_name, &user_surname,
+ &user_names_found, &replace_to);
+ }
+ else if (0 == (macro_type & (MACRO_TYPE_SCRIPT_NORMAL | MACRO_TYPE_SCRIPT_RECOVERY)))
+ {
+ ret = resolve_host_target_macros(m, dc_host, &interface, &require_address,
+ &replace_to);
+ }
}
else if (EVENT_SOURCE_INTERNAL == c_event->source && EVENT_OBJECT_TRIGGER == c_event->object)
{
@@ -3460,7 +3569,8 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to);
pos = token.loc.r;
}
- else if (0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
+ else if (NULL != actionid &&
+ 0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
{
ret = get_action_value(m, *actionid, &replace_to);
}
@@ -3468,7 +3578,7 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
replace_to = zbx_strdup(replace_to, zbx_date2str(time(NULL), tz));
}
- else if (0 == strcmp(m, MVAR_ESC_HISTORY))
+ else if (NULL != actionid && 0 == strcmp(m, MVAR_ESC_HISTORY))
{
get_escalation_history(*actionid, event, r_event, &replace_to, userid, tz);
}
@@ -3674,10 +3784,15 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
if (ZBX_TOKEN_USER_MACRO == token.type)
{
- DCget_user_macro(NULL, 0, m, &replace_to);
+ if (NULL == dc_host)
+ DCget_user_macro(NULL, 0, m, &replace_to);
+ else
+ DCget_user_macro(&dc_host->hostid, 1, m, &replace_to);
+
pos = token.loc.r;
}
- else if (0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
+ else if (NULL != actionid &&
+ 0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
{
ret = get_action_value(m, *actionid, &replace_to);
}
@@ -3809,15 +3924,25 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
if (NULL != alert)
replace_to = zbx_strdup(replace_to, alert->message);
}
+ else
+ {
+ ret = resolve_host_target_macros(m, dc_host, &interface, &require_address,
+ &replace_to);
+ }
}
else if (0 == indexed_macro && EVENT_SOURCE_AUTOREGISTRATION == c_event->source)
{
if (ZBX_TOKEN_USER_MACRO == token.type)
{
- DCget_user_macro(NULL, 0, m, &replace_to);
+ if (NULL == dc_host)
+ DCget_user_macro(NULL, 0, m, &replace_to);
+ else
+ DCget_user_macro(&dc_host->hostid, 1, m, &replace_to);
+
pos = token.loc.r;
}
- else if (0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
+ else if (NULL != actionid &&
+ 0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
{
ret = get_action_value(m, *actionid, &replace_to);
}
@@ -3900,6 +4025,11 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
if (NULL != alert)
replace_to = zbx_strdup(replace_to, alert->message);
}
+ else
+ {
+ ret = resolve_host_target_macros(m, dc_host, &interface, &require_address,
+ &replace_to);
+ }
}
else if (0 == indexed_macro && EVENT_SOURCE_INTERNAL == c_event->source &&
EVENT_OBJECT_ITEM == c_event->object)
@@ -3910,7 +4040,8 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to);
pos = token.loc.r;
}
- else if (0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
+ else if (NULL != actionid &&
+ 0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
{
ret = get_action_value(m, *actionid, &replace_to);
}
@@ -3918,7 +4049,7 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
replace_to = zbx_strdup(replace_to, zbx_date2str(time(NULL), tz));
}
- else if (0 == strcmp(m, MVAR_ESC_HISTORY))
+ else if (NULL != actionid && 0 == strcmp(m, MVAR_ESC_HISTORY))
{
get_escalation_history(*actionid, event, r_event, &replace_to, userid, tz);
}
@@ -4056,7 +4187,8 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
DCget_user_macro(hostids.values, hostids.values_num, m, &replace_to);
pos = token.loc.r;
}
- else if (0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
+ else if (NULL != actionid &&
+ 0 == strncmp(m, MVAR_ACTION, ZBX_CONST_STRLEN(MVAR_ACTION)))
{
ret = get_action_value(m, *actionid, &replace_to);
}
@@ -4064,7 +4196,7 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
replace_to = zbx_strdup(replace_to, zbx_date2str(time(NULL), tz));
}
- else if (0 == strcmp(m, MVAR_ESC_HISTORY))
+ else if (NULL != actionid && 0 == strcmp(m, MVAR_ESC_HISTORY))
{
get_escalation_history(*actionid, event, r_event, &replace_to, userid, tz);
}
@@ -4516,39 +4648,12 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
}
else if (NULL != userid)
{
- /* use only one DB request for all occurrences of 5 macros */
- if (0 == user_names_found && (0 == strcmp(m, MVAR_USER_ALIAS) ||
- 0 == strcmp(m, MVAR_USER_USERNAME) || 0 == strcmp(m, MVAR_USER_NAME) ||
+ if (0 == strcmp(m, MVAR_USER_USERNAME) || 0 == strcmp(m, MVAR_USER_NAME) ||
0 == strcmp(m, MVAR_USER_SURNAME) ||
- 0 == strcmp(m, MVAR_USER_FULLNAME)))
- {
- if (SUCCEED == DBget_user_names(*userid, &user_username, &user_name,
- &user_surname))
- {
- user_names_found = 1;
- }
- }
-
- if (0 != user_names_found)
+ 0 == strcmp(m, MVAR_USER_FULLNAME) || 0 == strcmp(m, MVAR_USER_ALIAS))
{
- if (0 == strcmp(m, MVAR_USER_ALIAS) || 0 == strcmp(m, MVAR_USER_USERNAME))
- {
- replace_to = zbx_strdup(replace_to, user_username);
- }
- else if (0 == strcmp(m, MVAR_USER_NAME))
- {
- replace_to = zbx_strdup(replace_to, user_name);
- }
- else if (0 == strcmp(m, MVAR_USER_SURNAME))
- {
- replace_to = zbx_strdup(replace_to, user_surname);
- }
- else if (0 == strcmp(m, MVAR_USER_FULLNAME))
- {
- zbx_free(replace_to);
- replace_to = format_user_fullname(user_name, user_surname,
- user_username);
- }
+ resolve_user_macros(*userid, m, &user_username, &user_name, &user_surname,
+ &user_names_found, &replace_to);
}
}
}
@@ -4694,7 +4799,7 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
}
else if (0 != (macro_type & MACRO_TYPE_TRIGGER_TAG))
{
- if (EVENT_SOURCE_TRIGGERS == event->source)
+ if (EVENT_SOURCE_TRIGGERS == event->source || EVENT_SOURCE_INTERNAL == event->source)
{
if (ZBX_TOKEN_USER_MACRO == token.type)
{
@@ -4743,24 +4848,28 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
ret = DBget_trigger_value(event->trigger.expression, &replace_to, N_functionid,
ZBX_REQUEST_HOST_PORT);
}
- else if (0 == strcmp(m, MVAR_ITEM_LASTVALUE))
- {
- ret = DBitem_lastvalue(event->trigger.expression, &replace_to, N_functionid,
- raw_value);
- }
- else if (0 == strcmp(m, MVAR_ITEM_VALUE))
- {
- ret = DBitem_value(event->trigger.expression, &replace_to, N_functionid,
- event->clock, event->ns, raw_value);
- }
- else if (0 == strncmp(m, MVAR_ITEM_LOG, ZBX_CONST_STRLEN(MVAR_ITEM_LOG)))
- {
- ret = get_history_log_value(m, event->trigger.expression, &replace_to,
- N_functionid, event->clock, event->ns, tz);
- }
- else if (0 == strcmp(m, MVAR_TRIGGER_ID))
+
+ if (EVENT_SOURCE_TRIGGERS == event->source)
{
- replace_to = zbx_dsprintf(replace_to, ZBX_FS_UI64, event->objectid);
+ if (0 == strcmp(m, MVAR_ITEM_LASTVALUE))
+ {
+ ret = DBitem_lastvalue(event->trigger.expression, &replace_to,
+ N_functionid, raw_value);
+ }
+ else if (0 == strcmp(m, MVAR_ITEM_VALUE))
+ {
+ ret = DBitem_value(event->trigger.expression, &replace_to, N_functionid,
+ event->clock, event->ns, raw_value);
+ }
+ else if (0 == strncmp(m, MVAR_ITEM_LOG, ZBX_CONST_STRLEN(MVAR_ITEM_LOG)))
+ {
+ ret = get_history_log_value(m, event->trigger.expression, &replace_to,
+ N_functionid, event->clock, event->ns, tz);
+ }
+ else if (0 == strcmp(m, MVAR_TRIGGER_ID))
+ {
+ replace_to = zbx_dsprintf(replace_to, ZBX_FS_UI64, event->objectid);
+ }
}
}
}
@@ -4768,7 +4877,11 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
{
/* Using dc_item to pass itemid and hostid only, all other fields are not initialized! */
- if (EVENT_SOURCE_TRIGGERS == event->source)
+ if (EVENT_SOURCE_TRIGGERS == event->source && 0 == strcmp(m, MVAR_TRIGGER_ID))
+ {
+ replace_to = zbx_dsprintf(replace_to, ZBX_FS_UI64, event->objectid);
+ }
+ else if (EVENT_SOURCE_TRIGGERS == event->source || EVENT_SOURCE_INTERNAL == event->source)
{
if (ZBX_TOKEN_USER_MACRO == token.type)
{
@@ -4810,10 +4923,6 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
get_interface_value(dc_item->host.hostid, dc_item->itemid, &replace_to,
ZBX_REQUEST_HOST_PORT);
}
- else if (0 == strcmp(m, MVAR_TRIGGER_ID))
- {
- replace_to = zbx_dsprintf(replace_to, ZBX_FS_UI64, event->objectid);
- }
}
}
else if (0 == indexed_macro && 0 != (macro_type & MACRO_TYPE_EXPRESSION))
@@ -4835,6 +4944,13 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
*data, &token.data.simple_macro, ZBX_FORMAT_RAW);
}
}
+ else if (0 == indexed_macro && 0 != (macro_type & MACRO_TYPE_REPORT))
+ {
+ if (0 == strcmp(m, MVAR_TIME))
+ {
+ replace_to = zbx_strdup(replace_to, zbx_time2str(time(NULL), tz));
+ }
+ }
if (0 != (macro_type & MACRO_TYPE_HTTP_JSON) && NULL != replace_to)
zbx_json_escape(&replace_to);
@@ -4888,6 +5004,8 @@ static int substitute_simple_macros_impl(zbx_uint64_t *actionid, const DB_EVENT
pos++;
}
+ zbx_vc_flush_stats();
+
zbx_free(user_username);
zbx_free(user_name);
zbx_free(user_surname);
@@ -5383,6 +5501,8 @@ static void zbx_evaluate_item_functions(zbx_hashset_t *funcs, zbx_vector_ptr_t *
}
}
+ zbx_vc_flush_stats();
+
DCconfig_clean_items(items, errcodes, itemids.values_num);
zbx_vector_uint64_destroy(&itemids);
@@ -6750,10 +6870,10 @@ int xml_xpath_check(const char *xpath, char *error, size_t errlen)
* (default setting) *
* *
******************************************************************************/
-int substitute_simple_macros(zbx_uint64_t *actionid, const DB_EVENT *event, const DB_EVENT *r_event,
- zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host, const DC_ITEM *dc_item,
- DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data, int macro_type, char *error,
- int maxerrlen)
+int substitute_simple_macros(const zbx_uint64_t *actionid, const DB_EVENT *event, const DB_EVENT *r_event,
+ const zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host, const DC_ITEM *dc_item,
+ const DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data, int macro_type,
+ char *error, int maxerrlen)
{
return substitute_simple_macros_impl(actionid, event, r_event, userid, hostid, dc_host, dc_item, alert, ack,
tz, data, macro_type, error, maxerrlen);
@@ -6767,10 +6887,10 @@ int substitute_simple_macros(zbx_uint64_t *actionid, const DB_EVENT *event, cons
* Purpose: substitute_simple_macros with unmasked secret macros *
* *
******************************************************************************/
-int substitute_simple_macros_unmasked(zbx_uint64_t *actionid, const DB_EVENT *event, const DB_EVENT *r_event,
- zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host, const DC_ITEM *dc_item,
- DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data, int macro_type, char *error,
- int maxerrlen)
+int substitute_simple_macros_unmasked(const zbx_uint64_t *actionid, const DB_EVENT *event, const DB_EVENT *r_event,
+ const zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host, const DC_ITEM *dc_item,
+ const DB_ALERT *alert, const DB_ACKNOWLEDGE *ack, const char *tz, char **data, int macro_type,
+ char *error, int maxerrlen)
{
unsigned char old_macro_env;
int ret;
diff --git a/src/libs/zbxserver/get_host_from_event.c b/src/libs/zbxserver/get_host_from_event.c
new file mode 100644
index 00000000000..30b738f79ed
--- /dev/null
+++ b/src/libs/zbxserver/get_host_from_event.c
@@ -0,0 +1,158 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "db.h"
+#include "dbcache.h"
+#include "get_host_from_event.h"
+#include "log.h"
+
+#ifdef HAVE_OPENIPMI
+# define ZBX_IPMI_FIELDS_NUM 4 /* number of selected IPMI-related fields in function */
+ /* get_host_from_event() */
+#else
+# define ZBX_IPMI_FIELDS_NUM 0
+#endif
+
+int get_host_from_event(const DB_EVENT *event, DC_HOST *host, char *error, size_t max_error_len)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ char sql[512]; /* do not forget to adjust size if SQLs change */
+ size_t offset;
+ int ret = SUCCEED;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ offset = zbx_snprintf(sql, sizeof(sql), "select distinct h.hostid,h.proxy_hostid,h.host,h.tls_connect");
+#ifdef HAVE_OPENIPMI
+ offset += zbx_snprintf(sql + offset, sizeof(sql) - offset,
+ /* do not forget to update ZBX_IPMI_FIELDS_NUM if number of selected IPMI fields changes */
+ ",h.ipmi_authtype,h.ipmi_privilege,h.ipmi_username,h.ipmi_password");
+#endif
+#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
+ offset += zbx_snprintf(sql + offset, sizeof(sql) - offset,
+ ",h.tls_issuer,h.tls_subject,h.tls_psk_identity,h.tls_psk");
+#endif
+ switch (event->source)
+ {
+ case EVENT_SOURCE_TRIGGERS:
+ zbx_snprintf(sql + offset, sizeof(sql) - offset,
+ " from functions f,items i,hosts h"
+ " where f.itemid=i.itemid"
+ " and i.hostid=h.hostid"
+ " and h.status=%d"
+ " and f.triggerid=" ZBX_FS_UI64,
+ HOST_STATUS_MONITORED, event->objectid);
+
+ break;
+ case EVENT_SOURCE_DISCOVERY:
+ offset += zbx_snprintf(sql + offset, sizeof(sql) - offset,
+ " from hosts h,interface i,dservices ds"
+ " where h.hostid=i.hostid"
+ " and i.ip=ds.ip"
+ " and i.useip=1"
+ " and h.status=%d",
+ HOST_STATUS_MONITORED);
+
+ switch (event->object)
+ {
+ case EVENT_OBJECT_DHOST:
+ zbx_snprintf(sql + offset, sizeof(sql) - offset,
+ " and ds.dhostid=" ZBX_FS_UI64, event->objectid);
+ break;
+ case EVENT_OBJECT_DSERVICE:
+ zbx_snprintf(sql + offset, sizeof(sql) - offset,
+ " and ds.dserviceid=" ZBX_FS_UI64, event->objectid);
+ break;
+ }
+ break;
+ case EVENT_SOURCE_AUTOREGISTRATION:
+ zbx_snprintf(sql + offset, sizeof(sql) - offset,
+ " from autoreg_host a,hosts h"
+ " where " ZBX_SQL_NULLCMP("a.proxy_hostid", "h.proxy_hostid")
+ " and a.host=h.host"
+ " and h.status=%d"
+ " and h.flags<>%d"
+ " and a.autoreg_hostid=" ZBX_FS_UI64,
+ HOST_STATUS_MONITORED, ZBX_FLAG_DISCOVERY_PROTOTYPE, event->objectid);
+ break;
+ default:
+ zbx_snprintf(error, max_error_len, "Unsupported event source [%d]", event->source);
+ return FAIL;
+ }
+
+ host->hostid = 0;
+
+ result = DBselect("%s", sql);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ if (0 != host->hostid)
+ {
+ switch (event->source)
+ {
+ case EVENT_SOURCE_TRIGGERS:
+ zbx_strlcpy(error, "Too many hosts in a trigger expression", max_error_len);
+ break;
+ case EVENT_SOURCE_DISCOVERY:
+ zbx_strlcpy(error, "Too many hosts with same IP addresses", max_error_len);
+ break;
+ }
+ ret = FAIL;
+ break;
+ }
+
+ ZBX_STR2UINT64(host->hostid, row[0]);
+ ZBX_DBROW2UINT64(host->proxy_hostid, row[1]);
+ strscpy(host->host, row[2]);
+ ZBX_STR2UCHAR(host->tls_connect, row[3]);
+
+#ifdef HAVE_OPENIPMI
+ host->ipmi_authtype = (signed char)atoi(row[4]);
+ host->ipmi_privilege = (unsigned char)atoi(row[5]);
+ strscpy(host->ipmi_username, row[6]);
+ strscpy(host->ipmi_password, row[7]);
+#endif
+#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
+ strscpy(host->tls_issuer, row[4 + ZBX_IPMI_FIELDS_NUM]);
+ strscpy(host->tls_subject, row[5 + ZBX_IPMI_FIELDS_NUM]);
+ strscpy(host->tls_psk_identity, row[6 + ZBX_IPMI_FIELDS_NUM]);
+ strscpy(host->tls_psk, row[7 + ZBX_IPMI_FIELDS_NUM]);
+#endif
+ }
+ DBfree_result(result);
+
+ if (FAIL == ret)
+ {
+ host->hostid = 0;
+ *host->host = '\0';
+ }
+ else if (0 == host->hostid)
+ {
+ *host->host = '\0';
+
+ zbx_strlcpy(error, "Cannot find a corresponding host", max_error_len);
+ ret = FAIL;
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+#undef ZBX_IPMI_FIELDS_NUM
diff --git a/src/libs/zbxserver/get_host_from_event.h b/src/libs/zbxserver/get_host_from_event.h
new file mode 100644
index 00000000000..cacf6dd8e2b
--- /dev/null
+++ b/src/libs/zbxserver/get_host_from_event.h
@@ -0,0 +1,25 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_GET_HOST_FROM_EVENT_H
+#define ZABBIX_GET_HOST_FROM_EVENT_H
+
+int get_host_from_event(const DB_EVENT *event, DC_HOST *host, char *error, size_t max_error_len);
+
+#endif
diff --git a/src/libs/zbxserver/zabbix_users.c b/src/libs/zbxserver/zabbix_users.c
new file mode 100644
index 00000000000..911c789dd10
--- /dev/null
+++ b/src/libs/zbxserver/zabbix_users.c
@@ -0,0 +1,73 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "db.h"
+#include "zabbix_users.h"
+
+/******************************************************************************
+ * *
+ * Function: check_perm2system *
+ * *
+ * Purpose: Check user permissions to access system *
+ * *
+ * Parameters: userid - user ID *
+ * *
+ * Return value: SUCCEED - access allowed, FAIL - otherwise *
+ * *
+ ******************************************************************************/
+int check_perm2system(zbx_uint64_t userid)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ int res = SUCCEED;
+
+ result = DBselect(
+ "select count(*)"
+ " from usrgrp g,users_groups ug"
+ " where ug.userid=" ZBX_FS_UI64
+ " and g.usrgrpid=ug.usrgrpid"
+ " and g.users_status=%d",
+ userid, GROUP_STATUS_DISABLED);
+
+ if (NULL != (row = DBfetch(result)) && SUCCEED != DBis_null(row[0]) && atoi(row[0]) > 0)
+ res = FAIL;
+
+ DBfree_result(result);
+
+ return res;
+}
+
+char *get_user_timezone(zbx_uint64_t userid)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ char *user_timezone;
+
+ result = DBselect("select timezone from users where userid=" ZBX_FS_UI64, userid);
+
+ if (NULL != (row = DBfetch(result)))
+ user_timezone = zbx_strdup(NULL, row[0]);
+ else
+ user_timezone = NULL;
+
+ DBfree_result(result);
+
+ return user_timezone;
+}
diff --git a/src/libs/zbxserver/zabbix_users.h b/src/libs/zbxserver/zabbix_users.h
new file mode 100644
index 00000000000..1439e66e518
--- /dev/null
+++ b/src/libs/zbxserver/zabbix_users.h
@@ -0,0 +1,26 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_ZABBIX_USERS_H_
+#define ZABBIX_ZABBIX_USERS_H_
+
+int check_perm2system(zbx_uint64_t userid);
+char *get_user_timezone(zbx_uint64_t userid);
+
+#endif /* ZABBIX_ZABBIX_USERS_H_ */
diff --git a/src/libs/zbxsys/threads.c b/src/libs/zbxsys/threads.c
index 7272dba10a2..f9e59941a53 100644
--- a/src/libs/zbxsys/threads.c
+++ b/src/libs/zbxsys/threads.c
@@ -188,8 +188,15 @@ int zbx_thread_wait(ZBX_THREAD_HANDLE thread)
status = dwstatus;
#else /* not _WINDOWS */
+ pid_t pid;
- if (0 >= waitpid(thread, &status, 0))
+ do
+ {
+ pid = waitpid(thread, &status, 0);
+ }
+ while (pid == -1 && EINTR == errno);
+
+ if (0 >= pid)
{
zbx_error("Error waiting for process with PID %d: %s", (int)thread, zbx_strerror(errno));
return ZBX_THREAD_ERROR;
diff --git a/src/zabbix_agent/logfiles/logfiles.c b/src/zabbix_agent/logfiles/logfiles.c
index 667ce6ec8ce..94cb722d0d8 100644
--- a/src/zabbix_agent/logfiles/logfiles.c
+++ b/src/zabbix_agent/logfiles/logfiles.c
@@ -218,7 +218,7 @@ static int split_filename(const char *filename, char **directory, char **filenam
if (0 == S_ISDIR(buf.st_mode))
{
- *err_msg = zbx_dsprintf(*err_msg, "Base path \"%s\" is not a directory.", *directory);
+ *err_msg = zbx_dsprintf(*err_msg, "Base path \"%s\" is not a directory.", ZBX_NULL2STR(*directory));
zbx_free(*directory);
zbx_free(*filename_regexp);
goto out;
@@ -595,6 +595,8 @@ static int examine_md5_and_place(const md5_byte_t *buf1, const md5_byte_t *buf2,
* use_ino - [IN] 0 - do not use inodes in comparison, *
* 1 - use up to 64-bit inodes in comparison, *
* 2 - use 128-bit inodes in comparison. *
+ * new_files - [IN] new file list *
+ * num_new - [IN] number of elements in the new file list *
* err_msg - [IN/OUT] error message why an item became *
* NOTSUPPORTED *
* *
@@ -610,9 +612,10 @@ static int examine_md5_and_place(const md5_byte_t *buf1, const md5_byte_t *buf2,
* *
******************************************************************************/
static int is_same_file_logcpt(const struct st_logfile *old_file, const struct st_logfile *new_file, int use_ino,
- char **err_msg)
+ const struct st_logfile *new_files, int num_new, char **err_msg)
{
- int is_same_place;
+ int is_same_place, ret = ZBX_SAME_FILE_NO, found_matching_md5 = 0, same_name_in_new_list = 0, i, f;
+ md5_byte_t md5tmp[MD5_DIGEST_SIZE];
if (old_file->mtime > new_file->mtime)
return ZBX_SAME_FILE_NO;
@@ -631,47 +634,102 @@ static int is_same_file_logcpt(const struct st_logfile *old_file, const struct s
is_same_place);
}
- if (0 < old_file->md5size && 0 < new_file->md5size)
+ if (0 == old_file->md5size || 0 == new_file->md5size)
+ return ZBX_SAME_FILE_NO;
+
+ /* MD5 sums have been calculated from initial blocks of different sizes */
+
+ if (old_file->md5size < new_file->md5size)
{
- /* MD5 sums have been calculated from initial blocks of different sizes */
+ if (-1 == (f = open_file_helper(new_file->filename, err_msg)))
+ return ZBX_SAME_FILE_ERROR;
- const struct st_logfile *p_smaller, *p_larger;
- int f, ret;
- md5_byte_t md5tmp[MD5_DIGEST_SIZE];
+ if (SUCCEED == file_start_md5(f, old_file->md5size, md5tmp, new_file->filename, err_msg))
+ ret = examine_md5_and_place(old_file->md5buf, md5tmp, sizeof(md5tmp), is_same_place);
+ else
+ ret = ZBX_SAME_FILE_ERROR;
- if (old_file->md5size < new_file->md5size)
+ if (0 != close(f) && ZBX_SAME_FILE_ERROR != ret)
{
- p_smaller = old_file;
- p_larger = new_file;
+ *err_msg = zbx_dsprintf(*err_msg, "Cannot close file \"%s\": %s",
+ new_file->filename, zbx_strerror(errno));
+ ret = ZBX_SAME_FILE_ERROR;
}
- else
+
+ return ret;
+ }
+
+ /* old_file->md5size > new_file->md5size. */
+
+ /* Now it is necessary to read the first 'new_file->md5size' bytes of */
+ /* the old file to calculate MD5 sum to compare. Unfortunately we */
+ /* cannot reliably use 'old_file->filename' to open the file because: */
+ /* - being from the old list it might be no longer available, */
+ /* - it can have a different name in the new file list; */
+ /* - 'old_file->filename' can be the same as 'new_file->filename' */
+ /* (see ZBX-18883) making comparison pointless. */
+
+ for (i = 0; i < num_new; i++)
+ {
+ if ((zbx_uint64_t)new_file->md5size > new_files[i].size)
+ continue;
+
+ if (0 == strcmp(old_file->filename, new_file->filename) ||
+ 0 == strcmp(new_files[i].filename, new_file->filename)) /* do not compare with self */
{
- p_smaller = new_file;
- p_larger = old_file;
+ same_name_in_new_list = 1;
+ continue;
}
- if (-1 == (f = open_file_helper(p_larger->filename, err_msg)))
+ if (-1 == (f = open_file_helper(new_files[i].filename, err_msg)))
return ZBX_SAME_FILE_ERROR;
- if (SUCCEED == file_start_md5(f, p_smaller->md5size, md5tmp, p_larger->filename, err_msg))
- ret = examine_md5_and_place(p_smaller->md5buf, md5tmp, sizeof(md5tmp), is_same_place);
+ if (SUCCEED == file_start_md5(f, new_file->md5size, md5tmp, new_files[i].filename, err_msg))
+ {
+ ret = examine_md5_and_place(new_file->md5buf, md5tmp, sizeof(md5tmp),
+ compare_file_places(old_file, new_files + i, use_ino));
+
+ if (ZBX_SAME_FILE_YES == ret || ZBX_SAME_FILE_COPY == ret)
+ found_matching_md5 = 1;
+ }
else
ret = ZBX_SAME_FILE_ERROR;
- if (0 != close(f))
+ if (0 != close(f) && ZBX_SAME_FILE_ERROR != ret)
{
- if (ZBX_SAME_FILE_ERROR != ret)
- {
- *err_msg = zbx_dsprintf(*err_msg, "Cannot close file \"%s\": %s", p_larger->filename,
- zbx_strerror(errno));
- ret = ZBX_SAME_FILE_ERROR;
- }
+ *err_msg = zbx_dsprintf(*err_msg, "Cannot close file \"%s\": %s", new_files[i].filename,
+ zbx_strerror(errno));
+ ret = ZBX_SAME_FILE_ERROR;
}
- return ret;
+ if (0 != found_matching_md5)
+ break;
}
- return ZBX_SAME_FILE_NO;
+ if (0 == found_matching_md5 && 0 == same_name_in_new_list)
+ {
+ /* last try - opening file with the name from the old list */
+
+ if (-1 == (f = open_file_helper(old_file->filename, err_msg)))
+ return ZBX_SAME_FILE_NO; /* not an error if it is no longer available */
+
+ if (SUCCEED == file_start_md5(f, new_file->md5size, md5tmp, old_file->filename, err_msg))
+ {
+ ret = examine_md5_and_place(new_file->md5buf, md5tmp, sizeof(md5tmp),
+ compare_file_places(old_file, new_file, use_ino));
+ }
+ else
+ ret = ZBX_SAME_FILE_NO;
+
+ if (0 != close(f))
+ {
+ *err_msg = zbx_dsprintf(*err_msg, "Cannot close file \"%s\": %s", old_file->filename,
+ zbx_strerror(errno));
+ ret = ZBX_SAME_FILE_ERROR;
+ }
+ }
+
+ return ret;
}
/******************************************************************************
@@ -688,6 +746,8 @@ static int is_same_file_logcpt(const struct st_logfile *old_file, const struct s
* 1 - use up to 64-bit inodes in comparison, *
* 2 - use 128-bit inodes in comparison. *
* options - [IN] log rotation options *
+ * new_files - [IN] new file list *
+ * num_new - [IN] number of elements in the new file list *
* err_msg - [IN/OUT] error message why an item became *
* NOTSUPPORTED *
* *
@@ -704,10 +764,10 @@ static int is_same_file_logcpt(const struct st_logfile *old_file, const struct s
* *
******************************************************************************/
static int is_same_file_logrt(const struct st_logfile *old_file, const struct st_logfile *new_file, int use_ino,
- zbx_log_rotation_options_t options, char **err_msg)
+ zbx_log_rotation_options_t options, const struct st_logfile *new_files, int num_new, char **err_msg)
{
if (ZBX_LOG_ROTATION_LOGCPT == options)
- return is_same_file_logcpt(old_file, new_file, use_ino, err_msg);
+ return is_same_file_logcpt(old_file, new_file, use_ino, new_files, num_new, err_msg);
if (ZBX_FILE_PLACE_OTHER == compare_file_places(old_file, new_file, use_ino))
{
@@ -1187,7 +1247,8 @@ static char *create_old2new_and_copy_of(zbx_log_rotation_options_t rotation_type
{
for (j = 0; j < num_new; j++)
{
- switch (is_same_file_logrt(old_files + i, new_files + j, use_ino, rotation_type, err_msg))
+ switch (is_same_file_logrt(old_files + i, new_files + j, use_ino, rotation_type, new_files,
+ num_new, err_msg))
{
case ZBX_SAME_FILE_NO:
p[j] = '0';
@@ -1821,7 +1882,7 @@ static char *buf_find_newline(char *p, char **p_next, const char *p_end, const c
/******************************************************************************
* *
- * Function: update_new_list_from_old *
+ * Function: zbx_read2 *
* *
* Comments: Thread-safe *
* *
@@ -2182,10 +2243,10 @@ static int process_log(unsigned char flags, const char *filename, zbx_uint64_t *
const char *server, unsigned short port, const char *hostname, const char *key,
zbx_uint64_t *processed_bytes, zbx_uint64_t seek_offset)
{
- int f, ret = FAIL;
+ int f, ret = FAIL;
- zabbix_log(LOG_LEVEL_DEBUG, "In %s() filename:'%s' lastlogsize:" ZBX_FS_UI64 " mtime:%d",
- __func__, filename, *lastlogsize, NULL != mtime ? *mtime : 0);
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() filename:'%s' lastlogsize:" ZBX_FS_UI64 " mtime:%d seek_offset:"
+ ZBX_FS_UI64, __func__, filename, *lastlogsize, NULL != mtime ? *mtime : 0, seek_offset);
if (-1 == (f = open_file_helper(filename, err_msg)))
goto out;
@@ -3150,7 +3211,36 @@ int process_logrt(unsigned char flags, const char *filename, zbx_uint64_t *lastl
*mtime = logfiles[i].mtime;
if (start_idx != i)
+ {
*lastlogsize = logfiles[i].processed_size;
+ }
+ else
+ {
+ /* When agent starts it can receive from server an out-of-date lastlogsize value, */
+ /* larger than current log file size. */
+
+ if (*lastlogsize > logfiles[i].size)
+ {
+ int j, found = 0;
+
+ /* check if there are other log files with the same mtime and size */
+ /* greater or equal to lastlogsize */
+ for (j = 0; j < logfiles_num; j++)
+ {
+ if (i == j || logfiles[i].mtime != logfiles[j].mtime)
+ continue;
+
+ if (*lastlogsize <= logfiles[j].size)
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (0 == found)
+ *lastlogsize = logfiles[i].processed_size;
+ }
+ }
if (0 == *skip_old_data)
{
diff --git a/src/zabbix_java/src/com/zabbix/gateway/GeneralInformation.java b/src/zabbix_java/src/com/zabbix/gateway/GeneralInformation.java
index 5a47e895953..2747f6dcb39 100644
--- a/src/zabbix_java/src/com/zabbix/gateway/GeneralInformation.java
+++ b/src/zabbix_java/src/com/zabbix/gateway/GeneralInformation.java
@@ -22,9 +22,9 @@ package com.zabbix.gateway;
class GeneralInformation
{
static final String APPLICATION_NAME = "Zabbix Java Gateway";
- static final String REVISION_DATE = "1 March 2021";
+ static final String REVISION_DATE = "26 April 2021";
static final String REVISION = "{ZABBIX_REVISION}";
- static final String VERSION = "5.4.0beta2";
+ static final String VERSION = "5.4.0rc1";
static void printVersion()
{
diff --git a/src/zabbix_proxy/Makefile.am b/src/zabbix_proxy/Makefile.am
index 73fe6d32628..53739114537 100644
--- a/src/zabbix_proxy/Makefile.am
+++ b/src/zabbix_proxy/Makefile.am
@@ -14,7 +14,6 @@ noinst_LIBRARIES = libzbxproxy.a
libzbxproxy_a_SOURCES = \
events.c \
proxy_lld.c \
- proxy_alerter_protocol.c\
servercomms.c \
servercomms.h
@@ -34,6 +33,7 @@ zabbix_proxy_LDADD = \
$(top_builddir)/src/zabbix_server/poller/libzbxpoller.a \
$(top_builddir)/src/zabbix_server/poller/libzbxpoller_proxy.a \
$(top_builddir)/src/zabbix_server/trapper/libzbxtrapper.a \
+ $(top_builddir)/src/zabbix_server/trapper/libzbxtrapper_proxy.a \
$(top_builddir)/src/zabbix_server/snmptrapper/libzbxsnmptrapper.a \
$(top_builddir)/src/zabbix_server/odbc/libzbxodbc.a \
datasender/libzbxdatasender.a \
@@ -56,6 +56,7 @@ zabbix_proxy_LDADD = \
$(top_builddir)/src/libs/zbxmemory/libzbxmemory.a \
$(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \
$(top_builddir)/src/libs/zbxself/libzbxself.a \
+ $(top_builddir)/src/libs/zbxself/libzbxself_proxy.a \
$(top_builddir)/src/libs/zbxnix/libzbxnix.a \
$(top_builddir)/src/libs/zbxipcservice/libzbxipcservice.a \
$(top_builddir)/src/libs/zbxsys/libzbxsys.a \
diff --git a/src/zabbix_proxy/datasender/datasender.c b/src/zabbix_proxy/datasender/datasender.c
index 363753c394d..f9ad87ccc8d 100644
--- a/src/zabbix_proxy/datasender/datasender.c
+++ b/src/zabbix_proxy/datasender/datasender.c
@@ -100,7 +100,7 @@ static int proxy_data_sender(int *more, int now, int *hist_upload_state)
char *error = NULL;
zbx_vector_ptr_t tasks;
- zabbix_log(3, "In %s()", __func__);
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
*more = ZBX_PROXY_DATA_DONE;
zbx_json_init(&j, 16 * ZBX_KIBIBYTE);
@@ -182,8 +182,11 @@ static int proxy_data_sender(int *more, int now, int *hist_upload_state)
if (SUCCEED != upload_state)
{
*more = ZBX_PROXY_DATA_DONE;
- zabbix_log(LOG_LEVEL_WARNING, "cannot send proxy data to server at \"%s\": %s",
- sock.peer, error);
+ if (ZBX_PROXY_UPLOAD_DISABLED != *hist_upload_state)
+ {
+ zabbix_log(LOG_LEVEL_WARNING, "cannot send proxy data to server at \"%s\": %s",
+ sock.peer, error);
+ }
zbx_free(error);
}
else
diff --git a/src/zabbix_proxy/proxy_alerter_protocol.c b/src/zabbix_proxy/proxy_alerter_protocol.c
deleted file mode 100644
index 134d8c31cec..00000000000
--- a/src/zabbix_proxy/proxy_alerter_protocol.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
-** Zabbix
-** Copyright (C) 2001-2021 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-#include "common.h"
-#include "../zabbix_server/alerter/alerter_protocol.h"
-
-zbx_uint32_t zbx_alerter_serialize_alert_send(unsigned char **data, zbx_uint64_t mediatypeid, unsigned char type,
- const char *smtp_server, const char *smtp_helo, const char *smtp_email, const char *exec_path,
- const char *gsm_modem, const char *username, const char *passwd, unsigned short smtp_port,
- unsigned char smtp_security, unsigned char smtp_verify_peer, unsigned char smtp_verify_host,
- unsigned char smtp_authentication, const char *exec_params, int maxsessions, int maxattempts,
- const char *attempt_interval, unsigned char content_type, const char *script, const char *timeout,
- const char *sendto, const char *subject, const char *message, const char *params)
-{
- ZBX_UNUSED(data);
- ZBX_UNUSED(mediatypeid);
- ZBX_UNUSED(type);
- ZBX_UNUSED(smtp_server);
- ZBX_UNUSED(smtp_helo);
- ZBX_UNUSED(smtp_email);
- ZBX_UNUSED(exec_path);
- ZBX_UNUSED(gsm_modem);
- ZBX_UNUSED(username);
- ZBX_UNUSED(passwd);
- ZBX_UNUSED(smtp_port);
- ZBX_UNUSED(smtp_security);
- ZBX_UNUSED(smtp_verify_peer);
- ZBX_UNUSED(smtp_verify_host);
- ZBX_UNUSED(smtp_authentication);
- ZBX_UNUSED(exec_params);
- ZBX_UNUSED(maxsessions);
- ZBX_UNUSED(maxattempts);
- ZBX_UNUSED(attempt_interval);
- ZBX_UNUSED(content_type);
- ZBX_UNUSED(script);
- ZBX_UNUSED(timeout);
- ZBX_UNUSED(sendto);
- ZBX_UNUSED(subject);
- ZBX_UNUSED(message);
- ZBX_UNUSED(params);
-
- THIS_SHOULD_NEVER_HAPPEN;
-
- return 0;
-}
-
-void zbx_alerter_deserialize_result(const unsigned char *data, char **value, int *errcode, char **error,
- char **debug)
-{
- ZBX_UNUSED(value);
- ZBX_UNUSED(data);
- ZBX_UNUSED(errcode);
- ZBX_UNUSED(error);
- ZBX_UNUSED(debug);
-
- THIS_SHOULD_NEVER_HAPPEN;
-}
diff --git a/src/zabbix_proxy/proxy_lld.c b/src/zabbix_proxy/proxy_lld.c
index 1d61a9bc18e..0de6c424f16 100644
--- a/src/zabbix_proxy/proxy_lld.c
+++ b/src/zabbix_proxy/proxy_lld.c
@@ -21,9 +21,10 @@
#include "sysinfo.h"
#include "zbxlld.h"
-void zbx_lld_process_agent_result(zbx_uint64_t itemid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error)
+void zbx_lld_process_agent_result(zbx_uint64_t itemid, zbx_uint64_t hostid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error)
{
ZBX_UNUSED(itemid);
+ ZBX_UNUSED(hostid);
ZBX_UNUSED(result);
ZBX_UNUSED(ts);
ZBX_UNUSED(error);
diff --git a/src/zabbix_proxy/taskmanager/taskmanager.c b/src/zabbix_proxy/taskmanager/taskmanager.c
index ccaa882e065..2531e0aaf5d 100644
--- a/src/zabbix_proxy/taskmanager/taskmanager.c
+++ b/src/zabbix_proxy/taskmanager/taskmanager.c
@@ -134,8 +134,8 @@ static int tm_execute_remote_command(zbx_uint64_t taskid, int clock, int ttl, in
ZBX_DBROW2UINT64(alertid, row[11]);
}
- if (SUCCEED != (ret = zbx_script_execute(&script, &host, NULL, NULL, ZBX_SCRIPT_CTX_HOST,
- 0 == alertid ? &info : NULL, error, sizeof(error), NULL)))
+ if (SUCCEED != (ret = zbx_script_execute(&script, &host, NULL, 0 == alertid ? &info : NULL,
+ error, sizeof(error), NULL)))
{
task->data = zbx_tm_remote_command_result_create(parent_taskid, ret, error);
}
diff --git a/src/zabbix_server/Makefile.am b/src/zabbix_server/Makefile.am
index 25a12848a4c..fdd79113ac5 100644
--- a/src/zabbix_server/Makefile.am
+++ b/src/zabbix_server/Makefile.am
@@ -22,7 +22,8 @@ SUBDIRS = \
scripts \
preprocessor \
availability \
- lld
+ lld \
+ reporter
sbin_PROGRAMS = zabbix_server
@@ -54,6 +55,8 @@ zabbix_server_LDADD = \
housekeeper/libzbxhousekeeper.a \
timer/libzbxtimer.a \
trapper/libzbxtrapper.a \
+ trapper/libzbxtrapper_server.a \
+ reporter/libzbxreporter.a \
snmptrapper/libzbxsnmptrapper.a \
httppoller/libzbxhttppoller.a \
escalator/libzbxescalator.a \
@@ -80,6 +83,7 @@ zabbix_server_LDADD = \
$(top_builddir)/src/libs/zbxmemory/libzbxmemory.a \
$(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \
$(top_builddir)/src/libs/zbxself/libzbxself.a \
+ $(top_builddir)/src/libs/zbxself/libzbxself_server.a \
$(top_builddir)/src/libs/zbxnix/libzbxnix.a \
$(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_builddir)/src/libs/zbxsys/libzbxsys.a \
diff --git a/src/zabbix_server/actions.c b/src/zabbix_server/actions.c
index b9d7df1ee83..110b74a5161 100644
--- a/src/zabbix_server/actions.c
+++ b/src/zabbix_server/actions.c
@@ -821,101 +821,6 @@ static int check_acknowledged_condition(const zbx_vector_ptr_t *esc_events, zbx_
/******************************************************************************
* *
- * Function: check_application_condition *
- * *
- * Purpose: check application condition *
- * *
- * Parameters: esc_events - [IN] events to check *
- * condition - [IN/OUT] condition for matching, outputs *
- * event ids that match condition *
- * *
- * Return value: SUCCEED - supported operator *
- * NOTSUPPORTED - not supported operator *
- * *
- ******************************************************************************/
-static int check_application_condition(const zbx_vector_ptr_t *esc_events, zbx_condition_t *condition)
-{
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- DB_RESULT result;
- DB_ROW row;
- zbx_vector_uint64_t objectids;
- zbx_uint64_t objectid;
- int i;
-
- if (CONDITION_OPERATOR_EQUAL != condition->op && CONDITION_OPERATOR_LIKE != condition->op &&
- CONDITION_OPERATOR_NOT_LIKE != condition->op)
- {
- return NOTSUPPORTED;
- }
-
- zbx_vector_uint64_create(&objectids);
-
- get_object_ids(esc_events, &objectids);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select distinct t.triggerid,a.name"
- " from applications a,items_applications i,functions f,triggers t"
- " where a.applicationid=i.applicationid"
- " and i.itemid=f.itemid"
- " and f.triggerid=t.triggerid"
- " and");
-
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "t.triggerid", objectids.values, objectids.values_num);
-
- result = DBselect("%s", sql);
-
- switch (condition->op)
- {
- case CONDITION_OPERATOR_EQUAL:
- while (NULL != (row = DBfetch(result)))
- {
- if (0 == strcmp(row[1], condition->value))
- {
- ZBX_STR2UINT64(objectid, row[0]);
- add_condition_match(esc_events, condition, objectid, EVENT_OBJECT_TRIGGER);
- }
- }
- break;
- case CONDITION_OPERATOR_LIKE:
- while (NULL != (row = DBfetch(result)))
- {
- if (NULL != strstr(row[1], condition->value))
- {
- ZBX_STR2UINT64(objectid, row[0]);
- add_condition_match(esc_events, condition, objectid, EVENT_OBJECT_TRIGGER);
- }
- }
- break;
- case CONDITION_OPERATOR_NOT_LIKE:
- while (NULL != (row = DBfetch(result)))
- {
- if (NULL != strstr(row[1], condition->value))
- {
- ZBX_STR2UINT64(objectid, row[0]);
-
- if (FAIL != (i = zbx_vector_uint64_search(&objectids, objectid,
- ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
- {
- zbx_vector_uint64_remove_noorder(&objectids, i);
- }
- }
- }
-
- for (i = 0; i < objectids.values_num; i++)
- add_condition_match(esc_events, condition, objectids.values[i], EVENT_OBJECT_TRIGGER);
- break;
- }
- DBfree_result(result);
-
- zbx_vector_uint64_destroy(&objectids);
- zbx_free(sql);
-
- return SUCCEED;
-}
-
-/******************************************************************************
- * *
* Function: check_condition_event_tag *
* *
* Purpose: check condition event tag *
@@ -1043,9 +948,6 @@ static void check_trigger_condition(const zbx_vector_ptr_t *esc_events, zbx_cond
case CONDITION_TYPE_EVENT_ACKNOWLEDGED:
ret = check_acknowledged_condition(esc_events, condition);
break;
- case CONDITION_TYPE_APPLICATION:
- ret = check_application_condition(esc_events, condition);
- break;
case CONDITION_TYPE_EVENT_TAG:
check_condition_event_tag(esc_events, condition);
ret = SUCCEED;
@@ -2611,124 +2513,6 @@ static int check_intern_host_condition(const zbx_vector_ptr_t *esc_events, zbx_c
/******************************************************************************
* *
- * Function: check_intern_application_condition *
- * *
- * Purpose: check application condition for internal events *
- * *
- * Parameters: esc_events - [IN] events to check *
- * condition - [IN/OUT] condition for matching, outputs *
- * event ids that match condition *
- * *
- * Return value: SUCCEED - supported operator *
- * NOTSUPPORTED - not supported operator *
- * *
- ******************************************************************************/
-static int check_intern_application_condition(const zbx_vector_ptr_t *esc_events, zbx_condition_t *condition)
-{
- char *sql = NULL;
- size_t sql_alloc = 0;
- DB_RESULT result;
- DB_ROW row;
- int objects[3] = {EVENT_OBJECT_TRIGGER, EVENT_OBJECT_ITEM, EVENT_OBJECT_LLDRULE}, i, j;
- zbx_vector_uint64_t objectids[3];
- zbx_uint64_t objectid;
-
- if (CONDITION_OPERATOR_EQUAL != condition->op && CONDITION_OPERATOR_LIKE != condition->op &&
- CONDITION_OPERATOR_NOT_LIKE != condition->op)
- return NOTSUPPORTED;
-
- for (i = 0; i < (int)ARRSIZE(objects); i++)
- zbx_vector_uint64_create(&objectids[i]);
-
- get_object_ids_internal(esc_events, objectids, objects, (int)ARRSIZE(objects));
-
- for (i = 0; i < (int)ARRSIZE(objects); i++)
- {
- size_t sql_offset = 0;
-
- if (0 == objectids[i].values_num)
- continue;
-
- if (EVENT_OBJECT_TRIGGER == objects[i])
- {
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select distinct t.triggerid,a.name"
- " from applications a,items_applications i,functions f,triggers t"
- " where a.applicationid=i.applicationid"
- " and i.itemid=f.itemid"
- " and f.triggerid=t.triggerid"
- " and");
-
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "t.triggerid",
- objectids[i].values, objectids[i].values_num);
- }
- else /* EVENT_OBJECT_ITEM, EVENT_OBJECT_LLDRULE */
- {
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select distinct i.itemid,a.name"
- " from applications a,items_applications i"
- " where a.applicationid=i.applicationid"
- " and");
-
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.itemid",
- objectids[i].values, objectids[i].values_num);
- }
-
- result = DBselect("%s", sql);
-
- switch (condition->op)
- {
- case CONDITION_OPERATOR_EQUAL:
- while (NULL != (row = DBfetch(result)))
- {
- if (0 == strcmp(row[1], condition->value))
- {
- ZBX_STR2UINT64(objectid, row[0]);
- add_condition_match(esc_events, condition, objectid, objects[i]);
- }
- }
- break;
- case CONDITION_OPERATOR_LIKE:
- while (NULL != (row = DBfetch(result)))
- {
- if (NULL != strstr(row[1], condition->value))
- {
- ZBX_STR2UINT64(objectid, row[0]);
- add_condition_match(esc_events, condition, objectid, objects[i]);
- }
- }
- break;
- case CONDITION_OPERATOR_NOT_LIKE:
- while (NULL != (row = DBfetch(result)))
- {
- if (NULL != strstr(row[1], condition->value))
- {
- ZBX_STR2UINT64(objectid, row[0]);
- if (FAIL != (j = zbx_vector_uint64_search(&objectids[i], objectid,
- ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
- {
- zbx_vector_uint64_remove_noorder(&objectids[i], j);
- }
- }
- }
-
- for (j = 0; j < objectids[i].values_num; j++)
- add_condition_match(esc_events, condition, objectids[i].values[j], objects[i]);
- break;
- }
- DBfree_result(result);
- }
-
- for (i = 0; i < (int)ARRSIZE(objects); i++)
- zbx_vector_uint64_destroy(&objectids[i]);
-
- zbx_free(sql);
-
- return SUCCEED;
-}
-
-/******************************************************************************
- * *
* Function: check_internal_condition *
* *
* Purpose: check if internal event matches single condition *
@@ -2759,8 +2543,13 @@ static void check_internal_condition(const zbx_vector_ptr_t *esc_events, zbx_con
case CONDITION_TYPE_HOST:
ret = check_intern_host_condition(esc_events, condition);
break;
- case CONDITION_TYPE_APPLICATION:
- ret = check_intern_application_condition(esc_events, condition);
+ case CONDITION_TYPE_EVENT_TAG:
+ check_condition_event_tag(esc_events, condition);
+ ret = SUCCEED;
+ break;
+ case CONDITION_TYPE_EVENT_TAG_VALUE:
+ check_condition_event_tag_value(esc_events,condition);
+ ret = SUCCEED;
break;
default:
ret = FAIL;
diff --git a/src/zabbix_server/alerter/alert_manager.c b/src/zabbix_server/alerter/alert_manager.c
index 7802cb01c7e..a0f3ad5f3c0 100644
--- a/src/zabbix_server/alerter/alert_manager.c
+++ b/src/zabbix_server/alerter/alert_manager.c
@@ -57,6 +57,8 @@
#define ZBX_ALERT_RESULT_BATCH_SIZE 1000
+#define ZBX_MEDIA_CONTENT_TYPE_DEFAULT 255
+
extern unsigned char process_type, program_type;
extern int server_num, process_num;
@@ -82,7 +84,7 @@ extern char *CONFIG_ALERT_SCRIPTS_PATH;
*
* When taking the next alert to send the following actions are done:
* 1) take the next media type object from media type queue
- * 2) take the next alert pool object from media type alertpool queue
+am * 2) take the next alert pool object from media type alertpool queue
* 3) take the next alert from alert pool alerts queue
* 4) if media type maxsessions limit has not reached, put the media type object back in queue
*
@@ -97,26 +99,29 @@ extern char *CONFIG_ALERT_SCRIPTS_PATH;
* was not removed
*/
+typedef char * zbx_shared_str_t;
+
/* alert data */
typedef struct
{
- zbx_uint64_t alertid;
- zbx_uint64_t mediatypeid;
- zbx_uint64_t alertpoolid;
- zbx_uint64_t eventid;
+ zbx_uint64_t alertid;
+ zbx_uint64_t mediatypeid;
+ zbx_uint64_t alertpoolid;
+ zbx_uint64_t eventid;
/* the problem event id for recovery events */
- zbx_uint64_t p_eventid;
- int nextsend;
+ zbx_uint64_t p_eventid;
+ int nextsend;
/* alert data */
- char *sendto;
- char *subject;
- char *message;
- char *params;
- int status;
- int retries;
-
- int objectid;
+ char *sendto;
+ char *subject;
+ zbx_shared_str_t message;
+ char *params;
+ unsigned char content_type;
+ int status;
+ int retries;
+
+ int objectid;
}
zbx_am_alert_t;
@@ -142,6 +147,18 @@ typedef struct
}
zbx_am_alertpool_t;
+/* report data */
+typedef struct
+{
+ char *subject;
+ char *message;
+ char *content;
+ char *content_name;
+ char *content_type;
+ zbx_uint32_t content_size;
+}
+zbx_am_dispatch_t;
+
/* alerter data */
typedef struct
{
@@ -152,6 +169,7 @@ typedef struct
}
zbx_am_alerter_t;
+
/* alert manager data */
typedef struct
{
@@ -191,6 +209,7 @@ typedef struct
}
zbx_am_t;
+
/* alerters client index hashset support */
static zbx_hash_t alerter_hash_func(const void *d)
@@ -293,6 +312,65 @@ static int am_mediatype_queue_compare(const void *d1, const void *d2)
/******************************************************************************
* *
+ * reference counted strings *
+ * *
+ ******************************************************************************/
+
+static zbx_shared_str_t shared_str_new(const char *src)
+{
+ size_t len;
+ char *ptr;
+
+ if (NULL == src)
+ return NULL;
+
+ len = strlen(src);
+ ptr = zbx_malloc(NULL, len + sizeof(zbx_uint32_t) + 1);
+ *((zbx_uint32_t *)ptr) = 0;
+ memcpy(ptr + sizeof(zbx_uint32_t), src, len + 1);
+
+ return ptr + 4;
+}
+
+static zbx_shared_str_t shared_str_addref(zbx_shared_str_t str)
+{
+ if (NULL != str)
+ {
+ zbx_uint32_t *refcount = (zbx_uint32_t *)(str - sizeof(zbx_uint32_t));
+ (*refcount)++;
+ }
+ return str;
+}
+
+static void shared_str_release(zbx_shared_str_t str)
+{
+ if (NULL != str)
+ {
+ zbx_uint32_t *refcount = (zbx_uint32_t *)(str - sizeof(zbx_uint32_t));
+
+ if (0 == --(*refcount))
+ zbx_free(refcount);
+ }
+}
+
+/******************************************************************************
+ * *
+ * Function: am_dispatch_free *
+ * *
+ ******************************************************************************/
+static void am_dispatch_free(zbx_am_dispatch_t *dispatch)
+{
+ zbx_free(dispatch->subject);
+ zbx_free(dispatch->message);
+ zbx_free(dispatch->content);
+ zbx_free(dispatch->content_name);
+ zbx_free(dispatch->content_type);
+ zbx_free(dispatch);
+}
+
+
+/******************************************************************************
+ * *
* Function: am_get_mediatype *
* *
* Purpose: gets media type object *
@@ -378,8 +456,9 @@ static void am_update_mediatype(zbx_am_t *manager, zbx_uint64_t mediatypeid, uns
}
else
{
- /* reset remove flag */
- mediatype->flags = ZBX_AM_MEDIATYPE_FLAG_NONE;
+ /* reset remove flag if normal media type is being added */
+ if (ZBX_AM_MEDIATYPE_FLAG_NONE == flags)
+ mediatype->flags = ZBX_AM_MEDIATYPE_FLAG_NONE;
}
mediatype->type = type;
@@ -680,8 +759,8 @@ static int am_release_alertpool(zbx_am_t *manager, zbx_am_alertpool_t *alertpool
* *
******************************************************************************/
static zbx_am_alert_t *am_create_alert(zbx_uint64_t alertid, zbx_uint64_t mediatypeid, int source, int object,
- zbx_uint64_t objectid, const char *sendto, const char *subject, const char *message, const char *params,
- int status, int retries, int nextsend)
+ zbx_uint64_t objectid, const char *sendto, const char *subject, zbx_shared_str_t message,
+ const char *params, unsigned char content_type, int status, int retries, int nextsend)
{
zbx_am_alert_t *alert;
@@ -690,6 +769,9 @@ static zbx_am_alert_t *am_create_alert(zbx_uint64_t alertid, zbx_uint64_t mediat
alert->mediatypeid = mediatypeid;
alert->alertpoolid = am_calc_alertpoolid(source, object, objectid);
alert->objectid = objectid;
+ alert->content_type = content_type;
+ alert->eventid = 0;
+ alert->p_eventid = 0;
if (NULL != sendto)
alert->sendto = zbx_strdup(NULL, sendto);
@@ -701,10 +783,7 @@ static zbx_am_alert_t *am_create_alert(zbx_uint64_t alertid, zbx_uint64_t mediat
else
alert->subject = NULL;
- if (NULL != message)
- alert->message = zbx_strdup(NULL, message);
- else
- alert->message = NULL;
+ alert->message = shared_str_addref(message);
if (NULL != params)
alert->params = zbx_strdup(NULL, params);
@@ -743,10 +822,14 @@ static zbx_am_alert_t *am_copy_db_alert(zbx_am_db_alert_t *db_alert)
alert->objectid = db_alert->objectid;
alert->eventid = db_alert->eventid;
alert->p_eventid = db_alert->p_eventid;
+ alert->content_type = ZBX_MEDIA_CONTENT_TYPE_DEFAULT;
alert->sendto = db_alert->sendto;
- alert->subject = db_alert-> subject;
- alert->message = db_alert->message;
+ alert->subject = db_alert->subject;
+
+ alert->message = shared_str_addref(shared_str_new(db_alert->message));
+ zbx_free(db_alert->message);
+
alert->params = db_alert->params;
alert->status = db_alert->status;
@@ -771,7 +854,7 @@ static void am_alert_free(zbx_am_alert_t *alert)
{
zbx_free(alert->sendto);
zbx_free(alert->subject);
- zbx_free(alert->message);
+ shared_str_release(alert->message);
zbx_free(alert->params);
zbx_free(alert);
}
@@ -920,7 +1003,6 @@ out:
static void am_alerter_free(zbx_am_alerter_t *alerter)
{
zbx_ipc_client_close(alerter->client);
-
zbx_free(alerter);
}
@@ -1092,8 +1174,8 @@ static void am_queue_watchdog_alerts(zbx_am_t *manager)
zbx_free(am_esc);
}
- alert = am_create_alert(0, media->mediatypeid, 0, 0, 0, media->sendto, alert_subject, alert_message,
- NULL, 0, 0, 0);
+ alert = am_create_alert(0, media->mediatypeid, 0, 0, 0, media->sendto, alert_subject,
+ shared_str_new(alert_message), NULL, mediatype->content_type, 0, 0, 0);
alertpool = am_get_alertpool(manager, alert->mediatypeid, alert->alertpoolid);
alertpool->refcount++;
@@ -1264,8 +1346,8 @@ static void am_external_alert_send_response(const zbx_ipc_service_t *alerter_ser
unsigned char *data;
zbx_uint32_t data_len;
- data_len = zbx_alerter_serialize_result(&data, value, errcode, error, debug);
- zbx_ipc_client_send(client, ZBX_IPC_ALERTER_ALERT, data, data_len);
+ data_len = zbx_alerter_serialize_result_ext(&data, alert->sendto, value, errcode, error, debug);
+ zbx_ipc_client_send(client, ZBX_IPC_ALERTER_SEND_ALERT, data, data_len);
zbx_free(data);
}
else
@@ -1432,6 +1514,7 @@ static int am_process_alert(zbx_am_t *manager, zbx_am_alerter_t *alerter, zbx_am
zbx_uint64_t command, p_eventid;
char *cmd = NULL, *error = NULL;
int ret = FAIL;
+ unsigned char content_type;
zabbix_log(LOG_LEVEL_DEBUG, "%s() alertid:" ZBX_FS_UI64 " mediatypeid:" ZBX_FS_UI64 " alertpoolid:0x"
ZBX_FS_UX64, __func__, alert->alertid, alert->mediatypeid, alert->alertpoolid);
@@ -1458,12 +1541,16 @@ static int am_process_alert(zbx_am_t *manager, zbx_am_alerter_t *alerter, zbx_am
case MEDIA_TYPE_EMAIL:
command = ZBX_IPC_ALERTER_EMAIL;
p_eventid = (0 == alert->p_eventid ? alert->eventid : alert->p_eventid);
+
+ if (ZBX_MEDIA_CONTENT_TYPE_DEFAULT == (content_type = alert->content_type))
+ content_type = mediatype->content_type;
+
data_len = zbx_alerter_serialize_email(&data, alert->alertid, alert->mediatypeid,
p_eventid, alert->sendto, alert->subject, alert->message,
mediatype->smtp_server, mediatype->smtp_port, mediatype->smtp_helo,
mediatype->smtp_email, mediatype->smtp_security, mediatype->smtp_verify_peer,
mediatype->smtp_verify_host, mediatype->smtp_authentication,
- mediatype->username, mediatype->passwd, mediatype->content_type);
+ mediatype->username, mediatype->passwd, content_type);
break;
case MEDIA_TYPE_SMS:
command = ZBX_IPC_ALERTER_SMS;
@@ -1854,7 +1941,7 @@ static void am_process_external_alert_request(zbx_am_t *manager, zbx_uint64_t id
{
zbx_uint64_t mediatypeid;
char *sendto, *subject, *message, *params, *smtp_server, *smtp_helo, *smtp_email, *exec_path,
- *gsm_modem, *username, *passwd,*exec_params,*attempt_interval, *script, *timeout;
+ *gsm_modem, *username, *passwd, *exec_params, *attempt_interval, *script, *timeout;
unsigned short smtp_port;
int maxsessions, maxattempts;
unsigned char type, smtp_security, smtp_verify_peer, smtp_verify_host, smtp_authentication, content_type;
@@ -1875,8 +1962,8 @@ static void am_process_external_alert_request(zbx_am_t *manager, zbx_uint64_t id
smtp_verify_host, smtp_authentication, exec_params, maxsessions, maxattempts,
attempt_interval, content_type, script, timeout, ZBX_AM_MEDIATYPE_FLAG_REMOVE);
- alert = am_create_alert(id, mediatypeid, ALERT_SOURCE_EXTERNAL, 0, id, sendto, subject, message, params, 0, 0,
- 0);
+ alert = am_create_alert(id, mediatypeid, ALERT_SOURCE_EXTERNAL, 0, id, sendto, subject, shared_str_new(message),
+ params, content_type, 0, 0, 0);
if (FAIL == am_queue_alert(manager, alert, 0))
{
@@ -1905,6 +1992,163 @@ static void am_process_external_alert_request(zbx_am_t *manager, zbx_uint64_t id
/******************************************************************************
* *
+ * Function: am_process_begin_dispatch *
+ * *
+ * Purpose: begin file dispatch *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * client - [IN] the connected worker IPC client data *
+ * message - [IN] the received message *
+ * *
+ ******************************************************************************/
+static void am_process_begin_dispatch(zbx_ipc_client_t *client, const unsigned char *data)
+{
+ zbx_am_dispatch_t *dispatch;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() clientid:" ZBX_FS_UI64, __func__, zbx_ipc_client_id(client));
+
+ dispatch = (zbx_am_dispatch_t*) zbx_malloc(NULL, sizeof(zbx_am_dispatch_t));
+ zbx_alerter_deserialize_begin_dispatch(data, &dispatch->subject, &dispatch->message, &dispatch->content_name,
+ &dispatch->content_type, &dispatch->content, &dispatch->content_size);
+
+ zbx_ipc_client_set_userdata(client, dispatch);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s() name:%s content_type:%s size:%u", __func__, dispatch->content_name,
+ dispatch->content_type, dispatch->content_size);
+}
+
+/******************************************************************************
+ * *
+ * Function: am_prepare_dispatch_message *
+ * *
+ * Purpose: prepare message to dispatch by attaching dispatch contents for *
+ * supported media types *
+ * *
+ * Parameters: dispatch - [IN] the dispatch data *
+ * mt - [IN] the media type *
+ * message - [OUT] the message to send *
+ * content_type - [OUT] the message content type *
+ * *
+ ******************************************************************************/
+static void am_prepare_dispatch_message(zbx_am_dispatch_t *dispatch, DB_MEDIATYPE *mt, zbx_shared_str_t *message,
+ unsigned char *content_type)
+{
+ char *body = NULL;
+
+ if (0 != dispatch->content_size)
+ {
+ if (MEDIA_TYPE_EMAIL == mt->type)
+ {
+ body = zbx_email_make_body(dispatch->message, mt->content_type, dispatch->content_name,
+ dispatch->content_type, dispatch->content, dispatch->content_size);
+ *content_type = ZBX_MEDIA_CONTENT_TYPE_MULTI;
+ }
+ }
+
+ if (NULL == body)
+ {
+ *message = shared_str_new(dispatch->message);
+ *content_type = mt->content_type;
+ }
+ else
+ {
+ *message = shared_str_new(body);
+ zbx_free(body);
+ }
+}
+
+/******************************************************************************
+ * *
+ * Function: am_process_send_dispatch *
+ * *
+ * Purpose: send dispatch to the specified media type users *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * client - [IN] the connected worker IPC client *
+ * message - [IN] the received message *
+ * *
+ ******************************************************************************/
+static void am_process_send_dispatch(zbx_am_t *manager, zbx_ipc_client_t *client, const unsigned char *data)
+{
+ int i;
+ zbx_vector_str_t recipients;
+ zbx_am_alert_t *alert;
+ DB_MEDIATYPE mt;
+ zbx_shared_str_t message;
+ unsigned char content_type;
+ zbx_uint64_t id;
+ zbx_am_dispatch_t *dispatch;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() clientid:" ZBX_FS_UI64, __func__, zbx_ipc_client_id(client));
+
+ if (NULL == (dispatch = (zbx_am_dispatch_t *)zbx_ipc_client_get_userdata(client)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+
+ zbx_ipc_client_send(client, ZBX_IPC_ALERTER_ABORT_DISPATCH, NULL, 0);
+ goto out;
+ }
+
+ id = zbx_ipc_client_id(client);
+
+ zbx_vector_str_create(&recipients);
+
+ zbx_alerter_deserialize_send_dispatch(data, &mt, &recipients);
+
+ /* update with initial 'remove' flag so the mediatype is removed */
+ /* if it's not used by other test alerts/dispatches */
+ am_update_mediatype(manager, mt.mediatypeid, mt.type, mt.smtp_server, mt.smtp_helo, mt.smtp_email, mt.exec_path,
+ mt.gsm_modem, mt.username, mt.passwd, mt.smtp_port, mt.smtp_security, mt.smtp_verify_peer,
+ mt.smtp_verify_host, mt.smtp_authentication, mt.exec_params, mt.maxsessions, mt.maxattempts,
+ mt.attempt_interval, mt.content_type, mt.script, mt.timeout, ZBX_AM_MEDIATYPE_FLAG_REMOVE);
+
+ am_prepare_dispatch_message(dispatch, &mt, &message, &content_type);
+
+ for (i = 0; i < recipients.values_num; i++)
+ {
+ alert = am_create_alert(id, mt.mediatypeid, ALERT_SOURCE_EXTERNAL, 0, id, recipients.values[i],
+ dispatch->subject, message, NULL, content_type, 0, 0, 0);
+
+ if (FAIL == am_queue_alert(manager, alert, 0))
+ {
+ am_external_alert_send_response(&manager->ipc, alert, NULL, FAIL, "Media type unavailable",
+ NULL);
+ am_alert_free(alert);
+ }
+ }
+
+ zbx_db_mediatype_clean(&mt);
+
+ zbx_vector_str_clear_ext(&recipients, zbx_str_free);
+ zbx_vector_str_destroy(&recipients);
+out:
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: am_process_end_dispatch *
+ * *
+ * Purpose: finish sending dispatches *
+ * *
+ * Parameters: client - [IN] the connected worker IPC client *
+ * *
+ ******************************************************************************/
+static void am_process_end_dispatch(zbx_ipc_client_t *client)
+{
+ zbx_am_dispatch_t *dispatch;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() clientid:" ZBX_FS_UI64, __func__, zbx_ipc_client_id(client));
+
+ dispatch = (zbx_am_dispatch_t *)zbx_ipc_client_get_userdata(client);
+ zbx_ipc_client_set_userdata(client, NULL);
+ am_dispatch_free(dispatch);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
* Function: am_remove_unused_mediatypes *
* *
* Purpose: remove unused media types *
@@ -2216,7 +2460,7 @@ ZBX_THREAD_ENTRY(alert_manager_thread, args)
else
failed_num++;
break;
- case ZBX_IPC_ALERTER_ALERT:
+ case ZBX_IPC_ALERTER_SEND_ALERT:
am_process_external_alert_request(&manager, zbx_ipc_client_id(client),
message->data);
break;
@@ -2244,6 +2488,15 @@ ZBX_THREAD_ENTRY(alert_manager_thread, args)
case ZBX_IPC_ALERTER_DIAG_TOP_SOURCES:
am_process_diag_top_sources(&manager, client, message);
break;
+ case ZBX_IPC_ALERTER_BEGIN_DISPATCH:
+ am_process_begin_dispatch(client, message->data);
+ break;
+ case ZBX_IPC_ALERTER_SEND_DISPATCH:
+ am_process_send_dispatch(&manager, client, message->data);
+ break;
+ case ZBX_IPC_ALERTER_END_DISPATCH:
+ am_process_end_dispatch(client);
+ break;
}
zbx_ipc_message_free(message);
diff --git a/src/zabbix_server/alerter/alert_syncer.c b/src/zabbix_server/alerter/alert_syncer.c
index fe013ce7f9c..69e31deb58a 100644
--- a/src/zabbix_server/alerter/alert_syncer.c
+++ b/src/zabbix_server/alerter/alert_syncer.c
@@ -464,8 +464,15 @@ static int am_db_compare_tags(const void *d1, const void *d2)
return strcmp(tag1->value, tag2->value);
}
-ZBX_VECTOR_DECL(tags, zbx_tag_t*)
-ZBX_VECTOR_IMPL(tags, zbx_tag_t*)
+ZBX_PTR_VECTOR_DECL(tags, zbx_tag_t*)
+ZBX_PTR_VECTOR_IMPL(tags, zbx_tag_t*)
+
+static void tag_free(zbx_tag_t *tag)
+{
+ zbx_free(tag->tag);
+ zbx_free(tag->value);
+ zbx_free(tag);
+}
typedef struct
{
@@ -475,8 +482,8 @@ typedef struct
}
zbx_event_tags_t;
-ZBX_VECTOR_DECL(events_tags, zbx_event_tags_t*)
-ZBX_VECTOR_IMPL(events_tags, zbx_event_tags_t*)
+ZBX_PTR_VECTOR_DECL(events_tags, zbx_event_tags_t*)
+ZBX_PTR_VECTOR_IMPL(events_tags, zbx_event_tags_t*)
static int zbx_event_tags_compare_func(const void *d1, const void *d2)
{
@@ -486,14 +493,11 @@ static int zbx_event_tags_compare_func(const void *d1, const void *d2)
return event_tags_1->eventid > event_tags_2->eventid;
}
-static void clean_events_tags(zbx_vector_events_tags_t *events_tags)
+static void event_tags_free(zbx_event_tags_t *event_tags)
{
- int i;
-
- for (i = 0; i < events_tags->values_num; i++)
- zbx_vector_tags_destroy(&((events_tags->values[i])->tags));
-
- zbx_vector_events_tags_destroy(events_tags);
+ zbx_vector_tags_clear_ext(&event_tags->tags, tag_free);
+ zbx_vector_tags_destroy(&event_tags->tags);
+ zbx_free(event_tags);
}
/******************************************************************************
@@ -759,7 +763,8 @@ static int am_db_flush_results(zbx_am_db_t *amdb)
zbx_db_insert_execute(&db_problem);
zbx_db_insert_clean(&db_problem);
- clean_events_tags(&update_events_tags);
+ zbx_vector_events_tags_clear_ext(&update_events_tags, event_tags_free);
+ zbx_vector_events_tags_destroy(&update_events_tags);
}
while (ZBX_DB_DOWN == DBcommit());
diff --git a/src/zabbix_server/alerter/alerter_protocol.c b/src/zabbix_server/alerter/alerter_protocol.c
index 5816d89cd5e..f359e3575fa 100644
--- a/src/zabbix_server/alerter/alerter_protocol.c
+++ b/src/zabbix_server/alerter/alerter_protocol.c
@@ -23,6 +23,7 @@
#include "zbxalgo.h"
#include "zbxipcservice.h"
#include "zbxalert.h"
+#include "db.h"
#include "alerter_protocol.h"
@@ -103,6 +104,42 @@ void zbx_alerter_deserialize_result(const unsigned char *data, char **value, int
(void)zbx_deserialize_str(data, debug, len);
}
+zbx_uint32_t zbx_alerter_serialize_result_ext(unsigned char **data, const char *recipient, const char *value,
+ int errcode, const char *error, const char *debug)
+{
+ unsigned char *ptr;
+ zbx_uint32_t data_len = 0, value_len, error_len, debug_len, recipient_len;
+
+ zbx_serialize_prepare_str(data_len, recipient);
+ zbx_serialize_prepare_str(data_len, value);
+ zbx_serialize_prepare_value(data_len, errcode);
+ zbx_serialize_prepare_str(data_len, error);
+ zbx_serialize_prepare_str(data_len, debug);
+
+ *data = (unsigned char *)zbx_malloc(NULL, data_len);
+
+ ptr = *data;
+ ptr += zbx_serialize_str(ptr, recipient, recipient_len);
+ ptr += zbx_serialize_str(ptr, value, value_len);
+ ptr += zbx_serialize_value(ptr, errcode);
+ ptr += zbx_serialize_str(ptr, error, error_len);
+ (void)zbx_serialize_str(ptr, debug, debug_len);
+
+ return data_len;
+}
+
+void zbx_alerter_deserialize_result_ext(const unsigned char *data, char **recipient, char **value, int *errcode,
+ char **error, char **debug)
+{
+ zbx_uint32_t len;
+
+ data += zbx_deserialize_str(data, recipient, len);
+ data += zbx_deserialize_str(data, value, len);
+ data += zbx_deserialize_value(data, errcode);
+ data += zbx_deserialize_str(data, error, len);
+ (void)zbx_deserialize_str(data, debug, len);
+}
+
zbx_uint32_t zbx_alerter_serialize_email(unsigned char **data, zbx_uint64_t alertid, zbx_uint64_t mediatypeid,
zbx_uint64_t eventid, const char *sendto, const char *subject, const char *message,
const char *smtp_server, unsigned short smtp_port, const char *smtp_helo, const char *smtp_email,
@@ -832,7 +869,7 @@ static void zbx_alerter_deserialize_top_mediatypes_result(const unsigned char *d
if (0 != mediatypes_num)
{
- zbx_vector_uint64_pair_reserve(mediatypes, mediatypes_num);
+ zbx_vector_uint64_pair_reserve(mediatypes, (size_t)mediatypes_num);
for (i = 0; i < mediatypes_num; i++)
{
@@ -898,7 +935,7 @@ static void zbx_alerter_deserialize_top_sources_result(const unsigned char *data
if (0 != sources_num)
{
- zbx_vector_ptr_reserve(sources, sources_num);
+ zbx_vector_ptr_reserve(sources, (size_t)sources_num);
for (i = 0; i < sources_num; i++)
{
@@ -1009,3 +1046,360 @@ out:
return ret;
}
+
+/******************************************************************************
+ * *
+ * ZBX_IPC_ALERTER_BEGIN_DISPATCH message serialization/deserialization *
+ * *
+ ******************************************************************************/
+zbx_uint32_t zbx_alerter_serialize_begin_dispatch(unsigned char **data, const char *subject, const char *message,
+ const char *content_name, const char *content_type, const char *content, zbx_uint32_t content_size)
+{
+ unsigned char *ptr;
+ zbx_uint32_t data_len = 0, subject_len, message_len, content_name_len, content_type_len;
+
+ zbx_serialize_prepare_str(data_len, subject);
+ zbx_serialize_prepare_str(data_len, message);
+ zbx_serialize_prepare_value(data_len, content_size);
+
+ if (0 != content_size)
+ {
+ data_len += content_size;
+ zbx_serialize_prepare_str(data_len, content_name);
+ zbx_serialize_prepare_str(data_len, content_type);
+ }
+
+ *data = (unsigned char *)zbx_malloc(NULL, data_len);
+
+ ptr = *data;
+ ptr += zbx_serialize_str(ptr, subject, subject_len);
+ ptr += zbx_serialize_str(ptr, message, message_len);
+ ptr += zbx_serialize_value(ptr, content_size);
+
+ if (0 != content_size)
+ {
+ memcpy(ptr, content, content_size);
+ ptr += content_size;
+ ptr += zbx_serialize_str(ptr, content_name, content_name_len);
+ (void)zbx_serialize_str(ptr, content_type, content_type_len);
+ }
+
+ return data_len;
+}
+
+void zbx_alerter_deserialize_begin_dispatch(const unsigned char *data, char **subject, char **message,
+ char **content_name, char **content_type, char **content, zbx_uint32_t *content_size)
+{
+ zbx_uint32_t len;
+
+ data += zbx_deserialize_str(data, subject, len);
+ data += zbx_deserialize_str(data, message, len);
+ data += zbx_deserialize_value(data, content_size);
+
+ if (0 != *content_size)
+ {
+ *content = zbx_malloc(NULL, *content_size);
+ memcpy(*content, data, *content_size);
+ data += *content_size;
+
+ data += zbx_deserialize_str(data, content_name, len);
+ (void)zbx_deserialize_str(data, content_type, len);
+ }
+}
+
+/******************************************************************************
+ * *
+ * ZBX_IPC_ALERTER_SEND_DISPATCH message serialization/deserialization *
+ * *
+ ******************************************************************************/
+
+zbx_uint32_t zbx_alerter_serialize_send_dispatch(unsigned char **data, const DB_MEDIATYPE *mt,
+ const zbx_vector_str_t *recipients)
+{
+ unsigned char *ptr;
+ zbx_uint32_t data_len = 0, data_alloc = 1024, data_offset = 0, *recipients_len;
+ int i;
+
+ *data = zbx_malloc(NULL, data_alloc);
+ zbx_serialize_mediatype(data, &data_alloc, &data_offset, mt);
+
+ zbx_serialize_prepare_value(data_len, recipients->values_num);
+
+ recipients_len = (zbx_uint32_t *)zbx_malloc(NULL, sizeof(zbx_uint32_t) * recipients->values_num);
+ for (i = 0; i < recipients->values_num; i++)
+ {
+ zbx_serialize_prepare_str_len(data_len, recipients->values[i], recipients_len[i]);
+ }
+
+ if (data_alloc - data_offset < data_len)
+ {
+ data_alloc = data_offset + data_len;
+ *data = (unsigned char *)zbx_realloc(*data, data_alloc);
+ }
+
+ ptr = *data + data_offset;
+ ptr += zbx_serialize_value(ptr, recipients->values_num);
+
+ for (i = 0; i < recipients->values_num; i++)
+ {
+ ptr += zbx_serialize_str(ptr, recipients->values[i], recipients_len[i]);
+ }
+
+ zbx_free(recipients_len);
+
+ return data_len + data_offset;
+}
+
+void zbx_alerter_deserialize_send_dispatch(const unsigned char *data, DB_MEDIATYPE *mt, zbx_vector_str_t *recipients)
+{
+ zbx_uint32_t len;
+ int recipients_num, i;
+
+ data += zbx_deserialize_mediatype(data, mt);
+ data += zbx_deserialize_value(data, &recipients_num);
+
+ zbx_vector_str_reserve(recipients, (size_t)recipients_num);
+ for (i = 0; i < recipients_num; i++)
+ {
+ char *recipient;
+
+ data += zbx_deserialize_str(data, &recipient, len);
+ zbx_vector_str_append(recipients, recipient);
+ }
+}
+
+#define ZBX_ALERTER_REPORT_TIMEOUT SEC_PER_MIN * 10
+
+/******************************************************************************
+ * *
+ * Function: zbx_alerter_begin_dispatch *
+ * *
+ * Purpose: begin data dispatch *
+ * *
+ * Parameters: dispatch - [IN] the dispatcher *
+ * subject - [IN] the subject *
+ * message - [IN] the message *
+ * content_name - [IN] the content name *
+ * content_type - [IN] the content type *
+ * content - [IN] the additional content to dispatch *
+ * content_size - [IN] the content size *
+ * error [OUT] the error message *
+ * *
+ * Return value: SUCCEED - the dispatch was started successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+int zbx_alerter_begin_dispatch(zbx_alerter_dispatch_t *dispatch, const char *subject, const char *message,
+ const char *content_name, const char *content_type, const char *content, zbx_uint32_t content_size,
+ char **error)
+{
+ unsigned char *data;
+ zbx_uint32_t size;
+ int ret = FAIL;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() subject:\"%s\" content_name:%s content_size:%u message:%s", __func__,
+ subject, ZBX_NULL2EMPTY_STR(content_type), content_size, message);
+
+ if (SUCCEED == zbx_ipc_async_socket_connected(&dispatch->alerter))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ zbx_ipc_async_socket_close(&dispatch->alerter);
+ }
+
+ if (FAIL == zbx_ipc_async_socket_open(&dispatch->alerter, ZBX_IPC_SERVICE_ALERTER, SEC_PER_MIN, error))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ exit(EXIT_FAILURE);
+ }
+
+ size = zbx_alerter_serialize_begin_dispatch(&data, subject, message, content_name, content_type, content,
+ content_size);
+
+ if (FAIL == zbx_ipc_async_socket_send(&dispatch->alerter, ZBX_IPC_ALERTER_BEGIN_DISPATCH, data, size))
+ {
+ *error = zbx_strdup(NULL, "cannot send request");
+ goto out;
+ }
+
+ zbx_vector_ptr_create(&dispatch->results);
+ dispatch->total_num = 0;
+ ret = SUCCEED;
+
+out:
+ zbx_free(data);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_alerter_send_dispatch *
+ * *
+ * Purpose: dispatch data *
+ * *
+ * Parameters: dispatch - [IN] the dispatcher *
+ * mediatype - [IN] the media type to use for sending *
+ * recipients - [IN] the dispatch recipients *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - the dispatch sent successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+int zbx_alerter_send_dispatch(zbx_alerter_dispatch_t *dispatch, const DB_MEDIATYPE *mediatype,
+ const zbx_vector_str_t *recipients, char **error)
+{
+ unsigned char *data;
+ zbx_uint32_t size;
+ int ret = FAIL;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() mediatypeid:" ZBX_FS_UI64 " recipients_num:%d", __func__,
+ mediatype->mediatypeid, recipients->values_num);
+
+ size = zbx_alerter_serialize_send_dispatch(&data, mediatype, recipients);
+
+ if (FAIL == zbx_ipc_async_socket_send(&dispatch->alerter, ZBX_IPC_ALERTER_SEND_DISPATCH, data, size))
+ {
+ *error = zbx_strdup(NULL, "cannot send request");
+ goto out;
+ }
+
+ dispatch->total_num += recipients->values_num;
+
+ ret = SUCCEED;
+out:
+ zbx_free(data);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_alerter_end_dispatch *
+ * *
+ * Purpose: finish data dispatch *
+ * *
+ * Parameters: dispatch - [IN] the dispatcher *
+ * sent_num - [OUT] the number of successfully dispatched *
+ * messages *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - the dispatch was finished successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+int zbx_alerter_end_dispatch(zbx_alerter_dispatch_t *dispatch, char **error)
+{
+ int i, ret = FAIL;
+ time_t time_stop;
+ zbx_alerter_dispatch_result_t *result;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ if (FAIL == zbx_ipc_async_socket_send(&dispatch->alerter, ZBX_IPC_ALERTER_END_DISPATCH, NULL, 0))
+ {
+ *error = zbx_strdup(NULL, "cannot send request");
+ goto out;
+ }
+
+ if (FAIL == zbx_ipc_async_socket_flush(&dispatch->alerter, SEC_PER_MIN))
+ {
+ *error = zbx_strdup(NULL, "cannot flush request");
+ goto out;
+ }
+
+ /* wait for the send alert responses for all recipients */
+
+ time_stop = time(NULL) + ZBX_ALERTER_REPORT_TIMEOUT;
+
+ for (i = 0; i < dispatch->total_num; i++)
+ {
+ char *value = NULL, *errmsg = NULL, *debug = NULL;
+ zbx_ipc_message_t *message;
+ time_t now;
+
+ if (time_stop <= (now = time(NULL)))
+ {
+ *error = zbx_strdup(NULL, "timeout while waiting for dispatches to be sent");
+ goto out;
+ }
+
+ if (FAIL == zbx_ipc_async_socket_recv(&dispatch->alerter, time_stop - (int)now, &message))
+ {
+ *error = zbx_strdup(NULL, "cannot receive response");
+ goto out;
+ }
+
+ if (NULL == message)
+ {
+ *error = zbx_strdup(NULL, "timeout while waiting for response");
+ goto out;
+ }
+
+ switch (message->code)
+ {
+ case ZBX_IPC_ALERTER_SEND_ALERT:
+ result = (zbx_alerter_dispatch_result_t *)zbx_malloc(NULL,
+ sizeof(zbx_alerter_dispatch_result_t));
+ memset(result, 0, sizeof(zbx_alerter_dispatch_result_t));
+
+ zbx_alerter_deserialize_result_ext(message->data, &result->recipient, &value,
+ &result->status, &errmsg, &debug);
+
+ if (SUCCEED != result->status)
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "failed to send report to \"%s\": %s",
+ result->recipient, ZBX_NULL2EMPTY_STR(errmsg));
+
+ result->info = errmsg;
+ errmsg = NULL;
+ }
+ else
+ {
+ result->info = value;
+ value = NULL;
+ }
+
+ zbx_vector_ptr_append(&dispatch->results, result);
+
+ zbx_free(value);
+ zbx_free(errmsg);
+ zbx_free(debug);
+
+ break;
+ case ZBX_IPC_ALERTER_ABORT_DISPATCH:
+ *error = zbx_strdup(NULL, "the dispatch was aborted");
+ zbx_ipc_message_free(message);
+ goto out;
+ }
+
+ zbx_ipc_message_free(message);
+ }
+
+ ret = SUCCEED;
+out:
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s error:%s", __func__, zbx_result_string(ret),
+ ZBX_NULL2EMPTY_STR(*error));
+
+ return ret;
+}
+
+void zbx_alerter_dispatch_result_free(zbx_alerter_dispatch_result_t *result)
+{
+ zbx_free(result->recipient);
+ zbx_free(result->info);
+ zbx_free(result);
+}
+
+void zbx_alerter_clear_dispatch(zbx_alerter_dispatch_t *dispatch)
+{
+ if (SUCCEED == zbx_ipc_async_socket_connected(&dispatch->alerter))
+ zbx_ipc_async_socket_close(&dispatch->alerter);
+
+ zbx_vector_ptr_clear_ext(&dispatch->results, (zbx_clean_func_t)zbx_alerter_dispatch_result_free);
+ zbx_vector_ptr_destroy(&dispatch->results);
+}
diff --git a/src/zabbix_server/alerter/alerter_protocol.h b/src/zabbix_server/alerter/alerter_protocol.h
index cedd18612d6..70e0c501d1a 100644
--- a/src/zabbix_server/alerter/alerter_protocol.h
+++ b/src/zabbix_server/alerter/alerter_protocol.h
@@ -29,12 +29,11 @@
/* alerter -> manager */
#define ZBX_IPC_ALERTER_REGISTER 1000
#define ZBX_IPC_ALERTER_RESULT 1001
-#define ZBX_IPC_ALERTER_ALERT 1002
-#define ZBX_IPC_ALERTER_MEDIATYPES 1003
-#define ZBX_IPC_ALERTER_ALERTS 1004
-#define ZBX_IPC_ALERTER_WATCHDOG 1005
-#define ZBX_IPC_ALERTER_RESULTS 1006
-#define ZBX_IPC_ALERTER_DROP_MEDIATYPES 1007
+#define ZBX_IPC_ALERTER_MEDIATYPES 1002
+#define ZBX_IPC_ALERTER_ALERTS 1003
+#define ZBX_IPC_ALERTER_WATCHDOG 1004
+#define ZBX_IPC_ALERTER_RESULTS 1005
+#define ZBX_IPC_ALERTER_DROP_MEDIATYPES 1006
/* manager -> alerter */
#define ZBX_IPC_ALERTER_EMAIL 1100
@@ -46,11 +45,16 @@
#define ZBX_IPC_ALERTER_DIAG_STATS 1200
#define ZBX_IPC_ALERTER_DIAG_TOP_MEDIATYPES 1201
#define ZBX_IPC_ALERTER_DIAG_TOP_SOURCES 1202
+#define ZBX_IPC_ALERTER_SEND_ALERT 1203
+#define ZBX_IPC_ALERTER_BEGIN_DISPATCH 1204
+#define ZBX_IPC_ALERTER_SEND_DISPATCH 1205
+#define ZBX_IPC_ALERTER_END_DISPATCH 1206
/* manager -> process */
#define ZBX_IPC_ALERTER_DIAG_STATS_RESULT 1300
#define ZBX_IPC_ALERTER_DIAG_TOP_MEDIATYPES_RESULT 1301
#define ZBX_IPC_ALERTER_DIAG_TOP_SOURCES_RESULT 1302
+#define ZBX_IPC_ALERTER_ABORT_DISPATCH 1303
#define ZBX_WATCHDOG_ALERT_FREQUENCY (15 * SEC_PER_MIN)
#define ZBX_ALERT_NO_DEBUG 0
@@ -185,6 +189,11 @@ zbx_uint32_t zbx_alerter_serialize_result(unsigned char **data, const char *valu
void zbx_alerter_deserialize_result(const unsigned char *data, char **value, int *errcode, char **error,
char **debug);
+zbx_uint32_t zbx_alerter_serialize_result_ext(unsigned char **data, const char *recipient, const char *value,
+ int errcode, const char *error, const char *debug);
+void zbx_alerter_deserialize_result_ext(const unsigned char *data, char **recipient, char **value, int *errcode,
+ char **error, char **debug);
+
zbx_uint32_t zbx_alerter_serialize_email(unsigned char **data, zbx_uint64_t alertid, zbx_uint64_t mediatypeid,
zbx_uint64_t eventid, const char *sendto, const char *subject, const char *message,
const char *smtp_server, unsigned short smtp_port, const char *smtp_helo, const char *smtp_email,
@@ -262,4 +271,14 @@ zbx_uint32_t zbx_alerter_serialize_top_mediatypes_result(unsigned char **data, z
zbx_uint32_t zbx_alerter_serialize_top_sources_result(unsigned char **data, zbx_am_source_stats_t **sources,
int sources_num);
+zbx_uint32_t zbx_alerter_serialize_begin_dispatch(unsigned char **data, const char *subject, const char *message,
+ const char *content_name, const char *content_type, const char *content, zbx_uint32_t content_size);
+void zbx_alerter_deserialize_begin_dispatch(const unsigned char *data, char **subject, char **message,
+ char **content_name, char **content_type, char **content, zbx_uint32_t *content_size);
+
+zbx_uint32_t zbx_alerter_serialize_send_dispatch(unsigned char **data, const DB_MEDIATYPE *mt,
+ const zbx_vector_str_t *recipients);
+void zbx_alerter_deserialize_send_dispatch(const unsigned char *data, DB_MEDIATYPE *mt, zbx_vector_str_t
+ *recipients);
+
#endif
diff --git a/src/zabbix_server/escalator/escalator.c b/src/zabbix_server/escalator/escalator.c
index 954cc755896..b0e8a452caa 100644
--- a/src/zabbix_server/escalator/escalator.c
+++ b/src/zabbix_server/escalator/escalator.c
@@ -32,6 +32,8 @@
#include "../scripts/scripts.h"
#include "zbxcrypto.h"
#include "comms.h"
+#include "../../libs/zbxserver/get_host_from_event.h"
+#include "../../libs/zbxserver/zabbix_users.h"
extern int CONFIG_ESCALATOR_FORKS;
@@ -88,39 +90,6 @@ static void add_message_alert(const DB_EVENT *event, const DB_EVENT *r_event, zb
zbx_uint64_t userid, zbx_uint64_t mediatypeid, const char *subject, const char *message,
const DB_ACKNOWLEDGE *ack, int err_type, const char *tz);
-/******************************************************************************
- * *
- * Function: check_perm2system *
- * *
- * Purpose: Check user permissions to access system *
- * *
- * Parameters: userid - user ID *
- * *
- * Return value: SUCCEED - access allowed, FAIL - otherwise *
- * *
- ******************************************************************************/
-static int check_perm2system(zbx_uint64_t userid)
-{
- DB_RESULT result;
- DB_ROW row;
- int res = SUCCEED;
-
- result = DBselect(
- "select count(*)"
- " from usrgrp g,users_groups ug"
- " where ug.userid=" ZBX_FS_UI64
- " and g.usrgrpid=ug.usrgrpid"
- " and g.users_status=%d",
- userid, GROUP_STATUS_DISABLED);
-
- if (NULL != (row = DBfetch(result)) && SUCCEED != DBis_null(row[0]) && atoi(row[0]) > 0)
- res = FAIL;
-
- DBfree_result(result);
-
- return res;
-}
-
static int get_user_type_and_timezone(zbx_uint64_t userid, char **user_timezone)
{
int user_type = -1;
@@ -143,24 +112,6 @@ static int get_user_type_and_timezone(zbx_uint64_t userid, char **user_timezone)
return user_type;
}
-static char *get_user_timezone(zbx_uint64_t userid)
-{
- DB_RESULT result;
- DB_ROW row;
- char *user_timezone;
-
- result = DBselect("select timezone from users where userid=" ZBX_FS_UI64, userid);
-
- if (NULL != (row = DBfetch(result)))
- user_timezone = zbx_strdup(NULL, row[0]);
- else
- user_timezone = NULL;
-
- DBfree_result(result);
-
- return user_timezone;
-}
-
/******************************************************************************
* *
* Function: get_hostgroups_permission *
@@ -410,8 +361,7 @@ out:
static void add_user_msg(zbx_uint64_t userid, zbx_uint64_t mediatypeid, ZBX_USER_MSG **user_msg, const char *subj,
const char *msg, zbx_uint64_t actionid, const DB_EVENT *event, const DB_EVENT *r_event,
- const DB_ACKNOWLEDGE *ack, int macro_type, const char *cancel_error, int err_type,
- const char *tz)
+ const DB_ACKNOWLEDGE *ack, int expand_macros, int macro_type, int err_type, const char *tz)
{
ZBX_USER_MSG *p, **pnext;
char *subject, *message, *tz_tmp;
@@ -419,14 +369,16 @@ static void add_user_msg(zbx_uint64_t userid, zbx_uint64_t mediatypeid, ZBX_USER
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
subject = zbx_strdup(NULL, subj);
- message = (NULL == cancel_error ? zbx_strdup(NULL, msg) :
- zbx_dsprintf(NULL, "NOTE: Escalation cancelled: %s\n%s", cancel_error, msg));
+ message = zbx_strdup(NULL, msg);
tz_tmp = zbx_strdup(NULL, tz);
- substitute_simple_macros(&actionid, event, r_event, &userid, NULL, NULL, NULL, NULL, ack, tz, &subject,
- macro_type, NULL, 0);
- substitute_simple_macros(&actionid, event, r_event, &userid, NULL, NULL, NULL, NULL, ack, tz, &message,
- macro_type, NULL, 0);
+ if (MACRO_EXPAND_YES == expand_macros)
+ {
+ substitute_simple_macros(&actionid, event, r_event, &userid, NULL, NULL, NULL, NULL, ack, tz, &subject,
+ macro_type, NULL, 0);
+ substitute_simple_macros(&actionid, event, r_event, &userid, NULL, NULL, NULL, NULL, ack, tz, &message,
+ macro_type, NULL, 0);
+ }
if (0 == mediatypeid)
{
@@ -483,8 +435,8 @@ static void add_user_msg(zbx_uint64_t userid, zbx_uint64_t mediatypeid, ZBX_USER
static void add_user_msgs(zbx_uint64_t userid, zbx_uint64_t operationid, zbx_uint64_t mediatypeid,
ZBX_USER_MSG **user_msg, zbx_uint64_t actionid, const DB_EVENT *event, const DB_EVENT *r_event,
- const DB_ACKNOWLEDGE *ack, int macro_t, unsigned char evt_src, unsigned char op_mode,
- const char *cancel_err, const char *default_timezone, const char *user_timezone)
+ const DB_ACKNOWLEDGE *ack, int macro_type, unsigned char evt_src, unsigned char op_mode,
+ const char *default_timezone, const char *user_timezone)
{
DB_RESULT result;
DB_ROW row;
@@ -493,33 +445,33 @@ static void add_user_msgs(zbx_uint64_t userid, zbx_uint64_t operationid, zbx_uin
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
- tz = NULL == user_timezone || 0 == strcmp(user_timezone, "default") ? default_timezone : user_timezone;
+ if (NULL == user_timezone || 0 == strcmp(user_timezone, ZBX_TIMEZONE_DEFAULT_VALUE))
+ tz = default_timezone;
+ else
+ tz = user_timezone;
- if (0 != operationid)
- {
- result = DBselect(
- "select mediatypeid,default_msg,subject,message"
- " from opmessage where operationid=" ZBX_FS_UI64, operationid);
+ result = DBselect(
+ "select mediatypeid,default_msg,subject,message from opmessage where operationid=" ZBX_FS_UI64,
+ operationid);
- if (NULL != (row = DBfetch(result)))
+ if (NULL != (row = DBfetch(result)))
+ {
+ if (0 == mediatypeid)
{
- if (0 == mediatypeid)
- {
- ZBX_DBROW2UINT64(mediatypeid, row[0]);
- }
-
- if (atoi(row[1]) != 1)
- {
- add_user_msg(userid, mediatypeid, user_msg, row[2], row[3], actionid, event, r_event,
- ack, macro_t, cancel_err, ZBX_ALERT_MESSAGE_ERR_NONE, tz);
- goto out;
- }
-
- DBfree_result(result);
+ ZBX_DBROW2UINT64(mediatypeid, row[0]);
}
- else
+
+ if (1 != atoi(row[1]))
+ {
+ add_user_msg(userid, mediatypeid, user_msg, row[2], row[3], actionid, event, r_event, ack,
+ MACRO_EXPAND_YES, macro_type, ZBX_ALERT_MESSAGE_ERR_NONE, tz);
goto out;
+ }
+
+ DBfree_result(result);
}
+ else
+ goto out;
mtid = mediatypeid;
@@ -553,18 +505,18 @@ static void add_user_msgs(zbx_uint64_t userid, zbx_uint64_t operationid, zbx_uin
if (0 != mtmid)
{
add_user_msg(userid, mediatypeid, user_msg, row[1], row[2], actionid, event, r_event, ack,
- macro_t, cancel_err, ZBX_ALERT_MESSAGE_ERR_NONE, tz);
+ MACRO_EXPAND_YES, macro_type, ZBX_ALERT_MESSAGE_ERR_NONE, tz);
}
else
{
- add_user_msg(userid, mediatypeid, user_msg, "", "", actionid, event, r_event, ack, macro_t,
- cancel_err, ZBX_ALERT_MESSAGE_ERR_MSG, tz);
+ add_user_msg(userid, mediatypeid, user_msg, "", "", actionid, event, r_event, ack,
+ MACRO_EXPAND_NO, 0, ZBX_ALERT_MESSAGE_ERR_MSG, tz);
}
}
if (0 == mediatypeid)
{
- add_user_msg(userid, mtid, user_msg, "", "", actionid, event, r_event, ack, macro_t, cancel_err,
+ add_user_msg(userid, mtid, user_msg, "", "", actionid, event, r_event, ack, MACRO_EXPAND_NO, 0,
0 == mtid ? ZBX_ALERT_MESSAGE_ERR_USR : ZBX_ALERT_MESSAGE_ERR_MSG, tz);
}
@@ -624,7 +576,7 @@ static void add_object_msg(zbx_uint64_t actionid, zbx_uint64_t operationid, ZBX_
}
add_user_msgs(userid, operationid, 0, user_msg, actionid, event, r_event, ack, macro_type, evt_src,
- op_mode, NULL, default_timezone, user_timezone);
+ op_mode, default_timezone, user_timezone);
clean:
zbx_free(user_timezone);
}
@@ -649,12 +601,11 @@ clean:
* ack - [IN] the acknowledge (optional, can be NULL) *
* evt_src - [IN] the action event source *
* op_mode - [IN] the operation mode *
- * cancel_err - [IN] the error message (optional, can be NULL) *
* *
******************************************************************************/
static void add_sentusers_msg(ZBX_USER_MSG **user_msg, zbx_uint64_t actionid, zbx_uint64_t operationid,
const DB_EVENT *event, const DB_EVENT *r_event, const DB_ACKNOWLEDGE *ack, unsigned char evt_src,
- unsigned char op_mode, const char *cancel_err, const char *default_timezone)
+ unsigned char op_mode, const char *default_timezone)
{
char *sql = NULL;
DB_RESULT result;
@@ -721,7 +672,101 @@ static void add_sentusers_msg(ZBX_USER_MSG **user_msg, zbx_uint64_t actionid, zb
}
add_user_msgs(userid, operationid, mediatypeid, user_msg, actionid, event, r_event, ack, message_type,
- evt_src, op_mode, cancel_err, default_timezone, user_timezone);
+ evt_src, op_mode, default_timezone, user_timezone);
+clean:
+ zbx_free(user_timezone);
+ }
+ DBfree_result(result);
+
+ zbx_free(sql);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: add_sentusers_msg_esc_cancel *
+ * *
+ * Purpose: adds message for the canceled escalation to be sent to all *
+ * recipients of messages previously generated by action operations *
+ * or acknowledgement operations, which is related with an event or *
+ * recovery event *
+ * *
+ * Parameters: user_msg - [IN/OUT] the message list *
+ * actionid - [IN] the action identifier *
+ * event - [IN] the event *
+ * error - [IN] the error message *
+ * default_timezone - [IN] the default timezone *
+ * *
+ ******************************************************************************/
+static void add_sentusers_msg_esc_cancel(ZBX_USER_MSG **user_msg, zbx_uint64_t actionid, const DB_EVENT *event,
+ const char *error, const char *default_timezone)
+{
+ char *message_dyn, *sql = NULL;
+ DB_RESULT result;
+ DB_ROW row;
+ zbx_uint64_t userid, mediatypeid, userid_prev = 0, mediatypeid_prev = 0;
+ int esc_step, esc_step_prev = 0;
+ size_t sql_alloc = 0, sql_offset = 0;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
+ "select distinct userid,mediatypeid,subject,message,esc_step"
+ " from alerts"
+ " where actionid=" ZBX_FS_UI64
+ " and mediatypeid is not null"
+ " and alerttype=%d"
+ " and acknowledgeid is null"
+ " and eventid=" ZBX_FS_UI64
+ " order by userid,mediatypeid,esc_step desc",
+ actionid, ALERT_TYPE_MESSAGE, event->eventid);
+
+ result = DBselect("%s", sql);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ char *user_timezone = NULL;
+ const char *tz;
+
+ ZBX_DBROW2UINT64(userid, row[0]);
+ ZBX_STR2UINT64(mediatypeid, row[1]);
+ esc_step = atoi(row[4]);
+
+ if (userid == userid_prev && mediatypeid == mediatypeid_prev && esc_step < esc_step_prev)
+ continue;
+
+ userid_prev = userid;
+ mediatypeid_prev = mediatypeid;
+ esc_step_prev = esc_step;
+
+ if (SUCCEED != check_perm2system(userid))
+ continue;
+
+ switch (event->object)
+ {
+ case EVENT_OBJECT_TRIGGER:
+ if (PERM_READ > get_trigger_permission(userid, event, &user_timezone))
+ goto clean;
+ break;
+ case EVENT_OBJECT_ITEM:
+ case EVENT_OBJECT_LLDRULE:
+ if (PERM_READ > get_item_permission(userid, event->objectid, &user_timezone))
+ goto clean;
+ break;
+ default:
+ user_timezone = get_user_timezone(userid);
+ }
+
+ message_dyn = zbx_dsprintf(NULL, "NOTE: Escalation cancelled: %s\nLast message sent:\n%s", error,
+ row[3]);
+
+ tz = NULL == user_timezone || 0 == strcmp(user_timezone, "default") ? default_timezone : user_timezone;
+
+ add_user_msg(userid, mediatypeid, user_msg, row[2], message_dyn, actionid, event, NULL, NULL,
+ MACRO_EXPAND_NO, 0, ZBX_ALERT_MESSAGE_ERR_NONE, tz);
+
+ zbx_free(message_dyn);
clean:
zbx_free(user_timezone);
}
@@ -780,7 +825,7 @@ static void add_sentusers_ack_msg(ZBX_USER_MSG **user_msg, zbx_uint64_t actionid
goto clean;
add_user_msgs(userid, operationid, 0, user_msg, actionid, event, r_event, ack, MACRO_TYPE_MESSAGE_ACK,
- evt_src, ZBX_OPERATION_MODE_ACK, NULL, default_timezone, user_timezone);
+ evt_src, ZBX_OPERATION_MODE_ACK, default_timezone, user_timezone);
clean:
zbx_free(user_timezone);
}
@@ -809,28 +854,9 @@ static void flush_user_msg(ZBX_USER_MSG **user_msg, int esc_step, const DB_EVENT
}
}
-static int get_scriptname_by_scriptid(zbx_uint64_t scriptid, char **script_name)
-{
- DB_RESULT result;
- DB_ROW row;
-
- result = DBselect("select name from scripts where scriptid=" ZBX_FS_UI64, scriptid);
-
- if (NULL != (row = DBfetch(result)))
- {
- *script_name = zbx_strdup(NULL, row[0]);
- DBfree_result(result);
- return SUCCEED;
- }
-
- DBfree_result(result);
-
- return FAIL;
-}
-
-static void add_command_alert(zbx_db_insert_t *db_insert, int alerts_num, zbx_uint64_t alertid, const DC_HOST *host,
+static void add_command_alert(zbx_db_insert_t *db_insert, int alerts_num, zbx_uint64_t alertid, const char *host,
const DB_EVENT *event, const DB_EVENT *r_event, zbx_uint64_t actionid, int esc_step,
- const zbx_script_t *script, zbx_alert_status_t status, const char *error)
+ const char *message, zbx_alert_status_t status, const char *error)
{
int now, alerttype = ALERT_TYPE_COMMAND, alert_status = status;
char *tmp = NULL;
@@ -846,27 +872,7 @@ static void add_command_alert(zbx_db_insert_t *db_insert, int alerts_num, zbx_ui
now = (int)time(NULL);
- if (ZBX_SCRIPT_TYPE_IPMI == script->type || ZBX_SCRIPT_TYPE_SSH == script->type
- || ZBX_SCRIPT_TYPE_TELNET == script->type || ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT == script->type)
- {
- tmp = zbx_dsprintf(tmp, "%s:%s", host->host, ZBX_NULL2EMPTY_STR(script->command_orig));
- }
- else
- {
- char *message = NULL;
-
- if (FAIL == get_scriptname_by_scriptid(script->scriptid, &message))
- {
- zabbix_log(LOG_LEVEL_WARNING, "failed to find scriptname using script id " ZBX_FS_UI64,
- script->scriptid);
-
- tmp = zbx_dsprintf(tmp, "%s:", host->host);
- }
- else
- tmp = zbx_dsprintf(tmp, "%s:%s", host->host, ZBX_NULL2EMPTY_STR(message));
-
- zbx_free(message);
- }
+ tmp = zbx_dsprintf(tmp, "%s:%s", host, message);
if (NULL == r_event)
{
@@ -884,140 +890,6 @@ static void add_command_alert(zbx_db_insert_t *db_insert, int alerts_num, zbx_ui
zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}
-#ifdef HAVE_OPENIPMI
-# define ZBX_IPMI_FIELDS_NUM 4 /* number of selected IPMI-related fields in functions */
- /* get_dynamic_hostid() and execute_commands() */
-#else
-# define ZBX_IPMI_FIELDS_NUM 0
-#endif
-
-static int get_dynamic_hostid(const DB_EVENT *event, DC_HOST *host, char *error, size_t max_error_len)
-{
- DB_RESULT result;
- DB_ROW row;
- char sql[512]; /* do not forget to adjust size if SQLs change */
- size_t offset;
- int ret = SUCCEED;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- offset = zbx_snprintf(sql, sizeof(sql), "select distinct h.hostid,h.proxy_hostid,h.host,h.tls_connect");
-#ifdef HAVE_OPENIPMI
- offset += zbx_snprintf(sql + offset, sizeof(sql) - offset,
- /* do not forget to update ZBX_IPMI_FIELDS_NUM if number of selected IPMI fields changes */
- ",h.ipmi_authtype,h.ipmi_privilege,h.ipmi_username,h.ipmi_password");
-#endif
-#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
- offset += zbx_snprintf(sql + offset, sizeof(sql) - offset,
- ",h.tls_issuer,h.tls_subject,h.tls_psk_identity,h.tls_psk");
-#endif
- switch (event->source)
- {
- case EVENT_SOURCE_TRIGGERS:
- zbx_snprintf(sql + offset, sizeof(sql) - offset,
- " from functions f,items i,hosts h"
- " where f.itemid=i.itemid"
- " and i.hostid=h.hostid"
- " and h.status=%d"
- " and f.triggerid=" ZBX_FS_UI64,
- HOST_STATUS_MONITORED, event->objectid);
-
- break;
- case EVENT_SOURCE_DISCOVERY:
- offset += zbx_snprintf(sql + offset, sizeof(sql) - offset,
- " from hosts h,interface i,dservices ds"
- " where h.hostid=i.hostid"
- " and i.ip=ds.ip"
- " and i.useip=1"
- " and h.status=%d",
- HOST_STATUS_MONITORED);
-
- switch (event->object)
- {
- case EVENT_OBJECT_DHOST:
- zbx_snprintf(sql + offset, sizeof(sql) - offset,
- " and ds.dhostid=" ZBX_FS_UI64, event->objectid);
- break;
- case EVENT_OBJECT_DSERVICE:
- zbx_snprintf(sql + offset, sizeof(sql) - offset,
- " and ds.dserviceid=" ZBX_FS_UI64, event->objectid);
- break;
- }
- break;
- case EVENT_SOURCE_AUTOREGISTRATION:
- zbx_snprintf(sql + offset, sizeof(sql) - offset,
- " from autoreg_host a,hosts h"
- " where " ZBX_SQL_NULLCMP("a.proxy_hostid", "h.proxy_hostid")
- " and a.host=h.host"
- " and h.status=%d"
- " and h.flags<>%d"
- " and a.autoreg_hostid=" ZBX_FS_UI64,
- HOST_STATUS_MONITORED, ZBX_FLAG_DISCOVERY_PROTOTYPE, event->objectid);
- break;
- default:
- zbx_snprintf(error, max_error_len, "Unsupported event source [%d]", event->source);
- return FAIL;
- }
-
- host->hostid = 0;
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- if (0 != host->hostid)
- {
- switch (event->source)
- {
- case EVENT_SOURCE_TRIGGERS:
- zbx_strlcpy(error, "Too many hosts in a trigger expression", max_error_len);
- break;
- case EVENT_SOURCE_DISCOVERY:
- zbx_strlcpy(error, "Too many hosts with same IP addresses", max_error_len);
- break;
- }
- ret = FAIL;
- break;
- }
-
- ZBX_STR2UINT64(host->hostid, row[0]);
- ZBX_DBROW2UINT64(host->proxy_hostid, row[1]);
- strscpy(host->host, row[2]);
- ZBX_STR2UCHAR(host->tls_connect, row[3]);
-
-#ifdef HAVE_OPENIPMI
- host->ipmi_authtype = (signed char)atoi(row[4]);
- host->ipmi_privilege = (unsigned char)atoi(row[5]);
- strscpy(host->ipmi_username, row[6]);
- strscpy(host->ipmi_password, row[7]);
-#endif
-#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
- strscpy(host->tls_issuer, row[4 + ZBX_IPMI_FIELDS_NUM]);
- strscpy(host->tls_subject, row[5 + ZBX_IPMI_FIELDS_NUM]);
- strscpy(host->tls_psk_identity, row[6 + ZBX_IPMI_FIELDS_NUM]);
- strscpy(host->tls_psk, row[7 + ZBX_IPMI_FIELDS_NUM]);
-#endif
- }
- DBfree_result(result);
-
- if (FAIL == ret)
- {
- host->hostid = 0;
- *host->host = '\0';
- }
- else if (0 == host->hostid)
- {
- *host->host = '\0';
-
- zbx_strlcpy(error, "Cannot find a corresponding host", max_error_len);
- ret = FAIL;
- }
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
-
- return ret;
-}
-
/******************************************************************************
* *
* Function: get_operation_groupids *
@@ -1047,6 +919,13 @@ static void get_operation_groupids(zbx_uint64_t operationid, zbx_vector_uint64_t
zbx_vector_uint64_destroy(&parent_groupids);
}
+#ifdef HAVE_OPENIPMI
+# define ZBX_IPMI_FIELDS_NUM 4 /* number of selected IPMI-related fields in function */
+ /* execute_commands() */
+#else
+# define ZBX_IPMI_FIELDS_NUM 0
+#endif
+
static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, const DB_ACKNOWLEDGE *ack,
zbx_uint64_t actionid, zbx_uint64_t operationid, int esc_step, int macro_type,
const char *default_timezone)
@@ -1072,8 +951,9 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
{
zbx_strcpy_alloc(&buffer, &buffer_alloc, &buffer_offset,
/* the 1st 'select' works if remote command target is "Host group" */
- "select distinct h.hostid,h.proxy_hostid,h.host,o.type,o.scriptid,o.execute_on,o.port"
- ",o.authtype,o.username,o.password,o.publickey,o.privatekey,o.command,h.tls_connect"
+ "select distinct h.hostid,h.proxy_hostid,h.host,s.type,s.scriptid,s.execute_on,s.port"
+ ",s.authtype,s.username,s.password,s.publickey,s.privatekey,s.command,s.groupid"
+ ",s.scope,s.timeout,s.name,h.tls_connect"
#ifdef HAVE_OPENIPMI
/* do not forget to update ZBX_IPMI_FIELDS_NUM if number of selected IPMI fields changes */
",h.ipmi_authtype,h.ipmi_privilege,h.ipmi_username,h.ipmi_password"
@@ -1084,8 +964,9 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
);
zbx_snprintf_alloc(&buffer, &buffer_alloc, &buffer_offset,
- " from opcommand o,hosts_groups hg,hosts h"
+ " from opcommand o,hosts_groups hg,hosts h,scripts s"
" where o.operationid=" ZBX_FS_UI64
+ " and o.scriptid=s.scriptid"
" and hg.hostid=h.hostid"
" and h.status=%d"
" and",
@@ -1101,8 +982,9 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
zbx_strcpy_alloc(&buffer, &buffer_alloc, &buffer_offset,
/* the 2nd 'select' works if remote command target is "Host" */
- "select distinct h.hostid,h.proxy_hostid,h.host,o.type,o.scriptid,o.execute_on,o.port"
- ",o.authtype,o.username,o.password,o.publickey,o.privatekey,o.command,h.tls_connect"
+ "select distinct h.hostid,h.proxy_hostid,h.host,s.type,s.scriptid,s.execute_on,s.port"
+ ",s.authtype,s.username,s.password,s.publickey,s.privatekey,s.command,s.groupid"
+ ",s.scope,s.timeout,s.name,h.tls_connect"
#ifdef HAVE_OPENIPMI
",h.ipmi_authtype,h.ipmi_privilege,h.ipmi_username,h.ipmi_password"
#endif
@@ -1111,15 +993,17 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
#endif
);
zbx_snprintf_alloc(&buffer, &buffer_alloc, &buffer_offset,
- " from opcommand o,opcommand_hst oh,hosts h"
+ " from opcommand o,opcommand_hst oh,hosts h,scripts s"
" where o.operationid=oh.operationid"
+ " and o.scriptid=s.scriptid"
" and oh.hostid=h.hostid"
" and o.operationid=" ZBX_FS_UI64
" and h.status=%d"
" union "
/* the 3rd 'select' works if remote command target is "Current host" */
- "select distinct 0,0,null,o.type,o.scriptid,o.execute_on,o.port"
- ",o.authtype,o.username,o.password,o.publickey,o.privatekey,o.command,%d",
+ "select distinct 0,0,null,s.type,s.scriptid,s.execute_on,s.port"
+ ",s.authtype,s.username,s.password,s.publickey,s.privatekey,s.command,s.groupid"
+ ",s.scope,s.timeout,s.name,%d",
operationid, HOST_STATUS_MONITORED, ZBX_TCP_SEC_UNENCRYPTED);
#ifdef HAVE_OPENIPMI
zbx_strcpy_alloc(&buffer, &buffer_alloc, &buffer_offset,
@@ -1130,8 +1014,9 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
",null,null,null,null");
#endif
zbx_snprintf_alloc(&buffer, &buffer_alloc, &buffer_offset,
- " from opcommand o,opcommand_hst oh"
+ " from opcommand o,opcommand_hst oh,scripts s"
" where o.operationid=oh.operationid"
+ " and o.scriptid=s.scriptid"
" and o.operationid=" ZBX_FS_UI64
" and oh.hostid is null",
operationid);
@@ -1143,38 +1028,84 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
while (NULL != (row = DBfetch(result)))
{
- int rc = SUCCEED;
- char error[ALERT_ERROR_LEN_MAX];
+ int rc = SUCCEED, scope, i;
DC_HOST host;
zbx_script_t script;
zbx_alert_status_t status = ALERT_STATUS_NOT_SENT;
- zbx_uint64_t alertid;
+ zbx_uint64_t alertid, groupid;
+ char *webhook_params_json = NULL, *script_name = NULL;
+ zbx_vector_ptr_pair_t webhook_params;
+ char error[ALERT_ERROR_LEN_MAX];
*error = '\0';
memset(&host, 0, sizeof(host));
zbx_script_init(&script);
+ zbx_vector_ptr_pair_create(&webhook_params);
- script.type = (unsigned char)atoi(row[3]);
+ /* fill 'script' elements */
- if (ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT != script.type)
+ ZBX_STR2UCHAR(script.type, row[3]);
+
+ if (ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT == script.type)
+ ZBX_STR2UCHAR(script.execute_on, row[5]);
+
+ if (ZBX_SCRIPT_TYPE_SSH == script.type)
{
- script.command = zbx_strdup(script.command, row[12]);
- script.command_orig = zbx_strdup(script.command_orig, row[12]);
- substitute_simple_macros_unmasked(&actionid, event, r_event, NULL, NULL,
- NULL, NULL, NULL, ack, default_timezone, &script.command, macro_type, NULL, 0);
- substitute_simple_macros(&actionid, event, r_event, NULL, NULL,
- NULL, NULL, NULL, ack, default_timezone, &script.command_orig, macro_type, NULL, 0);
+ ZBX_STR2UCHAR(script.authtype, row[7]);
+ script.publickey = zbx_strdup(script.publickey, row[10]);
+ script.privatekey = zbx_strdup(script.privatekey, row[11]);
}
- if (ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT == script.type)
- script.execute_on = (unsigned char)atoi(row[5]);
+ if (ZBX_SCRIPT_TYPE_SSH == script.type || ZBX_SCRIPT_TYPE_TELNET == script.type)
+ {
+ script.port = zbx_strdup(script.port, row[6]);
+ script.username = zbx_strdup(script.username, row[8]);
+ script.password = zbx_strdup(script.password, row[9]);
+ }
+
+ script.command = zbx_strdup(script.command, row[12]);
+ script.command_orig = zbx_strdup(script.command_orig, row[12]);
+
+ ZBX_DBROW2UINT64(script.scriptid, row[4]);
+
+ if (SUCCEED != is_time_suffix(row[15], &script.timeout, ZBX_LENGTH_UNLIMITED))
+ {
+ zbx_strlcpy(error, "Invalid timeout value in script configuration.", sizeof(error));
+ rc = FAIL;
+ goto fail;
+ }
+
+ script_name = row[16];
+
+ /* validate script permissions */
+
+ scope = atoi(row[14]);
+ ZBX_DBROW2UINT64(groupid, row[13]);
ZBX_STR2UINT64(host.hostid, row[0]);
ZBX_DBROW2UINT64(host.proxy_hostid, row[1]);
+ if (ZBX_SCRIPT_SCOPE_ACTION != scope)
+ {
+ zbx_snprintf(error, sizeof(error), "Script is not allowed in action operations: scope:%d",
+ scope);
+ rc = FAIL;
+ goto fail;
+ }
+
+ if (0 < groupid && SUCCEED != zbx_check_script_permissions(groupid, host.hostid))
+ {
+ zbx_strlcpy(error, "Script does not have permission to be executed on the host.",
+ sizeof(error));
+ rc = FAIL;
+ goto fail;
+ }
+
+ /* get host details */
+
if (ZBX_SCRIPT_EXECUTE_ON_SERVER != script.execute_on)
{
- if (0 != host.hostid)
+ if (0 != host.hostid) /* target is from "Host" list or "Host group" list */
{
if (FAIL != zbx_vector_uint64_search(&executed_on_hosts, host.hostid,
ZBX_DEFAULT_UINT64_COMPARE_FUNC))
@@ -1184,22 +1115,22 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
zbx_vector_uint64_append(&executed_on_hosts, host.hostid);
strscpy(host.host, row[2]);
- host.tls_connect = (unsigned char)atoi(row[13]);
+ host.tls_connect = (unsigned char)atoi(row[17]);
#ifdef HAVE_OPENIPMI
- host.ipmi_authtype = (signed char)atoi(row[14]);
- host.ipmi_privilege = (unsigned char)atoi(row[15]);
- strscpy(host.ipmi_username, row[16]);
- strscpy(host.ipmi_password, row[17]);
+ host.ipmi_authtype = (signed char)atoi(row[18]);
+ host.ipmi_privilege = (unsigned char)atoi(row[19]);
+ strscpy(host.ipmi_username, row[20]);
+ strscpy(host.ipmi_password, row[21]);
#endif
#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
- strscpy(host.tls_issuer, row[14 + ZBX_IPMI_FIELDS_NUM]);
- strscpy(host.tls_subject, row[15 + ZBX_IPMI_FIELDS_NUM]);
- strscpy(host.tls_psk_identity, row[16 + ZBX_IPMI_FIELDS_NUM]);
- strscpy(host.tls_psk, row[17 + ZBX_IPMI_FIELDS_NUM]);
+ strscpy(host.tls_issuer, row[18 + ZBX_IPMI_FIELDS_NUM]);
+ strscpy(host.tls_subject, row[19 + ZBX_IPMI_FIELDS_NUM]);
+ strscpy(host.tls_psk_identity, row[20 + ZBX_IPMI_FIELDS_NUM]);
+ strscpy(host.tls_psk, row[21 + ZBX_IPMI_FIELDS_NUM]);
#endif
}
- else if (SUCCEED == (rc = get_dynamic_hostid((NULL != r_event ? r_event : event), &host, error,
- sizeof(error))))
+ else if (SUCCEED == (rc = get_host_from_event((NULL != r_event ? r_event : event), &host, error,
+ sizeof(error)))) /* target is "Current host" */
{
if (FAIL != zbx_vector_uint64_search(&executed_on_hosts, host.hostid,
ZBX_DEFAULT_UINT64_COMPARE_FUNC))
@@ -1213,35 +1144,64 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
else
zbx_strlcpy(host.host, "Zabbix server", sizeof(host.host));
- alertid = DBget_maxid("alerts");
+ /* substitute macros in script body and webhook parameters */
- if (SUCCEED == rc)
+ if (ZBX_SCRIPT_TYPE_WEBHOOK != script.type)
{
- switch (script.type)
+ if (SUCCEED != substitute_simple_macros_unmasked(&actionid, event, r_event, NULL, NULL, &host,
+ NULL, NULL, ack, default_timezone, &script.command, macro_type, error,
+ sizeof(error)))
{
- case ZBX_SCRIPT_TYPE_SSH:
- script.authtype = (unsigned char)atoi(row[7]);
- script.publickey = zbx_strdup(script.publickey, row[10]);
- script.privatekey = zbx_strdup(script.privatekey, row[11]);
- ZBX_FALLTHROUGH;
- case ZBX_SCRIPT_TYPE_TELNET:
- script.port = zbx_strdup(script.port, row[6]);
- script.username = zbx_strdup(script.username, row[8]);
- script.password = zbx_strdup(script.password, row[9]);
- break;
- case ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT:
- ZBX_DBROW2UINT64(script.scriptid, row[4]);
- break;
+ rc = FAIL;
+ goto fail;
+ }
+
+ /* expand macros in command_orig used for non-secure logging */
+ if (SUCCEED != substitute_simple_macros(&actionid, event, r_event, NULL, NULL, &host,
+ NULL, NULL, ack, default_timezone, &script.command_orig, macro_type, error,
+ sizeof(error)))
+ {
+ /* script command_orig is a copy of script command - if the script command */
+ /* macro substitution succeeded, then it will succeed also for command_orig */
+ THIS_SHOULD_NEVER_HAPPEN;
+ rc = FAIL;
+ goto fail;
+ }
+ }
+ else
+ {
+ if (SUCCEED != DBfetch_webhook_params(script.scriptid, &webhook_params, error, sizeof(error)))
+ {
+ rc = FAIL;
+ goto fail;
}
- if (SUCCEED == (rc = zbx_script_prepare(&script, &host, NULL, ZBX_SCRIPT_CTX_ACTION,
- event->eventid, error, sizeof(error), (DB_EVENT**)&event)))
+ for (i = 0; i < webhook_params.values_num; i++)
+ {
+ if (SUCCEED != substitute_simple_macros_unmasked(&actionid, event, r_event, NULL, NULL,
+ &host, NULL, NULL, ack, default_timezone,
+ (char **)&webhook_params.values[i].second, macro_type, error,
+ sizeof(error)))
+ {
+ rc = FAIL;
+ goto fail;
+ }
+ }
+
+ zbx_webhook_params_pack_json(&webhook_params, &webhook_params_json);
+ }
+fail:
+ alertid = DBget_maxid("alerts");
+
+ if (SUCCEED == rc)
+ {
+ if (SUCCEED == (rc = zbx_script_prepare(&script, &host.hostid, error, sizeof(error))))
{
if (0 == host.proxy_hostid || ZBX_SCRIPT_EXECUTE_ON_SERVER == script.execute_on ||
ZBX_SCRIPT_TYPE_WEBHOOK == script.type)
{
- rc = zbx_script_execute(&script, &host, NULL, event, ZBX_SCRIPT_CTX_ACTION,
- NULL, error, sizeof(error), NULL);
+ rc = zbx_script_execute(&script, &host, webhook_params_json, NULL, error,
+ sizeof(error), NULL);
status = ALERT_STATUS_SENT;
}
else
@@ -1255,9 +1215,19 @@ static void execute_commands(const DB_EVENT *event, const DB_EVENT *r_event, con
if (FAIL == rc)
status = ALERT_STATUS_FAILED;
- add_command_alert(&db_insert, alerts_num++, alertid, &host, event, r_event, actionid, esc_step,
- &script, status, error);
+ add_command_alert(&db_insert, alerts_num++, alertid, host.host, event, r_event, actionid, esc_step,
+ (ZBX_SCRIPT_TYPE_WEBHOOK == script.type) ? script_name : script.command_orig,
+ status, error);
skip:
+ zbx_free(webhook_params_json);
+
+ for (i = 0; i < webhook_params.values_num; i++)
+ {
+ zbx_free(webhook_params.values[i].first);
+ zbx_free(webhook_params.values[i].second);
+ }
+
+ zbx_vector_ptr_pair_destroy(&webhook_params);
zbx_script_clean(&script);
}
DBfree_result(result);
@@ -1732,7 +1702,7 @@ static void escalation_execute_recovery_operations(const DB_EVENT *event, const
break;
case OPERATION_TYPE_RECOVERY_MESSAGE:
add_sentusers_msg(&user_msg, action->actionid, operationid, event, r_event, NULL,
- action->eventsource, ZBX_OPERATION_MODE_RECOVERY, NULL, default_timezone);
+ action->eventsource, ZBX_OPERATION_MODE_RECOVERY, default_timezone);
break;
case OPERATION_TYPE_COMMAND:
execute_commands(event, r_event, NULL, action->actionid, operationid, 1,
@@ -1797,7 +1767,7 @@ static void escalation_execute_acknowledge_operations(const DB_EVENT *event, con
break;
case OPERATION_TYPE_ACK_MESSAGE:
add_sentusers_msg(&user_msg, action->actionid, operationid, event, r_event, ack,
- action->eventsource, ZBX_OPERATION_MODE_ACK, NULL, default_timezone);
+ action->eventsource, ZBX_OPERATION_MODE_ACK, default_timezone);
add_sentusers_ack_msg(&user_msg, action->actionid, operationid, event, r_event, ack,
action->eventsource, default_timezone);
break;
@@ -1951,6 +1921,29 @@ static const char *check_escalation_result_string(int result)
}
}
+static int postpone_escalation(const DB_ESCALATION *escalation)
+{
+ int ret;
+ char *sql;
+ DB_RESULT result;
+ DB_ROW row;
+
+ sql = zbx_dsprintf(NULL, "select eventid from alerts where eventid=" ZBX_FS_UI64 " and actionid=" ZBX_FS_UI64
+ " and status in (0,3)", escalation->eventid, escalation->actionid);
+
+ result = DBselectN(sql, 1);
+ zbx_free(sql);
+
+ if (NULL != (row = DBfetch(result)))
+ ret = ZBX_ESCALATION_SKIP;
+ else
+ ret = ZBX_ESCALATION_PROCESS;
+
+ DBfree_result(result);
+
+ return ret;
+}
+
/******************************************************************************
* *
* Function: check_escalation *
@@ -1991,11 +1984,23 @@ static int check_escalation(const DB_ESCALATION *escalation, const DB_ACTION *ac
maintenance = (ZBX_PROBLEM_SUPPRESSED_TRUE == event->suppressed ? HOST_MAINTENANCE_STATUS_ON :
HOST_MAINTENANCE_STATUS_OFF);
+
+ if (0 != escalation->r_eventid)
+ {
+ ret = postpone_escalation(escalation);
+ goto out;
+ }
}
else if (EVENT_SOURCE_INTERNAL == event->source)
{
if (EVENT_OBJECT_ITEM == event->object || EVENT_OBJECT_LLDRULE == event->object)
{
+ if (0 != escalation->r_eventid)
+ {
+ ret = postpone_escalation(escalation);
+ goto out;
+ }
+
/* item disabled or deleted? */
DCconfig_get_items_by_itemids(&item, &escalation->itemid, &errcode, 1);
@@ -2103,12 +2108,12 @@ static void escalation_cancel(DB_ESCALATION *escalation, const DB_ACTION *action
/* the cancellation notification can be sent if no objects are deleted */
if (NULL != action && NULL != event && 0 != event->trigger.triggerid && 0 != escalation->esc_step)
{
- add_sentusers_msg(&user_msg, action->actionid, 0, event, NULL, NULL, action->eventsource,
- ZBX_OPERATION_MODE_NORMAL, ZBX_NULL2EMPTY_STR(error), default_timezone);
+ add_sentusers_msg_esc_cancel(&user_msg, action->actionid, event, ZBX_NULL2EMPTY_STR(error),
+ default_timezone);
flush_user_msg(&user_msg, escalation->esc_step, event, NULL, action->actionid, NULL);
}
- escalation_log_cancel_warning(escalation, error);
+ escalation_log_cancel_warning(escalation, ZBX_NULL2EMPTY_STR(error));
escalation->status = ESCALATION_STATUS_COMPLETED;
zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
@@ -2460,8 +2465,8 @@ static int process_db_escalations(int now, int *nextcheck, zbx_vector_ptr_t *esc
{
if (0 == escalation->esc_step)
escalation_execute(escalation, action, event, default_timezone);
-
- escalation_recover(escalation, action, event, r_event, default_timezone);
+ else
+ escalation_recover(escalation, action, event, r_event, default_timezone);
}
else if (escalation->nextcheck <= now)
{
diff --git a/src/zabbix_server/events.c b/src/zabbix_server/events.c
index 60db1c1510e..b1f0f3889b6 100644
--- a/src/zabbix_server/events.c
+++ b/src/zabbix_server/events.c
@@ -266,8 +266,44 @@ DB_EVENT *zbx_add_event(unsigned char source, unsigned char object, zbx_uint64_t
zbx_vector_ptr_destroy(&item_tags);
}
- else if (EVENT_SOURCE_INTERNAL == source && NULL != error)
- event->name = zbx_strdup(NULL, error);
+ else if (EVENT_SOURCE_INTERNAL == source)
+ {
+ if (NULL != error)
+ event->name = zbx_strdup(NULL, error);
+
+ zbx_vector_ptr_create(&event->tags);
+ zbx_vector_ptr_create(&item_tags);
+
+ switch (object)
+ {
+ case EVENT_OBJECT_TRIGGER:
+ if (NULL != trigger_tags)
+ {
+ event->trigger.expression = zbx_strdup(NULL, trigger_expression);
+ event->trigger.recovery_expression = zbx_strdup(NULL,
+ trigger_recovery_expression);
+
+ for (i = 0; i < trigger_tags->values_num; i++)
+ process_trigger_tag(event, (const zbx_tag_t *)trigger_tags->values[i]);
+
+ zbx_free(event->trigger.expression);
+ zbx_free(event->trigger.recovery_expression);
+ }
+
+ get_item_tags_by_expression(trigger_expression, &item_tags);
+ break;
+ case EVENT_OBJECT_ITEM:
+ zbx_dc_get_item_tags(objectid, &item_tags);
+ }
+
+ for (i = 0; i < item_tags.values_num; i++)
+ {
+ process_item_tag(event, (const zbx_item_tag_t *)item_tags.values[i]);
+ zbx_free_item_tag(item_tags.values[i]);
+ }
+
+ zbx_vector_ptr_destroy(&item_tags);
+ }
zbx_vector_ptr_append(&events, event);
@@ -370,7 +406,7 @@ static int save_events(void)
num++;
- if (EVENT_SOURCE_TRIGGERS != event->source)
+ if (EVENT_SOURCE_TRIGGERS != event->source && EVENT_SOURCE_INTERNAL != event->source)
continue;
if (0 == event->tags.values_num)
@@ -441,10 +477,14 @@ static void save_problems(void)
case EVENT_OBJECT_TRIGGER:
if (TRIGGER_STATE_UNKNOWN != event->value)
continue;
+
+ tags_num += event->tags.values_num;
break;
case EVENT_OBJECT_ITEM:
if (ITEM_STATE_NOTSUPPORTED != event->value)
continue;
+
+ tags_num += event->tags.values_num;
break;
case EVENT_OBJECT_LLDRULE:
if (ITEM_STATE_NOTSUPPORTED != event->value)
@@ -490,7 +530,7 @@ static void save_problems(void)
{
const DB_EVENT *event = (const DB_EVENT *)problems.values[j];
- if (EVENT_SOURCE_TRIGGERS != event->source)
+ if (EVENT_SOURCE_TRIGGERS != event->source && EVENT_SOURCE_INTERNAL != event->source)
continue;
for (k = 0; k < event->tags.values_num; k++)
@@ -1694,17 +1734,19 @@ static void zbx_clean_event(DB_EVENT *event)
{
zbx_free(event->name);
- if (EVENT_SOURCE_TRIGGERS == event->source)
+ switch (event->source)
{
- zbx_free(event->trigger.description);
- zbx_free(event->trigger.expression);
- zbx_free(event->trigger.recovery_expression);
- zbx_free(event->trigger.correlation_tag);
- zbx_free(event->trigger.opdata);
- zbx_free(event->trigger.event_name);
-
- zbx_vector_ptr_clear_ext(&event->tags, (zbx_clean_func_t)zbx_free_tag);
- zbx_vector_ptr_destroy(&event->tags);
+ case EVENT_SOURCE_TRIGGERS:
+ zbx_free(event->trigger.description);
+ zbx_free(event->trigger.expression);
+ zbx_free(event->trigger.recovery_expression);
+ zbx_free(event->trigger.correlation_tag);
+ zbx_free(event->trigger.opdata);
+ zbx_free(event->trigger.event_name);
+ ZBX_FALLTHROUGH;
+ case EVENT_SOURCE_INTERNAL:
+ zbx_vector_ptr_clear_ext(&event->tags, (zbx_clean_func_t)zbx_free_tag);
+ zbx_vector_ptr_destroy(&event->tags);
}
zbx_free(event);
@@ -1792,6 +1834,7 @@ void zbx_export_events(void)
zbx_json_addint64(&json, ZBX_PROTO_TAG_VALUE, event->value);
zbx_json_adduint64(&json, ZBX_PROTO_TAG_EVENTID, event->eventid);
zbx_json_addstring(&json, ZBX_PROTO_TAG_NAME, event->name, ZBX_JSON_TYPE_STRING);
+ zbx_json_addint64(&json, ZBX_PROTO_TAG_SEVERITY, event->severity);
get_hosts_by_expression(&hosts, event->trigger.expression,
event->trigger.recovery_expression);
diff --git a/src/zabbix_server/httppoller/httptest.c b/src/zabbix_server/httppoller/httptest.c
index 12a60857e1e..3ca99a9dd44 100644
--- a/src/zabbix_server/httppoller/httptest.c
+++ b/src/zabbix_server/httppoller/httptest.c
@@ -189,8 +189,8 @@ static void process_test_data(zbx_uint64_t httptestid, int lastfailedstep, doubl
}
items[i].state = ITEM_STATE_NORMAL;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, 0, &value, ts, items[i].state,
- NULL);
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, 0, &value,
+ ts, items[i].state, NULL);
free_result(&value);
}
@@ -332,8 +332,8 @@ static void process_step_data(zbx_uint64_t httpstepid, zbx_httpstat_t *stat, zbx
}
items[i].state = ITEM_STATE_NORMAL;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, 0, &value, ts, items[i].state,
- NULL);
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, 0, &value,
+ ts, items[i].state, NULL);
free_result(&value);
}
diff --git a/src/zabbix_server/ipmi/ipmi_manager.c b/src/zabbix_server/ipmi/ipmi_manager.c
index 35461a32d04..3555a52d076 100644
--- a/src/zabbix_server/ipmi/ipmi_manager.c
+++ b/src/zabbix_server/ipmi/ipmi_manager.c
@@ -808,8 +808,8 @@ static int ipmi_manager_schedule_requests(zbx_ipmi_manager_t *manager, int now,
{
zbx_timespec(&ts);
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL, &ts,
- state, error);
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+ items[i].flags, NULL, &ts, state, error);
DCrequeue_items(&items[i].itemid, &ts.sec, &errcode, 1);
zbx_free(error);
continue;
@@ -963,8 +963,8 @@ static void ipmi_manager_process_value_result(zbx_ipmi_manager_t *manager, zbx_i
init_result(&result);
SET_TEXT_RESULT(&result, value);
value = NULL;
- zbx_preprocess_item_value(itemid, ITEM_VALUE_TYPE_TEXT, flags, &result, &ts, state,
- NULL);
+ zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags,
+ &result, &ts, state, NULL);
free_result(&result);
}
break;
@@ -973,7 +973,8 @@ static void ipmi_manager_process_value_result(zbx_ipmi_manager_t *manager, zbx_i
case AGENT_ERROR:
case CONFIG_ERROR:
state = ITEM_STATE_NOTSUPPORTED;
- zbx_preprocess_item_value(itemid, ITEM_VALUE_TYPE_TEXT, flags, NULL, &ts, state, value);
+ zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags, NULL,
+ &ts, state, value);
break;
default:
/* don't change item's state when network related error occurs */
diff --git a/src/zabbix_server/lld/lld.c b/src/zabbix_server/lld/lld.c
index 31e4e114758..3ce4b792f86 100644
--- a/src/zabbix_server/lld/lld.c
+++ b/src/zabbix_server/lld/lld.c
@@ -725,9 +725,10 @@ static int regexp_strmatch_condition(const char *value, const char *pattern, uns
}
void lld_override_item(const zbx_vector_ptr_t *overrides, const char *name, const char **delay,
- const char **history, const char **trends, unsigned char *status, unsigned char *discover)
+ const char **history, const char **trends, zbx_vector_db_tag_ptr_t *override_tags,
+ unsigned char *status, unsigned char *discover)
{
- int i, j;
+ int i, j, k;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
@@ -768,6 +769,9 @@ void lld_override_item(const zbx_vector_ptr_t *overrides, const char *name, cons
if (NULL != override_operation->trends)
*trends = override_operation->trends;
+ for (k = 0; k < override_operation->tags.values_num; k++)
+ zbx_vector_db_tag_ptr_append(override_tags, override_operation->tags.values[k]);
+
if (NULL != status)
{
switch (override_operation->status)
diff --git a/src/zabbix_server/lld/lld.h b/src/zabbix_server/lld/lld.h
index 00070db5eeb..3bef0a66319 100644
--- a/src/zabbix_server/lld/lld.h
+++ b/src/zabbix_server/lld/lld.h
@@ -45,7 +45,8 @@ void lld_field_uint64_rollback(zbx_uint64_t *field, zbx_uint64_t *field_orig, zb
zbx_uint64_t flag);
void lld_override_item(const zbx_vector_ptr_t *overrides, const char *name, const char **delay,
- const char **history, const char **trends, unsigned char *status, unsigned char *discover);
+ const char **history, const char **trends, zbx_vector_db_tag_ptr_t *override_tags,
+ unsigned char *status, unsigned char *discover);
void lld_override_trigger(const zbx_vector_ptr_t *overrides, const char *name, unsigned char *severity,
zbx_vector_db_tag_ptr_t *override_tags, unsigned char *status, unsigned char *discover);
void lld_override_host(const zbx_vector_ptr_t *overrides, const char *name, zbx_vector_uint64_t *lnk_templateids,
diff --git a/src/zabbix_server/lld/lld_item.c b/src/zabbix_server/lld/lld_item.c
index ec30c876cbf..e5b579fb513 100644
--- a/src/zabbix_server/lld/lld_item.c
+++ b/src/zabbix_server/lld/lld_item.c
@@ -73,9 +73,9 @@ typedef struct
unsigned char allow_traps;
unsigned char discover;
zbx_vector_ptr_t lld_rows;
- zbx_vector_ptr_t applications;
zbx_vector_ptr_t preproc_ops;
zbx_vector_ptr_t item_params;
+ zbx_vector_ptr_t item_tags;
}
zbx_lld_item_prototype_t;
@@ -194,6 +194,8 @@ typedef struct
zbx_vector_ptr_t preproc_ops;
zbx_vector_ptr_t dependent_items;
zbx_vector_ptr_t item_params;
+ zbx_vector_ptr_t item_tags;
+ zbx_vector_db_tag_ptr_t override_tags;
unsigned char status;
unsigned char type;
}
@@ -245,43 +247,33 @@ typedef struct
}
zbx_lld_item_param_t;
-/* item index by prototype (parent) id and lld row */
-typedef struct
-{
- zbx_uint64_t parent_itemid;
- zbx_lld_row_t *lld_row;
- zbx_lld_item_t *item;
-}
-zbx_lld_item_index_t;
+#define ZBX_ITEM_TAG_FIELD_TAG 1
+#define ZBX_ITEM_TAG_FIELD_VALUE 2
typedef struct
{
- zbx_uint64_t application_prototypeid;
- zbx_uint64_t itemid;
- char *name;
+ zbx_uint64_t item_tagid;
+ char *tag;
+ char *value;
+
+#define ZBX_FLAG_LLD_ITEM_TAG_UNSET __UINT64_C(0x00)
+#define ZBX_FLAG_LLD_ITEM_TAG_DISCOVERED __UINT64_C(0x01)
+#define ZBX_FLAG_LLD_ITEM_TAG_UPDATE_TAG __UINT64_C(0x02)
+#define ZBX_FLAG_LLD_ITEM_TAG_UPDATE_VALUE __UINT64_C(0x04)
+#define ZBX_FLAG_LLD_ITEM_TAG_UPDATE \
+ (ZBX_FLAG_LLD_ITEM_TAG_UPDATE_TAG | ZBX_FLAG_LLD_ITEM_TAG_UPDATE_VALUE)
+ zbx_uint64_t flags;
}
-zbx_lld_application_prototype_t;
+zbx_lld_item_tag_t;
+/* item index by prototype (parent) id and lld row */
typedef struct
{
- zbx_uint64_t applicationid;
- zbx_uint64_t application_prototypeid;
- zbx_uint64_t application_discoveryid;
- int lastcheck;
- int ts_delete;
-#define ZBX_FLAG_LLD_APPLICATION_UNSET __UINT64_C(0x0000000000000000)
-#define ZBX_FLAG_LLD_APPLICATION_DISCOVERED __UINT64_C(0x0000000000000001)
-#define ZBX_FLAG_LLD_APPLICATION_UPDATE_NAME __UINT64_C(0x0000000000000002)
-#define ZBX_FLAG_LLD_APPLICATION_ADD_DISCOVERY __UINT64_C(0x0000000100000000)
-#define ZBX_FLAG_LLD_APPLICATION_REMOVE_DISCOVERY __UINT64_C(0x0000000200000000)
-#define ZBX_FLAG_LLD_APPLICATION_REMOVE __UINT64_C(0x0000000400000000)
- zbx_uint64_t flags;
- char *name;
- char *name_proto;
- char *name_orig;
- const zbx_lld_row_t *lld_row;
+ zbx_uint64_t parent_itemid;
+ zbx_lld_row_t *lld_row;
+ zbx_lld_item_t *item;
}
-zbx_lld_application_t;
+zbx_lld_item_index_t;
/* reference to an item either by its id (existing items) or structure (new items) */
typedef struct
@@ -291,44 +283,6 @@ typedef struct
}
zbx_lld_item_ref_t;
-/* reference to an application either by its id (existing applications) or structure (new applications) */
-typedef struct
-{
- zbx_uint64_t applicationid;
- zbx_lld_application_t *application;
-}
-zbx_lld_application_ref_t;
-
-/* item prototype-application link reference by application id (existing applications) */
-/* or application prototype structure (application prototypes) */
-typedef struct
-{
- zbx_lld_application_prototype_t *application_prototype;
- zbx_uint64_t applicationid;
-}
-zbx_lld_item_application_ref_t;
-
-/* item-application link */
-typedef struct
-{
- zbx_uint64_t itemappid;
- zbx_lld_item_ref_t item_ref;
- zbx_lld_application_ref_t application_ref;
-#define ZBX_FLAG_LLD_ITEM_APPLICATION_UNSET __UINT64_C(0x0000000000000000)
-#define ZBX_FLAG_LLD_ITEM_APPLICATION_DISCOVERED __UINT64_C(0x0000000000000001)
- zbx_uint64_t flags;
-}
-zbx_lld_item_application_t;
-
-/* application index by prototypeid and lld row */
-typedef struct
-{
- zbx_uint64_t application_prototypeid;
- const zbx_lld_row_t *lld_row;
- zbx_lld_application_t *application;
-}
-zbx_lld_application_index_t;
-
/* items index hashset support functions */
static zbx_hash_t lld_item_index_hash_func(const void *data)
{
@@ -351,58 +305,6 @@ static int lld_item_index_compare_func(const void *d1, const void *d2)
return 0;
}
-/* application index hashset support functions */
-static zbx_hash_t lld_application_index_hash_func(const void *data)
-{
- zbx_lld_application_index_t *application_index = (zbx_lld_application_index_t *)data;
- zbx_hash_t hash;
-
- hash = ZBX_DEFAULT_UINT64_HASH_ALGO(&application_index->application_prototypeid,
- sizeof(application_index->application_prototypeid), ZBX_DEFAULT_HASH_SEED);
- return ZBX_DEFAULT_PTR_HASH_ALGO(&application_index->lld_row, sizeof(application_index->lld_row), hash);
-}
-
-static int lld_application_index_compare_func(const void *d1, const void *d2)
-{
- zbx_lld_application_index_t *i1 = (zbx_lld_application_index_t *)d1;
- zbx_lld_application_index_t *i2 = (zbx_lld_application_index_t *)d2;
-
- ZBX_RETURN_IF_NOT_EQUAL(i1->application_prototypeid, i2->application_prototypeid);
- ZBX_RETURN_IF_NOT_EQUAL(i1->lld_row, i2->lld_row);
-
- return 0;
-}
-
-/* comparison function for discovered application lookup by name */
-static int lld_application_compare_name(const void *d1, const void *d2)
-{
- const zbx_lld_application_t *a1 = *(zbx_lld_application_t **)d1;
- const zbx_lld_application_t *a2 = *(zbx_lld_application_t **)d2;
-
- if (0 == (a1->flags & a2->flags))
- return -1;
-
- if (NULL == a1->name || NULL == a2->name)
- return -1;
-
- return strcmp(a1->name, a2->name);
-}
-
-/* comparison function for discovered application lookup by original name name */
-static int lld_application_compare_name_orig(const void *d1, const void *d2)
-{
- const zbx_lld_application_t *a1 = *(zbx_lld_application_t **)d1;
- const zbx_lld_application_t *a2 = *(zbx_lld_application_t **)d2;
-
- if (0 == (a1->flags & a2->flags))
- return -1;
-
- if (NULL == a1->name_orig || NULL == a2->name_orig)
- return -1;
-
- return strcmp(a1->name_orig, a2->name_orig);
-}
-
/* string pointer hashset (used to check for duplicate item keys) support functions */
static zbx_hash_t lld_items_keys_hash_func(const void *data)
{
@@ -414,35 +316,6 @@ static int lld_items_keys_compare_func(const void *d1, const void *d2)
return ZBX_DEFAULT_STR_COMPARE_FUNC(d1, d2);
}
-/* items - applications hashset support */
-static zbx_hash_t lld_item_application_hash_func(const void *data)
-{
- const zbx_lld_item_application_t *item_application = (zbx_lld_item_application_t *)data;
- zbx_hash_t hash;
-
- hash = ZBX_DEFAULT_HASH_ALGO(&item_application->item_ref.itemid, sizeof(item_application->item_ref.itemid),
- ZBX_DEFAULT_HASH_SEED);
- hash = ZBX_DEFAULT_HASH_ALGO(&item_application->item_ref.item, sizeof(item_application->item_ref.item), hash);
-
- hash = ZBX_DEFAULT_HASH_ALGO(&item_application->application_ref.applicationid,
- sizeof(item_application->application_ref.applicationid), hash);
- return ZBX_DEFAULT_HASH_ALGO(&item_application->application_ref.application,
- sizeof(item_application->application_ref.application), hash);
-}
-
-static int lld_item_application_compare_func(const void *d1, const void *d2)
-{
- const zbx_lld_item_application_t *ia1 = (zbx_lld_item_application_t *)d1;
- const zbx_lld_item_application_t *ia2 = (zbx_lld_item_application_t *)d2;
-
- ZBX_RETURN_IF_NOT_EQUAL(ia1->item_ref.itemid, ia2->item_ref.itemid);
- ZBX_RETURN_IF_NOT_EQUAL(ia1->item_ref.item, ia2->item_ref.item);
- ZBX_RETURN_IF_NOT_EQUAL(ia1->application_ref.applicationid, ia2->application_ref.applicationid);
- ZBX_RETURN_IF_NOT_EQUAL(ia1->application_ref.application, ia2->application_ref.application);
-
- return 0;
-}
-
static int lld_item_preproc_sort_by_step(const void *d1, const void *d2)
{
zbx_lld_item_preproc_t *op1 = *(zbx_lld_item_preproc_t **)d1;
@@ -461,18 +334,13 @@ static int lld_item_param_sort_by_name(const void *d1, const void *d2)
return 0;
}
-static void lld_application_prototype_free(zbx_lld_application_prototype_t *application_prototype)
+static int lld_item_tag_sort_by_tag(const void *d1, const void *d2)
{
- zbx_free(application_prototype->name);
- zbx_free(application_prototype);
-}
+ zbx_lld_item_tag_t *it1 = *(zbx_lld_item_tag_t **)d1;
+ zbx_lld_item_tag_t *it2 = *(zbx_lld_item_tag_t **)d2;
-static void lld_application_free(zbx_lld_application_t *application)
-{
- zbx_free(application->name_orig);
- zbx_free(application->name_proto);
- zbx_free(application->name);
- zbx_free(application);
+ ZBX_RETURN_IF_NOT_EQUAL(it1->tag, it2->tag);
+ return 0;
}
static void lld_item_preproc_free(zbx_lld_item_preproc_t *op)
@@ -489,6 +357,13 @@ static void lld_item_param_free(zbx_lld_item_param_t *param)
zbx_free(param);
}
+static void lld_item_tag_free(zbx_lld_item_tag_t *tag)
+{
+ zbx_free(tag->tag);
+ zbx_free(tag->value);
+ zbx_free(tag);
+}
+
static void lld_item_prototype_free(zbx_lld_item_prototype_t *item_prototype)
{
zbx_free(item_prototype->name);
@@ -522,15 +397,15 @@ static void lld_item_prototype_free(zbx_lld_item_prototype_t *item_prototype)
zbx_vector_ptr_destroy(&item_prototype->lld_rows);
- zbx_vector_ptr_clear_ext(&item_prototype->applications, zbx_default_mem_free_func);
- zbx_vector_ptr_destroy(&item_prototype->applications);
-
zbx_vector_ptr_clear_ext(&item_prototype->preproc_ops, (zbx_clean_func_t)lld_item_preproc_free);
zbx_vector_ptr_destroy(&item_prototype->preproc_ops);
zbx_vector_ptr_clear_ext(&item_prototype->item_params, (zbx_clean_func_t)lld_item_param_free);
zbx_vector_ptr_destroy(&item_prototype->item_params);
+ zbx_vector_ptr_clear_ext(&item_prototype->item_tags, (zbx_clean_func_t)lld_item_tag_free);
+ zbx_vector_ptr_destroy(&item_prototype->item_tags);
+
zbx_free(item_prototype);
}
@@ -588,8 +463,12 @@ static void lld_item_free(zbx_lld_item_t *item)
zbx_vector_ptr_destroy(&item->preproc_ops);
zbx_vector_ptr_clear_ext(&item->item_params, (zbx_clean_func_t)lld_item_param_free);
zbx_vector_ptr_destroy(&item->item_params);
+ zbx_vector_ptr_clear_ext(&item->item_tags, (zbx_clean_func_t)lld_item_tag_free);
+ zbx_vector_ptr_destroy(&item->item_tags);
zbx_vector_ptr_destroy(&item->dependent_items);
+ zbx_vector_db_tag_ptr_destroy(&item->override_tags);
+
zbx_free(item);
}
@@ -610,6 +489,7 @@ static void lld_items_get(const zbx_vector_ptr_t *item_prototypes, zbx_vector_pt
zbx_lld_item_t *item, *master;
zbx_lld_item_preproc_t *preproc_op;
zbx_lld_item_param_t *item_param;
+ zbx_lld_item_tag_t *item_tag;
const zbx_lld_item_prototype_t *item_prototype;
zbx_uint64_t db_valuemapid, db_interfaceid, itemid, master_itemid;
zbx_vector_uint64_t parent_itemids;
@@ -802,6 +682,8 @@ static void lld_items_get(const zbx_vector_ptr_t *item_prototypes, zbx_vector_pt
zbx_vector_ptr_create(&item->preproc_ops);
zbx_vector_ptr_create(&item->dependent_items);
zbx_vector_ptr_create(&item->item_params);
+ zbx_vector_ptr_create(&item->item_tags);
+ zbx_vector_db_tag_ptr_create(&item->override_tags);
zbx_vector_ptr_append(items, item);
}
@@ -911,6 +793,40 @@ static void lld_items_get(const zbx_vector_ptr_t *item_prototypes, zbx_vector_pt
zbx_vector_ptr_append(&item->item_params, item_param);
}
DBfree_result(result);
+
+ sql_offset = 0;
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
+ "select it.itemtagid,it.itemid,it.tag,it.value"
+ " from item_discovery id"
+ " join item_tag it"
+ " on id.itemid=it.itemid"
+ " where");
+
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "id.parent_itemid", parent_itemids.values,
+ parent_itemids.values_num);
+
+ result = DBselect("%s", sql);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_STR2UINT64(itemid, row[1]);
+
+ if (FAIL == (index = zbx_vector_ptr_bsearch(items, &itemid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ continue;
+ }
+
+ item = (zbx_lld_item_t *)items->values[index];
+
+ item_tag = (zbx_lld_item_tag_t *)zbx_malloc(NULL, sizeof(zbx_lld_item_tag_t));
+ item_tag->flags = ZBX_FLAG_LLD_ITEM_TAG_UNSET;
+ ZBX_STR2UINT64(item_tag->item_tagid, row[0]);
+ item_tag->tag = zbx_strdup(NULL, row[2]);
+ item_tag->value = zbx_strdup(NULL, row[3]);
+ zbx_vector_ptr_append(&item->item_tags, item_tag);
+ }
+ DBfree_result(result);
out:
zbx_free(sql);
zbx_vector_uint64_destroy(&parent_itemids);
@@ -978,6 +894,45 @@ static int lld_validate_item_param(zbx_uint64_t itemid, int type, size_t len, ch
/******************************************************************************
* *
+ * Function: lld_validate_item_tag *
+ * *
+ ******************************************************************************/
+static int lld_validate_item_tag(zbx_uint64_t itemid, int type, char *tag, char **error)
+{
+ size_t len;
+ if (SUCCEED != zbx_is_utf8(tag))
+ {
+ char *tag_utf8;
+
+ tag_utf8 = zbx_strdup(NULL, tag);
+ zbx_replace_invalid_utf8(tag_utf8);
+ *error = zbx_strdcatf(*error, "Cannot %s item: tag's %s \"%s\" has invalid UTF-8 sequence.\n",
+ (0 != itemid ? "update" : "create"),
+ (ZBX_ITEM_TAG_FIELD_TAG != type ? "tag" : "value"), tag_utf8);
+ zbx_free(tag_utf8);
+ return FAIL;
+ }
+
+ len = zbx_strlen_utf8(tag);
+
+ if (ITEM_TAG_FIELD_LEN < len)
+ {
+ *error = zbx_strdcatf(*error, "Cannot %s item: tag's %s \"%s\" is too long.\n",
+ (0 != itemid ? "update" : "create"),
+ (ZBX_ITEM_TAG_FIELD_TAG != type ? "tag" : "value"), tag);
+ return FAIL;
+ }
+ else if (0 == len && ZBX_ITEM_TAG_FIELD_TAG == type)
+ {
+ *error = zbx_strdcatf(*error, "Cannot %s item: empty tag name.\n", (0 != itemid ? "update" : "create"));
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
* Function: lld_validate_item_field *
* *
******************************************************************************/
@@ -1740,6 +1695,50 @@ static void lld_items_validate(zbx_uint64_t hostid, zbx_vector_ptr_t *items, zbx
}
}
+ /* check item tags for new and updated discovered items */
+ for (i = 0; i < items->values_num; i++)
+ {
+ item = (zbx_lld_item_t *)items->values[i];
+
+ if (0 == (item->flags & ZBX_FLAG_LLD_ITEM_DISCOVERED))
+ continue;
+
+ for (j = 0; j < item->item_tags.values_num; j++)
+ {
+ zbx_lld_item_tag_t *item_tag = (zbx_lld_item_tag_t *)item->item_tags.values[j], *tag_dup;
+ int k;
+
+ if (SUCCEED != lld_validate_item_tag(item->itemid, ZBX_ITEM_TAG_FIELD_TAG, item_tag->tag,
+ error) || SUCCEED != lld_validate_item_tag(item->itemid,
+ ZBX_ITEM_TAG_FIELD_VALUE, item_tag->value, error))
+ {
+ item->flags &= ~ZBX_FLAG_LLD_ITEM_DISCOVERED;
+ break;
+ }
+
+ if (0 == (item_tag->flags & ZBX_FLAG_LLD_ITEM_TAG_DISCOVERED))
+ continue;
+
+ /* check for duplicated tag */
+ for (k = 0; k < j; k++)
+ {
+ tag_dup = (zbx_lld_item_tag_t *)item->item_tags.values[k];
+
+ if (0 == strcmp(item_tag->tag, tag_dup->tag) &&
+ 0 == strcmp(item_tag->value, tag_dup->value))
+ {
+ item->flags &= ~ZBX_FLAG_LLD_ITEM_DISCOVERED;
+ *error = zbx_strdcatf(*error, "Cannot create item tag: tag \"%s\","
+ "\"%s\" already exists.\n", item_tag->tag, item_tag->value);
+ break;
+ }
+ }
+
+ if (0 == (item->flags & ZBX_FLAG_LLD_ITEM_DISCOVERED))
+ break;
+ }
+ }
+
/* check preprocessing steps for new and updated discovered items */
for (i = 0; i < items->values_num; i++)
{
@@ -2024,7 +2023,10 @@ static zbx_lld_item_t *lld_item_make(const zbx_lld_item_prototype_t *item_protot
item->status = item_prototype->status;
discover = item_prototype->discover;
- lld_override_item(&lld_row->overrides, item->name, &delay, &history, &trends, &item->status, &discover);
+ zbx_vector_db_tag_ptr_create(&item->override_tags);
+
+ lld_override_item(&lld_row->overrides, item->name, &delay, &history, &trends, &item->override_tags,
+ &item->status, &discover);
item->key = zbx_strdup(NULL, item_prototype->key);
item->key_orig = NULL;
@@ -2185,6 +2187,7 @@ static zbx_lld_item_t *lld_item_make(const zbx_lld_item_prototype_t *item_protot
zbx_vector_ptr_create(&item->preproc_ops);
zbx_vector_ptr_create(&item->dependent_items);
zbx_vector_ptr_create(&item->item_params);
+ zbx_vector_ptr_create(&item->item_tags);
if (SUCCEED != ret || ZBX_PROTOTYPE_NO_DISCOVER == discover)
{
@@ -2235,7 +2238,8 @@ static void lld_item_update(const zbx_lld_item_prototype_t *item_prototype, cons
trends = item_prototype->trends;
discover = item_prototype->discover;
- lld_override_item(&lld_row->overrides, item->name, &delay, &history, &trends, NULL, &discover);
+ lld_override_item(&lld_row->overrides, item->name, &delay, &history, &trends, &item->override_tags, NULL,
+ &discover);
if (0 != strcmp(item->key_proto, item_prototype->key))
{
@@ -2965,6 +2969,123 @@ static void lld_items_param_make(const zbx_vector_ptr_t *item_prototypes,
/******************************************************************************
* *
+ * Function: lld_items_tags_make *
+ * *
+ * Purpose: updates existing items tags and create new based on item *
+ * prototypes *
+ * *
+ * Parameters: item_prototypes - [IN] the item prototypes *
+ * lld_macro_paths - [IN] use json path to extract from jp_row *
+ * items - [IN/OUT] sorted list of items *
+ * *
+ ******************************************************************************/
+static void lld_items_tags_make(const zbx_vector_ptr_t *item_prototypes, const zbx_vector_ptr_t *lld_macro_paths,
+ zbx_vector_ptr_t *items)
+{
+ int i, j, index, item_tag_num;
+ zbx_lld_item_t *item;
+ zbx_lld_item_prototype_t *item_proto;
+ zbx_lld_item_tag_t *itsrc, *itdst;
+ zbx_db_tag_t *override_tags;
+ char *buffer = NULL;
+ const char *name, *value;
+
+ for (i = 0; i < items->values_num; i++)
+ {
+ item = (zbx_lld_item_t *)items->values[i];
+
+ if (0 == (item->flags & ZBX_FLAG_LLD_ITEM_DISCOVERED))
+ continue;
+
+ if (FAIL == (index = zbx_vector_ptr_bsearch(item_prototypes, &item->parent_itemid,
+ ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ continue;
+ }
+
+ zbx_vector_ptr_sort(&item->item_tags, lld_item_tag_sort_by_tag);
+ zbx_vector_db_tag_ptr_sort(&item->override_tags, zbx_db_tag_compare_func);
+
+ item_proto = (zbx_lld_item_prototype_t *)item_prototypes->values[index];
+
+ item_tag_num = MAX(item->item_tags.values_num,
+ item_proto->item_tags.values_num + item->override_tags.values_num);
+
+ for (j = 0; j < item_tag_num; j++)
+ {
+ if (j < item->item_tags.values_num &&
+ j >= item_proto->item_tags.values_num + item->override_tags.values_num)
+ {
+ itdst = (zbx_lld_item_tag_t *)item->item_tags.values[j];
+ itdst->flags &= ~ZBX_FLAG_LLD_ITEM_TAG_DISCOVERED;
+ continue;
+ }
+
+ if (j >= item_proto->item_tags.values_num)
+ {
+ override_tags = item->override_tags.values[j - item_proto->item_tags.values_num];
+ name = override_tags->tag;
+ value = override_tags->value;
+ }
+ else
+ {
+ itsrc = (zbx_lld_item_tag_t *)item_proto->item_tags.values[j];
+ name = itsrc->tag;
+ value = itsrc->value;
+ }
+
+ if (j >= item->item_tags.values_num)
+ {
+ itdst = (zbx_lld_item_tag_t *)zbx_malloc(NULL, sizeof(zbx_lld_item_tag_t));
+ itdst->item_tagid = 0;
+ itdst->flags = ZBX_FLAG_LLD_ITEM_TAG_DISCOVERED | ZBX_FLAG_LLD_ITEM_TAG_UPDATE;
+ itdst->tag = zbx_strdup(NULL, name);
+ itdst->value = zbx_strdup(NULL, value);
+
+ substitute_lld_macros(&itdst->tag, &item->lld_row->jp_row,
+ lld_macro_paths, ZBX_MACRO_ANY, NULL, 0);
+ substitute_lld_macros(&itdst->value, &item->lld_row->jp_row,
+ lld_macro_paths, ZBX_MACRO_ANY, NULL, 0);
+
+ zbx_vector_ptr_append(&item->item_tags, itdst);
+ continue;
+ }
+
+ itdst = (zbx_lld_item_tag_t *)item->item_tags.values[j];
+ itdst->flags |= ZBX_FLAG_LLD_ITEM_TAG_DISCOVERED;
+
+ buffer = zbx_strdup(buffer, name);
+ substitute_lld_macros(&buffer, &item->lld_row->jp_row, lld_macro_paths, ZBX_MACRO_ANY, NULL, 0);
+
+ if (0 != strcmp(itdst->tag, buffer))
+ {
+ zbx_free(itdst->tag);
+ itdst->tag = buffer;
+ buffer = NULL;
+ itdst->flags |= ZBX_FLAG_LLD_ITEM_PARAM_UPDATE_NAME;
+ }
+ else
+ zbx_free(buffer);
+
+ buffer = zbx_strdup(buffer, value);
+ substitute_lld_macros(&buffer, &item->lld_row->jp_row, lld_macro_paths, ZBX_MACRO_ANY, NULL, 0);
+
+ if (0 != strcmp(itdst->value, buffer))
+ {
+ zbx_free(itdst->value);
+ itdst->value = buffer;
+ buffer = NULL;
+ itdst->flags |= ZBX_FLAG_LLD_ITEM_PARAM_UPDATE_VALUE;
+ }
+ else
+ zbx_free(buffer);
+ }
+ }
+}
+
+/******************************************************************************
+ * *
* Function: lld_item_save *
* *
* Purpose: recursively prepare LLD item bulk insert if any and *
@@ -3947,338 +4068,169 @@ out:
/******************************************************************************
* *
- * Function: lld_applications_save *
+ * Function: lld_items_tags_save *
* *
- * Parameters: hostid - [IN] host id *
- * applications - [IN/OUT] applications to save *
- * application_prototypes - [IN] the application prototypes *
- * host_locked - [IN/OUT] host record is locked *
+ * Purpose: saves/updates/removes item tags *
+ * *
+ * Parameters: hostid - [IN] parent host id *
+ * items - [IN] items *
+ * host_locked - [IN/OUT] host record is locked *
* *
******************************************************************************/
-static int lld_applications_save(zbx_uint64_t hostid, zbx_vector_ptr_t *applications,
- const zbx_vector_ptr_t *application_prototypes, int *host_locked)
+static int lld_items_tags_save(zbx_uint64_t hostid, zbx_vector_ptr_t *items, int *host_locked)
{
- int ret = SUCCEED, i, new_applications = 0, new_discoveries = 0, index;
- zbx_lld_application_t *application;
- const zbx_lld_application_prototype_t *application_prototype;
- zbx_uint64_t applicationid, application_discoveryid;
- zbx_db_insert_t db_insert, db_insert_discovery;
- zbx_vector_uint64_t del_applicationids, del_discoveryids;
- char *sql_a = NULL, *sql_ad = NULL, *name;
- size_t sql_a_alloc = 0, sql_a_offset = 0, sql_ad_alloc = 0, sql_ad_offset = 0;
+ int ret = SUCCEED, i, j, new_tag_num = 0, update_tag_num = 0, delete_tag_num = 0;
+ zbx_lld_item_t *item;
+ zbx_lld_item_tag_t *item_tag;
+ zbx_vector_uint64_t deleteids;
+ zbx_db_insert_t db_insert;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
- if (0 == applications->values_num)
- goto out;
-
- if (0 == *host_locked)
- {
- if (SUCCEED != DBlock_hostid(hostid))
- {
- /* the host was removed while processing lld rule */
- ret = FAIL;
- goto out;
- }
-
- *host_locked = 1;
- }
-
- zbx_vector_uint64_create(&del_applicationids);
- zbx_vector_uint64_create(&del_discoveryids);
+ zbx_vector_uint64_create(&deleteids);
- /* Count new applications and application discoveries. */
- /* Note that an application might have been discovered by another lld rule. */
- /* In this case the discovered items will be linked to this application and */
- /* new application discovery record, linking the prototype to this */
- /* application, will be created. */
- for (i = 0; i < applications->values_num; i++)
+ for (i = 0; i < items->values_num; i++)
{
- application = (zbx_lld_application_t *)applications->values[i];
-
- if (0 != (application->flags & ZBX_FLAG_LLD_APPLICATION_REMOVE))
- {
- zbx_vector_uint64_append(&del_applicationids, application->applicationid);
- continue;
- }
+ item = (zbx_lld_item_t *)items->values[i];
- if (0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_DISCOVERED))
+ if (0 == (item->flags & ZBX_FLAG_LLD_ITEM_DISCOVERED))
continue;
- if (0 == application->applicationid)
- new_applications++;
-
- if (0 != (application->flags & ZBX_FLAG_LLD_APPLICATION_ADD_DISCOVERY))
- new_discoveries++;
- }
-
- /* insert new applications, application discoveries and prepare a list of applications to be removed */
-
- if (0 != new_applications)
- {
- applicationid = DBget_maxid_num("applications", new_applications);
- zbx_db_insert_prepare(&db_insert, "applications", "applicationid", "hostid", "name", "flags", NULL);
- }
-
- if (0 != new_discoveries)
- {
- application_discoveryid = DBget_maxid_num("application_discovery", new_discoveries);
- zbx_db_insert_prepare(&db_insert_discovery, "application_discovery", "application_discoveryid",
- "applicationid", "application_prototypeid", "name", NULL);
- }
-
- for (i = 0; i < applications->values_num; i++)
- {
- application = (zbx_lld_application_t *)applications->values[i];
-
- if (0 != (application->flags & ZBX_FLAG_LLD_APPLICATION_REMOVE_DISCOVERY))
+ for (j = 0; j < item->item_tags.values_num; j++)
{
- zbx_vector_uint64_append(&del_discoveryids, application->application_discoveryid);
- continue;
- }
+ item_tag = (zbx_lld_item_tag_t *)item->item_tags.values[j];
- if (0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_DISCOVERED))
- continue;
+ if (0 == (item_tag->flags & ZBX_FLAG_LLD_ITEM_TAG_DISCOVERED))
+ {
+ zbx_vector_uint64_append(&deleteids, item_tag->item_tagid);
+ continue;
+ }
- if (FAIL == (index = zbx_vector_ptr_search(application_prototypes,
- &application->application_prototypeid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
- {
- THIS_SHOULD_NEVER_HAPPEN;
- continue;
- }
+ if (0 == item_tag->item_tagid)
+ {
+ new_tag_num++;
+ continue;
+ }
- application_prototype = (zbx_lld_application_prototype_t *)application_prototypes->values[index];
+ if (0 == (item_tag->flags & ZBX_FLAG_LLD_ITEM_TAG_UPDATE))
+ continue;
- if (0 == application->applicationid)
- {
- application->applicationid = applicationid++;
- zbx_db_insert_add_values(&db_insert, application->applicationid, hostid, application->name,
- ZBX_FLAG_DISCOVERY_CREATED);
+ update_tag_num++;
}
-
- if (0 != (application->flags & ZBX_FLAG_LLD_APPLICATION_UPDATE_NAME))
- {
- if (NULL == sql_a)
- DBbegin_multiple_update(&sql_a, &sql_a_alloc, &sql_a_offset);
- if (NULL == sql_ad)
- DBbegin_multiple_update(&sql_ad, &sql_ad_alloc, &sql_ad_offset);
-
- name = DBdyn_escape_string(application->name);
- zbx_snprintf_alloc(&sql_a, &sql_a_alloc, &sql_a_offset,
- "update applications set name='%s'"
- " where applicationid=" ZBX_FS_UI64 ";\n",
- name, application->applicationid);
- zbx_free(name);
-
- name = DBdyn_escape_string(application_prototype->name);
- zbx_snprintf_alloc(&sql_ad, &sql_ad_alloc, &sql_ad_offset,
- "update application_discovery set name='%s'"
- " where application_discoveryid=" ZBX_FS_UI64 ";\n",
- name, application->application_discoveryid);
- zbx_free(name);
-
- DBexecute_overflowed_sql(&sql_a, &sql_a_alloc, &sql_a_offset);
- DBexecute_overflowed_sql(&sql_ad, &sql_ad_alloc, &sql_ad_offset);
-
- continue;
- }
-
- if (0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_ADD_DISCOVERY))
- continue;
-
- application->application_discoveryid = application_discoveryid++;
- zbx_db_insert_add_values(&db_insert_discovery, application->application_discoveryid,
- application->applicationid, application->application_prototypeid,
- application_prototype->name);
- }
-
- if (NULL != sql_a)
- {
- DBend_multiple_update(&sql_a, &sql_a_alloc, &sql_a_offset);
-
- if (16 < sql_a_offset) /* in ORACLE always present begin..end; */
- DBexecute("%s", sql_a);
}
- if (NULL != sql_ad)
+ if (0 == *host_locked && (0 != update_tag_num || 0 != new_tag_num || 0 != deleteids.values_num))
{
- DBend_multiple_update(&sql_ad, &sql_ad_alloc, &sql_ad_offset);
+ if (SUCCEED != DBlock_hostid(hostid))
+ {
+ /* the host was removed while processing lld rule */
+ ret = FAIL;
+ goto out;
+ }
- if (16 < sql_ad_offset)
- DBexecute("%s", sql_ad);
+ *host_locked = 1;
}
- if (0 != del_applicationids.values_num)
+ if (0 != update_tag_num)
{
- sql_a_offset = 0;
-
- zbx_strcpy_alloc(&sql_a, &sql_a_alloc, &sql_a_offset, "delete from applications where");
- DBadd_condition_alloc(&sql_a, &sql_a_alloc, &sql_a_offset, "applicationid", del_applicationids.values,
- del_applicationids.values_num);
- zbx_strcpy_alloc(&sql_a, &sql_a_alloc, &sql_a_offset, ";\n");
-
- DBexecute("%s", sql_a);
+ DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
}
- if (0 != del_discoveryids.values_num)
+ if (0 != new_tag_num)
{
- sql_ad_offset = 0;
-
- zbx_strcpy_alloc(&sql_a, &sql_a_alloc, &sql_a_offset, "delete from application_discovery where");
- DBadd_condition_alloc(&sql_a, &sql_a_alloc, &sql_a_offset, "application_discoveryid",
- del_discoveryids.values, del_discoveryids.values_num);
- zbx_strcpy_alloc(&sql_a, &sql_a_alloc, &sql_a_offset, ";\n");
-
- DBexecute("%s", sql_ad);
+ zbx_db_insert_prepare(&db_insert, "item_tag", "itemtagid", "itemid", "tag", "value",
+ NULL);
}
- zbx_free(sql_a);
- zbx_free(sql_ad);
-
- if (0 != new_applications)
+ for (i = 0; i < items->values_num; i++)
{
- zbx_db_insert_execute(&db_insert);
- zbx_db_insert_clean(&db_insert);
+ item = (zbx_lld_item_t *)items->values[i];
- zbx_vector_ptr_sort(applications, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
- }
+ if (0 == (item->flags & ZBX_FLAG_LLD_ITEM_DISCOVERED))
+ continue;
- if (0 != new_discoveries)
- {
- zbx_db_insert_execute(&db_insert_discovery);
- zbx_db_insert_clean(&db_insert_discovery);
- }
+ for (j = 0; j < item->item_tags.values_num; j++)
+ {
+ char delim = ' ';
- zbx_vector_uint64_destroy(&del_discoveryids);
- zbx_vector_uint64_destroy(&del_applicationids);
-out:
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+ item_tag = (zbx_lld_item_tag_t *)item->item_tags.values[j];
- return ret;
-}
+ if (0 == item_tag->item_tagid)
+ {
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), item->itemid, item_tag->tag,
+ item_tag->value);
+ continue;
+ }
-/******************************************************************************
- * *
- * Function: lld_item_application_validate *
- * *
- * Purpose: validates undiscovered item-application link to determine if it *
- * should be removed *
- * *
- * Parameters: items_application - [IN] an item-application link to validate *
- * items - [IN] the related items *
- * *
- * Return value: SUCCEED - item-application link should not be removed *
- * FAIL - item-application link should be removed *
- * *
- * Comments: Undiscovered item-application link must be removed if item was *
- * discovered. *
- * *
- ******************************************************************************/
-static int lld_item_application_validate(const zbx_lld_item_application_t *item_application,
- const zbx_vector_ptr_t *items)
-{
- int index;
+ if (0 == (item_tag->flags & ZBX_FLAG_LLD_ITEM_TAG_UPDATE))
+ continue;
- if (FAIL == (index = zbx_vector_ptr_bsearch(items, &item_application->item_ref.itemid,
- ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
- {
- THIS_SHOULD_NEVER_HAPPEN;
- return FAIL;
- }
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update item_tag set");
- return 0 != (((zbx_lld_item_t *)items->values[index])->flags & ZBX_FLAG_LLD_ITEM_DISCOVERED) ? FAIL : SUCCEED;
-}
+ if (0 != (item_tag->flags & ZBX_FLAG_LLD_ITEM_TAG_UPDATE_TAG))
+ {
+ char *tag_esc;
-/******************************************************************************
- * *
- * Function: lld_items_applications_save *
- * *
- * Parameters: items_applications - [IN] item-application links *
- * *
- ******************************************************************************/
-static void lld_items_applications_save(zbx_hashset_t *items_applications, const zbx_vector_ptr_t *items)
-{
- zbx_hashset_iter_t iter;
- zbx_lld_item_application_t *item_application;
- zbx_vector_uint64_t del_itemappids;
- int new_item_applications = 0;
- zbx_uint64_t itemappid, applicationid, itemid;
- zbx_db_insert_t db_insert;
+ tag_esc = DBdyn_escape_string(item_tag->tag);
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%ctag='%s'", delim, tag_esc);
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+ zbx_free(tag_esc);
+ delim = ',';
+ }
- if (0 == items_applications->num_data)
- goto out;
+ if (0 != (item_tag->flags & ZBX_FLAG_LLD_ITEM_TAG_UPDATE_VALUE))
+ {
+ char *value_esc;
- zbx_vector_uint64_create(&del_itemappids);
+ value_esc = DBdyn_escape_string(item_tag->value);
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%cvalue='%s'", delim, value_esc);
- /* count new item-application links */
- zbx_hashset_iter_reset(items_applications, &iter);
+ zbx_free(value_esc);
+ }
- while (NULL != (item_application = (zbx_lld_item_application_t *)zbx_hashset_iter_next(&iter)))
- {
- if (0 == item_application->itemappid)
- new_item_applications++;
- }
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where itemtagid=" ZBX_FS_UI64 ";\n",
+ item_tag->item_tagid);
- if (0 != new_item_applications)
- {
- itemappid = DBget_maxid_num("items_applications", new_item_applications);
- zbx_db_insert_prepare(&db_insert, "items_applications", "itemappid", "applicationid", "itemid", NULL);
+ DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
+ }
}
- zbx_hashset_iter_reset(items_applications, &iter);
-
- while (NULL != (item_application = (zbx_lld_item_application_t *)zbx_hashset_iter_next(&iter)))
+ if (0 != update_tag_num)
{
- if (0 != item_application->itemappid)
- {
- /* add for removal the old links that aren't discovered and can be removed */
- if (0 == (item_application->flags & ZBX_FLAG_LLD_ITEM_APPLICATION_DISCOVERED) &&
- FAIL == lld_item_application_validate(item_application, items))
- {
- zbx_vector_uint64_append(&del_itemappids, item_application->itemappid);
- }
-
- continue;
- }
-
- if (0 == (applicationid = item_application->application_ref.applicationid))
- applicationid = item_application->application_ref.application->applicationid;
-
- if (0 == (itemid = item_application->item_ref.itemid))
- itemid = item_application->item_ref.item->itemid;
+ DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
- item_application->itemappid = itemappid++;
- zbx_db_insert_add_values(&db_insert, item_application->itemappid, applicationid, itemid);
+ if (16 < sql_offset) /* in ORACLE always present begin..end; */
+ DBexecute("%s", sql);
}
- if (0 != new_item_applications)
+ if (0 != new_tag_num)
{
+ zbx_db_insert_autoincrement(&db_insert, "itemtagid");
zbx_db_insert_execute(&db_insert);
zbx_db_insert_clean(&db_insert);
}
- /* remove deprecated links */
- if (0 != del_itemappids.values_num)
+ if (0 != deleteids.values_num)
{
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
-
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from items_applications where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemappid", del_itemappids.values,
- del_itemappids.values_num);
-
+ sql_offset = 0;
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "delete from item_tag where");
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemtagid", deleteids.values,
+ deleteids.values_num);
DBexecute("%s", sql);
- zbx_free(sql);
+ delete_tag_num = deleteids.values_num;
}
-
- zbx_vector_uint64_destroy(&del_itemappids);
out:
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
+ zbx_free(sql);
+ zbx_vector_uint64_destroy(&deleteids);
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s() added:%d updated:%d removed:%d", __func__, new_tag_num,
+ update_tag_num, delete_tag_num);
+
+ return ret;
+}
static void get_item_info(const void *object, zbx_uint64_t *id, int *discovery_flag, int *lastcheck,
int *ts_delete)
@@ -4293,191 +4245,6 @@ static void get_item_info(const void *object, zbx_uint64_t *id, int *discovery_f
*ts_delete = item->ts_delete;
}
-/******************************************************************************
- * *
- * Function: lld_remove_lost_applications *
- * *
- * Purpose: updates application_discovery lastcheck and ts_delete fields, *
- * removes lost resources *
- * *
- ******************************************************************************/
-static void lld_remove_lost_applications(zbx_uint64_t lld_ruleid, const zbx_vector_ptr_t *applications,
- int lifetime, int lastcheck)
-{
- DB_RESULT result;
- DB_ROW row;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_vector_uint64_t del_applicationids, del_discoveryids, ts_discoveryids, lc_discoveryids;
- zbx_vector_uint64_pair_t discovery_applicationts;
- int i, index;
- const zbx_lld_application_t *application;
- zbx_uint64_t applicationid;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- if (0 == applications->values_num)
- goto out;
-
- zbx_vector_uint64_create(&del_applicationids);
- zbx_vector_uint64_create(&del_discoveryids);
- zbx_vector_uint64_create(&ts_discoveryids);
- zbx_vector_uint64_create(&lc_discoveryids);
- zbx_vector_uint64_pair_create(&discovery_applicationts);
-
- /* prepare application discovery update vector */
- for (i = 0; i < applications->values_num; i++)
- {
- application = (const zbx_lld_application_t *)applications->values[i];
-
- if (0 == application->applicationid)
- continue;
-
- if (0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_DISCOVERED))
- {
- int ts_delete = lld_end_of_life(application->lastcheck, lifetime);
-
- if (lastcheck > ts_delete)
- {
- zbx_vector_uint64_append(&del_applicationids, application->applicationid);
- zbx_vector_uint64_append(&del_discoveryids, application->application_discoveryid);
- }
- else if (application->ts_delete != ts_delete)
- {
- zbx_uint64_pair_t applicationts;
-
- applicationts.first = application->application_discoveryid;
- applicationts.second = ts_delete;
- zbx_vector_uint64_pair_append(&discovery_applicationts, applicationts);
- }
- }
- else
- {
- zbx_vector_uint64_append(&lc_discoveryids, application->application_discoveryid);
- if (0 != application->ts_delete)
- zbx_vector_uint64_append(&ts_discoveryids, application->application_discoveryid);
- }
- }
-
- /* check if the applications are really 'lost' (not discovered by other discovery rules) */
- if (0 != del_applicationids.values_num)
- {
- zbx_vector_uint64_sort(&del_applicationids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select ad.applicationid from application_discovery ad,application_prototype ap"
- " where ad.application_prototypeid=ap.application_prototypeid"
- " and ap.itemid<>" ZBX_FS_UI64
- " and",
- lld_ruleid);
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "ad.applicationid", del_applicationids.values,
- del_applicationids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " order by ad.applicationid desc");
-
- result = DBselect("%s", sql);
-
- sql_offset = 0;
-
- while (NULL != (row = DBfetch(result)))
- {
- ZBX_STR2UINT64(applicationid, row[0]);
-
- if (FAIL != (index = zbx_vector_uint64_bsearch(&del_applicationids, applicationid,
- ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
- {
- zbx_vector_uint64_remove(&del_applicationids, index);
- }
- }
-
- DBfree_result(result);
- }
-
- if (0 == discovery_applicationts.values_num && 0 == del_applicationids.values_num &&
- 0 == del_discoveryids.values_num && 0 == ts_discoveryids.values_num &&
- 0 == lc_discoveryids.values_num)
- {
- goto clean;
- }
-
- /* remove lost applications and update application discovery table */
-
- DBbegin();
-
- DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
-
- for (i = 0; i < discovery_applicationts.values_num; i++)
- {
- zbx_uint64_pair_t *applicationts = &(discovery_applicationts.values[i]);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "update application_discovery"
- " set ts_delete=%d"
- " where application_discoveryid=" ZBX_FS_UI64 ";\n",
- (int)applicationts->second, applicationts->first);
-
- DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
- }
-
- if (0 != del_discoveryids.values_num)
- {
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from application_discovery where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "application_discoveryid",
- del_discoveryids.values, del_discoveryids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
-
- DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
- }
-
- if (0 != ts_discoveryids.values_num)
- {
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update application_discovery"
- " set ts_delete=0 where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "application_discoveryid",
- ts_discoveryids.values, ts_discoveryids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
-
- DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
- }
-
- if (0 != lc_discoveryids.values_num)
- {
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update application_discovery"
- " set lastcheck=%d where", lastcheck);
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "application_discoveryid",
- lc_discoveryids.values, lc_discoveryids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
-
- DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
- }
-
- if (0 != del_applicationids.values_num)
- {
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from applications where");
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "applicationid", del_applicationids.values,
- del_applicationids.values_num);
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
-
- DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
- }
-
- DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
-
- if (16 < sql_offset) /* in ORACLE always present begin..end; */
- DBexecute("%s", sql);
-
- DBcommit();
-clean:
- zbx_free(sql);
-
- zbx_vector_uint64_pair_destroy(&discovery_applicationts);
- zbx_vector_uint64_destroy(&lc_discoveryids);
- zbx_vector_uint64_destroy(&ts_discoveryids);
- zbx_vector_uint64_destroy(&del_discoveryids);
- zbx_vector_uint64_destroy(&del_applicationids);
-out:
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
static void lld_item_links_populate(const zbx_vector_ptr_t *item_prototypes, zbx_vector_ptr_t *lld_rows,
zbx_hashset_t *items_index)
{
@@ -4525,795 +4292,6 @@ void lld_item_links_sort(zbx_vector_ptr_t *lld_rows)
/******************************************************************************
* *
- * Function: lld_application_prototypes_get *
- * *
- * Purpose: gets the discovery rule application prototypes from database *
- * *
- * Parameters: lld_ruleid - [IN] the discovery rule id *
- * application_prototypes - [OUT] the applications prototypes *
- * defined for the discovery rule, *
- * sorted by prototype id *
- * *
- ******************************************************************************/
-static void lld_application_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *application_prototypes)
-{
- DB_RESULT result;
- DB_ROW row;
- zbx_lld_application_prototype_t *application_prototype;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- result = DBselect(
- "select application_prototypeid,name"
- " from application_prototype"
- " where itemid=" ZBX_FS_UI64,
- lld_ruleid);
-
- while (NULL != (row = DBfetch(result)))
- {
- application_prototype = (zbx_lld_application_prototype_t *)zbx_malloc(NULL,
- sizeof(zbx_lld_application_prototype_t));
-
- ZBX_STR2UINT64(application_prototype->application_prototypeid, row[0]);
- application_prototype->itemid = lld_ruleid;
- application_prototype->name = zbx_strdup(NULL, row[1]);
-
- zbx_vector_ptr_append(application_prototypes, application_prototype);
- }
- DBfree_result(result);
-
- zbx_vector_ptr_sort(application_prototypes, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d prototypes", __func__, application_prototypes->values_num);
-}
-
-/******************************************************************************
- * *
- * Function: lld_item_application_prototypes_get *
- * *
- * Purpose: gets the discovery rule item-application link prototypes from *
- * database *
- * *
- * Parameters: item_prototypes - [IN/OUT] item prototypes *
- * application_prototypes - [IN] the application prototypes *
- * defined for the discovery rule *
- * *
- ******************************************************************************/
-static void lld_item_application_prototypes_get(const zbx_vector_ptr_t *item_prototypes,
- const zbx_vector_ptr_t *application_prototypes)
-{
- DB_RESULT result;
- DB_ROW row;
- int i, index;
- zbx_uint64_t application_prototypeid, itemid;
- zbx_vector_uint64_t item_prototypeids;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_lld_item_application_ref_t *item_application_prototype;
- zbx_lld_item_prototype_t *item_prototype;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_vector_uint64_create(&item_prototypeids);
-
- /* get item prototype links to application prototypes */
-
- for (i = 0; i < item_prototypes->values_num; i++)
- {
- item_prototype = (zbx_lld_item_prototype_t *)item_prototypes->values[i];
-
- zbx_vector_uint64_append(&item_prototypeids, item_prototype->itemid);
- }
-
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
- "select application_prototypeid,itemid"
- " from item_application_prototype"
- " where");
-
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid",
- item_prototypeids.values, item_prototypeids.values_num);
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- ZBX_STR2UINT64(application_prototypeid, row[0]);
-
- if (FAIL == (index = zbx_vector_ptr_search(application_prototypes, &application_prototypeid,
- ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
- {
- THIS_SHOULD_NEVER_HAPPEN;
- continue;
- }
-
- item_application_prototype = (zbx_lld_item_application_ref_t *)zbx_malloc(NULL,
- sizeof(zbx_lld_item_application_ref_t));
-
- item_application_prototype->application_prototype = (zbx_lld_application_prototype_t *)application_prototypes->values[index];
- item_application_prototype->applicationid = 0;
-
- ZBX_STR2UINT64(itemid, row[1]);
- index = zbx_vector_ptr_bsearch(item_prototypes, &itemid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
- item_prototype = (zbx_lld_item_prototype_t *)item_prototypes->values[index];
-
- zbx_vector_ptr_append(&item_prototype->applications, item_application_prototype);
- }
- DBfree_result(result);
-
- /* get item prototype links to real applications */
-
- sql_offset = 0;
- zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
- "select applicationid,itemid"
- " from items_applications"
- " where");
-
- DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid",
- item_prototypeids.values, item_prototypeids.values_num);
-
- result = DBselect("%s", sql);
-
- while (NULL != (row = DBfetch(result)))
- {
- item_application_prototype = (zbx_lld_item_application_ref_t *)zbx_malloc(NULL,
- sizeof(zbx_lld_item_application_ref_t));
-
- item_application_prototype->application_prototype = NULL;
- ZBX_STR2UINT64(item_application_prototype->applicationid, row[0]);
-
- ZBX_STR2UINT64(itemid, row[1]);
- index = zbx_vector_ptr_bsearch(item_prototypes, &itemid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
- item_prototype = (zbx_lld_item_prototype_t *)item_prototypes->values[index];
-
- zbx_vector_ptr_append(&item_prototype->applications, item_application_prototype);
- }
- DBfree_result(result);
-
- zbx_free(sql);
- zbx_vector_uint64_destroy(&item_prototypeids);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-/******************************************************************************
- * *
- * Function: lld_applications_get *
- * *
- * Purpose: gets applications previously discovered by the discovery rule *
- * *
- * Parameters: lld_ruleid - [IN] the discovery rule id *
- * applications - [OUT] the applications *
- * *
- ******************************************************************************/
-static void lld_applications_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *applications)
-{
- DB_RESULT result;
- DB_ROW row;
- zbx_lld_application_t *application;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- result = DBselect(
- "select a.applicationid,a.name,ap.application_prototypeid,ad.lastcheck,ad.ts_delete,ad.name,"
- "ad.application_discoveryid"
- " from applications a,application_discovery ad,application_prototype ap"
- " where ap.itemid=" ZBX_FS_UI64
- " and ad.application_prototypeid=ap.application_prototypeid"
- " and a.applicationid=ad.applicationid",
- lld_ruleid);
-
- while (NULL != (row = DBfetch(result)))
- {
- application = (zbx_lld_application_t *)zbx_malloc(NULL, sizeof(zbx_lld_application_t));
-
- ZBX_STR2UINT64(application->applicationid, row[0]);
- ZBX_STR2UINT64(application->application_prototypeid, row[2]);
- ZBX_STR2UINT64(application->application_discoveryid, row[6]);
- application->name = zbx_strdup(NULL, row[1]);
- application->lastcheck = atoi(row[3]);
- application->ts_delete = atoi(row[4]);
- application->name_proto = zbx_strdup(NULL, row[5]);
- application->name_orig = NULL;
- application->flags = ZBX_FLAG_LLD_APPLICATION_UNSET;
- application->lld_row = NULL;
-
- zbx_vector_ptr_append(applications, application);
- }
- DBfree_result(result);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d applications", __func__, applications->values_num);
-}
-
-/******************************************************************************
- * *
- * Function: lld_application_make *
- * *
- * Purpose: create a new application or mark an existing application as *
- * discovered based on prototype and lld row *
- * *
- * Parameters: application_prototype - [IN] the application prototype *
- * lld_row - [IN] the lld row *
- * lld_macro_paths - [IN] use json path to extract from *
- * lld_row *
- * applications - [IN/OUT] the applications *
- * applications_index - [IN/OUT] the application index by *
- * prototype id and lld row *
- * *
- ******************************************************************************/
-static void lld_application_make(const zbx_lld_application_prototype_t *application_prototype,
- const zbx_lld_row_t *lld_row, const zbx_vector_ptr_t *lld_macro_paths, zbx_vector_ptr_t *applications,
- zbx_hashset_t *applications_index)
-{
- zbx_lld_application_t *application;
- zbx_lld_application_index_t *application_index, application_index_local;
- struct zbx_json_parse *jp_row = (struct zbx_json_parse *)&lld_row->jp_row;
- char *buffer = NULL;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s(), proto %s", __func__, application_prototype->name);
-
- application_index_local.application_prototypeid = application_prototype->application_prototypeid;
- application_index_local.lld_row = lld_row;
-
- if (NULL == (application_index = (zbx_lld_application_index_t *)zbx_hashset_search(applications_index, &application_index_local)))
- {
- application = (zbx_lld_application_t *)zbx_malloc(NULL, sizeof(zbx_lld_application_t));
- application->applicationid = 0;
- application->application_prototypeid = application_prototype->application_prototypeid;
- application->application_discoveryid = 0;
- application->ts_delete = 0;
-
- application->name = zbx_strdup(NULL, application_prototype->name);
- substitute_lld_macros(&application->name, jp_row, lld_macro_paths, ZBX_MACRO_ANY, NULL, 0);
- zbx_lrtrim(application->name, ZBX_WHITESPACE);
-
- application->name_proto = zbx_strdup(NULL, application_prototype->name);
- application->name_orig = NULL;
- application->flags = ZBX_FLAG_LLD_APPLICATION_ADD_DISCOVERY;
- application->lld_row = lld_row;
-
- zbx_vector_ptr_append(applications, application);
-
- application_index_local.application = application;
- zbx_hashset_insert(applications_index, &application_index_local, sizeof(zbx_lld_application_index_t));
-
- zabbix_log(LOG_LEVEL_TRACE, "%s(): created new application, proto %s, name %s", __func__,
- application_prototype->name, application->name);
- }
- else
- {
- application = application_index->application;
-
- if (0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_UPDATE_NAME))
- {
- buffer = zbx_strdup(NULL, application_prototype->name);
- substitute_lld_macros(&buffer, jp_row, lld_macro_paths, ZBX_MACRO_ANY, NULL, 0);
- zbx_lrtrim(buffer, ZBX_WHITESPACE);
-
- if (0 != strcmp(application->name, buffer))
- {
- application->name_orig = application->name;
- application->name = buffer;
- application->flags |= ZBX_FLAG_LLD_APPLICATION_UPDATE_NAME;
- zabbix_log(LOG_LEVEL_TRACE, "%s(): updated application name to %s", __func__,
- application->name);
- }
- else
- zbx_free(buffer);
- }
- }
-
- application->flags |= ZBX_FLAG_LLD_APPLICATION_DISCOVERED;
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-/******************************************************************************
- * *
- * Function: lld_applications_make *
- * *
- * Purpose: makes new applications and marks old applications as discovered *
- * based on application prototypes and lld rows *
- * *
- * Parameters: application_prototypes - [IN] the application prototypes *
- * lld_rows - [IN] the lld rows *
- * applications - [IN/OUT] the applications *
- * applications_index - [OUT] the application index by *
- * prototype id and lld row *
- * *
- ******************************************************************************/
-static void lld_applications_make(const zbx_vector_ptr_t *application_prototypes,
- const zbx_vector_ptr_t *lld_rows, const zbx_vector_ptr_t *lld_macro_paths,
- zbx_vector_ptr_t *applications, zbx_hashset_t *applications_index)
-{
- int i, j;
- zbx_lld_application_t *application;
- zbx_lld_row_t *lld_row;
- zbx_lld_application_index_t application_index_local;
- char *buffer = NULL;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- /* index existing applications */
-
- for (i = 0; i < applications->values_num; i++)
- {
- application = (zbx_lld_application_t *)applications->values[i];
-
- for (j = 0; j < lld_rows->values_num; j++)
- {
- lld_row = (zbx_lld_row_t *)lld_rows->values[j];
-
- buffer = zbx_strdup(buffer, application->name_proto);
- substitute_lld_macros(&buffer, &lld_row->jp_row, lld_macro_paths, ZBX_MACRO_ANY, NULL, 0);
- zbx_lrtrim(buffer, ZBX_WHITESPACE);
-
- if (0 == strcmp(application->name, buffer))
- {
- application_index_local.application_prototypeid = application->application_prototypeid;
- application_index_local.lld_row = lld_row;
- application_index_local.application = application;
- zbx_hashset_insert(applications_index, &application_index_local,
- sizeof(application_index_local));
-
- application->lld_row = lld_row;
- }
- }
- }
-
- zbx_free(buffer);
-
- /* make the applications */
- for (i = 0; i < application_prototypes->values_num; i++)
- {
- for (j = 0; j < lld_rows->values_num; j++)
- {
- lld_application_make((zbx_lld_application_prototype_t *)application_prototypes->values[i],
- (zbx_lld_row_t *)lld_rows->values[j], lld_macro_paths, applications,
- applications_index);
- }
- }
-
- zbx_vector_ptr_sort(applications, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d applications", __func__, applications->values_num);
-}
-
-/******************************************************************************
- * *
- * Function: lld_applications_validate *
- * *
- * Purpose: validates the discovered and renamed applications *
- * *
- * Parameters: hostid - [IN] host id *
- * lld_ruleid - [IN] the discovery rule id *
- * applications - [IN/OUT] the applications *
- * applications_index - [OUT] the application index by *
- * prototype id and lld row *
- * error - [IN/OUT] the lld error message *
- * *
- ******************************************************************************/
-static void lld_applications_validate(zbx_uint64_t hostid, zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *applications,
- zbx_hashset_t *applications_index, char **error)
-{
- int i, j, index;
- DB_RESULT result;
- DB_ROW row;
- zbx_lld_application_t *application, *new_application, application_local;
- char *sql = NULL;
- size_t sql_alloc = 0, sql_offset = 0;
- zbx_vector_str_t names_new, names_old;
- zbx_lld_application_index_t *application_index, application_index_local;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- if (0 == applications->values_num)
- goto out;
-
- zbx_vector_str_create(&names_new);
- zbx_vector_str_create(&names_old);
-
- /* check for conflicting application names in the discovered applications */
-
- for (i = 0; i < applications->values_num; i++)
- {
- application = (zbx_lld_application_t *)applications->values[i];
-
- if (0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_DISCOVERED))
- continue;
-
- if (0 != application->applicationid && 0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_UPDATE_NAME))
- continue;
-
- /* iterate in reverse order so existing applications would have more priority */
- /* than new applications which have 0 applicationid and therefore are located */
- /* at the beginning of applications vector which is sorted by applicationid */
- for (j = applications->values_num - 1; j > i; j--)
- {
- zbx_lld_application_t *application_compare = (zbx_lld_application_t *)applications->values[j];
-
- if (0 != strcmp(application->name, application_compare->name))
- continue;
-
- /* Applications with matching names are validated depending on their prototypes. */
- /* If they are discovered by different prototypes we must fail with appropriate */
- /* lld error. */
- /* Otherwise we 'merge' application by updating index of the validated */
- /* validated application to point at the application with the same name. */
- /* In both cases the validated application is flagged as non-discovered. */
- application->flags &= ~ZBX_FLAG_LLD_ITEM_DISCOVERED;
-
- /* fail if application has different prototype */
- if (application->application_prototypeid != application_compare->application_prototypeid)
- {
- *error = zbx_strdcatf(*error, "Cannot %s application:"
- " application with the same name \"%s\" already exists.\n",
- (0 != application->applicationid ? "update" : "create"),
- application->name);
-
- break;
- }
-
- /* update application index to use the matching application */
-
- application_index_local.application_prototypeid = application->application_prototypeid;
- application_index_local.lld_row = application->lld_row;
-
- if (NULL == (application_index = (zbx_lld_application_index_t *)zbx_hashset_search(applications_index,
- &application_index_local)))
- {
- THIS_SHOULD_NEVER_HAPPEN;
- break;
- }
-
- application_index->application = application_compare;
- break;
- }
-
- /* Prepare name lists to resolve naming conflicts with applications */
- /* discovered by other discovery rules: */
- /* names_new - to check if discovered/renamed application names */
- /* don't match existing applications discovered by */
- /* other discovery rules */
- /* names_old - to check if renamed applications were also */
- /* discovered by other discovery rules */
- if (i == j)
- {
- zbx_vector_str_append(&names_new, application->name);
-
- if (NULL != application->name_orig)
- zbx_vector_str_append(&names_old, application->name_orig);
- }
- }
-
- /* validate new/renamed application names against applications discovered */
- /* by other discovery rules */
-
- if (0 != names_new.values_num)
- {
- zbx_vector_str_sort(&names_new, ZBX_DEFAULT_STR_COMPARE_FUNC);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select applicationid,name,flags"
- " from applications"
- " where hostid=" ZBX_FS_UI64
- " and",
- hostid);
- DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "name",
- (const char **)names_new.values, names_new.values_num);
-
- result = DBselect("%s", sql);
-
- application_local.flags = ZBX_FLAG_LLD_APPLICATION_DISCOVERED;
-
- while (NULL != (row = DBfetch(result)))
- {
- application_local.name = row[1];
-
- if (FAIL == (index = zbx_vector_ptr_search(applications, &application_local,
- lld_application_compare_name)))
- {
- THIS_SHOULD_NEVER_HAPPEN;
- continue;
- }
-
- application = (zbx_lld_application_t *)applications->values[index];
-
- /* only discovered applications can be 'shared' between discovery rules */
- if (ZBX_FLAG_DISCOVERY_CREATED != atoi(row[2]))
- {
- /* conflicting application name, reset discovery flags */
- application->flags = ZBX_FLAG_LLD_APPLICATION_UNSET;
-
- *error = zbx_strdcatf(*error, "Cannot create application:"
- " non-discovered application"
- " with the same name \"%s\" already exists.\n",
- application->name);
-
- continue;
- }
-
- if (0 != (application->flags & ZBX_FLAG_LLD_APPLICATION_UPDATE_NAME))
- {
- /* During discovery process the application was renamed to an */
- /* application already discovered by another discovery rule. */
- /* In this case we must delete the old application and relink */
- /* its items to the application we have found. */
-
- /* create a pseudo application to remove the renamed application */
-
- new_application = (zbx_lld_application_t *)zbx_malloc(NULL,
- sizeof(zbx_lld_application_t));
-
- memset(new_application, 0, sizeof(zbx_lld_application_t));
- new_application->applicationid = application->applicationid;
- new_application->flags = ZBX_FLAG_LLD_APPLICATION_REMOVE;
-
- zbx_vector_ptr_append(applications, new_application);
-
- /* update application flags so that instead of renaming it a new */
- /* discovery record is created */
-
- application->application_discoveryid = 0;
- application->flags &= ~ZBX_FLAG_LLD_APPLICATION_UPDATE_NAME;
- application->flags |= ZBX_FLAG_LLD_APPLICATION_ADD_DISCOVERY;
- }
-
- /* reuse application created by another discovery rule */
- ZBX_STR2UINT64(application->applicationid, row[0]);
- }
- DBfree_result(result);
- }
-
- /* if an application shared with other discovery rule has been renamed we must */
- /* create a new application with the new name instead of renaming the old one */
-
- if (0 != names_old.values_num)
- {
- sql_offset = 0;
-
- zbx_vector_str_sort(&names_old, ZBX_DEFAULT_STR_COMPARE_FUNC);
-
- zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
- "select a.name"
- " from applications a,application_discovery ad,application_prototype ap"
- " where a.applicationid=ad.applicationid"
- " and ad.application_prototypeid=ap.application_prototypeid"
- " and a.hostid=" ZBX_FS_UI64
- " and ap.itemid<>" ZBX_FS_UI64
- " and",
- hostid, lld_ruleid);
- DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "a.name",
- (const char **)names_old.values, names_old.values_num);
-
- result = DBselect("%s", sql);
-
- application_local.flags = ZBX_FLAG_LLD_APPLICATION_DISCOVERED;
-
- while (NULL != (row = DBfetch(result)))
- {
- application_local.name_orig = row[0];
-
- if (FAIL == (index = zbx_vector_ptr_search(applications, &application_local,
- lld_application_compare_name_orig)))
- {
- THIS_SHOULD_NEVER_HAPPEN;
- continue;
- }
-
- application = (zbx_lld_application_t *)applications->values[index];
-
- /* add a pseudo application to remove the application discovery record */
- /* of the shared application and current discovery rule */
- new_application = (zbx_lld_application_t *)zbx_malloc(NULL, sizeof(zbx_lld_application_t));
- memset(new_application, 0, sizeof(zbx_lld_application_t));
- new_application->applicationid = application->applicationid;
- new_application->application_prototypeid = application->application_prototypeid;
- new_application->application_discoveryid = application->application_discoveryid;
- new_application->flags = ZBX_FLAG_LLD_APPLICATION_REMOVE_DISCOVERY;
- zbx_vector_ptr_append(applications, new_application);
-
- /* reset applicationid, application_discoveryid and flags */
- /* so a new application is created instead of renaming the shared one */
- application->applicationid = 0;
- application->application_discoveryid = 0;
- application->flags = ZBX_FLAG_LLD_APPLICATION_ADD_DISCOVERY |
- ZBX_FLAG_LLD_APPLICATION_DISCOVERED;
- }
- DBfree_result(result);
- }
-
- zbx_vector_str_destroy(&names_old);
- zbx_vector_str_destroy(&names_new);
-
- zbx_free(sql);
-
- zbx_vector_ptr_sort(applications, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
-out:
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-}
-
-/******************************************************************************
- * *
- * Function: lld_items_applications_get *
- * *
- * Purpose: gets item-application links for the lld rule *
- * *
- * Parameters: lld_rule - [IN] the lld rule *
- * items_applications - [OUT] the item-application links *
- * *
- ******************************************************************************/
-static void lld_items_applications_get(zbx_uint64_t lld_ruleid, zbx_hashset_t *items_applications)
-{
- DB_RESULT result;
- DB_ROW row;
- zbx_lld_item_application_t item_application;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- result = DBselect(
- "select ia.itemappid,ia.itemid,ia.applicationid"
- " from items_applications ia,item_discovery id1,item_discovery id2"
- " where id1.itemid=ia.itemid"
- " and id1.parent_itemid=id2.itemid"
- " and id2.parent_itemid=" ZBX_FS_UI64,
- lld_ruleid);
-
- item_application.application_ref.application = NULL;
- item_application.item_ref.item = NULL;
-
- while (NULL != (row = DBfetch(result)))
- {
- ZBX_STR2UINT64(item_application.itemappid, row[0]);
- ZBX_STR2UINT64(item_application.item_ref.itemid, row[1]);
- ZBX_STR2UINT64(item_application.application_ref.applicationid, row[2]);
- item_application.flags = ZBX_FLAG_LLD_ITEM_APPLICATION_UNSET;
-
- zbx_hashset_insert(items_applications, &item_application, sizeof(zbx_lld_item_application_t));
- }
- DBfree_result(result);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d links", __func__, items_applications->num_data);
-}
-
-/******************************************************************************
- * *
- * Function: lld_items_applications_make *
- * *
- * Purpose: makes new item-application links and marks existing links as *
- * discovered based on item_prototypes applications links *
- * *
- * Parameters: item_prototypes - [IN] the item prototypes *
- * items - [IN] the items *
- * applications_index - [IN] the application index by *
- * prototype id and lld row *
- * items_applications - [IN/OUT] the item-application links *
- * *
- ******************************************************************************/
-static void lld_items_applications_make(const zbx_vector_ptr_t *item_prototypes, const zbx_vector_ptr_t *items,
- zbx_hashset_t *applications_index, zbx_hashset_t *items_applications)
-{
- int i, j, index;
- zbx_lld_item_application_t *item_application, item_application_local;
- zbx_lld_application_t *application;
- zbx_lld_item_prototype_t *item_prototype;
- zbx_lld_item_t *item;
- zbx_lld_item_application_ref_t *itemapp_prototype;
- zbx_lld_application_index_t *application_index, application_index_local;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- item_application_local.itemappid = 0;
- item_application_local.flags = ZBX_FLAG_LLD_ITEM_APPLICATION_DISCOVERED;
-
- for (i = 0; i < items->values_num; i++)
- {
- item = (zbx_lld_item_t *)items->values[i];
-
- if (0 == (item->flags & ZBX_FLAG_LLD_ITEM_DISCOVERED))
- continue;
-
- /* if item is discovered its prototype must be in item_prototypes vector */
- index = zbx_vector_ptr_bsearch(item_prototypes, &item->parent_itemid,
- ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
- item_prototype = (zbx_lld_item_prototype_t *)item_prototypes->values[index];
-
- application_index_local.lld_row = item->lld_row;
-
- if (0 == (item_application_local.item_ref.itemid = item->itemid))
- item_application_local.item_ref.item = item;
- else
- item_application_local.item_ref.item = NULL;
-
- for (j = 0; j < item_prototype->applications.values_num; j++)
- {
- itemapp_prototype = (zbx_lld_item_application_ref_t *)item_prototype->applications.values[j];
-
- if (NULL != itemapp_prototype->application_prototype)
- {
- application_index_local.application_prototypeid =
- itemapp_prototype->application_prototype->application_prototypeid;
-
- if (NULL == (application_index = (zbx_lld_application_index_t *)zbx_hashset_search(applications_index,
- &application_index_local)))
- {
- continue;
- }
-
- application = application_index->application;
-
- if (0 == (application->flags & ZBX_FLAG_LLD_APPLICATION_DISCOVERED))
- continue;
-
- if (0 == (item_application_local.application_ref.applicationid =
- application->applicationid))
- {
- item_application_local.application_ref.application = application;
- }
- else
- item_application_local.application_ref.application = NULL;
- }
- else
- {
- item_application_local.application_ref.application = NULL;
- item_application_local.application_ref.applicationid = itemapp_prototype->applicationid;
- }
-
- if (NULL == (item_application = (zbx_lld_item_application_t *)zbx_hashset_search(items_applications,
- &item_application_local)))
- {
- item_application = (zbx_lld_item_application_t *)zbx_hashset_insert(items_applications, &item_application_local,
- sizeof(zbx_lld_item_application_t));
- }
-
- item_application->flags = ZBX_FLAG_LLD_ITEM_APPLICATION_DISCOVERED;
- }
- }
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d links", __func__, items_applications->num_data);
-}
-
-/******************************************************************************
- * *
- * Function: lld_validate_application_with_links *
- * *
- * Purpose: mark applications with no links to item_prototypes not to be *
- * discovered *
- * *
- * Parameters: applications - [IN/OUT] the applications *
- * items_applications - [IN] the item-application links *
- * *
- ******************************************************************************/
-static void lld_validate_application_with_links(zbx_vector_ptr_t *applications, zbx_hashset_t *items_applications)
-{
- zbx_lld_item_application_t *link;
- zbx_hashset_iter_t iter;
- int i;
-
- for (i = 0; i < applications->values_num; i++)
- {
- zbx_lld_application_t *app = (zbx_lld_application_t *)applications->values[i];
-
- if (0 != app->applicationid)
- continue;
-
- zbx_hashset_iter_reset(items_applications, &iter);
-
- while (NULL != (link = (zbx_lld_item_application_t *)zbx_hashset_iter_next(&iter)))
- {
- if (app == link->application_ref.application
- && 0 != (link->flags & ZBX_FLAG_LLD_ITEM_APPLICATION_DISCOVERED))
- {
- break;
- }
- }
-
- if (NULL == link)
- app->flags = ZBX_FLAG_LLD_APPLICATION_UNSET;
- }
-}
-
-/******************************************************************************
- * *
* Function: lld_item_prototypes_get *
* *
* Purpose: load discovery rule item prototypes *
@@ -5329,6 +4307,7 @@ static void lld_item_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *i
zbx_lld_item_prototype_t *item_prototype;
zbx_lld_item_preproc_t *preproc_op;
zbx_lld_item_param_t *item_param;
+ zbx_lld_item_tag_t *item_tag;
zbx_uint64_t itemid;
int index, i;
@@ -5400,9 +4379,9 @@ static void lld_item_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *i
ZBX_STR2UCHAR(item_prototype->discover, row[44]);
zbx_vector_ptr_create(&item_prototype->lld_rows);
- zbx_vector_ptr_create(&item_prototype->applications);
zbx_vector_ptr_create(&item_prototype->preproc_ops);
zbx_vector_ptr_create(&item_prototype->item_params);
+ zbx_vector_ptr_create(&item_prototype->item_tags);
zbx_vector_ptr_append(item_prototypes, item_prototype);
}
@@ -5485,6 +4464,41 @@ static void lld_item_prototypes_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *i
item_prototype = (zbx_lld_item_prototype_t *)item_prototypes->values[i];
zbx_vector_ptr_sort(&item_prototype->item_params, lld_item_param_sort_by_name);
}
+
+ /* get item prototype tags */
+
+ result = DBselect(
+ "select it.itemid,it.tag,it.value"
+ " from item_tag it,item_discovery id"
+ " where it.itemid=id.itemid"
+ " and id.parent_itemid=" ZBX_FS_UI64,
+ lld_ruleid);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ ZBX_STR2UINT64(itemid, row[0]);
+
+ if (FAIL == (index = zbx_vector_ptr_bsearch(item_prototypes, &itemid,
+ ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ continue;
+ }
+
+ item_prototype = (zbx_lld_item_prototype_t *)item_prototypes->values[index];
+
+ item_tag = (zbx_lld_item_tag_t *)zbx_malloc(NULL, sizeof(zbx_lld_item_tag_t));
+ item_tag->tag = zbx_strdup(NULL, row[1]);
+ item_tag->value = zbx_strdup(NULL, row[2]);
+ zbx_vector_ptr_append(&item_prototype->item_tags, item_tag);
+ }
+ DBfree_result(result);
+
+ for (i = 0; i < item_prototypes->values_num; i++)
+ {
+ item_prototype = (zbx_lld_item_prototype_t *)item_prototypes->values[i];
+ zbx_vector_ptr_sort(&item_prototype->item_tags, lld_item_tag_sort_by_tag);
+ }
out:
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d prototypes", __func__, item_prototypes->values_num);
}
@@ -5541,8 +4555,8 @@ static void lld_link_dependent_items(zbx_vector_ptr_t *items, zbx_hashset_t *ite
int lld_update_items(zbx_uint64_t hostid, zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *lld_rows,
const zbx_vector_ptr_t *lld_macro_paths, char **error, int lifetime, int lastcheck)
{
- zbx_vector_ptr_t applications, application_prototypes, items, item_prototypes, item_dependencies;
- zbx_hashset_t applications_index, items_index, items_applications;
+ zbx_vector_ptr_t items, item_prototypes, item_dependencies;
+ zbx_hashset_t items_index;
int ret = SUCCEED, host_record_is_locked = 0;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
@@ -5554,30 +4568,15 @@ int lld_update_items(zbx_uint64_t hostid, zbx_uint64_t lld_ruleid, zbx_vector_pt
if (0 == item_prototypes.values_num)
goto out;
- zbx_vector_ptr_create(&application_prototypes);
-
- lld_application_prototypes_get(lld_ruleid, &application_prototypes);
-
- zbx_vector_ptr_create(&applications);
- zbx_hashset_create(&applications_index, application_prototypes.values_num * lld_rows->values_num,
- lld_application_index_hash_func, lld_application_index_compare_func);
-
zbx_vector_ptr_create(&items);
zbx_hashset_create(&items_index, item_prototypes.values_num * lld_rows->values_num, lld_item_index_hash_func,
lld_item_index_compare_func);
- zbx_hashset_create(&items_applications, 100, lld_item_application_hash_func, lld_item_application_compare_func);
-
- lld_applications_get(lld_ruleid, &applications);
- lld_applications_make(&application_prototypes, lld_rows, lld_macro_paths, &applications, &applications_index);
- lld_applications_validate(hostid, lld_ruleid, &applications, &applications_index, error);
-
- lld_item_application_prototypes_get(&item_prototypes, &application_prototypes);
-
lld_items_get(&item_prototypes, &items);
lld_items_make(&item_prototypes, lld_rows, lld_macro_paths, &items, &items_index, error);
lld_items_preproc_make(&item_prototypes, lld_macro_paths, &items);
lld_items_param_make(&item_prototypes, lld_macro_paths, &items);
+ lld_items_tags_make(&item_prototypes, lld_macro_paths, &items);
lld_link_dependent_items(&items, &items_index);
@@ -5586,20 +4585,13 @@ int lld_update_items(zbx_uint64_t hostid, zbx_uint64_t lld_ruleid, zbx_vector_pt
lld_items_validate(hostid, &items, &item_prototypes, &item_dependencies, error);
- lld_items_applications_get(lld_ruleid, &items_applications);
- lld_items_applications_make(&item_prototypes, &items, &applications_index, &items_applications);
- lld_validate_application_with_links(&applications, &items_applications);
-
DBbegin();
if (SUCCEED == lld_items_save(hostid, &item_prototypes, &items, &items_index, &host_record_is_locked) &&
SUCCEED == lld_items_preproc_save(hostid, &items, &host_record_is_locked) &&
SUCCEED == lld_items_param_save(hostid, &items, &host_record_is_locked) &&
- SUCCEED == lld_applications_save(hostid, &applications, &application_prototypes,
- &host_record_is_locked))
+ SUCCEED == lld_items_tags_save(hostid, &items, &host_record_is_locked))
{
- lld_items_applications_save(&items_applications, &items);
-
if (ZBX_DB_OK != DBcommit())
{
ret = FAIL;
@@ -5615,9 +4607,7 @@ int lld_update_items(zbx_uint64_t hostid, zbx_uint64_t lld_ruleid, zbx_vector_pt
lld_item_links_populate(&item_prototypes, lld_rows, &items_index);
lld_remove_lost_objects("item_discovery", "itemid", &items, lifetime, lastcheck, DBdelete_items, get_item_info);
- lld_remove_lost_applications(lld_ruleid, &applications, lifetime, lastcheck);
clean:
- zbx_hashset_destroy(&items_applications);
zbx_hashset_destroy(&items_index);
zbx_vector_ptr_clear_ext(&item_dependencies, zbx_ptr_free);
@@ -5626,14 +4616,6 @@ clean:
zbx_vector_ptr_clear_ext(&items, (zbx_clean_func_t)lld_item_free);
zbx_vector_ptr_destroy(&items);
- zbx_hashset_destroy(&applications_index);
-
- zbx_vector_ptr_clear_ext(&applications, (zbx_clean_func_t)lld_application_free);
- zbx_vector_ptr_destroy(&applications);
-
- zbx_vector_ptr_clear_ext(&application_prototypes, (zbx_clean_func_t)lld_application_prototype_free);
- zbx_vector_ptr_destroy(&application_prototypes);
-
zbx_vector_ptr_clear_ext(&item_prototypes, (zbx_clean_func_t)lld_item_prototype_free);
out:
zbx_vector_ptr_destroy(&item_prototypes);
diff --git a/src/zabbix_server/lld/lld_manager.c b/src/zabbix_server/lld/lld_manager.c
index 9f545f63000..0b428f4644d 100644
--- a/src/zabbix_server/lld/lld_manager.c
+++ b/src/zabbix_server/lld/lld_manager.c
@@ -301,7 +301,7 @@ static void lld_register_worker(zbx_lld_manager_t *manager, zbx_ipc_client_t *cl
******************************************************************************/
static void lld_queue_rule(zbx_lld_manager_t *manager, zbx_lld_rule_t *rule)
{
- zbx_binary_heap_elem_t elem = {rule->itemid, rule};
+ zbx_binary_heap_elem_t elem = {rule->hostid, rule};
zbx_binary_heap_insert(&manager->rule_queue, &elem);
}
@@ -318,7 +318,7 @@ static void lld_queue_rule(zbx_lld_manager_t *manager, zbx_lld_rule_t *rule)
******************************************************************************/
static void lld_queue_request(zbx_lld_manager_t *manager, const zbx_ipc_message_t *message)
{
- zbx_uint64_t itemid;
+ zbx_uint64_t hostid;
zbx_lld_rule_t *rule;
zbx_lld_data_t *data;
@@ -326,32 +326,49 @@ static void lld_queue_request(zbx_lld_manager_t *manager, const zbx_ipc_message_
data = (zbx_lld_data_t *)zbx_malloc(NULL, sizeof(zbx_lld_data_t));
data->next = NULL;
- zbx_lld_deserialize_item_value(message->data, &itemid, &data->value, &data->ts, &data->meta, &data->lastlogsize,
- &data->mtime, &data->error);
- if (NULL == (rule = zbx_hashset_search(&manager->rule_index, &itemid)))
+ zbx_lld_deserialize_item_value(message->data, &data->itemid, &hostid, &data->value, &data->ts, &data->meta,
+ &data->lastlogsize, &data->mtime, &data->error);
+
+ if (NULL == (rule = zbx_hashset_search(&manager->rule_index, &hostid)))
{
- zbx_lld_rule_t rule_local = {itemid, 0, data, data};
+ zbx_lld_rule_t rule_local = {.hostid = hostid, .values_num = 0, .tail = data, .head = data};
+
+ data->prev = NULL;
rule = zbx_hashset_insert(&manager->rule_index, &rule_local, sizeof(rule_local));
lld_queue_rule(manager, rule);
}
else
{
- if (0 == data->meta && 0 == zbx_strcmp_null(data->error, rule->tail->error) &&
- 0 == zbx_strcmp_null(data->value, rule->tail->value))
+ if (0 == data->meta)
{
- zabbix_log(LOG_LEVEL_DEBUG, "skip repeating discovery rule values: " ZBX_FS_UI64, itemid);
+ zbx_lld_data_t *data_ptr;
+
+ for (data_ptr = rule->tail; NULL != data_ptr; data_ptr = data_ptr->prev)
+ {
+ /* if there are multiple values then they should be different, check only last one */
+ if (data_ptr->itemid == data->itemid)
+ break;
+ }
+
+ if (NULL != data_ptr && 0 == zbx_strcmp_null(data->error, data_ptr->error) &&
+ 0 == zbx_strcmp_null(data->value, data_ptr->value))
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "skip repeating values for discovery rule:" ZBX_FS_UI64,
+ data->itemid);
- lld_data_free(data);
- goto out;
+ lld_data_free(data);
+ goto out;
+ }
}
+ data->prev = rule->tail;
rule->tail->next = data;
rule->tail = data;
}
- zabbix_log(LOG_LEVEL_DEBUG, "queuing discovery rule: " ZBX_FS_UI64, itemid);
+ zabbix_log(LOG_LEVEL_DEBUG, "queuing discovery rule:" ZBX_FS_UI64, data->itemid);
rule->values_num++;
manager->queued_num++;
@@ -381,7 +398,7 @@ static void lld_process_next_request(zbx_lld_manager_t *manager, zbx_lld_worker_
zbx_binary_heap_remove_min(&manager->rule_queue);
data = worker->rule->head;
- buf_len = zbx_lld_serialize_item_value(&buf, worker->rule->itemid, data->value, &data->ts, data->meta,
+ buf_len = zbx_lld_serialize_item_value(&buf, data->itemid, 0, data->value, &data->ts, data->meta,
data->lastlogsize, data->mtime, data->error);
zbx_ipc_client_send(worker->client, ZBX_IPC_LLD_TASK, buf, buf_len);
zbx_free(buf);
@@ -429,7 +446,7 @@ static void lld_process_result(zbx_lld_manager_t *manager, zbx_ipc_client_t *cli
worker = lld_get_worker_by_client(manager, client);
- zabbix_log(LOG_LEVEL_DEBUG, "discovery rule:" ZBX_FS_UI64 " has been processed", worker->rule->itemid);
+ zabbix_log(LOG_LEVEL_DEBUG, "discovery rule:" ZBX_FS_UI64 " has been processed", worker->rule->head->itemid);
rule = worker->rule;
worker->rule = NULL;
@@ -443,6 +460,7 @@ static void lld_process_result(zbx_lld_manager_t *manager, zbx_ipc_client_t *cli
}
else
{
+ rule->head->prev = NULL;
rule->values_num--;
lld_queue_rule(manager, rule);
}
@@ -487,8 +505,8 @@ static void lld_process_diag_stats(zbx_lld_manager_t *manager, zbx_ipc_client_t
******************************************************************************/
static int lld_diag_item_compare_values_desc(const void *d1, const void *d2)
{
- zbx_lld_rule_t *r1 = *(zbx_lld_rule_t **)d1;
- zbx_lld_rule_t *r2 = *(zbx_lld_rule_t **)d2;
+ zbx_lld_rule_info_t *r1 = *(zbx_lld_rule_info_t **)d1;
+ zbx_lld_rule_info_t *r2 = *(zbx_lld_rule_info_t **)d2;
return r2->values_num - r1->values_num;
}
@@ -512,27 +530,48 @@ static void lld_process_top_items(zbx_lld_manager_t *manager, zbx_ipc_client_t *
zbx_uint32_t data_len;
zbx_vector_ptr_t view;
zbx_hashset_iter_t iter;
- zbx_lld_rule_t *item;
- int items_num;
+ zbx_hashset_t rule_infos;
+ zbx_lld_rule_t *rule;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
zbx_lld_deserialize_top_items_request(message->data, &limit);
+ zbx_hashset_create(&rule_infos, MAX(1000, (size_t)manager->rule_index.num_data), ZBX_DEFAULT_UINT64_HASH_FUNC,
+ ZBX_DEFAULT_UINT64_COMPARE_FUNC);
zbx_vector_ptr_create(&view);
zbx_hashset_iter_reset(&manager->rule_index, &iter);
- while (NULL != (item = (zbx_lld_rule_t *)zbx_hashset_iter_next(&iter)))
- zbx_vector_ptr_append(&view, item);
+ while (NULL != (rule = (zbx_lld_rule_t *)zbx_hashset_iter_next(&iter)))
+ {
+ zbx_lld_data_t *data_ptr;
+
+ for (data_ptr = rule->head; NULL != data_ptr; data_ptr = data_ptr->next)
+ {
+ zbx_lld_rule_info_t *rule_info, rule_info_local = {.itemid = data_ptr->itemid};
+
+ rule_info = (zbx_lld_rule_info_t *)zbx_hashset_search(&rule_infos, &rule_info_local);
+
+ if (NULL == rule_info)
+ {
+ rule_info = (zbx_lld_rule_info_t *)zbx_hashset_insert(&rule_infos, &rule_info_local,
+ sizeof(zbx_lld_rule_info_t));
+ zbx_vector_ptr_append(&view, rule_info);
+ }
+
+ rule_info->values_num++;
+ }
+ }
zbx_vector_ptr_sort(&view, lld_diag_item_compare_values_desc);
- items_num = MIN(limit, view.values_num);
- data_len = zbx_lld_serialize_top_items_result(&data, (zbx_lld_rule_t **)view.values, items_num);
+ data_len = zbx_lld_serialize_top_items_result(&data, (const zbx_lld_rule_info_t **)view.values,
+ MIN(limit, view.values_num));
zbx_ipc_client_send(client, ZBX_IPC_LLD_TOP_ITEMS_RESULT, data, data_len);
zbx_free(data);
zbx_vector_ptr_destroy(&view);
+ zbx_hashset_destroy(&rule_infos);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}
diff --git a/src/zabbix_server/lld/lld_manager.h b/src/zabbix_server/lld/lld_manager.h
index 9fdd7243c1a..52817ceaf7c 100644
--- a/src/zabbix_server/lld/lld_manager.h
+++ b/src/zabbix_server/lld/lld_manager.h
@@ -24,6 +24,9 @@
typedef struct zbx_lld_value
{
+ /* the LLD rule id */
+ zbx_uint64_t itemid;
+
char *value;
char *error;
zbx_timespec_t ts;
@@ -31,28 +34,38 @@ typedef struct zbx_lld_value
zbx_uint64_t lastlogsize;
int mtime;
unsigned char meta;
-
+ struct zbx_lld_value *prev;
struct zbx_lld_value *next;
}
zbx_lld_data_t;
-/* queue of values for one LLD rule */
+/* queue of values for one host */
typedef struct
{
- /* the LLD rule id */
- zbx_uint64_t itemid;
+ /* the LLD rule host id */
+ zbx_uint64_t hostid;
/* the number of queued values */
int values_num;
- /* the oldest value in queue */
+ /* the newest value in queue */
zbx_lld_data_t *tail;
- /* the newest value in queue */
+ /* the oldest value in queue */
zbx_lld_data_t *head;
}
zbx_lld_rule_t;
+typedef struct
+{
+ /* the LLD rule item id */
+ zbx_uint64_t itemid;
+
+ /* the number of queued values */
+ int values_num;
+}
+zbx_lld_rule_info_t;
+
ZBX_THREAD_ENTRY(lld_manager_thread, args);
#endif
diff --git a/src/zabbix_server/lld/lld_protocol.c b/src/zabbix_server/lld/lld_protocol.c
index d852c0a9ea7..adf6130f1fb 100644
--- a/src/zabbix_server/lld/lld_protocol.c
+++ b/src/zabbix_server/lld/lld_protocol.c
@@ -31,13 +31,15 @@
* Function: zbx_lld_serialize_item_value *
* *
******************************************************************************/
-zbx_uint32_t zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, const char *value,
- const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime, const char *error)
+zbx_uint32_t zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, zbx_uint64_t hostid,
+ const char *value, const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime,
+ const char *error)
{
unsigned char *ptr;
zbx_uint32_t data_len = 0, value_len, error_len;
zbx_serialize_prepare_value(data_len, itemid);
+ zbx_serialize_prepare_value(data_len, hostid);
zbx_serialize_prepare_str(data_len, value);
zbx_serialize_prepare_value(data_len, *ts);
zbx_serialize_prepare_str(data_len, error);
@@ -53,6 +55,7 @@ zbx_uint32_t zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t ite
ptr = *data;
ptr += zbx_serialize_value(ptr, itemid);
+ ptr += zbx_serialize_value(ptr, hostid);
ptr += zbx_serialize_str(ptr, value, value_len);
ptr += zbx_serialize_value(ptr, *ts);
ptr += zbx_serialize_str(ptr, error, error_len);
@@ -71,12 +74,14 @@ zbx_uint32_t zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t ite
* Function: zbx_lld_deserialize_item_value *
* *
******************************************************************************/
-void zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, char **value,
- zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime, char **error)
+void zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, zbx_uint64_t *hostid,
+ char **value, zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime,
+ char **error)
{
zbx_uint32_t value_len, error_len;
data += zbx_deserialize_value(data, itemid);
+ data += zbx_deserialize_value(data, hostid);
data += zbx_deserialize_str(data, value, value_len);
data += zbx_deserialize_value(data, ts);
data += zbx_deserialize_str(data, error, error_len);
@@ -153,29 +158,30 @@ void zbx_lld_deserialize_top_items_request(const unsigned char *data, int *limit
* Function: zbx_lld_serialize_top_items_result *
* *
******************************************************************************/
-zbx_uint32_t zbx_lld_serialize_top_items_result(unsigned char **data, zbx_lld_rule_t **items, int items_num)
+zbx_uint32_t zbx_lld_serialize_top_items_result(unsigned char **data, const zbx_lld_rule_info_t **rule_infos,
+ int num)
{
unsigned char *ptr;
zbx_uint32_t data_len = 0, item_len = 0;
int i;
- if (0 != items_num)
+ if (0 != num)
{
- zbx_serialize_prepare_value(item_len, items[0]->itemid);
- zbx_serialize_prepare_value(item_len, items[0]->values_num);
+ zbx_serialize_prepare_value(item_len, rule_infos[0]->itemid);
+ zbx_serialize_prepare_value(item_len, rule_infos[0]->values_num);
}
- zbx_serialize_prepare_value(data_len, items_num);
- data_len += item_len * items_num;
+ zbx_serialize_prepare_value(data_len, num);
+ data_len += item_len * num;
*data = (unsigned char *)zbx_malloc(NULL, data_len);
ptr = *data;
- ptr += zbx_serialize_value(ptr, items_num);
+ ptr += zbx_serialize_value(ptr, num);
- for (i = 0; i < items_num; i++)
+ for (i = 0; i < num; i++)
{
- ptr += zbx_serialize_value(ptr, items[i]->itemid);
- ptr += zbx_serialize_value(ptr, items[i]->values_num);
+ ptr += zbx_serialize_value(ptr, rule_infos[i]->itemid);
+ ptr += zbx_serialize_value(ptr, rule_infos[i]->values_num);
}
return data_len;
@@ -216,13 +222,14 @@ static void zbx_lld_deserialize_top_items_result(const unsigned char *data, zbx_
* Purpose: process low level discovery value/error *
* *
* Parameters: itemid - [IN] the LLD rule id *
+ * hostid - [IN] the host id *
* value - [IN] the rule value (can be NULL if error is set) *
* ts - [IN] the value timestamp *
* error - [IN] the error message (can be NULL) *
* *
******************************************************************************/
-void zbx_lld_process_value(zbx_uint64_t itemid, const char *value, const zbx_timespec_t *ts, unsigned char meta,
- zbx_uint64_t lastlogsize, int mtime, const char *error)
+void zbx_lld_process_value(zbx_uint64_t itemid, zbx_uint64_t hostid, const char *value, const zbx_timespec_t *ts,
+ unsigned char meta, zbx_uint64_t lastlogsize, int mtime, const char *error)
{
static zbx_ipc_socket_t socket;
char *errmsg = NULL;
@@ -236,7 +243,7 @@ void zbx_lld_process_value(zbx_uint64_t itemid, const char *value, const zbx_tim
exit(EXIT_FAILURE);
}
- data_len = zbx_lld_serialize_item_value(&data, itemid, value, ts, meta, lastlogsize, mtime, error);
+ data_len = zbx_lld_serialize_item_value(&data, itemid, hostid, value, ts, meta, lastlogsize, mtime, error);
if (FAIL == zbx_ipc_socket_write(&socket, ZBX_IPC_LLD_REQUEST, data, data_len))
{
@@ -254,12 +261,14 @@ void zbx_lld_process_value(zbx_uint64_t itemid, const char *value, const zbx_tim
* Purpose: process low level discovery agent result *
* *
* Parameters: itemid - [IN] the LLD rule id *
+ * hostid - [IN] the host id *
* result - [IN] the agent result *
* ts - [IN] the value timestamp *
* error - [IN] the error message (can be NULL) *
* *
******************************************************************************/
-void zbx_lld_process_agent_result(zbx_uint64_t itemid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error)
+void zbx_lld_process_agent_result(zbx_uint64_t itemid, zbx_uint64_t hostid, AGENT_RESULT *result,
+ zbx_timespec_t *ts, char *error)
{
const char *value = NULL;
unsigned char meta = 0;
@@ -280,7 +289,7 @@ void zbx_lld_process_agent_result(zbx_uint64_t itemid, AGENT_RESULT *result, zbx
}
if (NULL != value || NULL != error || 0 != meta)
- zbx_lld_process_value(itemid, value, ts, meta, lastlogsize, mtime, error);
+ zbx_lld_process_value(itemid, hostid, value, ts, meta, lastlogsize, mtime, error);
}
/******************************************************************************
diff --git a/src/zabbix_server/lld/lld_protocol.h b/src/zabbix_server/lld/lld_protocol.h
index c21fbe48a05..21fabccde4e 100644
--- a/src/zabbix_server/lld/lld_protocol.h
+++ b/src/zabbix_server/lld/lld_protocol.h
@@ -51,16 +51,19 @@
#define ZBX_IPC_LLD_TOP_ITEMS_RESULT 1403
-zbx_uint32_t zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, const char *value,
- const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime, const char *error);
+zbx_uint32_t zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, zbx_uint64_t hostid,
+ const char *value, const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime,
+ const char *error);
-void zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, char **value,
- zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime, char **error);
+void zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, zbx_uint64_t *hostid,
+ char **value, zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime,
+ char **error);
zbx_uint32_t zbx_lld_serialize_diag_stats(unsigned char **data, zbx_uint64_t items_num, zbx_uint64_t values_num);
void zbx_lld_deserialize_top_items_request(const unsigned char *data, int *limit);
-zbx_uint32_t zbx_lld_serialize_top_items_result(unsigned char **data, zbx_lld_rule_t **items, int items_num);
+zbx_uint32_t zbx_lld_serialize_top_items_result(unsigned char **data, const zbx_lld_rule_info_t **rule_infos,
+ int num);
#endif
diff --git a/src/zabbix_server/lld/lld_worker.c b/src/zabbix_server/lld/lld_worker.c
index 51b3d25b44d..4d46669e4d0 100644
--- a/src/zabbix_server/lld/lld_worker.c
+++ b/src/zabbix_server/lld/lld_worker.c
@@ -63,7 +63,7 @@ static void lld_register_worker(zbx_ipc_socket_t *socket)
******************************************************************************/
static void lld_process_task(zbx_ipc_message_t *message)
{
- zbx_uint64_t itemid, lastlogsize;
+ zbx_uint64_t itemid, hostid, lastlogsize;
char *value, *error;
zbx_timespec_t ts;
zbx_item_diff_t diff;
@@ -73,7 +73,7 @@ static void lld_process_task(zbx_ipc_message_t *message)
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
- zbx_lld_deserialize_item_value(message->data, &itemid, &value, &ts, &meta, &lastlogsize, &mtime, &error);
+ zbx_lld_deserialize_item_value(message->data, &itemid, &hostid, &value, &ts, &meta, &lastlogsize, &mtime, &error);
DCconfig_get_items_by_itemids(&item, &itemid, &errcode, 1);
if (SUCCEED != errcode)
diff --git a/src/zabbix_server/pinger/pinger.c b/src/zabbix_server/pinger/pinger.c
index ff9a322079a..86baab12a2e 100644
--- a/src/zabbix_server/pinger/pinger.c
+++ b/src/zabbix_server/pinger/pinger.c
@@ -78,7 +78,8 @@ static void process_value(zbx_uint64_t itemid, zbx_uint64_t *value_ui64, double
if (NOTSUPPORTED == ping_result)
{
item.state = ITEM_STATE_NOTSUPPORTED;
- zbx_preprocess_item_value(item.itemid, item.value_type, item.flags, NULL, ts, item.state, error);
+ zbx_preprocess_item_value(item.itemid, item.host.hostid, item.value_type, item.flags, NULL, ts,
+ item.state, error);
}
else
{
@@ -90,7 +91,8 @@ static void process_value(zbx_uint64_t itemid, zbx_uint64_t *value_ui64, double
SET_DBL_RESULT(&value, *value_dbl);
item.state = ITEM_STATE_NORMAL;
- zbx_preprocess_item_value(item.itemid, item.value_type, item.flags, &value, ts, item.state, NULL);
+ zbx_preprocess_item_value(item.itemid, item.host.hostid, item.value_type, item.flags, &value, ts,
+ item.state, NULL);
free_result(&value);
}
@@ -442,8 +444,8 @@ static void get_pinger_hosts(icmpitem_t **icmp_items, int *icmp_items_alloc, int
zbx_timespec(&ts);
items[i].state = ITEM_STATE_NOTSUPPORTED;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL, &ts,
- items[i].state, error);
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+ items[i].flags, NULL, &ts, items[i].state, error);
DCrequeue_items(&items[i].itemid, &ts.sec, &errcode, 1);
}
diff --git a/src/zabbix_server/poller/checks_aggregate.c b/src/zabbix_server/poller/checks_aggregate.c
index bd49c8af092..7c38358ef04 100644
--- a/src/zabbix_server/poller/checks_aggregate.c
+++ b/src/zabbix_server/poller/checks_aggregate.c
@@ -475,6 +475,8 @@ static int evaluate_aggregate(const DC_ITEM *item, AGENT_RESULT *res, int grp_fu
zbx_history_record_vector_destroy(&values, items[i].value_type);
}
+ zbx_vc_flush_stats();
+
if (0 == group_values.values_num)
{
char *tmp = NULL;
diff --git a/src/zabbix_server/poller/checks_calculated.c b/src/zabbix_server/poller/checks_calculated.c
index 26fe1b89f7b..88738c61f65 100644
--- a/src/zabbix_server/poller/checks_calculated.c
+++ b/src/zabbix_server/poller/checks_calculated.c
@@ -21,6 +21,7 @@
#include "zbxserver.h"
#include "log.h"
#include "../../libs/zbxserver/evalfunc.h"
+#include "valuecache.h"
typedef struct
{
@@ -298,6 +299,8 @@ static int calcitem_evaluate_expression(expression_t *exp, char *error, size_t m
exp->exp = buf;
}
+ zbx_vc_flush_stats();
+
DCconfig_clean_items(items, errcodes, exp->functions_num);
zbx_free(errcodes);
diff --git a/src/zabbix_server/poller/checks_simple_vmware.c b/src/zabbix_server/poller/checks_simple_vmware.c
index ee999e70e87..269b954f43a 100644
--- a/src/zabbix_server/poller/checks_simple_vmware.c
+++ b/src/zabbix_server/poller/checks_simple_vmware.c
@@ -724,7 +724,7 @@ int check_vcenter_eventlog(AGENT_REQUEST *request, const DC_ITEM *item, AGENT_RE
}
else if (0 == strcmp(skip, "skip"))
{
- skip_old = 1;
+ skip_old = (0 == request->lastlogsize ? 1 : 0);
}
else
{
@@ -737,8 +737,10 @@ int check_vcenter_eventlog(AGENT_REQUEST *request, const DC_ITEM *item, AGENT_RE
if (NULL == (service = get_vmware_service(url, item->username, item->password, result, &ret)))
goto unlock;
- if (ZBX_VMWARE_EVENT_KEY_UNINITIALIZED == service->eventlog.last_key)
+ if (ZBX_VMWARE_EVENT_KEY_UNINITIALIZED == service->eventlog.last_key ||
+ (0 != skip_old && 0 != service->eventlog.last_key ))
{
+ /* this may happen if recreate item vmware.eventlog for the same service URL */
service->eventlog.last_key = request->lastlogsize;
service->eventlog.skip_old = skip_old;
}
@@ -747,7 +749,7 @@ int check_vcenter_eventlog(AGENT_REQUEST *request, const DC_ITEM *item, AGENT_RE
SET_MSG_RESULT(result, zbx_strdup(NULL, "Not enough shared memory to store VMware events."));
goto unlock;
}
- else if (request->lastlogsize < service->eventlog.last_key)
+ else if (request->lastlogsize < service->eventlog.last_key && 0 != request->lastlogsize)
{
/* this may happen if there are multiple vmware.eventlog items for the same service URL or item has */
/* been polled, but values got stuck in history cache and item's lastlogsize hasn't been updated yet */
diff --git a/src/zabbix_server/poller/poller.c b/src/zabbix_server/poller/poller.c
index 1d35b7c7c0b..3879c6ac54b 100644
--- a/src/zabbix_server/poller/poller.c
+++ b/src/zabbix_server/poller/poller.c
@@ -855,8 +855,8 @@ static int get_values(unsigned char poller_type, int *nextcheck)
if (0 == add_results.values_num)
{
items[i].state = ITEM_STATE_NORMAL;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags,
- &results[i], &timespec, items[i].state, NULL);
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+ items[i].flags, &results[i], &timespec, items[i].state, NULL);
}
else
{
@@ -872,16 +872,16 @@ static int get_values(unsigned char poller_type, int *nextcheck)
if (ISSET_MSG(add_result))
{
items[i].state = ITEM_STATE_NOTSUPPORTED;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type,
- items[i].flags, NULL, &ts_tmp, items[i].state,
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid,
+ items[i].value_type, items[i].flags, NULL, &ts_tmp, items[i].state,
add_result->msg);
}
else
{
items[i].state = ITEM_STATE_NORMAL;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type,
- items[i].flags, add_result, &ts_tmp, items[i].state,
- NULL);
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid,
+ items[i].value_type, items[i].flags, add_result,
+ &ts_tmp, items[i].state, NULL);
}
/* ensure that every log item value timestamp is unique */
@@ -896,8 +896,8 @@ static int get_values(unsigned char poller_type, int *nextcheck)
else if (NOTSUPPORTED == errcodes[i] || AGENT_ERROR == errcodes[i] || CONFIG_ERROR == errcodes[i])
{
items[i].state = ITEM_STATE_NOTSUPPORTED;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL, &timespec,
- items[i].state, results[i].msg);
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+ items[i].flags, NULL, &timespec, items[i].state, results[i].msg);
}
DCpoller_requeue_items(&items[i].itemid, &timespec.sec, &errcodes[i], 1, poller_type,
diff --git a/src/zabbix_server/preprocessor/preproc_manager.c b/src/zabbix_server/preprocessor/preproc_manager.c
index db89eedae1f..584be292562 100644
--- a/src/zabbix_server/preprocessor/preproc_manager.c
+++ b/src/zabbix_server/preprocessor/preproc_manager.c
@@ -519,7 +519,8 @@ static void preprocessor_flush_value(const zbx_preproc_item_value_t *value)
value->ts, value->state, value->error);
}
else
- zbx_lld_process_agent_result(value->itemid, value->result_ptr->result, value->ts, value->error);
+ zbx_lld_process_agent_result(value->itemid, value->hostid, value->result_ptr->result, value->ts,
+ value->error);
}
/******************************************************************************
diff --git a/src/zabbix_server/preprocessor/preprocessing.c b/src/zabbix_server/preprocessor/preprocessing.c
index 6d548b122c3..b5031c0bed4 100644
--- a/src/zabbix_server/preprocessor/preprocessing.c
+++ b/src/zabbix_server/preprocessor/preprocessing.c
@@ -136,13 +136,14 @@ static zbx_uint32_t message_pack_data(zbx_ipc_message_t *message, zbx_packed_fie
******************************************************************************/
static zbx_uint32_t preprocessor_pack_value(zbx_ipc_message_t *message, zbx_preproc_item_value_t *value)
{
- zbx_packed_field_t fields[23], *offset = fields; /* 23 - max field count */
+ zbx_packed_field_t fields[24], *offset = fields; /* 24 - max field count */
unsigned char ts_marker, result_marker, log_marker;
ts_marker = (NULL != value->ts);
result_marker = (NULL != value->result_ptr->result);
*offset++ = PACKED_FIELD(&value->itemid, sizeof(zbx_uint64_t));
+ *offset++ = PACKED_FIELD(&value->hostid, sizeof(zbx_uint64_t));
*offset++ = PACKED_FIELD(&value->item_value_type, sizeof(unsigned char));
*offset++ = PACKED_FIELD(&value->item_flags, sizeof(unsigned char));
*offset++ = PACKED_FIELD(&value->state, sizeof(unsigned char));
@@ -723,6 +724,7 @@ zbx_uint32_t zbx_preprocessor_unpack_value(zbx_preproc_item_value_t *value, unsi
unsigned char *offset = data, ts_marker, result_marker, log_marker;
offset += zbx_deserialize_uint64(offset, &value->itemid);
+ offset += zbx_deserialize_uint64(offset, &value->hostid);
offset += zbx_deserialize_char(offset, &value->item_value_type);
offset += zbx_deserialize_char(offset, &value->item_flags);
offset += zbx_deserialize_char(offset, &value->state);
@@ -992,9 +994,10 @@ static void preprocessor_send(zbx_uint32_t code, unsigned char *data, zbx_uint32
* *
* Function: zbx_preprocess_item_value *
* *
- * Purpose: perform item value preprocessing and dependend item processing *
+ * Purpose: perform item value preprocessing and dependent item processing *
* *
* Parameters: itemid - [IN] the itemid *
+ * itemid - [IN] the hostid *
* item_value_type - [IN] the item value type *
* item_flags - [IN] the item flags (e. g. lld rule) *
* result - [IN] agent result containing the value *
@@ -1005,10 +1008,10 @@ static void preprocessor_send(zbx_uint32_t code, unsigned char *data, zbx_uint32
* ITEM_STATE_NOTSUPPORTED *
* *
******************************************************************************/
-void zbx_preprocess_item_value(zbx_uint64_t itemid, unsigned char item_value_type, unsigned char item_flags,
- AGENT_RESULT *result, zbx_timespec_t *ts, unsigned char state, char *error)
+void zbx_preprocess_item_value(zbx_uint64_t itemid, zbx_uint64_t hostid, unsigned char item_value_type,
+ unsigned char item_flags, AGENT_RESULT *result, zbx_timespec_t *ts, unsigned char state, char *error)
{
- zbx_preproc_item_value_t value = {.itemid = itemid, .item_value_type = item_value_type,
+ zbx_preproc_item_value_t value = {.itemid = itemid, .hostid = hostid, .item_value_type = item_value_type,
.error = error, .item_flags = item_flags, .state = state, .ts = ts};
zbx_result_ptr_t result_ptr = {.result = result};
size_t value_len = 0, len;
diff --git a/src/zabbix_server/preprocessor/preprocessing.h b/src/zabbix_server/preprocessor/preprocessing.h
index 5d1bfd6bf83..bd0e5094f66 100644
--- a/src/zabbix_server/preprocessor/preprocessing.h
+++ b/src/zabbix_server/preprocessor/preprocessing.h
@@ -48,6 +48,7 @@ typedef struct {
typedef struct
{
zbx_uint64_t itemid; /* item id */
+ zbx_uint64_t hostid; /* host id */
unsigned char item_value_type; /* item value type */
zbx_result_ptr_t *result_ptr; /* item value (if any) to be shared between master and dependent items */
zbx_timespec_t *ts; /* timestamp of a value */
diff --git a/src/zabbix_server/reporter/Makefile.am b/src/zabbix_server/reporter/Makefile.am
new file mode 100644
index 00000000000..fd5b31122ef
--- /dev/null
+++ b/src/zabbix_server/reporter/Makefile.am
@@ -0,0 +1,12 @@
+## Process this file with automake to produce Makefile.in
+
+noinst_LIBRARIES = libzbxreporter.a
+
+libzbxreporter_a_SOURCES = \
+ report_manager.c \
+ report_manager.h \
+ report_writer.c \
+ report_writer.h \
+ report_protocol.c \
+ report_protocol.h
+
diff --git a/src/zabbix_server/reporter/report_manager.c b/src/zabbix_server/reporter/report_manager.c
new file mode 100644
index 00000000000..6c76cbb73ad
--- /dev/null
+++ b/src/zabbix_server/reporter/report_manager.c
@@ -0,0 +1,2508 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "log.h"
+#include "zbxself.h"
+#include "zbxipcservice.h"
+#include "daemon.h"
+#include "db.h"
+#include "zbxjson.h"
+#include "base64.h"
+#include "zbxalgo.h"
+#include "zbxmedia.h"
+#include "dbcache.h"
+#include "zbxreport.h"
+#include "../../libs/zbxcrypto/aes.h"
+#include "../../libs/zbxalgo/vectorimpl.h"
+#include "zbxalert.h"
+#include "zbxserver.h"
+
+#include "report_manager.h"
+#include "report_protocol.h"
+
+#define ZBX_REPORT_INCLUDE_USER 0
+#define ZBX_REPORT_EXCLUDE_USER 1
+
+#define ZBX_REPORT_UPDATE_LASTSENT 0x0001
+#define ZBX_REPORT_UPDATE_STATE 0x0002
+#define ZBX_REPORT_UPDATE_ERROR 0x0004
+#define ZBX_REPORT_UPDATE (ZBX_REPORT_UPDATE_LASTSENT | ZBX_REPORT_UPDATE_STATE | ZBX_REPORT_UPDATE_ERROR)
+
+#define ZBX_REPORT_STATE_SUCCESS 1
+#define ZBX_REPORT_STATE_ERROR 2
+#define ZBX_REPORT_STATE_SUCCESS_INFO 3
+
+extern unsigned char process_type, program_type;
+extern int server_num, process_num, CONFIG_REPORTWRITER_FORKS;
+
+/* report manager data */
+typedef struct
+{
+ /* config.url, config.session_key fields synced from database together with reports */
+ char *zabbix_url;
+ char *session_key;
+
+ /* the IPC service */
+ zbx_ipc_service_t ipc;
+
+ /* the next writer index to be assigned to new IPC service clients */
+ int next_writer_index;
+
+ /* identifier of the last batchid (autogenerated) */
+ zbx_uint64_t last_batchid;
+
+ /* report writer vector, created during manager initialization */
+ zbx_vector_ptr_t writers;
+ zbx_queue_ptr_t free_writers;
+
+ zbx_hashset_t sessions;
+ zbx_hashset_t reports;
+ zbx_hashset_t batches;
+
+ zbx_vector_uint64_t flush_queue;
+
+ zbx_binary_heap_t report_queue;
+
+ zbx_list_t job_queue;
+}
+zbx_rm_t;
+
+typedef struct
+{
+ zbx_uint64_t id;
+ zbx_uint64_t access_userid;
+}
+zbx_rm_recipient_t;
+
+ZBX_VECTOR_DECL(recipient, zbx_rm_recipient_t)
+ZBX_VECTOR_IMPL(recipient, zbx_rm_recipient_t)
+
+typedef struct
+{
+ zbx_uint64_t reportid;
+ zbx_uint64_t userid;
+ zbx_uint64_t dashboardid;
+ char *name;
+ char *timezone;
+ char *error;
+ unsigned char period;
+ unsigned char cycle;
+ unsigned char weekdays;
+ unsigned char status;
+ int start_time;
+ int state;
+ zbx_uint32_t flags;
+ int nextcheck;
+ int active_since;
+ int active_till;
+ int lastsent;
+
+ zbx_vector_ptr_pair_t params;
+ zbx_vector_recipient_t usergroups;
+ zbx_vector_recipient_t users;
+ zbx_vector_uint64_t users_excl;
+}
+zbx_rm_report_t;
+
+typedef struct
+{
+ zbx_uint64_t access_userid;
+ zbx_uint64_t batchid;
+ int report_width;
+ int report_height;
+ char *url;
+ char *cookie;
+ char *report_name;
+ zbx_vector_uint64_t userids;
+ zbx_vector_ptr_pair_t params;
+
+ zbx_ipc_client_t *client;
+}
+zbx_rm_job_t;
+
+typedef struct
+{
+ zbx_uint64_t batchid;
+ zbx_uint64_t reportid;
+ int error_num;
+ int sent_num;
+ int total_num;
+ char *info;
+ size_t info_alloc;
+ size_t info_offset;
+ zbx_vector_ptr_t jobs;
+}
+zbx_rm_batch_t;
+
+/* user session, cached to generate authentication cookies */
+typedef struct
+{
+ zbx_uint64_t userid;
+ char *sid;
+ char *cookie;
+ int db_lastaccess;
+ int lastaccess;
+}
+zbx_rm_session_t;
+
+typedef struct
+{
+ /* the connected report writer client */
+ zbx_ipc_client_t *client;
+
+ zbx_rm_job_t *job;
+}
+zbx_rm_writer_t;
+
+/******************************************************************************
+ * *
+ * Function: rm_get_writer *
+ * *
+ * Purpose: return writer with the specified client *
+ * *
+ ******************************************************************************/
+static zbx_rm_writer_t *rm_get_writer(zbx_rm_t *manager, const zbx_ipc_client_t *client)
+{
+ int i;
+
+ for (i = 0; i < manager->writers.values_num; i++)
+ {
+ zbx_rm_writer_t *writer = (zbx_rm_writer_t *)manager->writers.values[i];
+
+ if (writer->client == client)
+ return writer;
+ }
+
+ return NULL;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_writer_free *
+ * *
+ ******************************************************************************/
+static void rm_writer_free(zbx_rm_writer_t *writer)
+{
+ zbx_ipc_client_close(writer->client);
+ zbx_free(writer);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_compare_nextcheck *
+ * *
+ ******************************************************************************/
+static int rm_report_compare_nextcheck(const void *d1, const void *d2)
+{
+ const zbx_binary_heap_elem_t *e1 = (const zbx_binary_heap_elem_t *)d1;
+ const zbx_binary_heap_elem_t *e2 = (const zbx_binary_heap_elem_t *)d2;
+
+ return ((zbx_rm_report_t *)e1->data)->nextcheck - ((zbx_rm_report_t *)e2->data)->nextcheck;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_clean *
+ * *
+ ******************************************************************************/
+static void rm_report_clean(zbx_rm_report_t *report)
+{
+ zbx_free(report->name);
+ zbx_free(report->timezone);
+ zbx_free(report->error);
+
+ report_destroy_params(&report->params);
+
+ zbx_vector_recipient_destroy(&report->usergroups);
+ zbx_vector_recipient_destroy(&report->users);
+ zbx_vector_uint64_destroy(&report->users_excl);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_job_free *
+ * *
+ ******************************************************************************/
+static void rm_job_free(zbx_rm_job_t *job)
+{
+ if (NULL != job->client)
+ zbx_ipc_client_release(job->client);
+
+ zbx_free(job->report_name);
+ zbx_free(job->url);
+ zbx_free(job->cookie);
+
+ zbx_vector_uint64_destroy(&job->userids);
+ report_destroy_params(&job->params);
+
+ zbx_free(job);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_batch_clean *
+ * *
+ ******************************************************************************/
+static void rm_batch_clean(zbx_rm_batch_t *batch)
+{
+ zbx_vector_ptr_clear_ext(&batch->jobs, (zbx_ptr_free_func_t)rm_job_free);
+ zbx_vector_ptr_destroy(&batch->jobs);
+ zbx_free(batch->info);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_init *
+ * *
+ * Purpose: initializes report manager *
+ * *
+ * Parameters: manager - [IN] the manager to initialize *
+ * *
+ ******************************************************************************/
+static int rm_init(zbx_rm_t *manager, char **error)
+{
+ int i, ret;
+ zbx_rm_writer_t *writer;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() writers:%d", __func__, CONFIG_REPORTWRITER_FORKS);
+
+ if (FAIL == (ret = zbx_ipc_service_start(&manager->ipc, ZBX_IPC_SERVICE_REPORTER, error)))
+ goto out;
+
+ zbx_vector_ptr_create(&manager->writers);
+ zbx_queue_ptr_create(&manager->free_writers);
+ zbx_hashset_create(&manager->sessions, 0, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_hashset_create(&manager->reports, 0, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_hashset_create(&manager->batches, 0, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_binary_heap_create(&manager->report_queue, rm_report_compare_nextcheck, ZBX_BINARY_HEAP_OPTION_DIRECT);
+
+ zbx_vector_uint64_create(&manager->flush_queue);
+
+ zbx_list_create(&manager->job_queue);
+
+ manager->next_writer_index = 0;
+ manager->session_key = NULL;
+ manager->zabbix_url = NULL;
+ manager->last_batchid = 0;
+
+ for (i = 0; i < CONFIG_REPORTWRITER_FORKS; i++)
+ {
+ writer = (zbx_rm_writer_t *)zbx_malloc(NULL, sizeof(zbx_rm_writer_t));
+ writer->client = NULL;
+ zbx_vector_ptr_append(&manager->writers, writer);
+ }
+out:
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_destroy *
+ * *
+ * Purpose: destroys report manager *
+ * *
+ * Parameters: manager - [IN] the manager to destroy *
+ * *
+ ******************************************************************************/
+static void rm_destroy(zbx_rm_t *manager)
+{
+ zbx_hashset_iter_t iter;
+ zbx_rm_session_t *session;
+ zbx_rm_report_t *report;
+ zbx_rm_job_t *job;
+ zbx_rm_batch_t *batch;
+
+ while (SUCCEED == zbx_list_pop(&manager->job_queue, (void **)&job))
+ rm_job_free(job);
+
+ zbx_hashset_iter_reset(&manager->sessions, &iter);
+ while (NULL != (session = (zbx_rm_session_t *)zbx_hashset_iter_next(&iter)))
+ zbx_free(session->sid);
+ zbx_hashset_destroy(&manager->sessions);
+
+ zbx_hashset_iter_reset(&manager->reports, &iter);
+ while (NULL != (report = (zbx_rm_report_t *)zbx_hashset_iter_next(&iter)))
+ rm_report_clean(report);
+ zbx_hashset_destroy(&manager->reports);
+
+ zbx_hashset_iter_reset(&manager->batches, &iter);
+ while (NULL != (batch = (zbx_rm_batch_t *)zbx_hashset_iter_next(&iter)))
+ rm_batch_clean(batch);
+ zbx_hashset_destroy(&manager->batches);
+
+ zbx_vector_uint64_destroy(&manager->flush_queue);
+
+ zbx_queue_ptr_destroy(&manager->free_writers);
+ zbx_vector_ptr_clear_ext(&manager->writers, (zbx_mem_free_func_t)rm_writer_free);
+ zbx_vector_ptr_destroy(&manager->writers);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_register_writer *
+ * *
+ * Purpose: registers report writer *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * client - [IN] the connected writer *
+ * message - [IN] the received message *
+ * *
+ ******************************************************************************/
+static void rm_register_writer(zbx_rm_t *manager, zbx_ipc_client_t *client, zbx_ipc_message_t *message)
+{
+ zbx_rm_writer_t *writer = NULL;
+ pid_t ppid;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ memcpy(&ppid, message->data, sizeof(ppid));
+
+ if (ppid != getppid())
+ {
+ zbx_ipc_client_close(client);
+ zabbix_log(LOG_LEVEL_DEBUG, "refusing connection from foreign process");
+ }
+ else
+ {
+ if (manager->next_writer_index == manager->writers.values_num)
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ exit(EXIT_FAILURE);
+ }
+
+ writer = (zbx_rm_writer_t *)manager->writers.values[manager->next_writer_index++];
+ writer->client = client;
+
+ zbx_queue_ptr_push(&manager->free_writers, writer);
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_time_to_urlfield *
+ * *
+ * Purpose: convert timestamp to range format used in URL query fields *
+ * *
+ * Parameters: tm - [IN] the timestamp *
+ * *
+ * Return value: formatted time to be used in URL query fields *
+ * *
+ ******************************************************************************/
+static char *rm_time_to_urlfield(const struct tm *tm)
+{
+ static char buf[26];
+
+ zbx_snprintf(buf, sizeof(buf), "%02d-%02d-%02d%%20%02d%%3A%02d%%3A%02d", tm->tm_year + 1900, tm->tm_mon + 1,
+ tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return buf;
+}
+
+/******************************************************************************
+ * *
+ * Function: report_create_cookie *
+ * *
+ * Purpose: create zbx_session cookie for frontend authentication *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * sessionid - [IN] the session id *
+ * *
+ * Return value: zbx_session cookie *
+ * *
+ ******************************************************************************/
+static char *report_create_cookie(zbx_rm_t *manager, const char *sessionid)
+{
+ struct zbx_json j;
+ char *sign = NULL, *cookie = NULL, *sign_esc, *sign_raw;
+ size_t size, i;
+ unsigned char *data;
+ struct AES_ctx ctx;
+
+ zbx_json_init(&j, 512);
+ zbx_json_addstring(&j, ZBX_PROTO_TAG_SESSIONID, sessionid, ZBX_JSON_TYPE_STRING);
+
+ size = (j.buffer_size / 16 + 1) * 16;
+ data = zbx_malloc(NULL, size);
+ memcpy(data, j.buffer, j.buffer_size);
+
+ if (j.buffer_size < size)
+ memset(data + j.buffer_size, (int)(size - j.buffer_size), size - j.buffer_size);
+
+ AES_init_ctx(&ctx, (unsigned char *)manager->session_key);
+
+ for (i = 0; i < size / 16; i++)
+ AES_ECB_encrypt(&ctx, data + i * 16);
+
+ str_base64_encode_dyn((char *)data, &sign, size);
+ sign_esc = zbx_dyn_escape_string(sign, "/");
+ sign_raw = zbx_dsprintf(NULL, "\"%s\"", sign_esc);
+
+ zbx_json_addraw(&j, ZBX_PROTO_TAG_SIGN, sign_raw);
+ str_base64_encode_dyn(j.buffer, &cookie, j.buffer_size);
+
+ zbx_free(sign_raw);
+ zbx_free(sign_esc);
+ zbx_free(sign);
+ zbx_free(data);
+ zbx_json_clean(&j);
+
+ return cookie;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_get_session *
+ * *
+ * Purpose: get specified user session, creating one if necessary *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * userid - [IN] the userid *
+ * *
+ * Return value: session *
+ * *
+ * Comments: When returning new session it's cached and also stored in *
+ * database. *
+ * When returning cached session database is checked if the session *
+ * is not removed. In that case a new session is created. *
+ * *
+ ******************************************************************************/
+static zbx_rm_session_t *rm_get_session(zbx_rm_t *manager, zbx_uint64_t userid)
+{
+ zbx_rm_session_t *session;
+ int now;
+
+ now = (int)time(NULL);
+
+ if (NULL != (session = (zbx_rm_session_t *)zbx_hashset_search(&manager->sessions, &userid)))
+ {
+ DB_RESULT result;
+
+ result = DBselect("select NULL from sessions where sessionid='%s'", session->sid);
+ if (NULL == DBfetch(result))
+ {
+ zbx_hashset_remove_direct(&manager->sessions, session);
+ session = NULL;
+ }
+ DBfree_result(result);
+ }
+
+ if (NULL == session)
+ {
+ zbx_rm_session_t session_local;
+ zbx_db_insert_t db_insert;
+
+ session_local.userid = userid;
+ session = (zbx_rm_session_t *)zbx_hashset_insert(&manager->sessions, &session_local,
+ sizeof(session_local));
+
+ session->sid = zbx_create_token(0);
+ session->cookie = report_create_cookie(manager, session->sid);
+ session->db_lastaccess = now;
+
+ zbx_db_insert_prepare(&db_insert, "sessions", "sessionid", "userid", "lastaccess", "status", NULL);
+ zbx_db_insert_add_values(&db_insert, session->sid, userid, now, ZBX_SESSION_ACTIVE);
+ zbx_db_insert_execute(&db_insert);
+ zbx_db_insert_clean(&db_insert);
+ }
+
+ session->lastaccess = now;
+
+ return session;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_db_flush_sessions *
+ * *
+ * Purpose: flushes session lastaccess changes to database *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_db_flush_sessions(zbx_rm_t *manager)
+{
+ zbx_hashset_iter_t iter;
+ zbx_rm_session_t *session;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ DBbegin();
+ DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ zbx_hashset_iter_reset(&manager->sessions, &iter);
+ while (NULL != (session = (zbx_rm_session_t *)zbx_hashset_iter_next(&iter)))
+ {
+ if (session->lastaccess == session->db_lastaccess)
+ continue;
+
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update sessions set lastaccess=%d"
+ " where sessionid='%s';\n", session->lastaccess, session->sid);
+ DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
+ session->db_lastaccess = session->lastaccess;
+ }
+
+ DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ if (16 < sql_offset)
+ DBexecute("%s", sql);
+
+ DBcommit();
+ zbx_free(sql);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_db_flush_reports *
+ * *
+ * Purpose: flushes report state, lastaccess and error fields *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_db_flush_reports(zbx_rm_t *manager)
+{
+ int i;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ if (0 == manager->flush_queue.values_num)
+ goto out;
+
+ zbx_vector_uint64_sort(&manager->flush_queue, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_vector_uint64_uniq(&manager->flush_queue, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+
+ DBbegin();
+ DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ for (i = 0; i < manager->flush_queue.values_num; i++)
+ {
+ zbx_rm_report_t *report;
+ char delim = ' ';
+
+ if (NULL == (report = (zbx_rm_report_t *)zbx_hashset_search(&manager->reports,
+ &manager->flush_queue.values[i])))
+ {
+ /* report was removed */
+ continue;
+ }
+
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update report set");
+
+ if (0 != (report->flags & ZBX_REPORT_UPDATE_LASTSENT))
+ {
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%clastsent=%d", delim, (int)time(NULL));
+ delim = ',';
+ }
+
+ if (0 != (report->flags & ZBX_REPORT_UPDATE_STATE))
+ {
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%cstate=%d", delim, report->state);
+ delim = ',';
+ }
+
+ if (0 != (report->flags & ZBX_REPORT_UPDATE_ERROR))
+ {
+ char *esc, *empty = "";
+
+ if (NULL != report->error)
+ esc = DBdyn_escape_string_len(report->error, REPORT_ERROR_LEN);
+ else
+ esc = empty;
+
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%cinfo='%s'", delim, esc);
+
+ if (esc != empty)
+ zbx_free(esc);
+ }
+
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where reportid=" ZBX_FS_UI64 ";\n",
+ report->reportid);
+
+ DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
+
+ report->flags = 0;
+ }
+
+ DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ if (16 < sql_offset) /* in ORACLE always present begin..end; */
+ DBexecute("%s", sql);
+
+ DBcommit();
+
+ /* recreate flush queue to release memory */
+ zbx_vector_uint64_destroy(&manager->flush_queue);
+ zbx_vector_uint64_create(&manager->flush_queue);
+
+ zbx_free(sql);
+out:
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_get_report_range *
+ * *
+ * Purpose: calculate report range from report time and period *
+ * *
+ * Parameters: report_time - [IN] the report writing time *
+ * period - [IN] the dashboard period *
+ * from - [OUT] the report start time *
+ * to - [OUT] the report end time *
+ * *
+ * Return value: SUCCEED - the report range was calculated successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rm_get_report_range(int report_time, unsigned char period, struct tm *from, struct tm *to)
+{
+ struct tm *tm;
+ time_t from_time = report_time;
+ zbx_time_unit_t period2unit[] = {ZBX_TIME_UNIT_DAY, ZBX_TIME_UNIT_WEEK, ZBX_TIME_UNIT_MONTH, ZBX_TIME_UNIT_YEAR};
+
+ if (ARRSIZE(period2unit) <= period || NULL == (tm = localtime(&from_time)))
+ return FAIL;
+
+ *to = *tm;
+ zbx_tm_round_down(to, period2unit[period]);
+
+ *from = *to;
+ zbx_tm_sub(from, 1, period2unit[period]);
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_get_report_name *
+ * *
+ * Purpose: make report attachment name based on report name and timestamp *
+ * *
+ * Parameters: name - [IN] the report name *
+ * report_time - [IN] the report time *
+ * *
+ * Return value: The report attachment name *
+ * *
+ ******************************************************************************/
+static char *rm_get_report_name(const char *name, int report_time)
+{
+ time_t rtime = report_time;
+ struct tm *tm;
+ char *name_esc, *ptr, *name_full;
+
+ name_esc = zbx_strdup(NULL, name);
+ for (ptr = name_esc; '\0' != *name; ptr++, name++)
+ {
+ switch (*name)
+ {
+ case ' ':
+ case '\t':
+ case ':':
+ case '/':
+ case '\\':
+ *ptr = '_';
+ break;
+ default:
+ *ptr = *name;
+ break;
+ }
+ }
+
+ if (NULL == (tm = localtime(&rtime)))
+ name_full = zbx_dsprintf(NULL, "%s.pdf", name_esc);
+ else
+ name_full = zbx_dsprintf(NULL, "%s_%04d-%02d-%02d_%02d-%02d.pdf", name_esc, tm->tm_year + 1900,
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+
+ zbx_free(name_esc);
+
+ return name_full;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_create_job *
+ * *
+ * Purpose: create new job to be processed by report writers *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * report_name - [IN] the report name *
+ * dashboardid - [IN] the dashboard to view *
+ * access_userid - [IN] the user accessing the dashboard *
+ * report_time - [IN] the report time *
+ * period - [IN] the report period *
+ * userids - [IN] the recipient user identifiers *
+ * userids_num - [IN] the number of recipients *
+ * report_width - [IN] the report width *
+ * report_height - [IN] the report height *
+ * params - [IN] the viewing and processing parameters *
+ * *
+ ******************************************************************************/
+static zbx_rm_job_t *rm_create_job(zbx_rm_t *manager, const char *report_name, zbx_uint64_t dashboardid,
+ zbx_uint64_t access_userid, int report_time, unsigned char period, zbx_uint64_t *userids,
+ int userids_num, int report_width, int report_height, const zbx_vector_ptr_pair_t *params, char **error)
+{
+ zbx_rm_job_t *job;
+ size_t url_alloc = 0, url_offset = 0;
+ zbx_rm_session_t *session;
+ struct tm from, to;
+ int i;
+
+ if ('\0' == *manager->zabbix_url)
+ {
+ *error = zbx_strdup(NULL, "The Frontend URL has not been configured");
+ return NULL;
+ }
+
+ if (SUCCEED != rm_get_report_range(report_time, period, &from, &to))
+ {
+ *error = zbx_strdup(NULL, "invalid report time or period");
+ return NULL;
+ }
+
+ job = (zbx_rm_job_t *)zbx_malloc(NULL, sizeof(zbx_rm_job_t));
+ memset(job, 0, sizeof(zbx_rm_job_t));
+
+ job->report_name = rm_get_report_name(report_name, report_time);
+
+ zbx_vector_ptr_pair_create(&job->params);
+ for (i = 0; i < params->values_num; i++)
+ {
+ zbx_ptr_pair_t pair;
+
+ pair.first = zbx_strdup(NULL, (const char *)params->values[i].first);
+ pair.second = zbx_strdup(NULL, (const char *)params->values[i].second);
+ zbx_vector_ptr_pair_append(&job->params, pair);
+ }
+
+ zbx_vector_uint64_create(&job->userids);
+ zbx_vector_uint64_append_array(&job->userids, userids, userids_num);
+
+ zbx_snprintf_alloc(&job->url, &url_alloc, &url_offset,
+ "%s/zabbix.php?action=dashboard.print&dashboardid=" ZBX_FS_UI64,
+ manager->zabbix_url, dashboardid);
+ zbx_snprintf_alloc(&job->url, &url_alloc, &url_offset, "&from=%s", rm_time_to_urlfield(&from));
+ zbx_snprintf_alloc(&job->url, &url_alloc, &url_offset, "&to=%s", rm_time_to_urlfield(&to));
+
+ session = rm_get_session(manager, access_userid);
+ job->cookie = zbx_strdup(NULL, session->cookie);
+
+ job->access_userid = access_userid;
+ job->report_width = report_width;
+ job->report_height = report_height;
+
+ return job;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_update_report *
+ * *
+ * Purpose: update report state, lastsent, error in cache *
+ * *
+ * Parameters: manager - [IN] the report manager *
+ * report - [IN] the report to process *
+ * state - [IN] the new report status *
+ * info - [IN] the new report error message *
+ * *
+ ******************************************************************************/
+static void rm_update_report(zbx_rm_t *manager, zbx_rm_report_t *report, int state, const char *info)
+{
+ zbx_uint32_t flags = 0;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() reportid:" ZBX_FS_UI64 ", state:%d info:%s", __func__,
+ report->reportid, state, ZBX_NULL2EMPTY_STR(info));
+
+ if (ZBX_REPORT_STATE_ERROR != state)
+ flags |= ZBX_REPORT_UPDATE_LASTSENT;
+
+ if (report->state != state)
+ {
+ report->state = state;
+ flags |= ZBX_REPORT_UPDATE_STATE;
+ }
+
+ if (NULL == info)
+ {
+ if (NULL != report->error && '\0' != *report->error)
+ {
+ flags |= ZBX_REPORT_UPDATE_ERROR;
+ zbx_free(report->error);
+ }
+ }
+ else if (NULL == report->error || 0 != strcmp(report->error, info))
+ {
+ report->error = zbx_strdup(report->error, info);
+ flags |= ZBX_REPORT_UPDATE_ERROR;
+ }
+
+ if (0 != flags)
+ {
+ report->flags |= flags;
+ zbx_vector_uint64_append(&manager->flush_queue, report->reportid);
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_calc_nextcheck *
+ * *
+ * Purpose: calculate time when report must be generated *
+ * *
+ * Parameters: report - [IN] the report *
+ * now - [IN] the current time *
+ * *
+ ******************************************************************************/
+static int rm_report_calc_nextcheck(const zbx_rm_report_t *report, int now, char **error)
+{
+ int nextcheck;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() now:%s %s", __func__, zbx_date2str(now, NULL), zbx_time2str(now, NULL));
+
+ if (ZBX_REPORT_CYCLE_WEEKLY == report->cycle && 0 == report->weekdays)
+ {
+ *error = zbx_strdup(NULL, "Cannot calculate report start time: weekdays must be set for weekly cycle");
+ nextcheck = -1;
+ }
+ else
+ {
+ if (-1 == (nextcheck = zbx_get_report_nextcheck(now, report->cycle, report->weekdays,
+ report->start_time, report->timezone)))
+ {
+ *error = zbx_dsprintf(NULL, "Cannot calculate report start time: %s",
+ zbx_strerror(errno));
+ }
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s() nextcheck:%s %s, error:%s", __func__, zbx_date2str(nextcheck, NULL),
+ zbx_time2str(nextcheck, NULL), ZBX_NULL2EMPTY_STR(*error));
+
+ return nextcheck;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_update_params *
+ * *
+ * Purpose: update report parameters *
+ * *
+ * Parameters: report - [IN] the report *
+ * params - [IN] the report parameters *
+ * *
+ ******************************************************************************/
+static void rm_report_update_params(zbx_rm_report_t *report, zbx_vector_ptr_pair_t *params)
+{
+ zbx_vector_ptr_pair_t old_params;
+ int i, j;
+
+ zbx_vector_ptr_pair_create(&old_params);
+
+ zbx_vector_ptr_pair_append_array(&old_params, report->params.values, report->params.values_num);
+ zbx_vector_ptr_pair_clear(&report->params);
+
+ for (i = 0; i < params->values_num; i++)
+ {
+ zbx_ptr_pair_t pair = {0};
+ zbx_ptr_pair_t *new_param = &params->values[i];
+
+ for (j = 0; j < old_params.values_num; j++)
+ {
+ zbx_ptr_pair_t *old_param = &old_params.values[j];
+
+ if (0 == strcmp(new_param->first, old_param->first))
+ {
+ pair.first = old_param->first;
+ old_param->first = NULL;
+
+ if (0 == strcmp(new_param->second, old_param->second))
+ {
+ pair.second = old_param->second;
+ old_param->second = NULL;
+ }
+ else
+ pair.second = zbx_strdup(old_param->second, new_param->second);
+
+ zbx_vector_ptr_pair_remove_noorder(&old_params, j);
+ break;
+ }
+ }
+
+ if (NULL == pair.first)
+ {
+ pair.first = zbx_strdup(NULL, new_param->first);
+ pair.second = zbx_strdup(NULL, new_param->second);
+ }
+
+ zbx_vector_ptr_pair_append(&report->params, pair);
+ }
+
+ report_destroy_params(&old_params);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_update_users *
+ * *
+ * Purpose: update report recipient users *
+ * *
+ * Parameters: report - [IN] the report *
+ * users - [IN] the recipient users *
+ * users_excl - [IN] the excluded user ids *
+ * *
+ ******************************************************************************/
+static void rm_report_update_users(zbx_rm_report_t *report, const zbx_vector_recipient_t *users,
+ const zbx_vector_uint64_t *users_excl)
+{
+ zbx_vector_recipient_clear(&report->users);
+ zbx_vector_recipient_append_array(&report->users, users->values, users->values_num);
+
+ zbx_vector_uint64_clear(&report->users_excl);
+ zbx_vector_uint64_append_array(&report->users_excl, users_excl->values, users_excl->values_num);
+ zbx_vector_uint64_sort(&report->users_excl, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_update_usergroups *
+ * *
+ * Purpose: update report recipient user groups *
+ * *
+ * Parameters: report - [IN] the report *
+ * usergroups - [IN] the recipient user groups *
+ * *
+ ******************************************************************************/
+static void rm_report_update_usergroups(zbx_rm_report_t *report, const zbx_vector_recipient_t *usergroups)
+{
+ zbx_vector_recipient_clear(&report->usergroups);
+ zbx_vector_recipient_append_array(&report->usergroups, usergroups->values, usergroups->values_num);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_update_cache_settings *
+ * *
+ * Purpose: update general settings cache *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_update_cache_settings(zbx_rm_t *manager)
+{
+ DB_RESULT result;
+ DB_ROW row;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ result = DBselect("select session_key,url from config");
+
+ if (NULL != (row = DBfetch(result)))
+ {
+ manager->session_key = zbx_strdup(manager->session_key, row[0]);
+ manager->zabbix_url = zbx_strdup(manager->zabbix_url, row[1]);
+ }
+ else
+ {
+ manager->session_key = zbx_strdup(manager->session_key, "");
+ manager->zabbix_url = zbx_strdup(manager->zabbix_url, "");
+ }
+ DBfree_result(result);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_is_report_active *
+ * *
+ * Purpose: check if the report is active based on the specified time *
+ * *
+ * Parameters: report - [IN] the report *
+ * now - [IN] the current time *
+ * *
+ * Return value: SUCCEED - the report is active *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rm_is_report_active(const zbx_rm_report_t *report, int now)
+{
+ if (0 != report->active_since && now < report->active_since)
+ return FAIL;
+
+ if (0 != report->active_till && now >= report->active_till)
+ return FAIL;
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_dequeue_report *
+ * *
+ * Purpose: remove report from queue if it was queued *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * report - [IN] the report *
+ * *
+ ******************************************************************************/
+static void rm_dequeue_report(zbx_rm_t *manager, zbx_rm_report_t *report)
+{
+ if (0 != report->nextcheck)
+ {
+ zbx_binary_heap_remove_direct(&manager->report_queue, report->reportid);
+ report->nextcheck = 0;
+ }
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_update_cache_reports *
+ * *
+ * Purpose: update reports cache *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * now - [IN] the current time *
+ * *
+ ******************************************************************************/
+static void rm_update_cache_reports(zbx_rm_t *manager, int now)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ zbx_vector_uint64_t reportids;
+ zbx_hashset_iter_t iter;
+ zbx_rm_report_t *report, report_local;
+ zbx_config_t cfg;
+ const char *tz;
+ char *error = NULL;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_config_get(&cfg, ZBX_CONFIG_FLAGS_DEFAULT_TIMEZONE);
+
+ zbx_vector_uint64_create(&reportids);
+
+ result = DBselect("select r.reportid,r.userid,r.name,r.dashboardid,r.period,r.cycle,r.weekdays,r.start_time,"
+ "r.active_since,r.active_till,u.timezone,r.state,r.info,r.lastsent,r.status"
+ " from report r,users u"
+ " where r.userid=u.userid");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ zbx_uint64_t reportid;
+ int nextcheck, start_time, active_since, active_till, reschedule = 0;
+ unsigned char period, cycle, weekdays;
+
+
+ ZBX_STR2UINT64(reportid, row[0]);
+ zbx_vector_uint64_append(&reportids, reportid);
+
+ tz = row[10];
+ if (0 == strcmp(tz, ZBX_TIMEZONE_DEFAULT_VALUE))
+ tz = cfg.default_timezone;
+
+ ZBX_STR2UCHAR(period, row[4]);
+ ZBX_STR2UCHAR(cycle, row[5]);
+ ZBX_STR2UCHAR(weekdays, row[6]);
+ start_time = atoi(row[7]);
+ active_since = atoi(row[8]);
+ active_till = atoi(row[9]);
+
+ if (NULL == (report = (zbx_rm_report_t *)zbx_hashset_search(&manager->reports, &reportid)))
+ {
+ report_local.reportid = reportid;
+ report = (zbx_rm_report_t *)zbx_hashset_insert(&manager->reports, &report_local,
+ sizeof(report_local));
+
+ zbx_vector_ptr_pair_create(&report->params);
+ zbx_vector_recipient_create(&report->usergroups);
+ zbx_vector_recipient_create(&report->users);
+ zbx_vector_uint64_create(&report->users_excl);
+ report->name = zbx_strdup(NULL, row[2]);
+ report->timezone = zbx_strdup(NULL, tz);
+ report->nextcheck = 0;
+ ZBX_STR2UCHAR(report->state, row[11]);
+ report->error = zbx_strdup(NULL, row[12]);
+ report->lastsent = atoi(row[13]);
+ report->flags = 0;
+
+ reschedule = 1;
+ }
+ else
+ {
+ if (report->period != period || report->cycle != cycle || report->weekdays != weekdays ||
+ report->start_time != start_time || report->active_since != active_since ||
+ report->active_till != active_till)
+ {
+ reschedule = 1;
+ }
+
+ if (0 != strcmp(report->name, row[2]))
+ report->name = zbx_strdup(report->name, row[2]);
+
+ if (0 != strcmp(report->timezone, tz))
+ {
+ report->timezone = zbx_strdup(report->timezone, tz);
+ reschedule = 1;
+ }
+ }
+
+ ZBX_STR2UINT64(report->userid, row[1]);
+ ZBX_STR2UINT64(report->dashboardid, row[3]);
+ ZBX_STR2UCHAR(report->period, row[4]);
+ ZBX_STR2UCHAR(report->cycle, row[5]);
+ ZBX_STR2UCHAR(report->weekdays, row[6]);
+ report->start_time = atoi(row[7]);
+ report->active_since = atoi(row[8]);
+ report->active_till = atoi(row[9]);
+ ZBX_STR2UCHAR(report->status, row[14]);
+
+ if (ZBX_REPORT_STATUS_DISABLED == report->status)
+ {
+ rm_dequeue_report(manager, report);
+ continue;
+ }
+
+ if (0 == reschedule)
+ continue;
+
+ if (-1 != (nextcheck = rm_report_calc_nextcheck(report, now, &error)))
+ {
+ if (nextcheck != report->nextcheck)
+ {
+ if (SUCCEED == rm_is_report_active(report, now))
+ {
+ zbx_binary_heap_elem_t elem = {report->reportid, (void *)report};
+ int nextcheck_old = report->nextcheck;
+
+ report->nextcheck = nextcheck;
+
+ if (0 != nextcheck_old)
+ zbx_binary_heap_update_direct(&manager->report_queue, &elem);
+ else
+ zbx_binary_heap_insert(&manager->report_queue, &elem);
+ }
+ else
+ rm_dequeue_report(manager, report);
+ }
+ }
+ else
+ {
+ rm_update_report(manager, report, ZBX_REPORT_STATE_ERROR, error);
+ rm_dequeue_report(manager, report);
+ zbx_free(error);
+ }
+ }
+ DBfree_result(result);
+
+ /* remove deleted reports from cache */
+ zbx_vector_uint64_sort(&reportids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_hashset_iter_reset(&manager->reports, &iter);
+ while (NULL != (report = (zbx_rm_report_t *)zbx_hashset_iter_next(&iter)))
+ {
+ if (FAIL == zbx_vector_uint64_bsearch(&reportids, report->reportid, ZBX_DEFAULT_UINT64_COMPARE_FUNC))
+ {
+ rm_dequeue_report(manager, report);
+ rm_report_clean(report);
+ zbx_hashset_iter_remove(&iter);
+ }
+ }
+
+ zbx_vector_uint64_destroy(&reportids);
+
+ zbx_config_clean(&cfg);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_update_cache_report_param *
+ * *
+ * Purpose: update cached report parameters *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_update_cache_reports_params(zbx_rm_t *manager)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ zbx_rm_report_t *report = NULL;
+ zbx_vector_ptr_pair_t params;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_vector_ptr_pair_create(&params);
+
+ result = DBselect("select rp.reportid,rp.name,rp.value"
+ " from report_param rp,report r"
+ " where rp.reportid=r.reportid"
+ " and r.status=%d"
+ " order by r.reportid",
+ ZBX_REPORT_STATUS_ENABLED);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ zbx_uint64_t reportid;
+ zbx_ptr_pair_t pair;
+
+ ZBX_STR2UINT64(reportid, row[0]);
+ if (NULL != report)
+ {
+ if (reportid != report->reportid)
+ {
+ rm_report_update_params(report, &params);
+ report_clear_params(&params);
+ }
+ report = NULL;
+ }
+
+ if (NULL == report)
+ {
+ if (NULL == (report = (zbx_rm_report_t *)zbx_hashset_search(&manager->reports, &reportid)))
+ continue;
+ }
+
+ pair.first = zbx_strdup(NULL, row[1]);
+ pair.second = zbx_strdup(NULL, row[2]);
+ zbx_vector_ptr_pair_append(&params, pair);
+ }
+ DBfree_result(result);
+
+ if (0 != params.values_num)
+ rm_report_update_params(report, &params);
+
+ report_destroy_params(&params);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_update_cache_reports_users *
+ * *
+ * Purpose: update cached report recipient users *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_update_cache_reports_users(zbx_rm_t *manager)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ zbx_rm_report_t *report = NULL;
+ zbx_vector_recipient_t users;
+ zbx_vector_uint64_t users_excl;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_vector_recipient_create(&users);
+ zbx_vector_uint64_create(&users_excl);
+
+ result = DBselect("select ru.reportid,ru.userid,ru.exclude,ru.access_userid"
+ " from report_user ru,report r"
+ " where ru.reportid=r.reportid"
+ " and r.status=%d"
+ " order by r.reportid",
+ ZBX_REPORT_STATUS_ENABLED);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ zbx_uint64_t reportid, userid;
+
+ ZBX_STR2UINT64(reportid, row[0]);
+ if (NULL != report)
+ {
+ if (reportid != report->reportid)
+ {
+ rm_report_update_users(report, &users, &users_excl);
+ zbx_vector_recipient_clear(&users);
+ zbx_vector_uint64_clear(&users_excl);
+ }
+ report = NULL;
+ }
+
+ if (NULL == report)
+ {
+ if (NULL == (report = (zbx_rm_report_t *)zbx_hashset_search(&manager->reports, &reportid)))
+ continue;
+ }
+
+ ZBX_STR2UINT64(userid, row[1]);
+ if (ZBX_REPORT_INCLUDE_USER == atoi(row[2]))
+ {
+ zbx_rm_recipient_t user;
+
+ user.id = userid;
+ ZBX_DBROW2UINT64(user.access_userid, row[3]);
+ zbx_vector_recipient_append(&users, user);
+ }
+ else
+ zbx_vector_uint64_append(&users_excl, userid);
+ }
+ DBfree_result(result);
+
+ if (0 != users.values_num || 0 != users_excl.values_num)
+ rm_report_update_users(report, &users, &users_excl);
+
+ zbx_vector_uint64_destroy(&users_excl);
+ zbx_vector_recipient_destroy(&users);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_update_cache_reports_usergroups *
+ * *
+ * Purpose: update cached report recipient user groups *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_update_cache_reports_usergroups(zbx_rm_t *manager)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ zbx_rm_report_t *report = NULL;
+ zbx_vector_recipient_t usergroups;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_vector_recipient_create(&usergroups);
+
+ result = DBselect("select rg.reportid,rg.usrgrpid,rg.access_userid"
+ " from report_usrgrp rg,report r"
+ " where rg.reportid=r.reportid"
+ " and r.status=%d"
+ " order by r.reportid",
+ ZBX_REPORT_STATUS_ENABLED);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ zbx_uint64_t reportid;
+ zbx_rm_recipient_t usergroup;
+
+ ZBX_STR2UINT64(reportid, row[0]);
+ if (NULL != report)
+ {
+ if (reportid != report->reportid)
+ {
+ rm_report_update_usergroups(report, &usergroups);
+ zbx_vector_recipient_clear(&usergroups);
+ }
+ report = NULL;
+ }
+
+ if (NULL == report)
+ {
+ if (NULL == (report = (zbx_rm_report_t *)zbx_hashset_search(&manager->reports, &reportid)))
+ continue;
+ }
+
+ ZBX_STR2UINT64(usergroup.id, row[1]);
+ ZBX_DBROW2UINT64(usergroup.access_userid, row[2]);
+ zbx_vector_recipient_append(&usergroups, usergroup);
+ }
+ DBfree_result(result);
+
+ if (0 != usergroups.values_num)
+ rm_report_update_usergroups(report, &usergroups);
+
+ zbx_vector_recipient_destroy(&usergroups);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_dump_cache *
+ * *
+ * Purpose: dump cached reports into log *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_dump_cache(zbx_rm_t *manager)
+{
+ zbx_hashset_iter_t iter;
+ zbx_rm_report_t *report;
+ char *str = NULL;
+ size_t str_alloc = 0, str_offset;
+ int i;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_hashset_iter_reset(&manager->reports, &iter);
+ while (NULL != (report = (zbx_rm_report_t *)zbx_hashset_iter_next(&iter)))
+ {
+ str_offset = 0;
+
+ zabbix_log(LOG_LEVEL_TRACE, "reportid:" ZBX_FS_UI64 ", name:%s, userid:" ZBX_FS_UI64 ", dashboardid:"
+ ZBX_FS_UI64 ", period:%d, cycle:%d, weekdays:0x%x",
+ report->reportid, report->name, report->userid, report->dashboardid, report->period,
+ report->cycle, report->weekdays);
+
+ zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "active:");
+ if (0 != report->active_since)
+ {
+ zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "%s %s",
+ zbx_date2str(report->active_since, NULL),
+ zbx_time2str(report->active_since, NULL));
+ }
+
+ zbx_strcpy_alloc(&str, &str_alloc, &str_offset, " - ");
+ if (0 != report->active_till)
+ {
+ zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "%s %s",
+ zbx_date2str(report->active_till, NULL),
+ zbx_time2str(report->active_till, NULL));
+ }
+
+ zbx_snprintf_alloc(&str, &str_alloc, &str_offset, ", start_time:%d:%02d:%02d, timezone:%s",
+ report->start_time / SEC_PER_HOUR, report->start_time % SEC_PER_HOUR / SEC_PER_MIN,
+ report->start_time % SEC_PER_MIN, report->timezone);
+ zbx_snprintf_alloc(&str, &str_alloc, &str_offset, ", nextcheck:%s %s",
+ zbx_date2str(report->nextcheck, NULL), zbx_time2str(report->nextcheck, NULL));
+ zabbix_log(LOG_LEVEL_TRACE, " %s", str);
+
+ zabbix_log(LOG_LEVEL_TRACE, " params:");
+ for (i = 0; i < report->params.values_num; i++)
+ {
+ zabbix_log(LOG_LEVEL_TRACE, " %s:%s", (char *)report->params.values[i].first,
+ (char *)report->params.values[i].second);
+ }
+
+ zabbix_log(LOG_LEVEL_TRACE, " users:");
+ for (i = 0; i < report->users.values_num; i++)
+ {
+ zbx_rm_recipient_t *user = (zbx_rm_recipient_t *)&report->users.values[i];
+
+ zabbix_log(LOG_LEVEL_TRACE, " userid:" ZBX_FS_UI64 ", acess_userid:" ZBX_FS_UI64,
+ user->id, user->access_userid);
+ }
+
+ zabbix_log(LOG_LEVEL_TRACE, " usergroups:");
+ for (i = 0; i < report->usergroups.values_num; i++)
+ {
+ zbx_rm_recipient_t *usergroup = (zbx_rm_recipient_t *)&report->usergroups.values[i];
+
+ zabbix_log(LOG_LEVEL_TRACE, " usrgrpid:" ZBX_FS_UI64 ", acess_userid:" ZBX_FS_UI64,
+ usergroup->id, usergroup->access_userid);
+ }
+
+ zabbix_log(LOG_LEVEL_TRACE, " exclude:");
+ for (i = 0; i < report->users_excl.values_num; i++)
+ {
+ zabbix_log(LOG_LEVEL_TRACE, " userid:" ZBX_FS_UI64, report->users_excl.values[i]);
+ }
+ }
+
+ zbx_free(str);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_update_cache *
+ * *
+ * Purpose: update configuration and report cache *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * *
+ ******************************************************************************/
+static void rm_update_cache(zbx_rm_t *manager)
+{
+ int now;
+
+ now = (int)time(NULL);
+
+ rm_update_cache_settings(manager);
+ rm_update_cache_reports(manager, now);
+ rm_update_cache_reports_params(manager);
+ rm_update_cache_reports_users(manager);
+ rm_update_cache_reports_usergroups(manager);
+
+ if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE))
+ rm_dump_cache(manager);
+}
+
+typedef struct
+{
+ zbx_uint64_t mediatypeid;
+ char *recipient;
+}
+zbx_report_dst_t;
+
+static void zbx_report_dst_free(zbx_report_dst_t *dst)
+{
+ zbx_free(dst->recipient);
+ zbx_free(dst);
+}
+
+#define ZBX_REPORT_DEFAULT_WIDTH 1920
+#define ZBX_REPORT_DEFAULT_HEIGHT 1080
+#define ZBX_REPORT_ROW_HEIGHT 70
+#define ZBX_REPORT_BOTTOM_MARGIN 12
+
+/******************************************************************************
+ * *
+ * Function: rm_get_report_dimensions *
+ * *
+ * Purpose: calculate report dimensions based on dashboard contents *
+ * *
+ * Parameters: dashboardid - [IN] the dashboard id *
+ * width - [OUT] the report width in pixels *
+ * height - [OUT] the report height in pixels *
+ * *
+ ******************************************************************************/
+static void rm_get_report_dimensions(zbx_uint64_t dashboardid, int *width, int *height)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ int y_max = 0;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() dashboardid:" ZBX_FS_UI64, __func__, dashboardid);
+
+ result = DBselect("select w.y,w.height"
+ " from widget w,dashboard_page p"
+ " where w.dashboard_pageid=p.dashboard_pageid"
+ " and p.dashboardid=" ZBX_FS_UI64
+ " and p.sortorder=0", dashboardid);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ int bottom;
+
+ bottom = atoi(row[0]) + atoi(row[1]);
+ if (bottom > y_max)
+ y_max = bottom;
+ }
+ DBfree_result(result);
+
+ if (0 != y_max)
+ *height = y_max * ZBX_REPORT_ROW_HEIGHT + ZBX_REPORT_BOTTOM_MARGIN;
+ else
+ *height = ZBX_REPORT_DEFAULT_HEIGHT;
+
+ *width = ZBX_REPORT_DEFAULT_WIDTH;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s() width:%d height:%d", __func__, *width, *height);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_writer_process_job *
+ * *
+ * Purpose: process job by sending it to writer *
+ * *
+ * Parameters: writer - [IN] the writer *
+ * job - [IN] the view to process *
+ * char - [OUT] the error message *
+ * *
+ ******************************************************************************/
+static int rm_writer_process_job(zbx_rm_writer_t *writer, zbx_rm_job_t *job, char **error)
+{
+ unsigned char *data = NULL;
+ zbx_uint32_t size;
+ int ret = FAIL, rc;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+ zbx_vector_uint64_t mediatypeids;
+ zbx_vector_ptr_t dsts;
+ DB_RESULT result;
+ DB_ROW row;
+ zbx_report_dst_t *dst;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() url:%s", __func__, job->url);
+
+ zbx_vector_uint64_create(&mediatypeids);
+ zbx_vector_ptr_create(&dsts);
+
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
+ "select m.sendto,mt.mediatypeid"
+ " from media m,media_type mt"
+ " where");
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "m.userid", job->userids.values, job->userids.values_num);
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
+ " and m.active=%d"
+ " and m.mediatypeid=mt.mediatypeid"
+ " and mt.type=%d"
+ " and mt.status=%d",
+ MEDIA_STATUS_ACTIVE, MEDIA_TYPE_EMAIL, MEDIA_TYPE_STATUS_ACTIVE);
+
+ result = DBselect("%s", sql);
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ dst = (zbx_report_dst_t *)zbx_malloc(NULL, sizeof(zbx_report_dst_t));
+ ZBX_STR2UINT64(dst->mediatypeid, row[1]);
+ dst->recipient = zbx_strdup(NULL, row[0]);
+ zbx_vector_ptr_append(&dsts, dst);
+ zbx_vector_uint64_append(&mediatypeids, dst->mediatypeid);
+ }
+ DBfree_result(result);
+
+ if (0 == dsts.values_num)
+ {
+ *error = zbx_dsprintf(NULL, "No media configured for the report recipients");
+ goto out;
+ }
+
+ size = report_serialize_begin_report(&data, job->report_name, job->url, job->cookie, job->report_width,
+ job->report_height, &job->params);
+
+ if (SUCCEED != zbx_ipc_client_send(writer->client, ZBX_IPC_REPORTER_BEGIN_REPORT, data, size))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ *error = zbx_dsprintf(NULL, "Cannot send message to report writer");
+ goto out;
+ }
+
+ zbx_free(data);
+
+ ret = SUCCEED;
+
+ if (0 != dsts.values_num)
+ {
+ zbx_vector_str_t recipients;
+ int index = 0;
+
+ zbx_vector_str_create(&recipients);
+
+ zbx_vector_ptr_sort(&dsts, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
+ zbx_vector_uint64_sort(&mediatypeids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_vector_uint64_uniq(&mediatypeids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+
+ sql_offset = 0;
+
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
+ "select mediatypeid,type,smtp_server,smtp_helo,smtp_email,exec_path,gsm_modem,username,"
+ "passwd,smtp_port,smtp_security,smtp_verify_peer,smtp_verify_host,"
+ "smtp_authentication,exec_params,maxsessions,maxattempts,attempt_interval,"
+ "content_type,script,timeout"
+ " from media_type"
+ " where");
+
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "mediatypeid", mediatypeids.values,
+ mediatypeids.values_num);
+
+ result = DBselect("%s", sql);
+
+ while (NULL != (row = DBfetch(result)) && SUCCEED == ret)
+ {
+ DB_MEDIATYPE mt;
+
+ ZBX_STR2UINT64(mt.mediatypeid, row[0]);
+
+ mt.type = atoi(row[1]);
+ mt.smtp_server = zbx_strdup(NULL, row[2]);
+ mt.smtp_helo = zbx_strdup(NULL, row[3]);
+ mt.smtp_email = zbx_strdup(NULL, row[4]);
+ mt.exec_path = zbx_strdup(NULL, row[5]);
+ mt.gsm_modem = zbx_strdup(NULL, row[6]);
+ mt.username = zbx_strdup(NULL, row[7]);
+ mt.passwd = zbx_strdup(NULL, row[8]);
+ mt.smtp_port = (unsigned short)atoi(row[9]);
+ ZBX_STR2UCHAR(mt.smtp_security, row[10]);
+ ZBX_STR2UCHAR(mt.smtp_verify_peer, row[11]);
+ ZBX_STR2UCHAR(mt.smtp_verify_host, row[12]);
+ ZBX_STR2UCHAR(mt.smtp_authentication, row[13]);
+ mt.exec_params = zbx_strdup(NULL, row[14]);
+ mt.maxsessions = atoi(row[15]);
+ mt.maxattempts = atoi(row[16]);
+ mt.attempt_interval = zbx_strdup(NULL, row[17]);
+ ZBX_STR2UCHAR(mt.content_type, row[18]);
+ mt.script = zbx_strdup(NULL, row[19]);
+ mt.timeout = zbx_strdup(NULL, row[20]);
+
+ for (; index < dsts.values_num; index++)
+ {
+ dst = (zbx_report_dst_t *)dsts.values[index];
+ if (dst->mediatypeid != mt.mediatypeid)
+ break;
+ zbx_vector_str_append(&recipients, dst->recipient);
+ }
+
+ if (0 != recipients.values_num)
+ {
+ size = report_serialize_send_report(&data, &mt, &recipients);
+ ret = zbx_ipc_client_send(writer->client, ZBX_IPC_REPORTER_SEND_REPORT, data, size);
+ zbx_free(data);
+ }
+ else
+ THIS_SHOULD_NEVER_HAPPEN;
+
+ zbx_vector_str_clear(&recipients);
+ zbx_db_mediatype_clean(&mt);
+ }
+ DBfree_result(result);
+
+ zbx_vector_str_destroy(&recipients);
+ }
+
+ /* attempt to send finish request even if last sending failed */
+ rc = zbx_ipc_client_send(writer->client, ZBX_IPC_REPORTER_END_REPORT, NULL, 0);
+ if (SUCCEED == ret)
+ ret = rc;
+out:
+ zbx_free(sql);
+ zbx_free(data);
+ zbx_vector_ptr_clear_ext(&dsts, (zbx_ptr_free_func_t)zbx_report_dst_free);
+ zbx_vector_ptr_destroy(&dsts);
+ zbx_vector_uint64_destroy(&mediatypeids);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_create_jobs *
+ * *
+ * Purpose: create jobs to process the report *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * report - [IN] the report to process *
+ * userid - [IN] the recipient user id *
+ * access_userid - [IN] the user id used to create the report *
+ * now - [IN] the current time *
+ * params - [IN] the report parameters *
+ * width - [IN] the report width *
+ * height - [IN] the report height *
+ * jobs - [IN/OUT] the created jobs *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - the user was added to existing job or a new was *
+ * successfully created. *
+ * FAIL - failed to create a new job for the user. *
+ * *
+ ******************************************************************************/
+static int rm_jobs_add_user(zbx_rm_t *manager, zbx_rm_report_t *report, zbx_uint64_t userid,
+ zbx_uint64_t access_userid, int now, const zbx_vector_ptr_pair_t *params, int width, int height,
+ zbx_vector_ptr_t *jobs, char **error)
+{
+ int i;
+ zbx_rm_job_t *job;
+
+ if (FAIL != zbx_vector_uint64_search(&report->users_excl, userid, ZBX_DEFAULT_UINT64_COMPARE_FUNC))
+ return SUCCEED;
+
+ for (i = 0; i < jobs->values_num; i++)
+ {
+ job = (zbx_rm_job_t *)jobs->values[i];
+ if (job->access_userid == access_userid)
+ break;
+ }
+
+ if (i == jobs->values_num)
+ {
+ if (NULL == (job = rm_create_job(manager, report->name, report->dashboardid, access_userid, now,
+ report->period, &userid, 1, width, height, params, error)))
+ {
+ return FAIL;
+ }
+ zbx_vector_ptr_append(jobs, job);
+ }
+
+ zbx_vector_uint64_append(&job->userids, userid);
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_create_usergroup_jobs *
+ * *
+ * Purpose: create user group based jobs *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * report - [IN] the report to process *
+ * now - [IN] the current time *
+ * params - [IN] the report parameters *
+ * width - [IN] the report width *
+ * height - [IN] the report height *
+ * jobs - [IN/OUT] the created jobs *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - jobs were created successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rm_report_create_usergroup_jobs(zbx_rm_t *manager, zbx_rm_report_t *report, int now,
+ const zbx_vector_ptr_pair_t *params, int width, int height, zbx_vector_ptr_t *jobs, char **error)
+{
+ DB_ROW row;
+ DB_RESULT result;
+ zbx_vector_uint64_t ids;
+ int i, ret = FAIL;
+ char *sql = NULL;
+ size_t sql_alloc = 0, sql_offset = 0;
+ zbx_uint64_t userid, usrgrpid, access_userid;
+
+ zbx_vector_uint64_create(&ids);
+
+ for (i = 0; i < report->usergroups.values_num; i++)
+ zbx_vector_uint64_append(&ids, report->usergroups.values[i].id);
+
+ zbx_vector_uint64_sort(&ids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_vector_uint64_uniq(&ids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+
+ zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "select userid,usrgrpid from users_groups where");
+ DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "usrgrpid", ids.values, ids.values_num);
+
+ result = DBselect("%s", sql);
+ while (NULL != (row = DBfetch(result)))
+ {
+ access_userid = 0;
+
+ ZBX_STR2UINT64(userid, row[0]);
+ ZBX_STR2UINT64(usrgrpid, row[1]);
+
+ for (i = 0; i < report->usergroups.values_num; i++)
+ {
+ if (report->usergroups.values[i].id == usrgrpid)
+ {
+ access_userid = report->usergroups.values[i].access_userid;
+ break;
+ }
+ }
+
+ if (0 == access_userid)
+ access_userid = userid;
+
+ if (SUCCEED != rm_jobs_add_user(manager, report, userid, access_userid, now, params, width, height,
+ jobs, error))
+ {
+ goto out;
+ }
+ }
+
+ ret = SUCCEED;
+out:
+ DBfree_result(result);
+
+ zbx_free(sql);
+ zbx_vector_uint64_destroy(&ids);
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_report_create_jobs *
+ * *
+ * Purpose: create jobs to process the report *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * report - [IN] the report to process *
+ * now - [IN] the current time *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - jobs were created successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rm_report_create_jobs(zbx_rm_t *manager, zbx_rm_report_t *report, int now, char **error)
+{
+ zbx_vector_ptr_t jobs;
+ int i, ret = FAIL, jobs_num, width, height;
+ zbx_uint64_t access_userid;
+ zbx_rm_batch_t *batch, batch_local;
+ zbx_vector_ptr_pair_t params;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() reportid:" ZBX_FS_UI64 , __func__, report->reportid);
+
+ rm_get_report_dimensions(report->dashboardid, &width, &height);
+
+ zbx_vector_ptr_create(&jobs);
+ zbx_vector_ptr_pair_create(&params);
+
+ for (i = 0; i < report->params.values_num; i++)
+ {
+ zbx_ptr_pair_t pair;
+
+ pair.first = zbx_strdup(NULL, report->params.values[i].first);
+ pair.second = zbx_strdup(NULL, report->params.values[i].second);
+
+ if (0 == strcmp(pair.first, ZBX_REPORT_PARAM_BODY) || 0 == strcmp(pair.first, ZBX_REPORT_PARAM_SUBJECT))
+ {
+ substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ (char **)&pair.second, MACRO_TYPE_REPORT, NULL, 0);
+ }
+
+ zbx_vector_ptr_pair_append(&params, pair);
+ }
+
+ DBbegin();
+
+ for (i = 0; i < report->users.values_num; i++)
+ {
+ if (0 == (access_userid = report->users.values[i].access_userid))
+ access_userid = report->users.values[i].id;
+
+ if (SUCCEED != rm_jobs_add_user(manager, report, report->users.values[i].id, access_userid, now,
+ &params, width, height, &jobs, error))
+ {
+ goto out;
+ }
+ }
+
+ if (0 != report->usergroups.values_num)
+ {
+ if (SUCCEED != rm_report_create_usergroup_jobs(manager, report, now, &params, width, height, &jobs,
+ error))
+ {
+ goto out;
+ }
+ }
+
+ /* create job batch for result tracking */
+ batch_local.batchid = ++manager->last_batchid;
+ batch = (zbx_rm_batch_t *)zbx_hashset_insert(&manager->batches, &batch_local, sizeof(batch_local));
+ batch->reportid = report->reportid;
+ batch->error_num = 0;
+ batch->sent_num = 0;
+ batch->total_num = 0;
+ batch->info = NULL;
+ batch->info_alloc = 0;
+ batch->info_offset = 0;
+ zbx_vector_ptr_create(&batch->jobs);
+ zbx_vector_ptr_append_array(&batch->jobs, jobs.values, jobs.values_num);
+
+ /* queue jobs */
+ for (i = 0; i < jobs.values_num; i++)
+ {
+ zbx_rm_job_t *job = (zbx_rm_job_t *)jobs.values[i];
+
+ zbx_vector_uint64_sort(&job->userids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_vector_uint64_uniq(&job->userids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
+ zbx_list_append(&manager->job_queue, job, NULL);
+ job->batchid = batch->batchid;
+ }
+
+ ret = SUCCEED;
+out:
+ if (SUCCEED == ret)
+ {
+ DBcommit();
+ jobs_num = jobs.values_num;
+ }
+ else
+ {
+ DBrollback();
+ jobs_num = 0;
+ }
+
+ report_destroy_params(&params);
+ zbx_vector_ptr_destroy(&jobs);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s jobs:%d %s", __func__, zbx_result_string(ret), jobs_num,
+ ZBX_NULL2EMPTY_STR(*error));
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_schedule_jobs *
+ * *
+ * Purpose: process queue *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * now - [IN] the current time *
+ * *
+ * Return value: The number of scheduled jobs. *
+ * *
+ ******************************************************************************/
+static int rm_schedule_jobs(zbx_rm_t *manager, int now)
+{
+ zbx_rm_report_t *report;
+ zbx_binary_heap_elem_t *elem;
+ int nextcheck, ret, jobs_num = 0;
+ char *error = NULL;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() queue:%d", __func__, manager->report_queue.elems_num);
+
+ while (SUCCEED != zbx_binary_heap_empty(&manager->report_queue))
+ {
+ elem = zbx_binary_heap_find_min(&manager->report_queue);
+ report = (zbx_rm_report_t *)elem->data;
+ if (now < report->nextcheck)
+ break;
+
+ zbx_binary_heap_remove_min(&manager->report_queue);
+ report->nextcheck = 0;
+
+ if (SUCCEED == (ret = rm_report_create_jobs(manager, report, now, &error)))
+ {
+ if (-1 != (nextcheck = rm_report_calc_nextcheck(report, now, &error)))
+ {
+ if (SUCCEED == rm_is_report_active(report, now))
+ {
+ zbx_binary_heap_elem_t elem_new = {report->reportid, report};
+
+ report->nextcheck = nextcheck;
+ zbx_binary_heap_insert(&manager->report_queue, &elem_new);
+
+ jobs_num++;
+ }
+ }
+ else
+ ret = FAIL;
+ }
+
+ if (FAIL == ret)
+ {
+ rm_update_report(manager, report, ZBX_REPORT_STATE_ERROR, error);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "Cannot process report: %s", error);
+ zbx_free(error);
+ }
+
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s() jobs:%d", __func__, jobs_num);
+
+ return jobs_num;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_finish_job *
+ * *
+ * Purpose: finish job *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * job - [IN] the job *
+ * status - [IN] the job status *
+ * info - [IN] additional information (errors) *
+ * *
+ ******************************************************************************/
+static void rm_finish_job(zbx_rm_t *manager, zbx_rm_job_t *job, int status, const char *error, int sent_num,
+ int total_num)
+{
+ zbx_rm_batch_t *batch;
+ int i;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ if (NULL == (batch = (zbx_rm_batch_t *)zbx_hashset_search(&manager->batches, &job->batchid)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ return;
+ }
+
+ if (SUCCEED != status)
+ {
+ size_t offset = batch->info_offset;
+
+ batch->error_num++;
+ zbx_strcpy_alloc(&batch->info, &batch->info_alloc, &batch->info_offset, error);
+ batch->info[offset] = toupper(batch->info[offset]);
+ zbx_strcpy_alloc(&batch->info, &batch->info_alloc, &batch->info_offset, ".\n");
+ }
+ else
+ {
+ batch->sent_num += sent_num;
+ batch->total_num += total_num;
+ }
+
+ for (i = 0; i < batch->jobs.values_num; i++)
+ {
+ if (batch->jobs.values[i] == job)
+ {
+ rm_job_free(job);
+ zbx_vector_ptr_remove_noorder(&batch->jobs, i);
+ break;
+ }
+ }
+
+ if (0 == batch->jobs.values_num)
+ {
+ zbx_rm_report_t *report;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "%s() batch finished with %d failed jobs", __func__, batch->error_num);
+
+ if (NULL != (report = (zbx_rm_report_t *)zbx_hashset_search(&manager->reports, &batch->reportid)))
+ {
+ char *info = NULL;
+ size_t info_alloc = 0, info_offset = 0;
+
+
+ status = ZBX_REPORT_STATE_SUCCESS;
+ if (batch->sent_num != batch->total_num)
+ {
+ zbx_snprintf_alloc(&info, &info_alloc, &info_offset,
+ "Failed to sent %d report(s) from %d.\n",
+ batch->total_num - batch->sent_num, batch->total_num);
+ status = ZBX_REPORT_STATE_SUCCESS_INFO;
+ }
+
+ if (0 != batch->error_num)
+ {
+ zbx_snprintf_alloc(&info, &info_alloc, &info_offset,
+ "Failed to create %d report(s):\n%s",
+ batch->error_num, batch->info);
+ status = ZBX_REPORT_STATE_ERROR;
+ }
+
+ rm_update_report(manager, report, status, info);
+
+ zbx_free(info);
+ }
+
+ rm_batch_clean(batch);
+ zbx_hashset_remove_direct(&manager->batches, batch);
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_send_test_error_result *
+ * *
+ * Purpose: send error result in response to test request *
+ * *
+ * Parameters: client - [IN] the connected trapper *
+ * error - [IN] the error message *
+ * *
+ ******************************************************************************/
+static void rm_send_test_error_result(zbx_ipc_client_t *client, const char *error)
+{
+ unsigned char *data;
+ zbx_uint32_t size;
+
+ size = report_serialize_response(&data, FAIL, error, NULL);
+ zbx_ipc_client_send(client, ZBX_IPC_REPORTER_TEST_RESULT, data, size);
+ zbx_free(data);
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_process_jobs *
+ * *
+ * Purpose: process queue *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * now - [IN] current time *
+ * *
+ * Return value: The number of started jobs. *
+ * *
+ ******************************************************************************/
+static int rm_process_jobs(zbx_rm_t *manager)
+{
+ zbx_rm_writer_t *writer;
+ zbx_rm_job_t *job;
+ char *error = NULL;
+ int jobs_num = 0;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ while (SUCCEED != zbx_queue_ptr_empty(&manager->free_writers))
+ {
+ if (SUCCEED != zbx_list_pop(&manager->job_queue, (void **)&job))
+ break;
+
+ writer = zbx_queue_ptr_pop(&manager->free_writers);
+
+ if (SUCCEED != rm_writer_process_job(writer, job, &error))
+ {
+ if (NULL != job->client)
+ {
+ rm_send_test_error_result(job->client, error);
+ rm_job_free(job);
+ }
+ else
+ rm_finish_job(manager, job, FAIL, error, 0, 0);
+
+ zbx_queue_ptr_push(&manager->free_writers, writer);
+ zbx_free(error);
+ }
+ else
+ writer->job = job;
+
+ jobs_num++;
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s() jobs:%d", __func__, jobs_num);
+
+ return jobs_num;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_test_report *
+ * *
+ * Purpose: test report *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * client - [IN] the connected writer *
+ * message - [IN] the received message *
+ * error - [IN] the error message *
+ * *
+ * Return value: SUCCEED - the test report job was created successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rm_test_report(zbx_rm_t *manager, zbx_ipc_client_t *client, zbx_ipc_message_t *message, char **error)
+{
+ zbx_uint64_t dashboardid, userid, access_userid;
+ zbx_vector_ptr_pair_t params;
+ int report_time, ret, width, height;
+ unsigned char period;
+ zbx_rm_job_t *job;
+ char *name;
+
+ zbx_vector_ptr_pair_create(&params);
+
+ report_deserialize_test_report(message->data, &name, &dashboardid, &userid, &access_userid, &report_time,
+ &period, &params);
+
+ rm_get_report_dimensions(dashboardid, &width, &height);
+
+ if (NULL != (job = rm_create_job(manager, name, dashboardid, access_userid, report_time, period, &userid, 1,
+ width, height, &params, error)))
+ {
+ zbx_ipc_client_addref(client);
+ job->client = client;
+ zbx_list_append(&manager->job_queue, job, NULL);
+ ret = SUCCEED;
+ }
+ else
+ ret = FAIL;
+
+ zbx_free(name);
+ report_destroy_params(&params);
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rm_process_result *
+ * *
+ * Purpose: process report result message *
+ * *
+ * Parameters: manager - [IN] the manager *
+ * client - [IN] the connected writer *
+ * message - [IN] the received message *
+ * *
+ ******************************************************************************/
+static void rm_process_result(zbx_rm_t *manager, zbx_ipc_client_t *client, zbx_ipc_message_t *message)
+{
+ zbx_rm_writer_t *writer;
+
+ if (NULL == (writer = rm_get_writer(manager, client)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ return;
+ }
+
+ if (NULL != writer->job->client)
+ {
+ /* external test request - forward the response to the requester */
+ if (SUCCEED == zbx_ipc_client_connected(writer->job->client))
+ {
+ zbx_ipc_client_send(writer->job->client, ZBX_IPC_REPORTER_TEST_RESULT, message->data,
+ message->size);
+ }
+ rm_job_free(writer->job);
+ }
+ else
+ {
+ zbx_vector_ptr_t results;
+ int status, i, total_num = 0, sent_num = 0;
+ zbx_alerter_dispatch_result_t *result;
+ char *error;
+
+ zbx_vector_ptr_create(&results);
+
+ report_deserialize_response(message->data, &status, &error, &results);
+
+ for (i = 0; i < results.values_num; i++)
+ {
+ result = (zbx_alerter_dispatch_result_t *)results.values[i];
+
+ if (SUCCEED == result->status)
+ {
+ sent_num++;
+ }
+ else
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "failed to send report to \"%s\": %s", result->recipient,
+ result->info);
+ }
+
+ total_num++;
+ }
+
+ rm_finish_job(manager, writer->job, status, error, sent_num, total_num);
+ zbx_free(error);
+
+ zbx_vector_ptr_clear_ext(&results, (zbx_clean_func_t)zbx_alerter_dispatch_result_free);
+ zbx_vector_ptr_destroy(&results);
+ }
+
+ writer->job = NULL;
+ zbx_queue_ptr_push(&manager->free_writers, writer);
+}
+
+ZBX_THREAD_ENTRY(report_manager_thread, args)
+{
+#define ZBX_STAT_INTERVAL 5 /* if a process is busy and does not sleep then update status not faster than */
+ /* once in STAT_INTERVAL seconds */
+#define ZBX_SYNC_INTERVAL 60 /* report configuration refresh interval */
+#define ZBX_FLUSH_INTERVAL 10
+
+ char *error = NULL;
+ zbx_ipc_client_t *client;
+ zbx_ipc_message_t *message;
+ double time_stat, time_idle = 0, time_now, sec, time_sync, time_flush_sessions, time_flush;
+ int ret, delay, processed_num = 0, created_num = 0;
+ zbx_rm_t manager;
+
+ process_type = ((zbx_thread_args_t *)args)->process_type;
+ server_num = ((zbx_thread_args_t *)args)->server_num;
+ process_num = ((zbx_thread_args_t *)args)->process_num;
+
+ zbx_setproctitle("%s #%d starting", get_process_type_string(process_type), process_num);
+
+ zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
+ server_num, get_process_type_string(process_type), process_num);
+
+ update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
+
+ if (FAIL == rm_init(&manager, &error))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "cannot initialize alert manager: %s", error);
+ zbx_free(error);
+ exit(EXIT_FAILURE);
+ }
+
+ DBconnect(ZBX_DB_CONNECT_NORMAL);
+
+ /* initialize statistics */
+ time_stat = zbx_time();
+ time_sync = 0;
+ time_flush_sessions = time_stat;
+ time_flush = time_stat;
+
+ zbx_setproctitle("%s #%d started", get_process_type_string(process_type), process_num);
+
+ while (ZBX_IS_RUNNING())
+ {
+ time_now = zbx_time();
+
+ if (ZBX_STAT_INTERVAL < time_now - time_stat)
+ {
+ zbx_setproctitle("%s #%d [jobs created %d, processed %d, idle " ZBX_FS_DBL " sec during "
+ ZBX_FS_DBL " sec]", get_process_type_string(process_type), process_num,
+ created_num, processed_num, time_idle, time_now - time_stat);
+
+ time_stat = time_now;
+ time_idle = 0;
+ created_num = 0;
+ processed_num = 0;
+ }
+
+ if (SEC_PER_HOUR < time_now - time_flush_sessions)
+ {
+ rm_db_flush_sessions(&manager);
+ time_flush_sessions = time_now;
+ }
+
+ if (ZBX_FLUSH_INTERVAL < time_now - time_flush)
+ {
+ rm_db_flush_reports(&manager);
+ time_flush = time_now;
+ }
+
+ if (time_now - time_sync >= ZBX_SYNC_INTERVAL)
+ {
+ rm_update_cache(&manager);
+ time_sync = time_now;
+ }
+
+ created_num += rm_schedule_jobs(&manager, (int)time(NULL));
+ processed_num += rm_process_jobs(&manager);
+
+ sec = zbx_time();
+ delay = (sec - time_now > 0.5 ? 0 : 1);
+ time_now = sec;
+
+ update_selfmon_counter(ZBX_PROCESS_STATE_IDLE);
+ ret = zbx_ipc_service_recv(&manager.ipc, delay, &client, &message);
+ update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
+
+ sec = zbx_time();
+ zbx_update_env(sec);
+
+ if (ZBX_IPC_RECV_IMMEDIATE != ret)
+ time_idle += sec - time_now;
+
+ if (NULL != message)
+ {
+ switch (message->code)
+ {
+ case ZBX_IPC_REPORTER_REGISTER:
+ rm_register_writer(&manager, client, message);
+ break;
+ case ZBX_IPC_REPORTER_TEST:
+ if (FAIL == rm_test_report(&manager, client, message, &error))
+ {
+ rm_send_test_error_result(client, error);
+ zbx_free(error);
+ }
+ break;
+ case ZBX_IPC_REPORTER_RESULT:
+ rm_process_result(&manager, client, message);
+ break;
+ }
+
+ zbx_ipc_message_free(message);
+ }
+
+ if (NULL != client)
+ zbx_ipc_client_release(client);
+ }
+
+ zbx_setproctitle("%s #%d [terminated]", get_process_type_string(process_type), process_num);
+
+ while (1)
+ zbx_sleep(SEC_PER_MIN);
+
+ zbx_ipc_service_close(&manager.ipc);
+ rm_destroy(&manager);
+}
diff --git a/src/zabbix_server/reporter/report_manager.h b/src/zabbix_server/reporter/report_manager.h
new file mode 100644
index 00000000000..3dda3706f4f
--- /dev/null
+++ b/src/zabbix_server/reporter/report_manager.h
@@ -0,0 +1,28 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_REPORT_MANAGER_H
+#define ZABBIX_REPORT_MANAGER_H
+
+#include "common.h"
+#include "threads.h"
+
+ZBX_THREAD_ENTRY(report_manager_thread, args);
+
+#endif
diff --git a/src/zabbix_server/reporter/report_protocol.c b/src/zabbix_server/reporter/report_protocol.c
new file mode 100644
index 00000000000..84f73e79da0
--- /dev/null
+++ b/src/zabbix_server/reporter/report_protocol.c
@@ -0,0 +1,480 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "zbxreport.h"
+#include "report_protocol.h"
+#include "zbxipcservice.h"
+#include "zbxserialize.h"
+#include "zbxalgo.h"
+#include "db.h"
+#include "zbxalert.h"
+
+static int json_uint_by_tag(const struct zbx_json_parse *jp, const char *tag, zbx_uint64_t *value, char **error)
+{
+ char buf[MAX_ID_LEN + 1];
+
+ if (SUCCEED != zbx_json_value_by_name(jp, tag, buf, sizeof(buf), NULL))
+ {
+ *error = zbx_dsprintf(*error, "cannot find tag: %s", tag);
+ return FAIL;
+ }
+
+ if (SUCCEED != is_uint64(buf, value))
+ {
+ *error = zbx_dsprintf(*error, "invalid tag %s value: %s", tag, buf);
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * ZBX_IPC_REPORTER_TEST_REPORT message serialization/deserialization *
+ * *
+ ******************************************************************************/
+
+static zbx_uint32_t report_serialize_test_report(unsigned char **data, const char *name, zbx_uint64_t dashboardid,
+ zbx_uint64_t userid, zbx_uint64_t viewer_userid, int report_time, unsigned char period,
+ const zbx_vector_ptr_pair_t *params)
+{
+ zbx_uint32_t data_len = 0, *len, name_len;
+ int i;
+ unsigned char *ptr;
+
+ zbx_serialize_prepare_str(data_len, name);
+ zbx_serialize_prepare_value(data_len, dashboardid);
+ zbx_serialize_prepare_value(data_len, userid);
+ zbx_serialize_prepare_value(data_len, viewer_userid);
+ zbx_serialize_prepare_value(data_len, report_time);
+ zbx_serialize_prepare_value(data_len, period);
+ zbx_serialize_prepare_value(data_len, params->values_num);
+
+ len = (zbx_uint32_t *)zbx_malloc(NULL, params->values_num * 2 * sizeof(zbx_uint32_t));
+
+ for (i = 0; i < params->values_num; i++)
+ {
+ zbx_serialize_prepare_str_len(data_len, params->values[i].first, len[i * 2]);
+ zbx_serialize_prepare_str_len(data_len, params->values[i].second, len[i * 2 + 1]);
+ }
+
+ *data = (unsigned char *)zbx_malloc(NULL, data_len);
+ ptr = *data;
+
+ ptr += zbx_serialize_str(ptr, name, name_len);
+ ptr += zbx_serialize_value(ptr, dashboardid);
+ ptr += zbx_serialize_value(ptr, userid);
+ ptr += zbx_serialize_value(ptr, viewer_userid);
+ ptr += zbx_serialize_value(ptr, report_time);
+ ptr += zbx_serialize_value(ptr, period);
+ ptr += zbx_serialize_value(ptr, params->values_num);
+
+ for (i = 0; i < params->values_num; i++)
+ {
+ ptr += zbx_serialize_str(ptr, params->values[i].first, len[i * 2]);
+ ptr += zbx_serialize_str(ptr, params->values[i].second, len[i * 2 + 1]);
+ }
+
+ zbx_free(len);
+
+ return data_len;
+}
+
+void report_deserialize_test_report(const unsigned char *data, char **name, zbx_uint64_t *dashboardid,
+ zbx_uint64_t *userid, zbx_uint64_t *viewer_userid, int *report_time, unsigned char *period,
+ zbx_vector_ptr_pair_t *params)
+{
+ int params_num, i;
+ zbx_uint32_t len;
+
+ data += zbx_deserialize_str(data, name, len);
+ data += zbx_deserialize_value(data, dashboardid);
+ data += zbx_deserialize_value(data, userid);
+ data += zbx_deserialize_value(data, viewer_userid);
+ data += zbx_deserialize_value(data, report_time);
+ data += zbx_deserialize_value(data, period);
+ data += zbx_deserialize_value(data, &params_num);
+
+ zbx_vector_ptr_pair_reserve(params, (size_t)params_num);
+ for (i = 0; i < params_num; i++)
+ {
+ zbx_ptr_pair_t pair;
+
+ data += zbx_deserialize_str(data, (char **)&pair.first, len);
+ data += zbx_deserialize_str(data, (char **)&pair.second, len);
+ zbx_vector_ptr_pair_append(params, pair);
+ }
+}
+
+#include "log.h"
+
+/******************************************************************************
+ * *
+ * ZBX_IPC_REPORTER_TEST_REPORT_RESULT, ZBX_IPC_REPORTER_REPORT_RESULT *
+ * message serialization/deserialization *
+ * *
+ ******************************************************************************/
+
+zbx_uint32_t report_serialize_response(unsigned char **data, int status, const char *error,
+ const zbx_vector_ptr_t *results)
+{
+ zbx_uint32_t data_len = 0, error_len, *recipient_len, *info_len;
+ unsigned char *ptr;
+ int i;
+ zbx_alerter_dispatch_result_t *result;
+
+ zbx_serialize_prepare_value(data_len, status);
+ zbx_serialize_prepare_str(data_len, error);
+
+ if (SUCCEED == status)
+ {
+ zbx_serialize_prepare_value(data_len, results->values_num);
+ recipient_len = (zbx_uint32_t *)zbx_malloc(NULL, sizeof(zbx_uint32_t) * results->values_num);
+ info_len = (zbx_uint32_t *)zbx_malloc(NULL, sizeof(zbx_uint32_t) * results->values_num);
+
+ for (i = 0; i < results->values_num; i++)
+ {
+ result = (zbx_alerter_dispatch_result_t *)results->values[i];
+ zbx_serialize_prepare_value(data_len, result->status);
+ zbx_serialize_prepare_str_len(data_len, result->recipient, recipient_len[i]);
+ zbx_serialize_prepare_str_len(data_len, result->info, info_len[i]);
+ }
+ }
+ else
+ {
+ i = 0;
+ zbx_serialize_prepare_value(data_len, i);
+ }
+
+ *data = (unsigned char *)zbx_malloc(NULL, data_len);
+ ptr = *data;
+
+ ptr += zbx_serialize_value(ptr, status);
+ ptr += zbx_serialize_str(ptr, error, error_len);
+
+ if (SUCCEED == status)
+ {
+ ptr += zbx_serialize_value(ptr, results->values_num);
+
+ for (i = 0; i < results->values_num; i++)
+ {
+ result = (zbx_alerter_dispatch_result_t *)results->values[i];
+ ptr += zbx_serialize_value(ptr, result->status);
+ ptr += zbx_serialize_str(ptr, result->recipient, recipient_len[i]);
+ ptr += zbx_serialize_str(ptr, result->info, info_len[i]);
+ }
+
+ zbx_free(info_len);
+ zbx_free(recipient_len);
+ }
+ else
+ (void)zbx_serialize_value(ptr, i);
+
+ return data_len;
+}
+
+void report_deserialize_response(const unsigned char *data, int *status, char **error, zbx_vector_ptr_t *results)
+{
+ zbx_uint32_t len;
+
+ data += zbx_deserialize_value(data, status);
+ data += zbx_deserialize_str(data, error, len);
+
+ if (SUCCEED == *status && NULL != results)
+ {
+ int i, results_num;
+ zbx_alerter_dispatch_result_t *result;
+
+ data += zbx_deserialize_value(data, &results_num);
+
+ if (0 != results_num)
+ {
+ zbx_vector_ptr_reserve(results, (size_t)results_num);
+
+ for (i = 0; i < results_num; i++)
+ {
+ result = zbx_malloc(NULL, sizeof(zbx_alerter_dispatch_result_t));
+ data += zbx_deserialize_value(data, &result->status);
+ data += zbx_deserialize_str(data, &result->recipient, len);
+ data += zbx_deserialize_str(data, &result->info, len);
+ zbx_vector_ptr_append(results, result);
+ }
+ }
+ }
+}
+
+/******************************************************************************
+ * *
+ * ZBX_IPC_REPORTER_BEGIN_REPORT message serialization/deserialization *
+ * *
+ ******************************************************************************/
+
+zbx_uint32_t report_serialize_begin_report(unsigned char **data, const char *name, const char *url,
+ const char *cookie, int width, int height, const zbx_vector_ptr_pair_t *params)
+{
+ zbx_uint32_t data_len = 0, *params_len, url_len, cookie_len, name_len;
+ unsigned char *ptr;
+ int i;
+
+ zbx_serialize_prepare_str(data_len, name);
+ zbx_serialize_prepare_str(data_len, url);
+ zbx_serialize_prepare_str(data_len, cookie);
+ zbx_serialize_prepare_value(data_len, width);
+ zbx_serialize_prepare_value(data_len, height);
+ zbx_serialize_prepare_value(data_len, params->values_num);
+
+ params_len = (zbx_uint32_t *)zbx_malloc(NULL, params->values_num * 2 * sizeof(zbx_uint32_t));
+ for (i = 0; i < params->values_num; i++)
+ {
+ zbx_serialize_prepare_str_len(data_len, params->values[i].first, params_len[i * 2]);
+ zbx_serialize_prepare_str_len(data_len, params->values[i].second, params_len[i * 2 + 1]);
+ }
+
+ *data = (unsigned char *)zbx_malloc(NULL, data_len);
+ ptr = *data;
+
+ ptr += zbx_serialize_str(ptr, name, name_len);
+ ptr += zbx_serialize_str(ptr, url, url_len);
+ ptr += zbx_serialize_str(ptr, cookie, cookie_len);
+ ptr += zbx_serialize_value(ptr, width);
+ ptr += zbx_serialize_value(ptr, height);
+
+ ptr += zbx_serialize_value(ptr, params->values_num);
+
+ for (i = 0; i < params->values_num; i++)
+ {
+ ptr += zbx_serialize_str(ptr, params->values[i].first, params_len[i * 2]);
+ ptr += zbx_serialize_str(ptr, params->values[i].second, params_len[i * 2 + 1]);
+ }
+
+ zbx_free(params_len);
+
+ return data_len;
+}
+
+void report_deserialize_begin_report(const unsigned char *data, char **name, char **url, char **cookie,
+ int *width, int *height, zbx_vector_ptr_pair_t *params)
+{
+ zbx_uint32_t len;
+ int i, params_num;
+
+ data += zbx_deserialize_str(data, name, len);
+ data += zbx_deserialize_str(data, url, len);
+ data += zbx_deserialize_str(data, cookie, len);
+ data += zbx_deserialize_value(data, width);
+ data += zbx_deserialize_value(data, height);
+
+ data += zbx_deserialize_value(data, &params_num);
+ zbx_vector_ptr_pair_reserve(params, (size_t)params_num);
+ for (i = 0; i < params_num; i++)
+ {
+ zbx_ptr_pair_t pair;
+
+ data += zbx_deserialize_str(data, (char **)&pair.first, len);
+ data += zbx_deserialize_str(data, (char **)&pair.second, len);
+ zbx_vector_ptr_pair_append(params, pair);
+ }
+}
+
+/******************************************************************************
+ * *
+ * ZBX_IPC_REPORTER_SEND_REPORT message serialization/deserialization *
+ * *
+ ******************************************************************************/
+
+zbx_uint32_t report_serialize_send_report(unsigned char **data, const DB_MEDIATYPE *mt,
+ const zbx_vector_str_t *emails)
+{
+ zbx_uint32_t data_len = 0, data_alloc = 1024, data_offset = 0, *params_len;
+ unsigned char *ptr;
+ int i;
+
+ *data = zbx_malloc(NULL, data_alloc);
+ zbx_serialize_mediatype(data, &data_alloc, &data_offset, mt);
+
+ zbx_serialize_prepare_value(data_len, emails->values_num);
+ params_len = (zbx_uint32_t *)zbx_malloc(NULL, emails->values_num * sizeof(zbx_uint32_t));
+ for (i = 0; i < emails->values_num; i++)
+ {
+ zbx_serialize_prepare_str_len(data_len, emails->values[i], params_len[i]);
+ }
+
+ if (data_alloc - data_offset < data_len)
+ {
+ data_alloc = data_offset + data_len;
+ *data = (unsigned char *)zbx_realloc(*data, data_alloc);
+ }
+
+ ptr = *data + data_offset;
+ ptr += zbx_serialize_value(ptr, emails->values_num);
+ for (i = 0; i < emails->values_num; i++)
+ {
+ ptr += zbx_serialize_str(ptr, emails->values[i], params_len[i]);
+ }
+
+ zbx_free(params_len);
+
+ return data_offset + data_len;
+}
+
+void report_deserialize_send_report(const unsigned char *data, DB_MEDIATYPE *mt, zbx_vector_str_t *sendtos)
+{
+ zbx_uint32_t len;
+ int i, sendto_num;
+
+ data += zbx_deserialize_mediatype(data, mt);
+
+ data += zbx_deserialize_value(data, &sendto_num);
+ zbx_vector_str_reserve(sendtos, (size_t)sendto_num);
+ for (i = 0; i < sendto_num; i++)
+ {
+ char *sendto;
+
+ data += zbx_deserialize_str(data, &sendto, len);
+ zbx_vector_str_append(sendtos, sendto);
+ }
+}
+
+static void report_clear_ptr_pairs(zbx_vector_ptr_pair_t *params)
+{
+ int i;
+
+ for (i = 0; i < params->values_num; i++)
+ {
+ zbx_free(params->values[i].first);
+ zbx_free(params->values[i].second);
+ }
+}
+
+void report_clear_params(zbx_vector_ptr_pair_t *params)
+{
+ report_clear_ptr_pairs(params);
+ zbx_vector_ptr_pair_clear(params);
+}
+
+void report_destroy_params(zbx_vector_ptr_pair_t *params)
+{
+ report_clear_ptr_pairs(params);
+ zbx_vector_ptr_pair_destroy(params);
+}
+
+void zbx_report_test(const struct zbx_json_parse *jp, zbx_uint64_t userid, struct zbx_json *j)
+{
+ zbx_uint64_t dashboardid, viewer_userid, ui64;
+ int ret = FAIL, period, report_time;
+ struct zbx_json_parse jp_params;
+ zbx_vector_ptr_pair_t params;
+ zbx_vector_ptr_t results;
+ zbx_uint32_t size;
+ unsigned char *data = NULL, *response = NULL;
+ char *name = NULL, *error = NULL;
+ size_t name_alloc = 0;
+
+ zbx_vector_ptr_pair_create(&params);
+ zbx_vector_ptr_create(&results);
+
+ if (SUCCEED != zbx_json_value_by_name_dyn(jp, ZBX_PROTO_TAG_NAME, &name, &name_alloc, NULL))
+ {
+ error = zbx_dsprintf(error, "cannot find tag: %s", ZBX_PROTO_TAG_NAME);
+ goto out;
+ }
+
+ if (SUCCEED != json_uint_by_tag(jp, ZBX_PROTO_TAG_DASHBOARDID, &dashboardid, &error))
+ goto out;
+
+ if (SUCCEED != json_uint_by_tag(jp, ZBX_PROTO_TAG_USERID, &viewer_userid, &error))
+ goto out;
+
+ if (SUCCEED != json_uint_by_tag(jp, ZBX_PROTO_TAG_PERIOD, &ui64, &error))
+ goto out;
+ period = (int)ui64;
+
+ if (SUCCEED != json_uint_by_tag(jp, ZBX_PROTO_TAG_NOW, &ui64, &error))
+ goto out;
+ report_time = (int)ui64;
+
+ if (SUCCEED == zbx_json_brackets_by_name(jp, ZBX_PROTO_TAG_PARAMS, &jp_params))
+ {
+ const char *pnext = NULL;
+ char key[MAX_STRING_LEN];
+
+ while (NULL != (pnext = zbx_json_pair_next(&jp_params, pnext, key, sizeof(key))))
+ {
+ char *value = NULL;
+ size_t value_alloc = 0;
+ zbx_ptr_pair_t pair;
+
+ zbx_json_decodevalue_dyn(pnext, &value, &value_alloc, NULL);
+ pair.first = zbx_strdup(NULL, key);
+ pair.second = value;
+ zbx_vector_ptr_pair_append(&params, pair);
+ }
+ }
+
+ size = report_serialize_test_report(&data, name, dashboardid, userid, viewer_userid, report_time, period,
+ &params);
+
+ if (SUCCEED != zbx_ipc_async_exchange(ZBX_IPC_SERVICE_REPORTER, ZBX_IPC_REPORTER_TEST,
+ SEC_PER_MIN, data, size, &response, &error))
+ {
+ goto out;
+ }
+
+ report_deserialize_response(response, &ret, &error, &results);
+out:
+ zbx_json_init(j, 1024);
+
+ if (SUCCEED == ret)
+ {
+ int i;
+ zbx_alerter_dispatch_result_t *result;
+
+ zbx_json_addstring(j, ZBX_PROTO_TAG_RESPONSE, ZBX_PROTO_VALUE_SUCCESS, ZBX_JSON_TYPE_STRING);
+ zbx_json_addobject(j, ZBX_PROTO_TAG_DATA);
+ zbx_json_addarray(j, ZBX_PROTO_TAG_RECIPIENTS);
+
+ for (i = 0; i < results.values_num; i++)
+ {
+ zbx_json_addobject(j, NULL);
+
+ result = (zbx_alerter_dispatch_result_t *)results.values[i];
+ zbx_json_addint64(j, ZBX_PROTO_TAG_STATUS, result->status);
+ zbx_json_addstring(j, ZBX_PROTO_TAG_RECIPIENT, result->recipient, ZBX_JSON_TYPE_STRING);
+ if (NULL != result->info)
+ zbx_json_addstring(j, ZBX_PROTO_TAG_INFO, result->info, ZBX_JSON_TYPE_STRING);
+
+ zbx_json_close(j);
+ }
+ }
+ else
+ {
+ zbx_json_addstring(j, ZBX_PROTO_TAG_RESPONSE, ZBX_PROTO_VALUE_FAILED, ZBX_JSON_TYPE_STRING);
+ zbx_json_addstring(j, ZBX_PROTO_TAG_INFO, error, ZBX_JSON_TYPE_STRING);
+ }
+
+ zbx_free(error);
+ zbx_free(response);
+ zbx_free(data);
+ zbx_free(name);
+
+ zbx_vector_ptr_clear_ext(&results, (zbx_clean_func_t)zbx_alerter_dispatch_result_free);
+ zbx_vector_ptr_destroy(&results);
+ report_destroy_params(&params);
+}
diff --git a/src/zabbix_server/reporter/report_protocol.h b/src/zabbix_server/reporter/report_protocol.h
new file mode 100644
index 00000000000..98304872857
--- /dev/null
+++ b/src/zabbix_server/reporter/report_protocol.h
@@ -0,0 +1,67 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_REPORT_PROTOCOL_H
+#define ZABBIX_REPORT_PROTOCOL_H
+
+#include "common.h"
+#include "zbxalgo.h"
+#include "db.h"
+
+#define ZBX_REPORT_PARAM_SUBJECT "subject"
+#define ZBX_REPORT_PARAM_BODY "body"
+
+#define ZBX_IPC_SERVICE_REPORTER "reporter"
+
+/* manager -> writer */
+#define ZBX_IPC_REPORTER_BEGIN_REPORT 1000
+#define ZBX_IPC_REPORTER_SEND_REPORT 1001
+#define ZBX_IPC_REPORTER_END_REPORT 1002
+
+/* writer -> manager */
+#define ZBX_IPC_REPORTER_REGISTER 1100
+#define ZBX_IPC_REPORTER_RESULT 1101
+
+/* process -> manager */
+#define ZBX_IPC_REPORTER_TEST 1010
+
+/* manager -> process */
+#define ZBX_IPC_REPORTER_TEST_RESULT 1011
+
+void report_clear_params(zbx_vector_ptr_pair_t *params);
+void report_destroy_params(zbx_vector_ptr_pair_t *params);
+
+void report_deserialize_test_report(const unsigned char *data, char **name, zbx_uint64_t *dashboardid,
+ zbx_uint64_t *userid, zbx_uint64_t *viewer_userid, int *report_time, unsigned char *period,
+ zbx_vector_ptr_pair_t *params);
+
+zbx_uint32_t report_serialize_response(unsigned char **data, int status, const char *error,
+ const zbx_vector_ptr_t *results);
+void report_deserialize_response(const unsigned char *data, int *status, char **error, zbx_vector_ptr_t *results);
+
+zbx_uint32_t report_serialize_begin_report(unsigned char **data, const char *name, const char *url,
+ const char *cookie, int width, int height, const zbx_vector_ptr_pair_t *params);
+void report_deserialize_begin_report(const unsigned char *data, char **name, char **url, char **cookie,
+ int *width, int *height, zbx_vector_ptr_pair_t *params);
+
+zbx_uint32_t report_serialize_send_report(unsigned char **data, const DB_MEDIATYPE *mt,
+ const zbx_vector_str_t *emails);
+void report_deserialize_send_report(const unsigned char *data, DB_MEDIATYPE *mt, zbx_vector_str_t *sendtos);
+
+#endif
diff --git a/src/zabbix_server/reporter/report_writer.c b/src/zabbix_server/reporter/report_writer.c
new file mode 100644
index 00000000000..dfe331efd3c
--- /dev/null
+++ b/src/zabbix_server/reporter/report_writer.c
@@ -0,0 +1,517 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "daemon.h"
+#include "zbxself.h"
+#include "log.h"
+#include "zbxipcservice.h"
+#include "zbxserialize.h"
+#include "zbxjson.h"
+#include "zbxalert.h"
+#include "db.h"
+#include "report_writer.h"
+#include "report_protocol.h"
+
+extern unsigned char process_type, program_type;
+extern int server_num, process_num;
+
+extern char *CONFIG_WEBSERVICE_URL;
+extern char *CONFIG_TLS_CA_FILE;
+extern char *CONFIG_TLS_CERT_FILE;
+extern char *CONFIG_TLS_KEY_FILE;
+
+typedef struct
+{
+ char *data;
+ size_t alloc;
+ size_t offset;
+}
+zbx_buffer_t;
+
+#if defined(HAVE_LIBCURL)
+
+static size_t curl_write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ size_t r_size = size * nmemb, buf_alloc;
+ zbx_buffer_t *buf = (zbx_buffer_t *)userdata;
+
+ buf_alloc = (0 == buf->alloc ? r_size : buf->alloc);
+ while (buf_alloc - buf->offset < r_size)
+ buf_alloc *= 2;
+
+ if (buf_alloc != buf->alloc)
+ {
+ buf->data = zbx_realloc(buf->data, buf_alloc);
+ buf->alloc = buf_alloc;
+ }
+
+ memcpy(buf->data + buf->offset, (const char *)ptr, r_size);
+ buf->offset += r_size;
+
+ return r_size;
+}
+
+static char *rw_curl_error(CURLcode err)
+{
+ char *error;
+
+ error = zbx_strdup(NULL, curl_easy_strerror(err));
+ *error = tolower((unsigned char)*error);
+
+ return error;
+}
+#endif
+
+/******************************************************************************
+ * *
+ * Function: rw_get_report *
+ * *
+ * Purpose: get report from web service *
+ * *
+ * Parameters: url - [IN] the report url *
+ * cookie - [IN] the authentication cookie *
+ * width - [IN] the report width *
+ * height - [IN] the report height *
+ * report - [OUT] the downloaded report *
+ * report_size - [OUT] the report size *
+ * *
+ * Return value: SUCCEED - the report was downloaded successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rw_get_report(const char *url, const char *cookie, int width, int height, char **report,
+ size_t *report_size, char **error)
+{
+#if !defined(HAVE_LIBCURL)
+ ZBX_UNUSED(url);
+ ZBX_UNUSED(cookie);
+ ZBX_UNUSED(width);
+ ZBX_UNUSED(height);
+ ZBX_UNUSED(report);
+ ZBX_UNUSED(report_size);
+
+ *error = zbx_strdup(NULL, "application compiled without cURL library");
+ return FAIL;
+
+#else
+ struct zbx_json j;
+ char *cookie_value, buffer[MAX_ID_LEN + 1], *curl_error = NULL;
+ int ret = FAIL;
+ long httpret;
+ zbx_buffer_t response = {NULL, 0, 0};
+ CURL *curl = NULL;
+ CURLcode err;
+ CURLoption opt;
+ struct curl_slist *headers = NULL;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() url:%s width:%d height:%d", __func__, url, width, height);
+
+ cookie_value = zbx_dsprintf(NULL, "zbx_session=%s", cookie);
+
+ zbx_json_init(&j, 1024);
+ zbx_json_addstring(&j, ZBX_PROTO_TAG_URL, url, ZBX_JSON_TYPE_STRING);
+
+ zbx_json_addobject(&j, ZBX_PROTO_TAG_HTTP_HEADERS);
+ zbx_json_addstring(&j, "Cookie", cookie_value, ZBX_JSON_TYPE_STRING);
+ zbx_json_close(&j);
+
+ zbx_json_addobject(&j, ZBX_PROTO_TAG_PARAMETERS);
+ zbx_snprintf(buffer, sizeof(buffer), "%d", width);
+ zbx_json_addstring(&j, "width", buffer, ZBX_JSON_TYPE_STRING);
+ zbx_snprintf(buffer, sizeof(buffer), "%d", height);
+ zbx_json_addstring(&j, "height", buffer, ZBX_JSON_TYPE_STRING);
+ zbx_json_close(&j);
+
+ if (NULL == (curl = curl_easy_init()))
+ {
+ *error = zbx_strdup(NULL, "Cannot initialize cURL library");
+ goto out;
+ }
+
+ headers = curl_slist_append(headers, "Content-Type:application/json");
+
+ if (CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_COOKIEFILE, "")) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_FOLLOWLOCATION, 1L)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_WRITEFUNCTION, curl_write_cb)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_WRITEDATA, &response)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_TIMEOUT, 60)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_POST, 1L)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_URL, CONFIG_WEBSERVICE_URL)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_HTTPHEADER, headers)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_POSTFIELDS, j.buffer)))
+ {
+ *error = zbx_dsprintf(*error, "Cannot set cURL option %d: %s.", (int)opt,
+ (curl_error = rw_curl_error(err)));
+ goto out;
+ }
+
+ if (NULL != CONFIG_TLS_CA_FILE && '\0' != *CONFIG_TLS_CA_FILE)
+ {
+ if (CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_CAINFO, CONFIG_TLS_CA_FILE)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_SSLCERT, CONFIG_TLS_CERT_FILE)) ||
+ CURLE_OK != (err = curl_easy_setopt(curl, opt = CURLOPT_SSLKEY, CONFIG_TLS_KEY_FILE)))
+ {
+ *error = zbx_dsprintf(*error, "Cannot set cURL option %d: %s.", (int)opt,
+ (curl_error = rw_curl_error(err)));
+ goto out;
+ }
+ }
+
+
+ if (CURLE_OK != (err = curl_easy_perform(curl)))
+ {
+ *error = zbx_dsprintf(*error, "Cannot connect to web service: %s", (curl_error = rw_curl_error(err)));
+ goto out;
+ }
+
+ if (CURLE_OK != (err = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpret)))
+ {
+ *error = zbx_dsprintf(*error, "Cannot obtain web service response code: %s",
+ (curl_error = rw_curl_error(err)));
+ goto out;
+ }
+
+ if (200 != httpret)
+ {
+ struct zbx_json_parse jp;
+
+ if (response.offset == response.alloc)
+ response.data = zbx_realloc(response.data, response.alloc + 1);
+
+ response.data[response.offset] = '\0';
+
+ if (SUCCEED != zbx_json_open(response.data, &jp))
+ {
+ *error = response.data;
+ zbx_rtrim(*error, "\n\r");
+ response.data = NULL;
+ }
+ else
+ {
+ size_t error_alloc = 0;
+
+ zbx_json_value_by_name_dyn(&jp, ZBX_PROTO_TAG_DETAIL, error, &error_alloc, NULL);
+ }
+
+ goto out;
+ }
+
+ if (4 > response.offset || 0 != strncmp(response.data, "%PDF", 4))
+ {
+ *error = zbx_dsprintf(*error, "Unsupported format document returned by web service,"
+ " please check WebServiceURL server configuration parameter.");
+ goto out;
+ }
+
+ *report = response.data;
+ *report_size = response.offset;
+ response.data = NULL;
+
+ ret = SUCCEED;
+out:
+ zbx_free(curl_error);
+ zbx_free(response.data);
+
+ curl_slist_free_all(headers);
+
+ if (NULL != curl)
+ curl_easy_cleanup(curl);
+
+ zbx_json_clean(&j);
+ zbx_free(cookie_value);
+
+ return ret;
+#endif
+}
+
+/******************************************************************************
+ * *
+ * Function: rw_begin_report *
+ * *
+ * Purpose: begin report dispatch *
+ * *
+ * Parameters: msg - [IN] the begin report request message *
+ * dispatch - [IN] the alerter dispatch *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - the report was started successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rw_begin_report(zbx_ipc_message_t *msg, zbx_alerter_dispatch_t *dispatch, char **error)
+{
+ zbx_vector_ptr_pair_t params;
+ int i, ret, width, height;
+ char *url, *cookie, *subject = "", *message = "", *report = NULL, *name;
+ size_t report_size = 0;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_vector_ptr_pair_create(&params);
+
+ report_deserialize_begin_report(msg->data, &name, &url, &cookie, &width, &height, &params);
+
+ for (i = 0; i < params.values_num; i++)
+ {
+ if (0 == strcmp(params.values[i].first, ZBX_REPORT_PARAM_SUBJECT))
+ {
+ subject = (char *)params.values[i].second;
+ }
+ else if (0 == strcmp(params.values[i].first, ZBX_REPORT_PARAM_BODY))
+ {
+ message = (char *)params.values[i].second;
+ }
+ else
+ {
+ zabbix_log(LOG_LEVEL_WARNING, "unsupported parameter: %s=%s", (char *)params.values[i].first,
+ (char *)params.values[i].second);
+ }
+ }
+
+ if (SUCCEED == (ret = rw_get_report(url, cookie, width, height, &report, &report_size, error)))
+ {
+ ret = zbx_alerter_begin_dispatch(dispatch, subject, message, name, "application/pdf", report,
+ report_size, error);
+ }
+
+ zbx_free(report);
+ zbx_free(name);
+ zbx_free(url);
+ zbx_free(cookie);
+
+ report_destroy_params(&params);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s report_size:" ZBX_FS_SIZE_T, __func__, zbx_result_string(ret),
+ report_size);
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rw_send_report *
+ * *
+ * Purpose: send report to the recipients using specified media type *
+ * *
+ * Parameters: msg - [IN] the send report request message *
+ * dispatch - [IN] the alerter dispatch *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - the report was sent successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rw_send_report(zbx_ipc_message_t *msg, zbx_alerter_dispatch_t *dispatch, char **error)
+{
+ int ret = FAIL;
+ zbx_vector_str_t recipients;
+ DB_MEDIATYPE mt;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_vector_str_create(&recipients);
+
+ /* The message data is identical (mediatype + recipients) so currently it */
+ /* could be forwarded without deserializing/serializing it. Also it could */
+ /* have been directly sent from report manager to alert manager, however */
+ /* then 'dispatch' message could be delivered before 'begin' message. */
+ /* While sending through writer does add overhead, it also adds */
+ /* synchronization. And the overhead is only at writer's side. */
+ report_deserialize_send_report(msg->data, &mt, &recipients);
+ ret = zbx_alerter_send_dispatch(dispatch, &mt, &recipients, error);
+
+ zbx_db_mediatype_clean(&mt);
+ zbx_vector_str_clear_ext(&recipients, zbx_str_free);
+ zbx_vector_str_destroy(&recipients);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rw_end_report *
+ * *
+ * Purpose: finish report dispatch *
+ * *
+ * Parameters: dispatch - [IN] the alerter dispatch *
+ * error - [OUT] the error message *
+ * *
+ * Return value: SUCCEED - the report was finished successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int rw_end_report(zbx_alerter_dispatch_t *dispatch, char **error)
+{
+ int ret;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ ret = zbx_alerter_end_dispatch(dispatch, error);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: rw_send_result *
+ * *
+ * Purpose: send report result back to manager *
+ * *
+ * Parameters: socket - [IN] the report manager IPC socket *
+ * status - [IN] the report status *
+ * error - [IN] the error message *
+ * *
+ ******************************************************************************/
+static void rw_send_result(zbx_ipc_socket_t *socket, zbx_alerter_dispatch_t *dispatch, int status, char *error)
+{
+ unsigned char *data;
+ zbx_uint32_t size;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() status:%d error:%s", __func__, status, ZBX_NULL2EMPTY_STR(error));
+
+ size = report_serialize_response(&data, status, error, &dispatch->results);
+ zbx_ipc_socket_write(socket, ZBX_IPC_REPORTER_RESULT, data, size);
+ zbx_free(data);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
+}
+
+/******************************************************************************
+ * *
+ * Function: report_writer_thread *
+ * *
+ ******************************************************************************/
+ZBX_THREAD_ENTRY(report_writer_thread, args)
+{
+#define ZBX_STAT_INTERVAL 5 /* if a process is busy and does not sleep then update status not faster than */
+ /* once in STAT_INTERVAL seconds */
+
+ pid_t ppid;
+ char *error = NULL;
+ zbx_ipc_socket_t socket;
+ zbx_ipc_message_t message;
+ zbx_alerter_dispatch_t dispatch = {0};
+ int report_status = FAIL, started_num = 0, sent_num = 0, finished_num = 0;
+ double time_now, time_stat, time_idle = 0, time_wake;
+
+ process_type = ((zbx_thread_args_t *)args)->process_type;
+ server_num = ((zbx_thread_args_t *)args)->server_num;
+ process_num = ((zbx_thread_args_t *)args)->process_num;
+
+ zbx_setproctitle("%s #%d starting", get_process_type_string(process_type), process_num);
+
+ zbx_ipc_message_init(&message);
+
+ if (FAIL == zbx_ipc_socket_open(&socket, ZBX_IPC_SERVICE_REPORTER, SEC_PER_MIN, &error))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "Cannot connect to reporting service: %s", error);
+ zbx_free(error);
+ exit(EXIT_FAILURE);
+ }
+
+ ppid = getppid();
+ zbx_ipc_socket_write(&socket, ZBX_IPC_REPORTER_REGISTER, (unsigned char *)&ppid, sizeof(ppid));
+
+ zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
+ server_num, get_process_type_string(process_type), process_num);
+
+ update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
+
+ zbx_setproctitle("%s #%d started", get_process_type_string(process_type), process_num);
+
+ time_stat = zbx_time();
+
+ while (ZBX_IS_RUNNING())
+ {
+ time_now = zbx_time();
+
+ if (ZBX_STAT_INTERVAL < time_now - time_stat)
+ {
+ zbx_setproctitle("%s #%d [reports started %d, sent %d, finished %d, idle " ZBX_FS_DBL
+ " sec during " ZBX_FS_DBL " sec]", get_process_type_string(process_type),
+ process_num, started_num, sent_num, finished_num, time_idle,
+ time_now - time_stat);
+
+ time_stat = time_now;
+ time_idle = 0;
+ started_num = 0;
+ sent_num = 0;
+ finished_num = 0;
+ }
+
+ update_selfmon_counter(ZBX_PROCESS_STATE_IDLE);
+
+ if (SUCCEED != zbx_ipc_socket_read(&socket, &message))
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "Cannot read reporter service request");
+ exit(EXIT_FAILURE);
+ }
+
+ update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
+
+ time_wake = zbx_time();
+ zbx_update_env(time_wake);
+ time_idle += time_wake = time_now;
+
+ switch (message.code)
+ {
+ case ZBX_IPC_REPORTER_BEGIN_REPORT:
+ if (SUCCEED != (report_status = rw_begin_report(&message, &dispatch, &error)))
+ zabbix_log(LOG_LEVEL_DEBUG, "failed to begin report dispatch: %s", error);
+ else
+ started_num++;
+ break;
+ case ZBX_IPC_REPORTER_SEND_REPORT:
+ if (SUCCEED == report_status)
+ {
+ if (SUCCEED != (report_status = rw_send_report(&message, &dispatch, &error)))
+ zabbix_log(LOG_LEVEL_DEBUG, "failed to send report: %s", error);
+ else
+ sent_num++;
+ }
+ break;
+ case ZBX_IPC_REPORTER_END_REPORT:
+ if (SUCCEED == report_status)
+ {
+ if (SUCCEED != (report_status = rw_end_report(&dispatch, &error)))
+ zabbix_log(LOG_LEVEL_DEBUG, "failed to end report dispatch: %s", error);
+ else
+ finished_num++;
+ }
+
+ rw_send_result(&socket, &dispatch, report_status, error);
+
+ zbx_alerter_clear_dispatch(&dispatch);
+ zbx_free(error);
+ break;
+ }
+
+ zbx_ipc_message_clean(&message);
+ }
+
+ zbx_setproctitle("%s #%d [terminated]", get_process_type_string(process_type), process_num);
+
+ while (1)
+ zbx_sleep(SEC_PER_MIN);
+}
diff --git a/src/zabbix_server/reporter/report_writer.h b/src/zabbix_server/reporter/report_writer.h
new file mode 100644
index 00000000000..f3423d8814a
--- /dev/null
+++ b/src/zabbix_server/reporter/report_writer.h
@@ -0,0 +1,28 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_REPORT_WRITER_H
+#define ZABBIX_REPORT_WRITER_H
+
+#include "common.h"
+#include "threads.h"
+
+ZBX_THREAD_ENTRY(report_writer_thread, args);
+
+#endif
diff --git a/src/zabbix_server/scripts/scripts.c b/src/zabbix_server/scripts/scripts.c
index 09955b66940..eceb208d3cb 100644
--- a/src/zabbix_server/scripts/scripts.c
+++ b/src/zabbix_server/scripts/scripts.c
@@ -192,44 +192,7 @@ fail:
return ret;
}
-static int DBget_script_by_scriptid(zbx_uint64_t scriptid, zbx_script_t *script, zbx_uint64_t *groupid)
-{
- int ret = FAIL;
- DB_RESULT result;
- DB_ROW row;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- result = DBselect(
- "select type,execute_on,command,groupid,host_access,timeout"
- " from scripts"
- " where scriptid=" ZBX_FS_UI64,
- scriptid);
-
- if (NULL != (row = DBfetch(result)))
- {
- char *tm;
-
- ZBX_STR2UCHAR(script->type, row[0]);
- ZBX_STR2UCHAR(script->execute_on, row[1]);
- script->command = zbx_strdup(script->command, row[2]);
- script->command_orig = zbx_strdup(script->command_orig, row[2]);
- ZBX_DBROW2UINT64(*groupid, row[3]);
- ZBX_STR2UCHAR(script->host_access, row[4]);
- tm = zbx_strdup(NULL, row[5]);
-
- ret = is_time_suffix(tm, &script->timeout, ZBX_LENGTH_UNLIMITED);
-
- zbx_free(tm);
- }
- DBfree_result(result);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
-
- return ret;
-}
-
-static int check_script_permissions(zbx_uint64_t groupid, zbx_uint64_t hostid)
+int zbx_check_script_permissions(zbx_uint64_t groupid, zbx_uint64_t hostid)
{
DB_RESULT result;
int ret = SUCCEED;
@@ -270,14 +233,14 @@ exit:
return ret;
}
-static int check_user_permissions(zbx_uint64_t userid, const DC_HOST *host, zbx_script_t *script)
+int zbx_check_script_user_permissions(zbx_uint64_t userid, zbx_uint64_t hostid, zbx_script_t *script)
{
int ret = SUCCEED;
DB_RESULT result;
DB_ROW row;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() userid:" ZBX_FS_UI64 " hostid:" ZBX_FS_UI64 " scriptid:" ZBX_FS_UI64,
- __func__, userid, host->hostid, script->scriptid);
+ __func__, userid, hostid, script->scriptid);
result = DBselect(
"select null"
@@ -289,7 +252,7 @@ static int check_user_permissions(zbx_uint64_t userid, const DC_HOST *host, zbx_
" group by hg.hostid"
" having min(r.permission)>%d"
" and max(r.permission)>=%d",
- host->hostid,
+ hostid,
userid,
PERM_DENY,
script->host_access);
@@ -320,27 +283,34 @@ void zbx_script_clean(zbx_script_t *script)
zbx_free(script->command_orig);
}
-static int zbx_get_event_by_eventid(zbx_uint64_t eventid, DB_EVENT **event_out)
+/******************************************************************************
+ * *
+ * Function: zbx_webhook_params_pack_json *
+ * *
+ * Purpose: pack webhook script parameters into JSON *
+ * *
+ * Parameters: params - [IN] vector of pairs of pointers to parameter *
+ * names and values *
+ * params_json - [OUT] JSON string *
+ * *
+ ******************************************************************************/
+void zbx_webhook_params_pack_json(const zbx_vector_ptr_pair_t *params, char **params_json)
{
- int ret = SUCCEED;
- zbx_vector_ptr_t events;
- zbx_vector_uint64_t eventids;
-
- zbx_vector_ptr_create(&events);
- zbx_vector_uint64_create(&eventids);
- zbx_vector_uint64_append(&eventids, eventid);
+ struct zbx_json json_data;
+ int i;
- zbx_db_get_events_by_eventids(&eventids, &events);
+ zbx_json_init(&json_data, ZBX_JSON_STAT_BUF_LEN);
- if (0 < events.values_num)
- *event_out = (DB_EVENT*)events.values[0];
- else
- ret = FAIL;
+ for (i = 0; i < params->values_num; i++)
+ {
+ zbx_ptr_pair_t pair = params->values[i];
- zbx_vector_ptr_destroy(&events);
- zbx_vector_uint64_destroy(&eventids);
+ zbx_json_addstring(&json_data, pair.first, pair.second, ZBX_JSON_TYPE_STRING);
+ }
- return ret;
+ zbx_json_close(&json_data);
+ *params_json = zbx_strdup(*params_json, json_data.buffer);
+ zbx_json_free(&json_data);
}
/***********************************************************************************
@@ -351,41 +321,34 @@ static int zbx_get_event_by_eventid(zbx_uint64_t eventid, DB_EVENT **event_out)
* *
* Parameters: script - [IN] the script to prepare *
* host - [IN] the host the script will be executed on *
- * user - [IN] the user executing script (can be NULL) *
- * ctx - [IN] the execution context of a script *
- * eventid - [IN] the eventid for macro resolving *
* error - [OUT] the error message buffer *
* max_error_len - [IN] the size of error message output buffer *
- * event - [IN/OUT] the event for the execution (can be NULL) *
* *
* Return value: SUCCEED - the script has been prepared successfully *
* FAIL - otherwise, error contains error message *
* *
* Comments: This function prepares script for execution by loading global *
- * script/expanding macros. *
+ * script/expanding macros (except in script body). *
* Prepared scripts must be always freed with zbx_script_clean() *
* function. *
* *
***********************************************************************************/
-int zbx_script_prepare(zbx_script_t *script, const DC_HOST *host, const zbx_user_t *user,
- zbx_script_exec_context ctx, zbx_uint64_t eventid, char *error, size_t max_error_len,
- DB_EVENT **event)
+int zbx_script_prepare(zbx_script_t *script, const zbx_uint64_t *hostid, char *error, size_t max_error_len)
{
- int macro_mask, ret = FAIL;
- zbx_uint64_t groupid, userid, *p_userid = NULL;
+ int ret = FAIL;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
switch (script->type)
{
case ZBX_SCRIPT_TYPE_SSH:
- substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL, NULL,
+ substitute_simple_macros(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL, NULL,
&script->publickey, MACRO_TYPE_COMMON, NULL, 0);
- substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL, NULL,
+ substitute_simple_macros(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL, NULL,
&script->privatekey, MACRO_TYPE_COMMON, NULL, 0);
ZBX_FALLTHROUGH;
case ZBX_SCRIPT_TYPE_TELNET:
- substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL, NULL,
+ substitute_simple_macros(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL, NULL,
&script->port, MACRO_TYPE_COMMON, NULL, 0);
if ('\0' != *script->port && SUCCEED != (ret = is_ushort(script->port, NULL)))
@@ -394,118 +357,15 @@ int zbx_script_prepare(zbx_script_t *script, const DC_HOST *host, const zbx_user
goto out;
}
- substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL,
+ substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL,
NULL, &script->username, MACRO_TYPE_COMMON, NULL, 0);
- substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL,
+ substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL,
NULL, &script->password, MACRO_TYPE_COMMON, NULL, 0);
break;
case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
dos2unix(script->command); /* CR+LF (Windows) => LF (Unix) */
- ZBX_FALLTHROUGH;
- case ZBX_SCRIPT_TYPE_WEBHOOK:
- macro_mask = MACRO_TYPE_SCRIPT;
-
- if ((ZBX_SCRIPT_CTX_EVENT == ctx && FAIL != zbx_get_event_by_eventid(eventid, event)) ||
- ZBX_SCRIPT_CTX_ACTION == ctx)
- {
- macro_mask |= (MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_MESSAGE_NORMAL |
- MACRO_TYPE_MESSAGE_RECOVERY);
- }
-
- if (NULL != user)
- {
- /* Make a copy to preserve const-correctness. */
- userid = user->userid;
- p_userid = &userid;
- }
-
- if (SUCCEED != substitute_simple_macros_unmasked(NULL, (event != NULL ? *event : NULL), NULL,
- p_userid, NULL, host, NULL, NULL, NULL, NULL, &script->command, macro_mask,
- error, max_error_len))
- {
- goto out;
- }
-
- if (SUCCEED != substitute_simple_macros(NULL, (event != NULL ? *event : NULL), NULL, p_userid,
- NULL, host, NULL, NULL, NULL, NULL, &script->command_orig, macro_mask,
- error, max_error_len))
- {
- THIS_SHOULD_NEVER_HAPPEN;
- }
-
- break;
- case ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT:
- if (SUCCEED != DBget_script_by_scriptid(script->scriptid, script, &groupid))
- {
- zbx_strlcpy(error, "Unknown script identifier.", max_error_len);
- goto out;
- }
-
- if (ZBX_SCRIPT_TYPE_WEBHOOK == script->type && ZBX_SCRIPT_CTX_HOST != ctx)
- {
- if (user != NULL && USER_TYPE_SUPER_ADMIN != user->type)
- {
- zbx_strlcpy(error, "Cannot determine permission of a script.",
- max_error_len);
- goto out;
- }
- else
- goto skip_perm_check;
- }
-
- if (groupid > 0 && SUCCEED != check_script_permissions(groupid, host->hostid))
- {
- zbx_strlcpy(error, "Script does not have permission to be executed on the host.",
- max_error_len);
- goto out;
- }
-
- if (user != NULL && USER_TYPE_SUPER_ADMIN != user->type &&
- SUCCEED != check_user_permissions(user->userid, host, script))
- {
- zbx_strlcpy(error, "User does not have permission to execute this script on the host.",
- max_error_len);
- goto out;
- }
-skip_perm_check:
- if (NULL != user)
- {
- /* zbx_script_prepare() receives 'user' as const-pointer but */
- /* substitute_simple_macros() takes 'userid' as non-const pointer. */
- /* Make a copy to preserve const-correctness. */
- userid = user->userid;
- p_userid = &userid;
- }
-
- if (SUCCEED != substitute_simple_macros_unmasked(NULL, NULL, NULL, p_userid, NULL, host,
- NULL, NULL, NULL, NULL, &script->command, MACRO_TYPE_SCRIPT, error,
- max_error_len))
- {
- goto out;
- }
-
- /* expand macros in command_orig used for non-secure logging */
- if (SUCCEED != substitute_simple_macros(NULL, NULL, NULL, p_userid, NULL, host, NULL,
- NULL, NULL, NULL, &script->command_orig, MACRO_TYPE_SCRIPT, error,
- max_error_len))
- {
- /* script command_orig is a copy of script command - if the script command */
- /* macro substitution succeeded, then it will succeed also for command_orig */
- THIS_SHOULD_NEVER_HAPPEN;
- }
-
- if (ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT == script->type)
- {
- /* DBget_script_by_scriptid() may overwrite type with anything but global script */
- /* ... therefore this recursion is no more than two layers deep */
- THIS_SHOULD_NEVER_HAPPEN;
- goto out;
- }
-
- if (FAIL == zbx_script_prepare(script, host, user, ctx, eventid, error, max_error_len, event))
- goto out;
-
break;
+ case ZBX_SCRIPT_TYPE_WEBHOOK:
case ZBX_SCRIPT_TYPE_IPMI:
break;
default:
@@ -519,131 +379,48 @@ out:
return ret;
}
-/**************************************************************************************************
- * *
- * Function: DBfetch_webhook_params *
- * *
- * Purpose: fetch webhook parameters and expand macros inside them *
- * *
- * Parameters: script - [IN] the script to be executed *
- * host - [IN] the host the script will be executed on *
- * event - [IN] the event for the execution case *
- * user - [IN] the user executing script (can be NULL) *
- * ctx - [IN] the script execution context *
- * params - [OUT] parsed parameters with expanded macros *
- * *
- * Return value: SUCCEED - processed successfully *
- * FAIL - an error occurred *
- * *
- **************************************************************************************************/
-static int DBfetch_webhook_params(const zbx_script_t *script, const DC_HOST *host, const DB_EVENT *event,
- const zbx_user_t *user, zbx_script_exec_context ctx, char **params)
+/******************************************************************************
+ * *
+ * Function: DBfetch_webhook_params *
+ * *
+ * Purpose: fetch webhook parameters *
+ * *
+ * Parameters: scriptid - [IN] the id of script to be executed *
+ * params - [OUT] parameters name-value pairs *
+ * error - [IN/OUT] the error message *
+ * error_len - [IN] the maximum error length *
+ * *
+ * Return value: SUCCEED - processed successfully *
+ * FAIL - an error occurred *
+ * *
+ ******************************************************************************/
+int DBfetch_webhook_params(zbx_uint64_t scriptid, zbx_vector_ptr_pair_t *params, char *error, size_t error_len)
{
int ret = SUCCEED;
- zbx_uint64_t userid, *p_userid = NULL;
- char error[MAX_STRING_LEN];
DB_RESULT result;
DB_ROW row;
- struct zbx_json json_data;
+ zbx_ptr_pair_t pair;
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() scriptid:" ZBX_FS_UI64, __func__, scriptid);
- result = DBselect("select name,value from script_param where scriptid=" ZBX_FS_UI64, script->scriptid);
+ result = DBselect("select name,value from script_param where scriptid=" ZBX_FS_UI64, scriptid);
if (NULL == result)
{
+ zbx_strlcpy(error, "Database error, cannot get webhook script parameters.", error_len);
ret = FAIL;
goto out;
}
- zbx_json_init(&json_data, ZBX_JSON_STAT_BUF_LEN);
-
while (NULL != (row = DBfetch(result)))
{
- char *name, *value;
-
- name = zbx_strdup(NULL, row[0]);
- value = zbx_strdup(NULL, row[1]);
- zbx_json_addstring(&json_data, name, value, ZBX_JSON_TYPE_STRING);
- zbx_free(name);
- zbx_free(value);
+ pair.first = zbx_strdup(NULL, row[0]);
+ pair.second = zbx_strdup(NULL, row[1]);
+ zbx_vector_ptr_pair_append(params, pair);
}
- zbx_json_close(&json_data);
-
- if (NULL != user)
- {
- userid = user->userid;
- p_userid = &userid;
- }
-
- *params = zbx_strdup(NULL, json_data.buffer);
-
- if (SUCCEED != substitute_simple_macros_unmasked(NULL, NULL, NULL, p_userid, NULL, host, NULL, NULL, NULL,
- NULL, params, MACRO_TYPE_SCRIPT, error, sizeof(error)))
- {
- zabbix_log(LOG_LEVEL_WARNING, "failed to expand macros for script '%s'", script->command_orig);
- }
-
- if (ZBX_SCRIPT_CTX_EVENT == ctx)
- {
- if (SUCCEED != substitute_simple_macros_unmasked(NULL, event, NULL, p_userid, NULL, host, NULL, NULL,
- NULL, NULL, params, (MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_MESSAGE_NORMAL |
- MACRO_TYPE_MESSAGE_RECOVERY), error, sizeof(error)))
- {
- zabbix_log(LOG_LEVEL_WARNING, "failed to expand macros for script '%s'", script->command_orig);
- }
- }
-
- zbx_json_free(&json_data);
-out:
DBfree_result(result);
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
-
- return ret;
-}
-
-/**************************************************************************************************
- * *
- * Function: zbx_execute_webhook *
- * *
- * Purpose: executes webhook *
- * *
- * Parameters: script - [IN] the script to be executed *
- * host - [IN] the host the script will be executed on *
- * event - [IN] the event for the execution case *
- * ctx - [IN] the script execution context *
- * user - [IN] the user executing script (can be NULL) *
- * error - [IN/OUT] the error reported by the script (or the script engine) *
- * max_error_len - [IN] the maximum error length *
- * result - [OUT] the result of a script execution *
- * debug - [OUT] the debug data (optional) *
- * *
- * Return value: SUCCEED - processed successfully *
- * FAIL - an error occurred *
- * *
- **************************************************************************************************/
-static int zbx_execute_webhook(const zbx_script_t *script, const DC_HOST *host, const DB_EVENT *event,
- zbx_script_exec_context ctx, const zbx_user_t *user, char *error, size_t max_error_len, char **result,
- char **debug)
-{
- int ret;
- char *params;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- if (FAIL == DBfetch_webhook_params(script, host, event, user, ctx, &params))
- {
- zabbix_log(LOG_LEVEL_WARNING, "failed to fetch script parameters for script id " ZBX_FS_UI64,
- script->scriptid);
- }
-
- if (ZBX_SCRIPT_CTX_ACTION != ctx && NULL != event)
- zbx_db_free_event((DB_EVENT*)event);
-
- ret = zbx_es_execute_command(script->command, params, script->timeout, result, error, max_error_len, debug);
-
- zbx_free(params);
+out:
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
@@ -657,9 +434,7 @@ static int zbx_execute_webhook(const zbx_script_t *script, const DC_HOST *host,
* *
* Parameters: script - [IN] the script to be executed *
* host - [IN] the host the script will be executed on *
- * user - [IN] the user executing script (can be NULL) *
- * event - [IN] the event for the execution case *
- * ctx - [IN] the script execution context *
+ * params - [IN] parameters for the script *
* result - [OUT] the result of a script execution *
* error - [OUT] the error reported by the script *
* max_error_len - [IN] the maximum error length *
@@ -670,8 +445,8 @@ static int zbx_execute_webhook(const zbx_script_t *script, const DC_HOST *host,
* TIMEOUT_ERROR - a timeout occurred *
* *
******************************************************************************/
-int zbx_script_execute(const zbx_script_t *script, const DC_HOST *host, const zbx_user_t *user, const DB_EVENT *event,
- zbx_script_exec_context ctx, char **result, char *error, size_t max_error_len, char **debug)
+int zbx_script_execute(const zbx_script_t *script, const DC_HOST *host, const char *params, char **result,
+ char *error, size_t max_error_len, char **debug)
{
int ret = FAIL;
@@ -682,7 +457,8 @@ int zbx_script_execute(const zbx_script_t *script, const DC_HOST *host, const zb
switch (script->type)
{
case ZBX_SCRIPT_TYPE_WEBHOOK:
- ret = zbx_execute_webhook(script, host, event, ctx, user, error, max_error_len, result, debug);
+ ret = zbx_es_execute_command(script->command, params, script->timeout, result, error,
+ max_error_len, debug);
break;
case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
switch (script->execute_on)
diff --git a/src/zabbix_server/scripts/scripts.h b/src/zabbix_server/scripts/scripts.h
index 46db95ab722..3b69dc4c0a6 100644
--- a/src/zabbix_server/scripts/scripts.h
+++ b/src/zabbix_server/scripts/scripts.h
@@ -23,19 +23,14 @@
#include "common.h"
#include "dbcache.h"
-typedef enum
-{
- ZBX_SCRIPT_CTX_HOST,
- ZBX_SCRIPT_CTX_EVENT,
- ZBX_SCRIPT_CTX_ACTION
-} zbx_script_exec_context;
-
void zbx_script_init(zbx_script_t *script);
void zbx_script_clean(zbx_script_t *script);
-int zbx_script_execute(const zbx_script_t *script, const DC_HOST *host, const zbx_user_t *user,
- const DB_EVENT *event, zbx_script_exec_context ctx, char **result, char *error, size_t max_error_len,
- char **debug);
-int zbx_script_prepare(zbx_script_t *script, const DC_HOST *host, const zbx_user_t *user,
- zbx_script_exec_context ctx, zbx_uint64_t eventid, char *error, size_t max_error_len, DB_EVENT **event);
+int zbx_check_script_permissions(zbx_uint64_t groupid, zbx_uint64_t hostid);
+int zbx_check_script_user_permissions(zbx_uint64_t userid, zbx_uint64_t hostid, zbx_script_t *script);
+int DBfetch_webhook_params(zbx_uint64_t scriptid, zbx_vector_ptr_pair_t *params, char *error, size_t error_len);
+void zbx_webhook_params_pack_json(const zbx_vector_ptr_pair_t *params, char **params_json);
+int zbx_script_prepare(zbx_script_t *script, const zbx_uint64_t *hostid, char *error, size_t max_error_len);
+int zbx_script_execute(const zbx_script_t *script, const DC_HOST *host, const char *params, char **result,
+ char *error, size_t max_error_len, char **debug);
zbx_uint64_t zbx_script_create_task(const zbx_script_t *script, const DC_HOST *host, zbx_uint64_t alertid, int now);
#endif
diff --git a/src/zabbix_server/server.c b/src/zabbix_server/server.c
index ad9d155f4dd..acf3b21a482 100644
--- a/src/zabbix_server/server.c
+++ b/src/zabbix_server/server.c
@@ -64,6 +64,8 @@
#include "availability/avail_manager.h"
#include "lld/lld_manager.h"
#include "lld/lld_worker.h"
+#include "reporter/report_manager.h"
+#include "reporter/report_writer.h"
#include "events.h"
#include "../libs/zbxdbcache/valuecache.h"
#include "setproctitle.h"
@@ -200,6 +202,8 @@ int CONFIG_LLDWORKER_FORKS = 2;
int CONFIG_ALERTDB_FORKS = 1;
int CONFIG_HISTORYPOLLER_FORKS = 5;
int CONFIG_AVAILMAN_FORKS = 1;
+int CONFIG_REPORTMANAGER_FORKS = 0;
+int CONFIG_REPORTWRITER_FORKS = 0;
int CONFIG_LISTEN_PORT = ZBX_DEFAULT_SERVER_PORT;
char *CONFIG_LISTEN_IP = NULL;
@@ -323,6 +327,8 @@ char *CONFIG_STATS_ALLOWED_IP = NULL;
int CONFIG_DOUBLE_PRECISION = ZBX_DB_DBL_PRECISION_DISABLED;
+char *CONFIG_WEBSERVICE_URL = NULL;
+
volatile sig_atomic_t zbx_diaginfo_scope = ZBX_DIAGINFO_UNDEFINED;
int get_process_info_by_thread(int local_server_num, unsigned char *local_process_type, int *local_process_num);
@@ -477,6 +483,16 @@ int get_process_info_by_thread(int local_server_num, unsigned char *local_proces
*local_process_type = ZBX_PROCESS_TYPE_AVAILMAN;
*local_process_num = local_server_num - server_count + CONFIG_AVAILMAN_FORKS;
}
+ else if (local_server_num <= (server_count += CONFIG_REPORTMANAGER_FORKS))
+ {
+ *local_process_type = ZBX_PROCESS_TYPE_REPORTMANAGER;
+ *local_process_num = local_server_num - server_count + CONFIG_REPORTMANAGER_FORKS;
+ }
+ else if (local_server_num <= (server_count += CONFIG_REPORTWRITER_FORKS))
+ {
+ *local_process_type = ZBX_PROCESS_TYPE_REPORTWRITER;
+ *local_process_num = local_server_num - server_count + CONFIG_REPORTWRITER_FORKS;
+ }
else
return FAIL;
@@ -548,6 +564,9 @@ static void zbx_set_defaults(void)
if (NULL == CONFIG_VAULTURL)
CONFIG_VAULTURL = zbx_strdup(CONFIG_VAULTURL, "https://127.0.0.1:8200");
+
+ if (0 != CONFIG_REPORTWRITER_FORKS)
+ CONFIG_REPORTMANAGER_FORKS = 1;
}
/******************************************************************************
@@ -623,6 +642,7 @@ static void zbx_validate_config(ZBX_TASK_EX *task)
err |= (FAIL == check_cfg_feature_str("VaultToken", CONFIG_VAULTTOKEN, "cURL library"));
err |= (FAIL == check_cfg_feature_str("VaultDBPath", CONFIG_VAULTDBPATH, "cURL library"));
+ err |= (FAIL == check_cfg_feature_int("StartReportWriters", CONFIG_REPORTWRITER_FORKS, "cURL library"));
#endif
#if !defined(HAVE_LIBXML2) || !defined(HAVE_LIBCURL)
@@ -657,6 +677,12 @@ static void zbx_validate_config(ZBX_TASK_EX *task)
#endif
err |= (FAIL == zbx_db_validate_config_features());
+ if (0 != CONFIG_REPORTWRITER_FORKS && NULL == CONFIG_WEBSERVICE_URL)
+ {
+ zabbix_log(LOG_LEVEL_CRIT, "\"WebServiceURL\" configuration parameter must be set when "
+ " setting \"StartReportWriters\" configuration parameter");
+ }
+
if (0 != err)
exit(EXIT_FAILURE);
}
@@ -876,6 +902,10 @@ static void zbx_load_config(ZBX_TASK_EX *task)
PARM_OPT, 0, 0},
{"StartHistoryPollers", &CONFIG_HISTORYPOLLER_FORKS, TYPE_INT,
PARM_OPT, 0, 1000},
+ {"StartReportWriters", &CONFIG_REPORTWRITER_FORKS, TYPE_INT,
+ PARM_OPT, 0, 100},
+ {"WebServiceURL", &CONFIG_WEBSERVICE_URL, TYPE_STRING,
+ PARM_OPT, 0, 0},
{NULL}
};
@@ -1259,8 +1289,9 @@ int MAIN_ZABBIX_ENTRY(int flags)
+ CONFIG_SNMPTRAPPER_FORKS + CONFIG_PROXYPOLLER_FORKS + CONFIG_SELFMON_FORKS
+ CONFIG_VMWARE_FORKS + CONFIG_TASKMANAGER_FORKS + CONFIG_IPMIMANAGER_FORKS
+ CONFIG_ALERTMANAGER_FORKS + CONFIG_PREPROCMAN_FORKS + CONFIG_PREPROCESSOR_FORKS
- + CONFIG_LLDMANAGER_FORKS + CONFIG_LLDWORKER_FORKS + CONFIG_ALERTDB_FORKS +
- CONFIG_HISTORYPOLLER_FORKS + CONFIG_AVAILMAN_FORKS;
+ + CONFIG_LLDMANAGER_FORKS + CONFIG_LLDWORKER_FORKS + CONFIG_ALERTDB_FORKS
+ + CONFIG_HISTORYPOLLER_FORKS + CONFIG_AVAILMAN_FORKS + CONFIG_REPORTMANAGER_FORKS
+ + CONFIG_REPORTWRITER_FORKS;
threads = (pid_t *)zbx_calloc(threads, threads_num, sizeof(pid_t));
threads_flags = (int *)zbx_calloc(threads_flags, threads_num, sizeof(int));
@@ -1409,6 +1440,12 @@ int MAIN_ZABBIX_ENTRY(int flags)
threads_flags[i] = ZBX_THREAD_WAIT_EXIT;
zbx_thread_start(availability_manager_thread, &thread_args, &threads[i]);
break;
+ case ZBX_PROCESS_TYPE_REPORTMANAGER:
+ zbx_thread_start(report_manager_thread, &thread_args, &threads[i]);
+ break;
+ case ZBX_PROCESS_TYPE_REPORTWRITER:
+ zbx_thread_start(report_writer_thread, &thread_args, &threads[i]);
+ break;
}
}
diff --git a/src/zabbix_server/snmptrapper/snmptrapper.c b/src/zabbix_server/snmptrapper/snmptrapper.c
index d1b1a4e4e00..83b9c535283 100644
--- a/src/zabbix_server/snmptrapper/snmptrapper.c
+++ b/src/zabbix_server/snmptrapper/snmptrapper.c
@@ -188,7 +188,7 @@ next:
}
items[i].state = ITEM_STATE_NORMAL;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags,
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, items[i].flags,
&results[i], ts, items[i].state, NULL);
itemids[i] = items[i].itemid;
@@ -196,7 +196,7 @@ next:
break;
case NOTSUPPORTED:
items[i].state = ITEM_STATE_NOTSUPPORTED;
- zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL,
+ zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, items[i].flags, NULL,
ts, items[i].state, results[i].msg);
itemids[i] = items[i].itemid;
diff --git a/src/zabbix_server/trapper/Makefile.am b/src/zabbix_server/trapper/Makefile.am
index 91c0c2b5d65..adece376064 100644
--- a/src/zabbix_server/trapper/Makefile.am
+++ b/src/zabbix_server/trapper/Makefile.am
@@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
-noinst_LIBRARIES = libzbxtrapper.a
+noinst_LIBRARIES = libzbxtrapper.a libzbxtrapper_server.a libzbxtrapper_proxy.a
libzbxtrapper_a_SOURCES = \
active.c \
@@ -20,4 +20,13 @@ libzbxtrapper_a_SOURCES = \
trapper_item_test.c \
trapper_item_test.h \
trapper.c \
- trapper.h
+ trapper.h \
+ trapper_request.h
+
+libzbxtrapper_server_a_SOURCES = \
+ trapper_server.c \
+ trapper_request.h
+
+libzbxtrapper_proxy_a_SOURCES = \
+ trapper_proxy.c \
+ trapper_request.h
diff --git a/src/zabbix_server/trapper/nodecommand.c b/src/zabbix_server/trapper/nodecommand.c
index b649a3949c9..a12dc5bfd8b 100644
--- a/src/zabbix_server/trapper/nodecommand.c
+++ b/src/zabbix_server/trapper/nodecommand.c
@@ -19,12 +19,15 @@
#include "common.h"
#include "comms.h"
+#include "zbxserver.h"
#include "db.h"
#include "log.h"
#include "../scripts/scripts.h"
#include "trapper_auth.h"
#include "nodecommand.h"
+#include "../../libs/zbxserver/get_host_from_event.h"
+#include "../../libs/zbxserver/zabbix_users.h"
/******************************************************************************
* *
@@ -36,7 +39,7 @@
* FAIL - an error occurred *
* *
******************************************************************************/
-static int execute_remote_script(zbx_script_t *script, DC_HOST *host, char **info, char *error,
+static int execute_remote_script(const zbx_script_t *script, const DC_HOST *host, char **info, char *error,
size_t max_error_len)
{
int ret = FAIL, time_start;
@@ -85,19 +88,28 @@ static int execute_remote_script(zbx_script_t *script, DC_HOST *host, char **inf
* *
* Purpose: record global script execution results into audit log *
* *
+ * Comments: 'hostid' should be always > 0. 'eventid' is > 0 in case of *
+ * "manual script on event" *
+ * *
******************************************************************************/
-static void auditlog_global_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, zbx_uint64_t proxy_hostid,
- zbx_uint64_t userid, const char *clientip, const char *command, unsigned char execute_on,
- const char *output, const char *error)
+static void auditlog_global_script(const zbx_script_t *script, zbx_uint64_t hostid, zbx_uint64_t eventid,
+ zbx_uint64_t proxy_hostid, zbx_uint64_t userid, const char *clientip, const char *output,
+ const char *error)
{
int now;
zbx_uint64_t auditid;
- char execute_on_s[MAX_ID_LEN + 1], hostid_s[MAX_ID_LEN + 1], proxy_hostid_s[MAX_ID_LEN + 1];
+ char execute_on_s[MAX_ID_LEN + 1], hostid_s[MAX_ID_LEN + 1], eventid_s[MAX_ID_LEN + 1],
+ proxy_hostid_s[MAX_ID_LEN + 1];
now = time(NULL);
auditid = DBget_maxid("auditlog");
- zbx_snprintf(execute_on_s, sizeof(execute_on_s), "%d", execute_on);
+ zbx_snprintf(execute_on_s, sizeof(execute_on_s), "%hhu", script->execute_on);
+
zbx_snprintf(hostid_s, sizeof(hostid_s), ZBX_FS_UI64, hostid);
+
+ if (0 != eventid)
+ zbx_snprintf(eventid_s, sizeof(eventid_s), ZBX_FS_UI64, eventid);
+
if (0 != proxy_hostid)
zbx_snprintf(proxy_hostid_s, sizeof(proxy_hostid_s), ZBX_FS_UI64, proxy_hostid);
@@ -114,10 +126,14 @@ static void auditlog_global_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, z
DBbegin();
zbx_db_insert_add_values(&db_audit, auditid, userid, now, AUDIT_ACTION_EXECUTE, AUDIT_RESOURCE_SCRIPT,
- clientip, scriptid);
+ clientip, script->scriptid);
zbx_db_insert_add_values(&db_details, __UINT64_C(0), auditid, "script", "execute_on", execute_on_s);
+
+ if (0 != eventid)
+ zbx_db_insert_add_values(&db_details, __UINT64_C(0), auditid, "script", "eventid", eventid_s);
+
zbx_db_insert_add_values(&db_details, __UINT64_C(0), auditid, "script", "hostid", hostid_s);
if (0 != proxy_hostid)
@@ -126,7 +142,11 @@ static void auditlog_global_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, z
proxy_hostid_s);
}
- zbx_db_insert_add_values(&db_details, __UINT64_C(0), auditid, "script", "command", command);
+ if (ZBX_SCRIPT_TYPE_WEBHOOK != script->type)
+ {
+ zbx_db_insert_add_values(&db_details, __UINT64_C(0), auditid, "script", "command",
+ script->command_orig);
+ }
if (NULL != output)
zbx_db_insert_add_values(&db_details, __UINT64_C(0), auditid, "script", "output", output);
@@ -140,7 +160,6 @@ static void auditlog_global_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, z
zbx_db_insert_autoincrement(&db_details, "auditdetailid");
zbx_db_insert_execute(&db_details);
zbx_db_insert_clean(&db_details);
-
}
while (ZBX_DB_DOWN == DBcommit());
}
@@ -156,7 +175,7 @@ static void auditlog_global_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, z
* FAIL - the access is denied *
* *
******************************************************************************/
-static int zbx_check_user_administration_actions_permissions(zbx_user_t *user, const char *role_rule)
+static int zbx_check_user_administration_actions_permissions(const zbx_user_t *user, const char *role_rule)
{
int ret = FAIL;
DB_RESULT result;
@@ -193,77 +212,366 @@ static int zbx_check_user_administration_actions_permissions(zbx_user_t *user, c
return ret;
}
-/**************************************************************************************************
- * *
- * Function: execute_script *
- * *
- * Parameters: scriptid - [IN] the id of a script to be executed *
- * hostid - [IN] the host the script will be executed on *
- * user - [IN] the user who executed the command *
- * clientip - [IN] the IP of client *
- * ctx - [IN] the execution context *
- * eventid - [IN] the id of an event (can be 0 for HOST context) *
- * result - [OUT] the result of a script execution *
- * debug - [OUT] the debug data (optional) *
- * *
- * Purpose: executing command *
- * *
- * Return value: SUCCEED - processed successfully *
- * FAIL - an error occurred *
- * *
- **************************************************************************************************/
-static int execute_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, zbx_user_t *user, const char *clientip,
- zbx_script_exec_context ctx, zbx_uint64_t eventid, char **result, char **debug)
+static int zbx_get_script_details(zbx_uint64_t scriptid, zbx_script_t *script, int *scope, zbx_uint64_t *usrgrpid,
+ zbx_uint64_t *groupid, char *error, size_t error_len)
+{
+ int ret = FAIL;
+ DB_RESULT db_result;
+ DB_ROW row;
+ zbx_uint64_t usrgrpid_l, groupid_l;
+
+ db_result = DBselect("select command,host_access,usrgrpid,groupid,type,execute_on,timeout,scope,port,authtype"
+ ",username,password,publickey,privatekey"
+ " from scripts"
+ " where scriptid=" ZBX_FS_UI64, scriptid);
+
+ if (NULL == db_result)
+ {
+ zbx_strlcpy(error, "Cannot select from table 'scripts'.", error_len);
+ return FAIL;
+ }
+
+ if (NULL == (row = DBfetch(db_result)))
+ {
+ zbx_strlcpy(error, "Script not found.", error_len);
+ goto fail;
+ }
+
+ ZBX_DBROW2UINT64(usrgrpid_l, row[2]);
+ *usrgrpid = usrgrpid_l;
+
+ ZBX_DBROW2UINT64(groupid_l, row[3]);
+ *groupid = groupid_l;
+
+ ZBX_STR2UCHAR(script->type, row[4]);
+
+ if (ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT == script->type)
+ ZBX_STR2UCHAR(script->execute_on, row[5]);
+
+ if (ZBX_SCRIPT_TYPE_SSH == script->type)
+ {
+ ZBX_STR2UCHAR(script->authtype, row[9]);
+ script->publickey = zbx_strdup(script->publickey, row[12]);
+ script->privatekey = zbx_strdup(script->privatekey, row[13]);
+ }
+
+ if (ZBX_SCRIPT_TYPE_SSH == script->type || ZBX_SCRIPT_TYPE_TELNET == script->type)
+ {
+ script->port = zbx_strdup(script->port, row[8]);
+ script->username = zbx_strdup(script->username, row[10]);
+ script->password = zbx_strdup(script->password, row[11]);
+ }
+
+ script->command = zbx_strdup(script->command, row[0]);
+ script->command_orig = zbx_strdup(script->command_orig, row[0]);
+
+ script->scriptid = scriptid;
+
+ ZBX_STR2UCHAR(script->host_access, row[1]);
+
+ if (SUCCEED != is_time_suffix(row[6], &script->timeout, ZBX_LENGTH_UNLIMITED))
+ {
+ zbx_strlcpy(error, "Invalid timeout value in script configuration.", error_len);
+ goto fail;
+ }
+
+ *scope = atoi(row[7]);
+
+ ret = SUCCEED;
+fail:
+ DBfree_result(db_result);
+
+ return ret;
+}
+
+static int is_user_in_allowed_group(zbx_uint64_t userid, zbx_uint64_t usrgrpid, char *error, size_t error_len)
{
- char error[MAX_STRING_LEN];
- int ret = FAIL, rc;
- DC_HOST host;
- DB_EVENT *event = NULL;
- zbx_script_t script;
+ DB_RESULT result;
+ int ret = FAIL;
+
+ result = DBselect("select null"
+ " from users_groups"
+ " where usrgrpid=" ZBX_FS_UI64
+ " and userid=" ZBX_FS_UI64,
+ usrgrpid, userid);
- zabbix_log(LOG_LEVEL_DEBUG, "In %s() scriptid:" ZBX_FS_UI64 " hostid:" ZBX_FS_UI64 " userid:" ZBX_FS_UI64,
- __func__, scriptid, hostid, user->userid);
+ if (NULL == result)
+ {
+ zbx_strlcpy(error, "Database error, cannot get user rights.", error_len);
+ goto fail;
+ }
+
+ if (NULL == DBfetch(result))
+ zbx_strlcpy(error, "User has no rights to execute this script.", error_len);
+ else
+ ret = SUCCEED;
+
+ DBfree_result(result);
+fail:
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_check_event_end_recovery_event *
+ * *
+ * Purpose: check if the specified event id corresponds to a problem event *
+ * caused by a trigger, find its recovery event (if it exists) *
+ * *
+ * Parameters: eventid - [IN] the id of event *
+ * r_eventid - [OUT] the id of recovery event (0 if there is *
+ * no recovery event *
+ * error - [OUT] the error message buffer *
+ * error_len - [IN] the size of error message buffer *
+ * *
+ * Return value: SUCCEED or FAIL (with 'error' message) *
+ * *
+ ******************************************************************************/
+static int zbx_check_event_end_recovery_event(zbx_uint64_t eventid, zbx_uint64_t *r_eventid, char *error,
+ size_t error_len)
+{
+ DB_RESULT db_result;
+ DB_ROW row;
+
+ if (NULL == (db_result = DBselect("select r_eventid from event_recovery where eventid="ZBX_FS_UI64, eventid)))
+ {
+ zbx_strlcpy(error, "Database error, cannot read from 'events' and 'event_recovery' tables.", error_len);
+ return FAIL;
+ }
+
+ if (NULL == (row = DBfetch(db_result)))
+ *r_eventid = 0;
+ else
+ ZBX_DBROW2UINT64(*r_eventid, row[0]);
+
+ DBfree_result(db_result);
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: execute_script *
+ * *
+ * Purpose: executing command *
+ * *
+ * Parameters: scriptid - [IN] the id of a script to be executed *
+ * hostid - [IN] the host the script will be executed on *
+ * eventid - [IN] the id of an event *
+ * user - [IN] the user who executes the command *
+ * clientip - [IN] the IP of client *
+ * result - [OUT] the result of a script execution *
+ * debug - [OUT] the debug data (optional) *
+ * *
+ * Return value: SUCCEED - processed successfully *
+ * FAIL - an error occurred *
+ * *
+ * Comments: either 'hostid' or 'eventid' must be > 0, but not both *
+ * *
+ ******************************************************************************/
+static int execute_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, zbx_uint64_t eventid, zbx_user_t *user,
+ const char *clientip, char **result, char **debug)
+{
+ int ret = FAIL, scope = 0, i, macro_type;
+ DC_HOST host;
+ zbx_script_t script;
+ zbx_uint64_t usrgrpid, groupid;
+ zbx_vector_uint64_t eventids;
+ zbx_vector_ptr_t events;
+ zbx_vector_ptr_pair_t webhook_params;
+ char *user_timezone = NULL, *webhook_params_json = NULL, error[MAX_STRING_LEN];
+ DB_EVENT *problem_event = NULL, *recovery_event = NULL;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() scriptid:" ZBX_FS_UI64 " hostid:" ZBX_FS_UI64 " eventid:" ZBX_FS_UI64
+ " userid:" ZBX_FS_UI64 " clientip:%s",
+ __func__, scriptid, hostid, eventid, user->userid, clientip);
*error = '\0';
+ memset(&host, 0, sizeof(host));
+ zbx_vector_uint64_create(&eventids);
+ zbx_vector_ptr_create(&events);
+ zbx_vector_ptr_pair_create(&webhook_params);
+
+ zbx_script_init(&script);
- if (ZBX_SCRIPT_CTX_HOST == ctx)
+ if (SUCCEED != zbx_get_script_details(scriptid, &script, &scope, &usrgrpid, &groupid, error, sizeof(error)))
+ goto fail;
+
+ /* validate script permissions */
+
+ if (0 < usrgrpid &&
+ USER_TYPE_SUPER_ADMIN != user->type &&
+ SUCCEED != is_user_in_allowed_group(user->userid, usrgrpid, error, sizeof(error)))
+ {
+ goto fail;
+ }
+
+ if (0 != hostid)
{
- if (SUCCEED != (rc = DCget_host_by_hostid(&host, hostid)))
+ if (ZBX_SCRIPT_SCOPE_HOST != scope)
+ {
+ zbx_snprintf(error, sizeof(error), "Script is not allowed in manual host action: scope:%d",
+ scope);
+ goto fail;
+ }
+ }
+ else if (ZBX_SCRIPT_SCOPE_EVENT != scope)
+ {
+ zbx_snprintf(error, sizeof(error), "Script is not allowed in manual event action: scope:%d", scope);
+ goto fail;
+ }
+
+ /* get host or event details */
+
+ if (0 != hostid)
+ {
+ if (SUCCEED != DCget_host_by_hostid(&host, hostid))
{
zbx_strlcpy(error, "Unknown host identifier.", sizeof(error));
goto fail;
}
}
- else if (ZBX_SCRIPT_CTX_EVENT == ctx || ZBX_SCRIPT_CTX_ACTION == ctx)
+ else /* eventid */
{
- memset(&host, 0, sizeof(host));
+ zbx_uint64_t r_eventid;
+
+ if (SUCCEED != zbx_check_event_end_recovery_event(eventid, &r_eventid, error, sizeof(error)))
+ goto fail;
+
+ zbx_vector_uint64_reserve(&eventids, 2);
+ zbx_vector_ptr_reserve(&events, 2);
+
+ zbx_vector_uint64_append(&eventids, eventid); /* problem event in element [0]*/
+
+ if (0 != r_eventid) /* optional recovery event in element [1] */
+ zbx_vector_uint64_append(&eventids, r_eventid);
+
+ zbx_db_get_events_by_eventids(&eventids, &events);
+
+ if (events.values_num != eventids.values_num)
+ {
+ zbx_strlcpy(error, "Specified event data not found.", sizeof(error));
+ goto fail;
+ }
+
+ switch (events.values_num)
+ {
+ case 1:
+ if (eventid == ((DB_EVENT *)(events.values[0]))->eventid)
+ {
+ problem_event = events.values[0];
+ }
+ else
+ {
+ zbx_strlcpy(error, "Specified event data not found.", sizeof(error));
+ goto fail;
+ }
+ break;
+ case 2:
+ if (r_eventid == ((DB_EVENT *)(events.values[0]))->eventid)
+ {
+ problem_event = events.values[1];
+ recovery_event = events.values[0];
+ }
+ else
+ {
+ problem_event = events.values[0];
+ recovery_event = events.values[1];
+ }
+ break;
+ default:
+ THIS_SHOULD_NEVER_HAPPEN;
+ zbx_snprintf(error, sizeof(error), "Internal error in %s() events.values_num:%d",
+ __func__, events.values_num);
+ goto fail;
+ }
+
+ if (EVENT_SOURCE_TRIGGERS != problem_event->source)
+ {
+ zbx_strlcpy(error, "The source of specified event is not a trigger.", sizeof(error));
+ goto fail;
+ }
+
+ if (TRIGGER_VALUE_PROBLEM != problem_event->value)
+ {
+ zbx_strlcpy(error, "The specified event is not a problem event.", sizeof(error));
+ goto fail;
+ }
+
+ if (SUCCEED != get_host_from_event((NULL != recovery_event) ? recovery_event : problem_event,
+ &host, error, sizeof(error)))
+ {
+ goto fail;
+ }
}
- else
- THIS_SHOULD_NEVER_HAPPEN;
- if (SUCCEED != (rc = zbx_check_user_administration_actions_permissions(user,
- ZBX_USER_ROLE_PERMISSION_ACTIONS_EXECUTE_SCRIPTS)))
+ if (SUCCEED != zbx_check_script_permissions(groupid, host.hostid))
{
- zbx_strlcpy(error, "Permission denied. No role access.", sizeof(error));
+ zbx_strlcpy(error, "Script does not have permission to be executed on the host.", sizeof(error));
goto fail;
}
- zbx_script_init(&script);
+ if (USER_TYPE_SUPER_ADMIN != user->type &&
+ SUCCEED != zbx_check_script_user_permissions(user->userid, host.hostid, &script))
+ {
+ zbx_strlcpy(error, "User does not have permission to execute this script on the host.", sizeof(error));
+ goto fail;
+ }
+
+ user_timezone = get_user_timezone(user->userid);
- script.type = ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT;
- script.scriptid = scriptid;
+ /* substitute macros in script body and webhook parameters */
- if (SUCCEED == (ret = zbx_script_prepare(&script, &host, user, ctx, eventid, error, sizeof(error), &event)))
+ if (0 != hostid) /* script on host */
+ macro_type = MACRO_TYPE_SCRIPT;
+ else
+ macro_type = (NULL != recovery_event) ? MACRO_TYPE_SCRIPT_RECOVERY : MACRO_TYPE_SCRIPT_NORMAL;
+
+ if (ZBX_SCRIPT_TYPE_WEBHOOK != script.type)
{
- const char *poutput = NULL, *perror = NULL;
+ if (SUCCEED != substitute_simple_macros_unmasked(NULL, problem_event, recovery_event, &user->userid,
+ NULL, &host, NULL, NULL, NULL, user_timezone, &script.command, macro_type, error,
+ sizeof(error)))
+ {
+ goto fail;
+ }
+
+ if (SUCCEED != substitute_simple_macros(NULL, problem_event, recovery_event, &user->userid, NULL, &host,
+ NULL, NULL, NULL, user_timezone, &script.command_orig, macro_type, error,
+ sizeof(error)))
+ {
+ THIS_SHOULD_NEVER_HAPPEN;
+ goto fail;
+ }
+ }
+ else
+ {
+ if (SUCCEED != DBfetch_webhook_params(script.scriptid, &webhook_params, error, sizeof(error)))
+ goto fail;
- if (NULL != event && ZBX_SCRIPT_CTX_HOST != ctx && ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT == script.type)
- zbx_db_free_event(event);
+ for (i = 0; i < webhook_params.values_num; i++)
+ {
+ if (SUCCEED != substitute_simple_macros_unmasked(NULL, problem_event, recovery_event,
+ &user->userid, NULL, &host, NULL, NULL, NULL, user_timezone,
+ (char **)&webhook_params.values[i].second, macro_type, error,
+ sizeof(error)))
+ {
+ goto fail;
+ }
+ }
+
+ zbx_webhook_params_pack_json(&webhook_params, &webhook_params_json);
+ }
+
+ if (SUCCEED == (ret = zbx_script_prepare(&script, &host.hostid, error, sizeof(error))))
+ {
+ const char *poutput = NULL, *perror = NULL;
if (0 == host.proxy_hostid || ZBX_SCRIPT_EXECUTE_ON_SERVER == script.execute_on ||
ZBX_SCRIPT_TYPE_WEBHOOK == script.type)
{
- ret = zbx_script_execute(&script, &host, user, event, ctx, result, error, sizeof(error), debug);
+ ret = zbx_script_execute(&script, &host, webhook_params_json, result, error, sizeof(error),
+ debug);
}
else
ret = execute_remote_script(&script, &host, result, error, sizeof(error));
@@ -273,15 +581,28 @@ static int execute_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, zbx_user_t
else
perror = error;
- auditlog_global_script(scriptid, hostid, host.proxy_hostid, user->userid, clientip, script.command_orig,
- script.execute_on, poutput, perror);
+ auditlog_global_script(&script, host.hostid, eventid, host.proxy_hostid, user->userid, clientip,
+ poutput, perror);
}
-
- zbx_script_clean(&script);
fail:
if (SUCCEED != ret)
*result = zbx_strdup(*result, error);
+ zbx_script_clean(&script);
+ zbx_free(webhook_params_json);
+ zbx_free(user_timezone);
+
+ for (i = 0; i < webhook_params.values_num; i++)
+ {
+ zbx_free(webhook_params.values[i].first);
+ zbx_free(webhook_params.values[i].second);
+ }
+
+ zbx_vector_ptr_pair_destroy(&webhook_params);
+ zbx_vector_ptr_clear_ext(&events, (zbx_clean_func_t)zbx_db_free_event);
+ zbx_vector_ptr_destroy(&events);
+ zbx_vector_uint64_destroy(&eventids);
+
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
@@ -297,12 +618,12 @@ fail:
* FAIL - an error occurred *
* *
******************************************************************************/
-int node_process_command(zbx_socket_t *sock, const char *data, struct zbx_json_parse *jp)
+int node_process_command(zbx_socket_t *sock, const char *data, const struct zbx_json_parse *jp)
{
- char clientip[MAX_STRING_LEN], tmp[64], *result = NULL, *send = NULL, *debug = NULL;
- int ret = FAIL;
+ char *result = NULL, *send = NULL, *debug = NULL, tmp[64], tmp_hostid[64], tmp_eventid[64],
+ clientip[MAX_STRING_LEN];
+ int ret = FAIL, got_hostid = 0, got_eventid = 0;
zbx_uint64_t scriptid, hostid = 0, eventid = 0;
- zbx_script_exec_context ctx;
struct zbx_json j;
zbx_user_t user;
@@ -310,6 +631,26 @@ int node_process_command(zbx_socket_t *sock, const char *data, struct zbx_json_p
zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN);
+ /* check who is connecting, get user details, check access rights */
+
+ if (FAIL == zbx_get_user_from_json(jp, &user, &result))
+ goto finish;
+
+ if (SUCCEED != check_perm2system(user.userid))
+ {
+ result = zbx_strdup(result, "Permission denied. User is a member of group with disabled access.");
+ goto finish;
+ }
+
+ if (SUCCEED != zbx_check_user_administration_actions_permissions(&user,
+ ZBX_USER_ROLE_PERMISSION_ACTIONS_EXECUTE_SCRIPTS))
+ {
+ result = zbx_strdup(result, "Permission denied. No role access.");
+ goto finish;
+ }
+
+ /* extract and validate other JSON elements */
+
if (SUCCEED != zbx_json_value_by_name(jp, ZBX_PROTO_TAG_SCRIPTID, tmp, sizeof(tmp), NULL) ||
FAIL == is_uint64(tmp, &scriptid))
{
@@ -317,33 +658,61 @@ int node_process_command(zbx_socket_t *sock, const char *data, struct zbx_json_p
goto finish;
}
- if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_EVENTID, tmp, sizeof(tmp), NULL) &&
- FAIL == is_uint64(tmp, &eventid))
+ if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_HOSTID, tmp_hostid, sizeof(tmp_hostid), NULL))
+ got_hostid = 1;
+
+ if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_EVENTID, tmp_eventid, sizeof(tmp_eventid), NULL))
+ got_eventid = 1;
+
+ if (0 == got_hostid && 0 == got_eventid)
{
- result = zbx_dsprintf(result, "Failed to parse eventid tag: %s.", ZBX_PROTO_TAG_EVENTID);
+ result = zbx_dsprintf(result, "Failed to parse command request tag %s or %s.",
+ ZBX_PROTO_TAG_HOSTID, ZBX_PROTO_TAG_EVENTID);
goto finish;
}
- else
- ctx = ZBX_SCRIPT_CTX_EVENT;
- if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_HOSTID, tmp, sizeof(tmp), NULL))
+ if (1 == got_hostid && 1 == got_eventid)
{
- if (FAIL == is_uint64(tmp, &hostid))
+ result = zbx_dsprintf(result, "Command request tags %s and %s cannot be used together.",
+ ZBX_PROTO_TAG_HOSTID, ZBX_PROTO_TAG_EVENTID);
+ goto finish;
+ }
+
+ if (1 == got_hostid)
+ {
+ if (SUCCEED != is_uint64(tmp_hostid, &hostid))
{
- result = zbx_dsprintf(result, "Failed to parse hostid tag: %s.", ZBX_PROTO_TAG_HOSTID);
+ result = zbx_dsprintf(result, "Failed to parse value of command request tag %s.",
+ ZBX_PROTO_TAG_HOSTID);
+ goto finish;
+ }
+
+ if (0 == hostid)
+ {
+ result = zbx_dsprintf(result, "%s value cannot be 0.", ZBX_PROTO_TAG_HOSTID);
goto finish;
}
- else
- ctx = ZBX_SCRIPT_CTX_HOST;
}
+ else
+ {
+ if (SUCCEED != is_uint64(tmp_eventid, &eventid))
+ {
+ result = zbx_dsprintf(result, "Failed to parse value of command request tag %s.",
+ ZBX_PROTO_TAG_EVENTID);
+ goto finish;
+ }
- if (FAIL == zbx_get_user_from_json(jp, &user, &result))
- goto finish;
+ if (0 == eventid)
+ {
+ result = zbx_dsprintf(result, "%s value cannot be 0.", ZBX_PROTO_TAG_EVENTID);
+ goto finish;
+ }
+ }
if (SUCCEED != zbx_json_value_by_name(jp, ZBX_PROTO_TAG_CLIENTIP, clientip, sizeof(clientip), NULL))
*clientip = '\0';
- if (SUCCEED == (ret = execute_script(scriptid, hostid, &user, clientip, ctx, eventid, &result, &debug)))
+ if (SUCCEED == (ret = execute_script(scriptid, hostid, eventid, &user, clientip, &result, &debug)))
{
zbx_json_addstring(&j, ZBX_PROTO_TAG_RESPONSE, ZBX_PROTO_VALUE_SUCCESS, ZBX_JSON_TYPE_STRING);
zbx_json_addstring(&j, ZBX_PROTO_TAG_DATA, result, ZBX_JSON_TYPE_STRING);
diff --git a/src/zabbix_server/trapper/nodecommand.h b/src/zabbix_server/trapper/nodecommand.h
index 9cbdad7907c..cd724a9f0e1 100644
--- a/src/zabbix_server/trapper/nodecommand.h
+++ b/src/zabbix_server/trapper/nodecommand.h
@@ -28,6 +28,6 @@ extern int CONFIG_TIMEOUT;
extern int CONFIG_TRAPPER_TIMEOUT;
extern char *CONFIG_SOURCE_IP;
-int node_process_command(zbx_socket_t *sock, const char *data, struct zbx_json_parse *jp);
+int node_process_command(zbx_socket_t *sock, const char *data, const struct zbx_json_parse *jp);
#endif
diff --git a/src/zabbix_server/trapper/proxydata.c b/src/zabbix_server/trapper/proxydata.c
index 3f408def77e..f551d2a89a3 100644
--- a/src/zabbix_server/trapper/proxydata.c
+++ b/src/zabbix_server/trapper/proxydata.c
@@ -103,7 +103,7 @@ int zbx_send_proxy_data_response(const DC_PROXY *proxy, zbx_socket_t *sock, cons
******************************************************************************/
void zbx_recv_proxy_data(zbx_socket_t *sock, struct zbx_json_parse *jp, zbx_timespec_t *ts)
{
- int ret = FAIL, upload_status = 0, status, version;
+ int ret = FAIL, upload_status = 0, status, version, responded = 0;
char *error = NULL;
DC_PROXY proxy;
@@ -130,33 +130,33 @@ void zbx_recv_proxy_data(zbx_socket_t *sock, struct zbx_json_parse *jp, zbx_time
goto out;
}
- if (FAIL == zbx_hc_check_proxy(proxy.hostid))
+ if (FAIL == (ret = zbx_hc_check_proxy(proxy.hostid)))
{
upload_status = ZBX_PROXY_UPLOAD_DISABLED;
- goto send_response;
}
else
+ {
upload_status = ZBX_PROXY_UPLOAD_ENABLED;
- if (SUCCEED != (ret = process_proxy_data(&proxy, jp, ts, HOST_STATUS_PROXY_ACTIVE, NULL, &error)))
- {
- zabbix_log(LOG_LEVEL_WARNING, "received invalid proxy data from proxy \"%s\" at \"%s\": %s",
- proxy.host, sock->peer, error);
- status = FAIL;
- goto out;
+ if (SUCCEED != (ret = process_proxy_data(&proxy, jp, ts, HOST_STATUS_PROXY_ACTIVE, NULL, &error)))
+ {
+ zabbix_log(LOG_LEVEL_WARNING, "received invalid proxy data from proxy \"%s\" at \"%s\": %s",
+ proxy.host, sock->peer, error);
+ goto out;
+ }
}
-send_response:
if (!ZBX_IS_RUNNING())
{
error = zbx_strdup(error, "Zabbix server shutdown in progress");
zabbix_log(LOG_LEVEL_WARNING, "cannot process proxy data from active proxy at \"%s\": %s",
sock->peer, error);
- ret = status = FAIL;
+ ret = FAIL;
goto out;
}
- else
- zbx_send_proxy_data_response(&proxy, sock, error, upload_status);
+
+ zbx_send_proxy_data_response(&proxy, sock, error, upload_status);
+ responded = 1;
out:
if (SUCCEED == status) /* moved the unpredictable long operation to the end */
@@ -166,14 +166,14 @@ out:
(0 != (sock->protocol & ZBX_TCP_COMPRESS) ? 1 : 0), 0);
}
- if (FAIL == ret)
+ if (0 == responded)
{
int flags = ZBX_TCP_PROTOCOL;
if (0 != (sock->protocol & ZBX_TCP_COMPRESS))
flags |= ZBX_TCP_COMPRESS;
- zbx_send_response_ext(sock, status, error, NULL, flags, CONFIG_TIMEOUT);
+ zbx_send_response_ext(sock, ret, error, NULL, flags, CONFIG_TIMEOUT);
}
zbx_free(error);
diff --git a/src/zabbix_server/trapper/trapper.c b/src/zabbix_server/trapper/trapper.c
index 595f4ed5d36..2cd27dd8354 100644
--- a/src/zabbix_server/trapper/trapper.c
+++ b/src/zabbix_server/trapper/trapper.c
@@ -29,7 +29,6 @@
#include "nodecommand.h"
#include "proxyconfig.h"
#include "proxydata.h"
-#include "../alerter/alerter_protocol.h"
#include "daemon.h"
#include "zbxcrypto.h"
@@ -42,6 +41,7 @@
#include "trapper_expressions_evaluate.h"
#include "trapper_item_test.h"
#include "trapper.h"
+#include "trapper_request.h"
#define ZBX_MAX_SECTION_ENTRIES 4
#define ZBX_MAX_ENTRY_ATTRIBUTES 3
@@ -462,149 +462,6 @@ out:
return ret;
}
-/******************************************************************************
- * *
- * Function: recv_alert_send *
- * *
- * Purpose: process alert send request that is used to test media types *
- * *
- * Parameters: sock - [IN] the request socket *
- * jp - [IN] the request data *
- * *
- ******************************************************************************/
-static void recv_alert_send(zbx_socket_t *sock, const struct zbx_json_parse *jp)
-{
- DB_RESULT result;
- DB_ROW row;
- int ret = FAIL, errcode;
- char tmp[ZBX_MAX_UINT64_LEN + 1], *sendto = NULL, *subject = NULL,
- *message = NULL, *error = NULL, *params = NULL, *value = NULL, *debug = NULL;
- zbx_uint64_t mediatypeid;
- size_t string_alloc;
- struct zbx_json json;
- struct zbx_json_parse jp_data, jp_params;
- unsigned char *data = NULL, smtp_security, smtp_verify_peer, smtp_verify_host,
- smtp_authentication, content_type, *response = NULL;
- zbx_uint32_t size;
- zbx_user_t user;
- unsigned short smtp_port;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);
-
- if (FAIL == zbx_get_user_from_json(jp, &user, NULL) || USER_TYPE_SUPER_ADMIN > user.type)
- {
- error = zbx_strdup(NULL, "Permission denied.");
- goto fail;
- }
-
- if (SUCCEED != zbx_json_brackets_by_name(jp, ZBX_PROTO_TAG_DATA, &jp_data))
- {
- error = zbx_dsprintf(NULL, "Cannot parse request tag: %s.", ZBX_PROTO_TAG_DATA);
- goto fail;
- }
-
- if (SUCCEED != zbx_json_value_by_name(&jp_data, ZBX_PROTO_TAG_MEDIATYPEID, tmp, sizeof(tmp), NULL) ||
- SUCCEED != is_uint64(tmp, &mediatypeid))
- {
- error = zbx_dsprintf(NULL, "Cannot parse request tag: %s.", ZBX_PROTO_TAG_MEDIATYPEID);
- goto fail;
- }
-
- string_alloc = 0;
- if (SUCCEED == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_SENDTO, &sendto, &string_alloc, NULL))
- string_alloc = 0;
- if (SUCCEED == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_SUBJECT, &subject, &string_alloc, NULL))
- string_alloc = 0;
- if (SUCCEED == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_MESSAGE, &message, &string_alloc, NULL))
- string_alloc = 0;
-
- if (SUCCEED == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_PARAMETERS, &jp_params))
- {
- size_t string_offset = 0;
-
- zbx_strncpy_alloc(&params, &string_alloc, &string_offset, jp_params.start,
- jp_params.end - jp_params.start + 1);
- }
-
- result = DBselect("select type,smtp_server,smtp_helo,smtp_email,exec_path,gsm_modem,username,"
- "passwd,smtp_port,smtp_security,smtp_verify_peer,smtp_verify_host,smtp_authentication,"
- "exec_params,maxsessions,maxattempts,attempt_interval,content_type,script,timeout"
- " from media_type"
- " where mediatypeid=" ZBX_FS_UI64, mediatypeid);
-
- if (NULL == (row = DBfetch(result)))
- {
- DBfree_result(result);
- error = zbx_dsprintf(NULL, "Cannot find the specified media type.");
- goto fail;
- }
-
- if (FAIL == is_ushort(row[8], &smtp_port))
- {
- DBfree_result(result);
- error = zbx_dsprintf(NULL, "Invalid port value.");
- goto fail;
- }
-
- ZBX_STR2UCHAR(smtp_security, row[9]);
- ZBX_STR2UCHAR(smtp_verify_peer, row[10]);
- ZBX_STR2UCHAR(smtp_verify_host, row[11]);
- ZBX_STR2UCHAR(smtp_authentication, row[12]);
- ZBX_STR2UCHAR(content_type, row[17]);
-
- size = zbx_alerter_serialize_alert_send(&data, mediatypeid, atoi(row[0]), row[1], row[2], row[3], row[4],
- row[5], row[6], row[7], smtp_port, smtp_security, smtp_verify_peer, smtp_verify_host,
- smtp_authentication, row[13], atoi(row[14]), atoi(row[15]), row[16], content_type, row[18],
- row[19], sendto, subject, message, params);
-
- DBfree_result(result);
-
- if (SUCCEED != zbx_ipc_async_exchange(ZBX_IPC_SERVICE_ALERTER, ZBX_IPC_ALERTER_ALERT, SEC_PER_MIN, data, size,
- &response, &error))
- {
- goto fail;
- }
-
- zbx_alerter_deserialize_result(response, &value, &errcode, &error, &debug);
- zbx_free(response);
-
- if (SUCCEED == errcode)
- ret = SUCCEED;
-fail:
- zbx_json_addstring(&json, ZBX_PROTO_TAG_RESPONSE, SUCCEED == ret ? ZBX_PROTO_VALUE_SUCCESS :
- ZBX_PROTO_VALUE_FAILED, ZBX_JSON_TYPE_STRING);
-
- if (SUCCEED == ret)
- {
- if (NULL != value)
- zbx_json_addstring(&json, ZBX_PROTO_TAG_DATA, value, ZBX_JSON_TYPE_STRING);
- }
- else
- {
- if (NULL != error && '\0' != *error)
- zbx_json_addstring(&json, ZBX_PROTO_TAG_INFO, error, ZBX_JSON_TYPE_STRING);
- }
-
- if (NULL != debug)
- zbx_json_addraw(&json, "debug", debug);
-
- (void)zbx_tcp_send(sock, json.buffer);
-
- zbx_free(params);
- zbx_free(message);
- zbx_free(subject);
- zbx_free(sendto);
- zbx_free(data);
- zbx_free(value);
- zbx_free(error);
- zbx_free(debug);
- zbx_json_free(&json);
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
-}
-
static int DBget_template_count(zbx_uint64_t *count)
{
DB_RESULT result;
@@ -1202,11 +1059,6 @@ static int process_trap(zbx_socket_t *sock, char *s, zbx_timespec_t *ts)
{
ret = send_internal_stats_json(sock, &jp);
}
- else if (0 == strcmp(value, ZBX_PROTO_VALUE_ZABBIX_ALERT_SEND))
- {
- if (0 != (program_type & ZBX_PROGRAM_TYPE_SERVER))
- recv_alert_send(sock, &jp);
- }
else if (0 == strcmp(value, ZBX_PROTO_VALUE_PREPROCESSING_TEST))
{
if (0 != (program_type & ZBX_PROGRAM_TYPE_SERVER))
@@ -1222,7 +1074,7 @@ static int process_trap(zbx_socket_t *sock, char *s, zbx_timespec_t *ts)
if (0 != (program_type & ZBX_PROGRAM_TYPE_SERVER))
zbx_trapper_item_test(sock, &jp);
}
- else
+ else if (SUCCEED != trapper_process_request(value, sock, &jp))
{
zabbix_log(LOG_LEVEL_WARNING, "unknown request received from \"%s\": [%s]", sock->peer,
value);
diff --git a/src/zabbix_server/trapper/trapper_proxy.c b/src/zabbix_server/trapper/trapper_proxy.c
new file mode 100644
index 00000000000..6fe888d1ebf
--- /dev/null
+++ b/src/zabbix_server/trapper/trapper_proxy.c
@@ -0,0 +1,30 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "trapper_request.h"
+
+int trapper_process_request(const char *request, zbx_socket_t *sock, const struct zbx_json_parse *jp)
+{
+ ZBX_UNUSED(request);
+ ZBX_UNUSED(sock);
+ ZBX_UNUSED(jp);
+
+ return FAIL;
+}
diff --git a/src/zabbix_server/trapper/trapper_request.h b/src/zabbix_server/trapper/trapper_request.h
new file mode 100644
index 00000000000..2b8eb4794b6
--- /dev/null
+++ b/src/zabbix_server/trapper/trapper_request.h
@@ -0,0 +1,28 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_TRAPPER_REQUEST_H
+#define ZABBIX_TRAPPER_REQUEST_H
+
+#include "comms.h"
+#include "zbxjson.h"
+
+int trapper_process_request(const char *request, zbx_socket_t *sock, const struct zbx_json_parse *jp);
+
+#endif
diff --git a/src/zabbix_server/trapper/trapper_server.c b/src/zabbix_server/trapper/trapper_server.c
new file mode 100644
index 00000000000..0540e0613e7
--- /dev/null
+++ b/src/zabbix_server/trapper/trapper_server.c
@@ -0,0 +1,228 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "common.h"
+#include "log.h"
+#include "cfg.h"
+#include "zbxjson.h"
+#include "trapper_request.h"
+#include "trapper_auth.h"
+#include "zbxreport.h"
+#include "zbxjson.h"
+#include "db.h"
+#include "../alerter/alerter_protocol.h"
+#include "zbxipcservice.h"
+
+extern int CONFIG_REPORTMANAGER_FORKS;
+
+static void trapper_process_report_test(zbx_socket_t *sock, const struct zbx_json_parse *jp)
+{
+ zbx_user_t user;
+ struct zbx_json_parse jp_data;
+ struct zbx_json j;
+
+ if (0 == CONFIG_REPORTMANAGER_FORKS)
+ {
+ zbx_send_response(sock, FAIL, "Report manager is disabled.", CONFIG_TIMEOUT);
+ return;
+ }
+
+ if (FAIL == zbx_get_user_from_json(jp, &user, NULL))
+ {
+ zbx_send_response(sock, FAIL, "Permission denied.", CONFIG_TIMEOUT);
+ return;
+ }
+
+ if (SUCCEED != zbx_json_brackets_by_name(jp, ZBX_PROTO_TAG_DATA, &jp_data))
+ {
+ char *error;
+
+ error = zbx_dsprintf(NULL, "cannot find tag: %s", ZBX_PROTO_TAG_DATA);
+ zbx_send_response(sock, FAIL, error, CONFIG_TIMEOUT);
+ zbx_free(error);
+
+ return;
+ }
+
+ zbx_report_test(&jp_data, user.userid, &j);
+ zbx_tcp_send_bytes_to(sock, j.buffer, j.buffer_size, CONFIG_TIMEOUT);
+ zbx_json_clean(&j);
+}
+
+/******************************************************************************
+ * *
+ * Function: recv_alert_send *
+ * *
+ * Purpose: process alert send request that is used to test media types *
+ * *
+ * Parameters: sock - [IN] the request socket *
+ * jp - [IN] the request data *
+ * *
+ ******************************************************************************/
+static void trapper_process_alert_send(zbx_socket_t *sock, const struct zbx_json_parse *jp)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ int ret = FAIL, errcode;
+ char tmp[ZBX_MAX_UINT64_LEN + 1], *sendto = NULL, *subject = NULL,
+ *message = NULL, *error = NULL, *params = NULL, *value = NULL, *debug = NULL;
+ zbx_uint64_t mediatypeid;
+ size_t string_alloc;
+ struct zbx_json json;
+ struct zbx_json_parse jp_data, jp_params;
+ unsigned char *data = NULL, smtp_security, smtp_verify_peer, smtp_verify_host,
+ smtp_authentication, content_type, *response = NULL;
+ zbx_uint32_t size;
+ zbx_user_t user;
+ unsigned short smtp_port;
+ unsigned char type;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);
+
+ if (FAIL == zbx_get_user_from_json(jp, &user, NULL) || USER_TYPE_SUPER_ADMIN > user.type)
+ {
+ error = zbx_strdup(NULL, "Permission denied.");
+ goto fail;
+ }
+
+ if (SUCCEED != zbx_json_brackets_by_name(jp, ZBX_PROTO_TAG_DATA, &jp_data))
+ {
+ error = zbx_dsprintf(NULL, "Cannot parse request tag: %s.", ZBX_PROTO_TAG_DATA);
+ goto fail;
+ }
+
+ if (SUCCEED != zbx_json_value_by_name(&jp_data, ZBX_PROTO_TAG_MEDIATYPEID, tmp, sizeof(tmp), NULL) ||
+ SUCCEED != is_uint64(tmp, &mediatypeid))
+ {
+ error = zbx_dsprintf(NULL, "Cannot parse request tag: %s.", ZBX_PROTO_TAG_MEDIATYPEID);
+ goto fail;
+ }
+
+ string_alloc = 0;
+ if (SUCCEED == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_SENDTO, &sendto, &string_alloc, NULL))
+ string_alloc = 0;
+ if (SUCCEED == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_SUBJECT, &subject, &string_alloc, NULL))
+ string_alloc = 0;
+ if (SUCCEED == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_MESSAGE, &message, &string_alloc, NULL))
+ string_alloc = 0;
+
+ if (SUCCEED == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_PARAMETERS, &jp_params))
+ {
+ size_t string_offset = 0;
+
+ zbx_strncpy_alloc(&params, &string_alloc, &string_offset, jp_params.start,
+ (size_t)(jp_params.end - jp_params.start + 1));
+ }
+
+ result = DBselect("select type,smtp_server,smtp_helo,smtp_email,exec_path,gsm_modem,username,"
+ "passwd,smtp_port,smtp_security,smtp_verify_peer,smtp_verify_host,smtp_authentication,"
+ "exec_params,maxsessions,maxattempts,attempt_interval,content_type,script,timeout"
+ " from media_type"
+ " where mediatypeid=" ZBX_FS_UI64, mediatypeid);
+
+ if (NULL == (row = DBfetch(result)))
+ {
+ DBfree_result(result);
+ error = zbx_dsprintf(NULL, "Cannot find the specified media type.");
+ goto fail;
+ }
+
+ if (FAIL == is_ushort(row[8], &smtp_port))
+ {
+ DBfree_result(result);
+ error = zbx_dsprintf(NULL, "Invalid port value.");
+ goto fail;
+ }
+
+ ZBX_STR2UCHAR(smtp_security, row[9]);
+ ZBX_STR2UCHAR(smtp_verify_peer, row[10]);
+ ZBX_STR2UCHAR(smtp_verify_host, row[11]);
+ ZBX_STR2UCHAR(smtp_authentication, row[12]);
+ ZBX_STR2UCHAR(content_type, row[17]);
+ ZBX_STR2UCHAR(type, row[0]);
+
+ size = zbx_alerter_serialize_alert_send(&data, mediatypeid, type, row[1], row[2], row[3], row[4],
+ row[5], row[6], row[7], smtp_port, smtp_security, smtp_verify_peer, smtp_verify_host,
+ smtp_authentication, row[13], atoi(row[14]), atoi(row[15]), row[16], content_type, row[18],
+ row[19], sendto, subject, message, params);
+
+ DBfree_result(result);
+
+ if (SUCCEED != zbx_ipc_async_exchange(ZBX_IPC_SERVICE_ALERTER, ZBX_IPC_ALERTER_SEND_ALERT, SEC_PER_MIN, data,
+ size, &response, &error))
+ {
+ goto fail;
+ }
+
+ zbx_free(sendto);
+ zbx_alerter_deserialize_result_ext(response, &sendto, &value, &errcode, &error, &debug);
+ zbx_free(response);
+
+ if (SUCCEED == errcode)
+ ret = SUCCEED;
+fail:
+ zbx_json_addstring(&json, ZBX_PROTO_TAG_RESPONSE, SUCCEED == ret ? ZBX_PROTO_VALUE_SUCCESS :
+ ZBX_PROTO_VALUE_FAILED, ZBX_JSON_TYPE_STRING);
+
+ if (SUCCEED == ret)
+ {
+ if (NULL != value)
+ zbx_json_addstring(&json, ZBX_PROTO_TAG_DATA, value, ZBX_JSON_TYPE_STRING);
+ }
+ else
+ {
+ if (NULL != error && '\0' != *error)
+ zbx_json_addstring(&json, ZBX_PROTO_TAG_INFO, error, ZBX_JSON_TYPE_STRING);
+ }
+
+ if (NULL != debug)
+ zbx_json_addraw(&json, "debug", debug);
+
+ (void)zbx_tcp_send(sock, json.buffer);
+
+ zbx_free(params);
+ zbx_free(message);
+ zbx_free(subject);
+ zbx_free(sendto);
+ zbx_free(data);
+ zbx_free(value);
+ zbx_free(error);
+ zbx_free(debug);
+ zbx_json_free(&json);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+}
+
+int trapper_process_request(const char *request, zbx_socket_t *sock, const struct zbx_json_parse *jp)
+{
+ if (0 == strcmp(request, ZBX_PROTO_VALUE_REPORT_TEST))
+ {
+ trapper_process_report_test(sock, jp);
+ return SUCCEED;
+ }
+ else if (0 == strcmp(request, ZBX_PROTO_VALUE_ZABBIX_ALERT_SEND))
+ {
+ trapper_process_alert_send(sock, jp);
+ return SUCCEED;
+ }
+
+ return FAIL;
+}
diff --git a/src/zabbix_server/vmware/vmware.c b/src/zabbix_server/vmware/vmware.c
index c802e3a5794..55e7994625a 100644
--- a/src/zabbix_server/vmware/vmware.c
+++ b/src/zabbix_server/vmware/vmware.c
@@ -662,6 +662,7 @@ static int zbx_soap_post(const char *fn_parent, CURL *easyhandle, const char *re
xmlDoc *doc;
ZBX_HTTPPAGE *resp;
int ret = SUCCEED;
+ char *val = NULL;
if (SUCCEED != zbx_http_post(easyhandle, request, &resp, error))
return FAIL;
@@ -669,11 +670,16 @@ static int zbx_soap_post(const char *fn_parent, CURL *easyhandle, const char *re
if (NULL != fn_parent)
zabbix_log(LOG_LEVEL_TRACE, "%s() SOAP response: %s", fn_parent, resp->data);
- if (SUCCEED != zbx_xml_try_read_value(resp->data, resp->offset, ZBX_XPATH_FAULTSTRING(), &doc, error, error)
- || NULL != *error)
+ if (SUCCEED != zbx_xml_try_read_value(resp->data, resp->offset, ZBX_XPATH_FAULTSTRING(), &doc, &val, error))
{
ret = FAIL;
}
+ else if (NULL != val)
+ {
+ zbx_free(*error);
+ *error = val;
+ ret = FAIL;
+ }
if (NULL != xdoc)
{
@@ -3561,7 +3567,15 @@ static int vmware_service_init_hv(zbx_vmware_service_t *service, CURL *easyhandl
zbx_vmware_vm_t *vm;
if (NULL != (vm = vmware_service_create_vm(service, easyhandle, vms.values[i], error)))
+ {
zbx_vector_ptr_append(&hv->vms, vm);
+ }
+ else if (NULL != *error)
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "Unable initialize vm %s: %s.", vms.values[i], *error);
+ zbx_free(*error);
+ }
+
}
ret = SUCCEED;
@@ -4271,6 +4285,7 @@ clean:
* *
* Parameters: service - [IN] the vmware service *
* easyhandle - [IN] the CURL handle *
+ * last_key - [IN] the ID of last processed event *
* events - [OUT] a pointer to the output variable *
* alloc_sz - [OUT] allocated memory size for events *
* error - [OUT] the error message in the case of failure *
@@ -4280,9 +4295,9 @@ clean:
* *
******************************************************************************/
static int vmware_service_get_event_data(const zbx_vmware_service_t *service, CURL *easyhandle,
- zbx_vector_ptr_t *events, zbx_uint64_t *alloc_sz, char **error)
+ zbx_uint64_t last_key, zbx_vector_ptr_t *events, zbx_uint64_t *alloc_sz, char **error)
{
- char *event_session = NULL;
+ char *event_session = NULL, *err = NULL;
int ret = FAIL, soap_count = 5; /* 10 - initial value of eventlog records number in one response */
xmlDoc *doc = NULL;
zbx_uint64_t eventlog_last_key;
@@ -4296,12 +4311,12 @@ static int vmware_service_get_event_data(const zbx_vmware_service_t *service, CU
goto end_session;
if (NULL != service->data && 0 != service->data->events.values_num &&
- ((const zbx_vmware_event_t *)service->data->events.values[0])->key > service->eventlog.last_key)
+ ((const zbx_vmware_event_t *)service->data->events.values[0])->key > last_key)
{
eventlog_last_key = ((const zbx_vmware_event_t *)service->data->events.values[0])->key;
}
else
- eventlog_last_key = service->eventlog.last_key;
+ eventlog_last_key = last_key;
do
{
@@ -4331,8 +4346,12 @@ static int vmware_service_get_event_data(const zbx_vmware_service_t *service, CU
ret = SUCCEED;
end_session:
- if (SUCCEED != vmware_service_destroy_event_session(easyhandle, event_session, error))
+ if (SUCCEED != vmware_service_destroy_event_session(easyhandle, event_session, &err))
+ {
+ *error = zbx_strdcatf(*error, "%s%s", NULL != *error ? "; " : "", err);
+ zbx_free(err);
ret = FAIL;
+ }
out:
zbx_free(event_session);
zbx_xml_free_doc(doc);
@@ -4445,8 +4464,8 @@ clean:
xmlXPathFreeContext(xpathCtx);
out:
zbx_xml_free_doc(doc);
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
-
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s last_key:" ZBX_FS_UI64, __func__, zbx_result_string(ret),
+ (SUCCEED == ret ? xml_event.id : 0));
return ret;
# undef ZBX_POST_VMWARE_LASTEVENT
@@ -5083,8 +5102,8 @@ static void vmware_service_update(zbx_vmware_service_t *service)
zbx_vector_ptr_t events;
int i, ret = FAIL;
ZBX_HTTPPAGE page; /* 347K/87K */
- unsigned char skip_old = service->eventlog.skip_old;
- zbx_uint64_t events_sz = 0;
+ unsigned char evt_pause = 0, evt_skip_old;
+ zbx_uint64_t evt_last_key, events_sz = 0;
char msg[MAX_STRING_LEN / 8];
zabbix_log(LOG_LEVEL_DEBUG, "In %s() '%s'@'%s'", __func__, service->username, service->url);
@@ -5102,6 +5121,11 @@ static void vmware_service_update(zbx_vmware_service_t *service)
zbx_vector_str_create(&hvs);
zbx_vector_str_create(&dss);
+ zbx_vmware_lock();
+ evt_last_key = service->eventlog.last_key;
+ evt_skip_old = service->eventlog.skip_old;
+ zbx_vmware_unlock();
+
if (NULL == (easyhandle = curl_easy_init()))
{
zabbix_log(LOG_LEVEL_WARNING, "Cannot initialize cURL library");
@@ -5129,6 +5153,12 @@ static void vmware_service_update(zbx_vmware_service_t *service)
goto clean;
}
+ if (NULL != service->data && 0 != service->data->events.values_num && 0 == evt_skip_old &&
+ ((const zbx_vmware_event_t *)service->data->events.values[0])->key > evt_last_key)
+ {
+ evt_pause = 1;
+ }
+
if (SUCCEED != vmware_service_get_hv_ds_dc_list(service, easyhandle, &hvs, &dss, &data->datacenters,
&data->error))
{
@@ -5162,6 +5192,11 @@ static void vmware_service_update(zbx_vmware_service_t *service)
{
zbx_hashset_insert(&data->hvs, &hv_local, sizeof(hv_local));
}
+ else if (NULL != data->error)
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "Unable initialize hv %s: %s.", hvs.values[i], data->error);
+ zbx_free(data->error);
+ }
}
for (i = 0; i < data->datastores.values_num; i++)
@@ -5172,19 +5207,18 @@ static void vmware_service_update(zbx_vmware_service_t *service)
zbx_vector_vmware_datastore_sort(&data->datastores, vmware_ds_name_compare);
- if (0 == service->eventlog.req_sz)
+ if (0 == service->eventlog.req_sz && 0 == evt_pause)
{
/* skip collection of event data if we don't know where */
/* we stopped last time or item can't accept values */
- if (ZBX_VMWARE_EVENT_KEY_UNINITIALIZED != service->eventlog.last_key &&
- 0 == service->eventlog.skip_old &&
- SUCCEED != vmware_service_get_event_data(service, easyhandle, &data->events,
- &events_sz, &data->error))
+ if (ZBX_VMWARE_EVENT_KEY_UNINITIALIZED != evt_last_key && 0 == evt_skip_old &&
+ SUCCEED != vmware_service_get_event_data(service, easyhandle, evt_last_key,
+ &data->events, &events_sz, &data->error))
{
goto clean;
}
- if (0 != service->eventlog.skip_old)
+ if (0 != evt_skip_old)
{
char *error = NULL;
@@ -5196,14 +5230,18 @@ static void vmware_service_update(zbx_vmware_service_t *service)
zbx_free(error);
}
else
- skip_old = 0;
+ evt_skip_old = 0;
}
}
- else
+ else if (0 != service->eventlog.req_sz)
{
zabbix_log(LOG_LEVEL_WARNING, "Postponed VMware events requires up to " ZBX_FS_UI64
" bytes of free VMwareCache memory. Reading events skipped", service->eventlog.req_sz);
}
+ else
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "Previous events have not been read. Reading new events skipped");
+ }
if (ZBX_VMWARE_TYPE_VCENTER == service->type &&
SUCCEED != vmware_service_get_cluster_list(easyhandle, &data->clusters, &data->error))
@@ -5278,7 +5316,7 @@ out:
vmware_mem->free_size, vmware_mem->free_size, vmware->strpool_sz,
vmware_mem->total_size);
}
- else
+ else if (0 == evt_pause)
{
int level;
@@ -5297,8 +5335,7 @@ out:
service->eventlog.req_sz = 0;
}
- if (NULL != service->data && 0 != service->data->events.values_num &&
- ((const zbx_vmware_event_t *)service->data->events.values[0])->key > service->eventlog.last_key)
+ if (0 != evt_pause)
{
zbx_vector_ptr_append_array(&events, service->data->events.values, service->data->events.values_num);
zbx_vector_ptr_reserve(&data->events, data->events.values_num + service->data->events.values_num);
@@ -5307,7 +5344,7 @@ out:
vmware_data_shared_free(service->data);
service->data = vmware_data_shared_dup(data);
- service->eventlog.skip_old = skip_old;
+ service->eventlog.skip_old = evt_skip_old;
if (0 != events.values_num)
zbx_vector_ptr_append_array(&service->data->events, events.values, events.values_num);
@@ -5316,9 +5353,18 @@ out:
vmware_service_update_perf_entities(service);
- zbx_snprintf(msg, sizeof(msg), "VMwareCache memory usage (free/strpool/total): " ZBX_FS_UI64 " / "
- ZBX_FS_UI64 " / " ZBX_FS_UI64, vmware_mem->free_size, vmware->strpool_sz,
- vmware_mem->total_size);
+ if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG))
+ zbx_mem_dump_stats(LOG_LEVEL_DEBUG, vmware_mem);
+
+ zbx_snprintf(msg, sizeof(msg), "Events:%d DC:%d DS:%d CL:%d HV:%d VM:%d"
+ " VMwareCache memory usage (free/strpool/total): " ZBX_FS_UI64 " / " ZBX_FS_UI64 " / "
+ ZBX_FS_UI64, NULL != service->data ? service->data->events.values_num : 0 ,
+ NULL != service->data ? service->data->datacenters.values_num : 0 ,
+ NULL != service->data ? service->data->datastores.values_num : 0 ,
+ NULL != service->data ? service->data->clusters.values_num : 0 ,
+ NULL != service->data ? service->data->hvs.num_data : 0 ,
+ NULL != service->data ? service->data->vms_index.num_data : 0 ,
+ vmware_mem->free_size, vmware->strpool_sz, vmware_mem->total_size);
zbx_vmware_unlock();