diff options
author | Artjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com> | 2021-05-07 13:47:40 +0300 |
---|---|---|
committer | Artjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com> | 2021-05-07 13:49:05 +0300 |
commit | 41e3dd118cc870619ab69d9ed0b97d568bde55f4 (patch) | |
tree | 2587d08132a68bac434cd6f125cb27cf973e0aa8 /src | |
parent | 5e5c5444e2f0102bdf34cec94851e4781b719022 (diff) | |
parent | f9b34c32c38493307a3b2ebc73a4130223be96b5 (diff) |
........S. [DEV-1836] updated to the latest master
Diffstat (limited to 'src')
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], ×pec, items[i].state, NULL); + zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, + items[i].flags, &results[i], ×pec, 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, ×pec, - 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, ×pec, items[i].state, results[i].msg); } DCpoller_requeue_items(&items[i].itemid, ×pec.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 = ¶ms->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(¶ms); + + 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, ¶ms); + report_clear_params(¶ms); + } + 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(¶ms, pair); + } + DBfree_result(result); + + if (0 != params.values_num) + rm_report_update_params(report, ¶ms); + + report_destroy_params(¶ms); + + 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(¶ms); + + 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(¶ms, 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, + ¶ms, width, height, &jobs, error)) + { + goto out; + } + } + + if (0 != report->usergroups.values_num) + { + if (SUCCEED != rm_report_create_usergroup_jobs(manager, report, now, ¶ms, 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(¶ms); + 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(¶ms); + + report_deserialize_test_report(message->data, &name, &dashboardid, &userid, &access_userid, &report_time, + &period, ¶ms); + + 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, ¶ms, 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(¶ms); + + 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, ¶ms_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, ¶ms_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(¶ms); + 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(¶ms, pair); + } + } + + size = report_serialize_test_report(&data, name, dashboardid, userid, viewer_userid, report_time, period, + ¶ms); + + 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(¶ms); +} 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(¶ms); + + report_deserialize_begin_report(msg->data, &name, &url, &cookie, &width, &height, ¶ms); + + 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(¶ms); + + 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, ¶ms)) - { - 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(¶ms, &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(¶ms, &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(); |