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

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndris Zeila <andris.zeila@zabbix.com>2022-01-20 13:20:53 +0300
committerAleksejs Sestakovs <aleksejs.sestakovs@zabbix.com>2022-01-20 13:33:46 +0300
commitc447d96509ae692d1ba79c65a221a81523280287 (patch)
tree33fed37cec8c780a618f8a71b7b712cbec735ac8 /src/go/plugins
parent03e25604336eaa8234f5b66c0f3c2fe293b90189 (diff)
...G...... [ZBXNEXT-7409] added native support for the vfs.dir.size and vfs.dir.count keys in Zabbix agent 2
* commit '8f3eaae228fd3646c87b6d779e526aaf99ce29e8': (26 commits) ...G...... [ZBXNEXT-7409] fixed coding style ...G...... [ZBXNEXT-7409] fixed agent2 vfs.dir.count to trim spaces in types ...G...... [ZBXNEXT-7409] fixed vfs.dir.size to sum hard links once ...G...... [ZBXNEXT-7409] fixed vfs.dir.count and vfs.dir.size for windows .D........ [ZBXNEXT-7409] fixed changelog entry .D........ [ZBXNEXT-7409] renamed changelog file .D........ [ZBXNEXT-7408] added changelog entry .......... [ZBXNEXT-6875] fixed symlink processing with vfs.dir.* functions ...G...... [ZBXNEXT-6875] removed unused function parameter ...G...... [ZBXNEXT-6875] fixed hard link and symlink interaction on Windows ...G...... [ZBXNEXT-6875] code clean up ...G...... [ZBXNEXT-6875] fixed vfs.dir.size vfs.dir.count stopping on file info fail ...G...... [ZBXNEXT-6875] fixed vfs.dir.count stopping on dir fail ...G...... [ZBXNEXT-6875] fixing sub issues and added changelog ...G...... [ZBXNEXT-6875] updated byte constants ...G...... [ZBXNEXT-6875] fixing sub-issues ...G...... [ZBXNEXT-6875] code clean-up ...G...... [ZBXNEXT-6875] code clean-up ...G...... [ZBXNEXT-6875] code clean-up ...G...... [ZBXNEXT-6875] implemented vfs.dir.size in Zabbix agent 2 for Windows ... (cherry picked from commit 577e78291bfff5120f13d6e0f79c1654e6906d92) (cherry picked from commit d8edaf13526317a33148934b24423dce1b5d1017)
Diffstat (limited to 'src/go/plugins')
-rw-r--r--src/go/plugins/plugins_linux.go1
-rw-r--r--src/go/plugins/plugins_windows.go1
-rw-r--r--src/go/plugins/vfs/dir/count.go487
-rw-r--r--src/go/plugins/vfs/dir/count_nix.go37
-rw-r--r--src/go/plugins/vfs/dir/count_test.go120
-rw-r--r--src/go/plugins/vfs/dir/count_windows.go74
-rw-r--r--src/go/plugins/vfs/dir/dir.go121
-rw-r--r--src/go/plugins/vfs/dir/size.go220
-rw-r--r--src/go/plugins/vfs/dir/size_nix.go73
-rw-r--r--src/go/plugins/vfs/dir/size_windows.go108
-rw-r--r--src/go/plugins/zabbix/sync/sync_nix.go3
-rw-r--r--src/go/plugins/zabbix/sync/sync_windows.go2
12 files changed, 1243 insertions, 4 deletions
diff --git a/src/go/plugins/plugins_linux.go b/src/go/plugins/plugins_linux.go
index 302e4942ade..33788d49989 100644
--- a/src/go/plugins/plugins_linux.go
+++ b/src/go/plugins/plugins_linux.go
@@ -47,6 +47,7 @@ import (
_ "zabbix.com/plugins/systemd"
_ "zabbix.com/plugins/systemrun"
_ "zabbix.com/plugins/vfs/dev"
+ _ "zabbix.com/plugins/vfs/dir"
_ "zabbix.com/plugins/vfs/file"
_ "zabbix.com/plugins/vfs/fs"
_ "zabbix.com/plugins/vm/memory"
diff --git a/src/go/plugins/plugins_windows.go b/src/go/plugins/plugins_windows.go
index 09c76f64dae..0c5c15c0dc8 100644
--- a/src/go/plugins/plugins_windows.go
+++ b/src/go/plugins/plugins_windows.go
@@ -41,6 +41,7 @@ import (
_ "zabbix.com/plugins/system/uptime"
_ "zabbix.com/plugins/system/users"
_ "zabbix.com/plugins/systemrun"
+ _ "zabbix.com/plugins/vfs/dir"
_ "zabbix.com/plugins/vfs/file"
_ "zabbix.com/plugins/vfs/fs"
_ "zabbix.com/plugins/vm/memory"
diff --git a/src/go/plugins/vfs/dir/count.go b/src/go/plugins/vfs/dir/count.go
new file mode 100644
index 00000000000..45438c83c2d
--- /dev/null
+++ b/src/go/plugins/vfs/dir/count.go
@@ -0,0 +1,487 @@
+/*
+** 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 dir
+
+import (
+ "fmt"
+ "io/fs"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "zabbix.com/pkg/plugin"
+
+ "zabbix.com/pkg/zbxerr"
+)
+
+const (
+ emptyParam = iota
+ firstParam
+ secondParam
+ thirdParam
+ fourthParam
+ fifthParam
+ sixthParam
+ seventhParam
+ eightParam
+ ninthParam
+ tenthParam
+ eleventhParam
+
+ regularFile = 0
+
+ unlimitedDepth = -1
+
+ kilobyteType = 'K'
+ megabyteType = 'M'
+ gigabyteType = 'G'
+ terabyteType = 'T'
+
+ kb = 1024
+ mb = kb * 1024
+ gb = mb * 1024
+ tb = gb * 1024
+
+ secondsType = 's'
+ minuteType = 'm'
+ hourType = 'h'
+ dayType = 'd'
+ weekType = 'w'
+
+ dayMultiplier = 24
+ weekMultiplier = 7
+)
+
+//Plugin -
+type Plugin struct {
+ plugin.Base
+}
+
+type countParams struct {
+ common
+ minSize string
+ maxSize string
+ minAge string
+ maxAge string
+ parsedMinSize int64
+ parsedMaxSize int64
+ parsedMinAge time.Time
+ parsedMaxAge time.Time
+ typesInclude map[fs.FileMode]bool
+ typesExclude map[fs.FileMode]bool
+}
+
+func (cp *countParams) getDirCount() (int, error) {
+ var count int
+
+ err := filepath.WalkDir(cp.path,
+ func(p string, d fs.DirEntry, err error) error {
+ if err != nil {
+ impl.Logger.Errf("failed to walk dir with path %s", p)
+ return nil
+ }
+
+ if p == cp.path {
+ return nil
+ }
+
+ s, err := cp.skip(p, d)
+ if s {
+ return err
+ }
+
+ count++
+
+ return nil
+ })
+
+ if err != nil {
+ return 0, zbxerr.ErrorCannotParseResult.Wrap(err)
+ }
+
+ return count, nil
+}
+
+func (cp *countParams) skip(path string, d fs.DirEntry) (bool, error) {
+ var s bool
+
+ s, err := cp.skipPath(path)
+ if s {
+ return true, err
+ }
+
+ s, err = cp.skipRegex(d)
+ if s {
+ return true, err
+ }
+
+ s, err = cp.skipInfo(d)
+ if s {
+ if err != nil {
+ impl.Logger.Errf("failed to get file info for path %s, %s", path, err.Error())
+ }
+
+ return true, nil
+ }
+
+ if cp.skipType(path, d) {
+ return true, nil
+ }
+
+ return false, nil
+}
+
+func (cp *countParams) skipInfo(d fs.DirEntry) (bool, error) {
+ i, err := d.Info()
+ if err != nil {
+ return true, err
+ }
+
+ if cp.minSize != "" {
+ if cp.parsedMinSize > i.Size() {
+ return true, nil
+ }
+ }
+
+ if cp.maxSize != "" {
+ if cp.parsedMaxSize < i.Size() {
+ return true, nil
+ }
+ }
+
+ if cp.minAge != "" && i.ModTime().After(cp.parsedMinAge) {
+ return true, nil
+ }
+
+ if cp.maxAge != "" && i.ModTime().Before(cp.parsedMaxAge) {
+ return true, nil
+ }
+
+ return false, nil
+}
+
+func (cp *countParams) setMinMax() (err error) {
+ err = cp.setMinParams()
+ if err != nil {
+ return
+ }
+
+ err = cp.setMaxParams()
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+func (cp *countParams) setMaxParams() (err error) {
+ cp.parsedMaxSize, err = parseByte(cp.maxSize)
+ if err != nil {
+ return
+ }
+
+ if cp.maxAge != "" {
+ var age time.Duration
+ age, err = parseTime(cp.maxAge)
+ if err != nil {
+ return
+ }
+
+ cp.parsedMaxAge = time.Now().Add(-age)
+ }
+
+ return
+}
+
+func (cp *countParams) setMinParams() (err error) {
+ cp.parsedMinSize, err = parseByte(cp.minSize)
+ if err != nil {
+ err = zbxerr.ErrorInvalidParams.Wrap(err)
+
+ return
+ }
+
+ if cp.minAge != "" {
+ var age time.Duration
+ age, err = parseTime(cp.minAge)
+ if err != nil {
+ return
+ }
+
+ cp.parsedMinAge = time.Now().Add(-age)
+ }
+
+ return
+}
+
+func isTypeMatch(in map[fs.FileMode]bool, fm fs.FileMode) bool {
+ if in[regularFile] && fm.IsRegular() {
+ return true
+ }
+
+ if in[fm.Type()] {
+ return true
+ }
+
+ return false
+}
+
+func getCountParams(params []string) (out countParams, err error) {
+ out.maxDepth = -1
+
+ switch len(params) {
+ case eleventhParam:
+ out.dirRegExclude, err = parseReg(params[10])
+ if err != nil {
+ err = zbxerr.New("Invalid eleventh parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case tenthParam:
+ out.maxAge = params[9]
+
+ fallthrough
+ case ninthParam:
+ out.minAge = params[8]
+
+ fallthrough
+ case eightParam:
+ out.maxSize = params[7]
+
+ fallthrough
+ case seventhParam:
+ out.minSize = params[6]
+
+ fallthrough
+ case sixthParam:
+ if params[5] != "" {
+ out.maxDepth, err = strconv.Atoi(params[5])
+ if err != nil {
+ err = zbxerr.New("Invalid sixth parameter.").Wrap(err)
+
+ return
+ }
+
+ if out.maxDepth < unlimitedDepth {
+ err = zbxerr.New("Invalid sixth parameter.")
+
+ return
+ }
+ }
+
+ fallthrough
+ case fifthParam:
+ out.typesExclude, err = parseType(params[4], true)
+ if err != nil {
+ err = zbxerr.New("Invalid fifth parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case fourthParam:
+ out.typesInclude, err = parseType(params[3], false)
+ if err != nil {
+ err = zbxerr.New("Invalid fourth parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case thirdParam:
+ out.regExclude, err = parseReg(params[2])
+ if err != nil {
+ err = zbxerr.New("Invalid third parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case secondParam:
+ out.regInclude, err = parseReg(params[1])
+ if err != nil {
+ err = zbxerr.New("Invalid second parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case firstParam:
+ out.path = params[0]
+ if out.path == "" {
+ err = zbxerr.New("Invalid first parameter.")
+
+ return
+ }
+
+ if !strings.HasSuffix(out.path, string(filepath.Separator)) {
+ out.path += string(filepath.Separator)
+ }
+
+ case emptyParam:
+ err = zbxerr.ErrorTooFewParameters
+
+ return
+ default:
+ err = zbxerr.ErrorTooManyParameters
+
+ return
+ }
+
+ return
+}
+
+func parseType(in string, exclude bool) (out map[fs.FileMode]bool, err error) {
+ if in == "" {
+ return getEmptyType(exclude), nil
+ }
+
+ out = make(map[fs.FileMode]bool)
+ types := strings.Split(in, ",")
+
+ for _, t := range types {
+ t = strings.TrimSpace(t)
+
+ switch t {
+ case "all":
+ //If all are set no need to iterate further
+ return getAllMode(), nil
+ default:
+ out, err = setIndividualType(out, t)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return out, nil
+}
+
+func setIndividualType(m map[fs.FileMode]bool, t string) (map[fs.FileMode]bool, error) {
+ switch t {
+ case "file":
+ m[regularFile] = true
+ case "dir":
+ m[fs.ModeDir] = true
+ case "sym":
+ m[fs.ModeSymlink] = true
+ case "sock":
+ m[fs.ModeSocket] = true
+ case "bdev":
+ m[fs.ModeDevice] = true
+ case "cdev":
+ m[fs.ModeDevice+fs.ModeCharDevice] = true
+ case "fifo":
+ m[fs.ModeNamedPipe] = true
+ case "dev":
+ m[fs.ModeDevice] = true
+ m[fs.ModeDevice+fs.ModeCharDevice] = true
+ default:
+ return nil, fmt.Errorf("invalid type: %s", t)
+ }
+
+ return m, nil
+}
+
+func getEmptyType(exclude bool) map[fs.FileMode]bool {
+ if exclude {
+ return nil
+ }
+
+ return getAllMode()
+}
+
+func getAllMode() map[fs.FileMode]bool {
+ out := make(map[fs.FileMode]bool)
+ out[regularFile] = true
+ out[fs.ModeDir] = true
+ out[fs.ModeSymlink] = true
+ out[fs.ModeSocket] = true
+ out[fs.ModeDevice] = true
+ out[fs.ModeCharDevice+fs.ModeDevice] = true
+ out[fs.ModeNamedPipe] = true
+
+ return out
+}
+
+func parseByte(in string) (int64, error) {
+ if in == "" {
+ return 0, nil
+ }
+
+ bytes, err := strconv.ParseInt(in, 10, 64)
+ if err != nil {
+ bytes, err := strconv.ParseInt(in[:len(in)-1], 10, 64)
+ if err != nil {
+ return 0, err
+ }
+
+ suffix := in[len(in)-1]
+ switch suffix {
+ case kilobyteType:
+ return bytes * kb, nil
+ case megabyteType:
+ return bytes * mb, nil
+ case gigabyteType:
+ return bytes * gb, nil
+ case terabyteType:
+ return bytes * tb, nil
+ default:
+ return 0, fmt.Errorf("unknown memory suffix %s", string(suffix))
+ }
+ }
+
+ return bytes, nil
+}
+
+func parseTime(in string) (time.Duration, error) {
+ if in == "" {
+ return 0 * time.Second, nil
+ }
+
+ t, err := strconv.ParseInt(in, 10, 64)
+ if err != nil {
+ t, err := strconv.ParseInt(in[:len(in)-1], 10, 64)
+ if err != nil {
+ return 0, err
+ }
+
+ suffix := in[len(in)-1]
+ switch suffix {
+ case secondsType:
+ return time.Duration(t) * time.Second, nil
+ case minuteType:
+ return time.Duration(t) * time.Minute, nil
+ case hourType:
+ return time.Duration(t) * time.Hour, nil
+ case dayType:
+ return time.Duration(t) * time.Hour * dayMultiplier, nil
+ case weekType:
+ return time.Duration(t) * time.Hour * dayMultiplier * weekMultiplier, nil
+ default:
+ return 0, fmt.Errorf("unknown time suffix %s", string(suffix))
+ }
+ }
+
+ return time.Duration(t) * time.Second, nil
+}
diff --git a/src/go/plugins/vfs/dir/count_nix.go b/src/go/plugins/vfs/dir/count_nix.go
new file mode 100644
index 00000000000..a0fe46c9d2d
--- /dev/null
+++ b/src/go/plugins/vfs/dir/count_nix.go
@@ -0,0 +1,37 @@
+//go:build !windows
+// +build !windows
+
+/*
+** 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 dir
+
+import "io/fs"
+
+func (cp *countParams) skipType(path string, d fs.DirEntry) bool {
+ if len(cp.typesInclude) > 0 && !isTypeMatch(cp.typesInclude, d.Type()) {
+ return true
+ }
+
+ if len(cp.typesExclude) > 0 && isTypeMatch(cp.typesExclude, d.Type()) {
+ return true
+ }
+
+ return false
+}
diff --git a/src/go/plugins/vfs/dir/count_test.go b/src/go/plugins/vfs/dir/count_test.go
new file mode 100644
index 00000000000..23c6a9f49ab
--- /dev/null
+++ b/src/go/plugins/vfs/dir/count_test.go
@@ -0,0 +1,120 @@
+/*
+** 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 dir
+
+import (
+ "io/fs"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func Test_parseByte(t *testing.T) {
+ type args struct {
+ in string
+ }
+ tests := []struct {
+ name string
+ args args
+ want int64
+ wantErr bool
+ }{
+ {"+byte", args{"1024"}, 1024, false},
+ {"+kB", args{"1K"}, 1024, false},
+ {"+mB", args{"1M"}, 1024 * 1024, false},
+ {"+gB", args{"1G"}, 1024 * 1024 * 1024, false},
+ {"+tB", args{"1T"}, 1024 * 1024 * 1024 * 1024, false},
+ {"-empty", args{""}, 0, false},
+ {"-invalid_string", args{"foobar"}, 0, true},
+ {"-invalid_suffix", args{"1A"}, 0, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := parseByte(tt.args.in)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("parseByte() error = %v, wantErr %v", err, tt.wantErr)
+
+ return
+ }
+ if got != tt.want {
+ t.Errorf("parseByte() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_parseTime(t *testing.T) {
+ type args struct {
+ in string
+ }
+ tests := []struct {
+ name string
+ args args
+ want time.Duration
+ wantErr bool
+ }{
+ {"+second", args{"60"}, time.Minute, false},
+ {"+second_suffix", args{"60s"}, time.Minute, false},
+ {"+minute", args{"1m"}, time.Minute, false},
+ {"+hour", args{"1h"}, time.Hour, false},
+ {"+hour", args{"1d"}, time.Hour * dayMultiplier, false},
+ {"+hour", args{"1w"}, time.Hour * dayMultiplier * weekMultiplier, false},
+ {"-empty", args{""}, 0, false},
+ {"-invalid_string", args{"foobar"}, 0, true},
+ {"-invalid_suffix", args{"1A"}, 0, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := parseTime(tt.args.in)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("parseTime() error = %v, wantErr %v", err, tt.wantErr)
+
+ return
+ }
+ if got != tt.want {
+ t.Errorf("parseTime() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_getAllMode(t *testing.T) {
+ tests := []struct {
+ name string
+ want map[fs.FileMode]bool
+ }{
+ {"new", map[fs.FileMode]bool{
+ regularFile: true,
+ fs.ModeDir: true,
+ fs.ModeSymlink: true,
+ fs.ModeSocket: true,
+ fs.ModeDevice: true,
+ fs.ModeCharDevice + fs.ModeDevice: true,
+ fs.ModeNamedPipe: true,
+ }},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := getAllMode(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("getAllMode() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/go/plugins/vfs/dir/count_windows.go b/src/go/plugins/vfs/dir/count_windows.go
new file mode 100644
index 00000000000..15343257eb0
--- /dev/null
+++ b/src/go/plugins/vfs/dir/count_windows.go
@@ -0,0 +1,74 @@
+/*
+** 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 dir
+
+import (
+ "io/fs"
+ "os"
+ "syscall"
+)
+
+func (cp *countParams) skipType(path string, d fs.DirEntry) bool {
+ var typeFile bool
+ var typeDir bool
+
+ i, err := d.Info()
+ if err != nil {
+ impl.Logger.Errf("failed to get file info for path %s, %s", path, err.Error())
+ return true
+ }
+
+ if attr, ok := i.Sys().(*syscall.Win32FileAttributeData); ok {
+ if (len(cp.typesInclude) > 0 && !isTypeMatch(cp.typesInclude, regularFile)) ||
+ (len(cp.typesExclude) > 0 && isTypeMatch(cp.typesExclude, regularFile)) {
+ typeFile = false
+ } else {
+ typeFile = true
+ }
+
+ if (len(cp.typesInclude) > 0 && !isTypeMatch(cp.typesInclude, fs.ModeDir)) ||
+ (len(cp.typesExclude) > 0 && isTypeMatch(cp.typesExclude, fs.ModeDir)) {
+ typeDir = false
+ } else {
+ typeDir = true
+ }
+
+ if attr.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
+ if attr.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 && typeDir {
+ return false
+ } else if attr.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 && typeFile {
+ for _, f := range cp.files {
+ if os.SameFile(f, i) {
+ return true
+ }
+ }
+
+ cp.common.files = append(cp.common.files, i)
+ return false
+ }
+ } else if attr.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 && typeFile {
+ return false
+ }
+ } else {
+ impl.Logger.Errf("failed to get system file attribute data")
+ }
+
+ return true
+}
diff --git a/src/go/plugins/vfs/dir/dir.go b/src/go/plugins/vfs/dir/dir.go
new file mode 100644
index 00000000000..40901c01d3b
--- /dev/null
+++ b/src/go/plugins/vfs/dir/dir.go
@@ -0,0 +1,121 @@
+/*
+** 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 dir
+
+import (
+ "io/fs"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "zabbix.com/pkg/plugin"
+ "zabbix.com/pkg/zbxerr"
+)
+
+var impl Plugin
+
+type common struct {
+ path string
+ maxDepth int
+ length int
+ regExclude *regexp.Regexp
+ regInclude *regexp.Regexp
+ dirRegExclude *regexp.Regexp
+ files []fs.FileInfo
+}
+
+//Export -
+func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) {
+ switch key {
+ case "vfs.dir.count":
+ return p.exportCount(params)
+ case "vfs.dir.size":
+ return p.exportSize(params)
+ default:
+ return nil, zbxerr.ErrorUnsupportedMetric
+ }
+}
+
+func (p *Plugin) exportCount(params []string) (result interface{}, err error) {
+ cp, err := getCountParams(params)
+ if err != nil {
+ return
+ }
+
+ cp.length = len(strings.SplitAfter(cp.path, string(filepath.Separator)))
+
+ err = cp.setMinMax()
+ if err != nil {
+ return 0, zbxerr.ErrorInvalidParams.Wrap(err)
+ }
+
+ return cp.getDirCount()
+}
+
+func (p *Plugin) exportSize(params []string) (result interface{}, err error) {
+ sp, err := getSizeParams(params)
+ if err != nil {
+ return
+ }
+
+ sp.length = len(strings.SplitAfter(sp.path, string(filepath.Separator)))
+
+ return sp.getDirSize()
+}
+
+func (cp *common) skipPath(path string) (bool, error) {
+ currentLength := len(strings.SplitAfter(path, string(filepath.Separator)))
+ if cp.maxDepth > unlimitedDepth && currentLength-cp.length > cp.maxDepth {
+ return true, fs.SkipDir
+ }
+
+ return false, nil
+}
+
+func (cp *common) skipRegex(d fs.DirEntry) (bool, error) {
+ if cp.regInclude != nil && !cp.regInclude.Match([]byte(d.Name())) {
+ return true, nil
+ }
+
+ if cp.regExclude != nil && cp.regExclude.Match([]byte(d.Name())) {
+ return true, nil
+ }
+
+ if cp.dirRegExclude != nil && d.IsDir() && cp.dirRegExclude.Match([]byte(d.Name())) {
+ return true, fs.SkipDir
+ }
+
+ return false, nil
+}
+
+func parseReg(in string) (*regexp.Regexp, error) {
+ if in == "" {
+ return nil, nil
+ }
+
+ return regexp.Compile(in)
+}
+
+func init() {
+ plugin.RegisterMetrics(&impl, "VFSDir",
+ "vfs.dir.count", "Directory entry count.",
+ "vfs.dir.size", "All directory entry size.",
+ )
+}
diff --git a/src/go/plugins/vfs/dir/size.go b/src/go/plugins/vfs/dir/size.go
new file mode 100644
index 00000000000..2e69615f062
--- /dev/null
+++ b/src/go/plugins/vfs/dir/size.go
@@ -0,0 +1,220 @@
+/*
+** 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 dir
+
+import (
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "zabbix.com/pkg/zbxerr"
+)
+
+const (
+ diskBlockSize = 512
+)
+
+type sizeParams struct {
+ common
+ diskMode bool
+ disk string
+}
+
+func (sp *sizeParams) getDirSize() (int64, error) {
+ var parentSize int64
+ var dirSize int64
+
+ err := filepath.WalkDir(sp.path,
+ func(p string, d fs.DirEntry, err error) error {
+ if err != nil {
+ impl.Logger.Errf("failed to walk dir with path %s", p)
+ return nil
+ }
+
+ if p == sp.path {
+ parentSize, err = sp.handleHomeDir(p, d)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }
+
+ s, err := sp.skip(p, d)
+ if s {
+ return err
+ }
+
+ fi, err := d.Info()
+ if err != nil {
+ impl.Logger.Errf("failed to get file info for path %s, %s", p, err.Error())
+ return nil
+ }
+
+ tmpSize, err := sp.getSize(fi, p)
+ if err != nil {
+ return err
+ }
+
+ dirSize += tmpSize
+
+ return nil
+ })
+
+ if err != nil {
+ return 0, zbxerr.ErrorCannotParseResult.Wrap(err)
+ }
+
+ return parentSize + dirSize, nil
+}
+
+func (sp *sizeParams) getParentSize(dir fs.DirEntry) (int64, error) {
+ var parentSize int64
+
+ stat, err := os.Stat(sp.path)
+ if err != nil {
+ return 0, err
+ }
+
+ s, err := sp.skipRegex(dir)
+ if err != nil {
+ return 0, err
+ }
+
+ if s {
+ return 0, nil
+ }
+
+ parentSize, err = sp.getSize(stat, sp.path)
+ if err != nil {
+ return 0, err
+ }
+
+ return parentSize, nil
+}
+
+func (sp *sizeParams) skip(path string, d fs.DirEntry) (bool, error) {
+ s, err := sp.skipPath(path)
+ if s {
+ return true, err
+ }
+
+ s, err = sp.skipRegex(d)
+ if s {
+ return true, err
+ }
+
+ if skipDir(d) {
+ return true, nil
+ }
+
+ if sp.osSkip(path, d) {
+ return true, nil
+ }
+
+ return false, nil
+}
+
+func getSizeParams(params []string) (out sizeParams, err error) {
+ out.maxDepth = -1
+
+ switch len(params) {
+ case sixthParam:
+ out.dirRegExclude, err = parseReg(params[5])
+ if err != nil {
+ err = zbxerr.New("Invalid sixth parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case fifthParam:
+ if params[4] != "" {
+ out.maxDepth, err = strconv.Atoi(params[4])
+ if err != nil {
+ err = zbxerr.New("Invalid fifth parameter.").Wrap(err)
+
+ return
+ }
+
+ if out.maxDepth < unlimitedDepth {
+ err = zbxerr.New("Invalid fifth parameter.")
+
+ return
+ }
+ }
+
+ fallthrough
+ case fourthParam:
+ switch params[3] {
+ case "apparent", "":
+ case "disk":
+ out.diskMode = true
+ default:
+ err = zbxerr.New("Invalid fourth parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case thirdParam:
+ out.regExclude, err = parseReg(params[2])
+ if err != nil {
+ err = zbxerr.New("Invalid third parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case secondParam:
+ out.regInclude, err = parseReg(params[1])
+ if err != nil {
+ err = zbxerr.New("Invalid second parameter.").Wrap(err)
+
+ return
+ }
+
+ fallthrough
+ case firstParam:
+ out.path = params[0]
+ if out.path == "" {
+ err = zbxerr.New("Invalid first parameter.")
+
+ return
+ }
+
+ if !strings.HasSuffix(out.path, string(filepath.Separator)) {
+ out.path += string(filepath.Separator)
+ }
+
+ case emptyParam:
+ err = zbxerr.ErrorTooFewParameters
+
+ return
+ default:
+ err = zbxerr.ErrorTooManyParameters
+
+ return
+ }
+
+ return
+}
diff --git a/src/go/plugins/vfs/dir/size_nix.go b/src/go/plugins/vfs/dir/size_nix.go
new file mode 100644
index 00000000000..343fbb07a33
--- /dev/null
+++ b/src/go/plugins/vfs/dir/size_nix.go
@@ -0,0 +1,73 @@
+// +build !windows
+
+/*
+** 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 dir
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "syscall"
+)
+
+func (sp *sizeParams) getSize(fs fs.FileInfo, path string) (int64, error) {
+ sys, ok := fs.Sys().(*syscall.Stat_t)
+ if !ok {
+ return 0, fmt.Errorf("failed to read %s file size", fs.Name())
+ }
+
+ if !sp.diskMode {
+ return sys.Size, nil
+ }
+
+ return sys.Blocks * diskBlockSize, nil
+}
+
+func skipDir(d fs.DirEntry) bool {
+ return false
+}
+
+func (sp *sizeParams) handleHomeDir(path string, d fs.DirEntry) (int64, error) {
+ parentSize, err := sp.getParentSize(d)
+ if err != nil {
+ return 0, err
+ }
+
+ return parentSize, nil
+}
+
+func (cp *common) osSkip(path string, d fs.DirEntry) bool {
+ i, err := d.Info()
+ if err != nil {
+ impl.Logger.Errf("failed to get file info for path %s, %s", path, err.Error())
+ return true
+ }
+
+ for _, f := range cp.files {
+ if os.SameFile(f, i) {
+ return true
+ }
+ }
+
+ cp.files = append(cp.files, i)
+
+ return false
+}
diff --git a/src/go/plugins/vfs/dir/size_windows.go b/src/go/plugins/vfs/dir/size_windows.go
new file mode 100644
index 00000000000..84f12d6a0c3
--- /dev/null
+++ b/src/go/plugins/vfs/dir/size_windows.go
@@ -0,0 +1,108 @@
+/*
+** 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 dir
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "syscall"
+
+ "zabbix.com/pkg/win32"
+)
+
+func (sp *sizeParams) handleHomeDir(path string, d fs.DirEntry) (int64, error) {
+ if sp.diskMode {
+ _, len, err := win32.GetFullPathName(path)
+ if err != nil {
+ return 0, err
+ }
+
+ sp.disk, err = win32.GetVolumePathName(path, len)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ // home dir is not counter on windows
+ return 0, nil
+}
+
+func skipDir(d fs.DirEntry) bool {
+ if d.IsDir() {
+ return true
+ }
+
+ return false
+}
+
+func (sp *sizeParams) getSize(fs fs.FileInfo, path string) (int64, error) {
+ sys, ok := fs.Sys().(*syscall.Win32FileAttributeData)
+ if !ok {
+ return 0, fmt.Errorf("failed to read %s file size", fs.Name())
+ }
+
+ fileSize := uint64(sys.FileSizeHigh)<<32 | uint64(sys.FileSizeLow)
+
+ if sp.diskMode {
+ clusterSize, err := sp.getClusterSize(path)
+ if err != nil {
+ return 0, err
+ }
+
+ mod := fileSize % clusterSize
+ if mod != 0 {
+ fileSize += clusterSize - mod
+ }
+ }
+
+ return int64(fileSize), nil
+}
+
+func (sp *sizeParams) getClusterSize(path string) (uint64, error) {
+ clusters, err := win32.GetDiskFreeSpace(sp.disk)
+ if err != nil {
+ return 0, err
+ }
+
+ return uint64(clusters.LpSectorsPerCluster) * uint64(clusters.LpBytesPerSector), nil
+}
+
+func (cp *common) osSkip(path string, d fs.DirEntry) bool {
+ if d.Type() == fs.ModeSymlink {
+ return true
+ }
+
+ i, err := d.Info()
+ if err != nil {
+ impl.Logger.Errf("failed to get file info for path %s, %s", path, err.Error())
+ return true
+ }
+
+ for _, f := range cp.files {
+ if os.SameFile(f, i) {
+ return true
+ }
+ }
+
+ cp.files = append(cp.files, i)
+
+ return false
+}
diff --git a/src/go/plugins/zabbix/sync/sync_nix.go b/src/go/plugins/zabbix/sync/sync_nix.go
index 607096b7118..17192f049c1 100644
--- a/src/go/plugins/zabbix/sync/sync_nix.go
+++ b/src/go/plugins/zabbix/sync/sync_nix.go
@@ -1,3 +1,4 @@
+//go:build !windows
// +build !windows
/*
@@ -25,8 +26,6 @@ func getMetrics() []string {
return []string{
"net.dns", "Checks if DNS service is up.",
"net.dns.record", "Performs DNS query.",
- "vfs.dir.count", "Directory entry count.",
"vfs.dir.get", "Directory entry list.",
- "vfs.dir.size", "Directory size (in bytes).",
}
}
diff --git a/src/go/plugins/zabbix/sync/sync_windows.go b/src/go/plugins/zabbix/sync/sync_windows.go
index f1848a797d7..62701cfb8df 100644
--- a/src/go/plugins/zabbix/sync/sync_windows.go
+++ b/src/go/plugins/zabbix/sync/sync_windows.go
@@ -23,8 +23,6 @@ func getMetrics() []string {
return []string{
"net.dns", "Checks if DNS service is up.",
"net.dns.record", "Performs DNS query.",
- "vfs.dir.count", "Directory entry count.",
"vfs.dir.get", "Directory entry list.",
- "vfs.dir.size", "Directory size (in bytes).",
}
}