/* ** Copyright (C) 2001-2025 Zabbix SIA ** ** This program is free software: you can redistribute it and/or modify it under the terms of ** the GNU Affero General Public License as published by the Free Software Foundation, version 3. ** ** 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 Affero General Public License for more details. ** ** You should have received a copy of the GNU Affero General Public License along with this program. ** If not, see <https://www.gnu.org/licenses/>. **/ package vfsfs import ( "syscall" "golang.org/x/sys/windows" "golang.zabbix.com/sdk/errs" "golang.zabbix.com/sdk/plugin" ) func init() { err := plugin.RegisterMetrics( &impl, "VfsFs", "vfs.fs.discovery", "List of mounted filesystems. Used for low-level discovery.", "vfs.fs.get", "List of mounted filesystems with statistics.", "vfs.fs.size", "Disk space in bytes or in percentage from total.", ) if err != nil { panic(errs.Wrap(err, "failed to register metrics")) } } func getMountPaths() (paths []string, err error) { buffer := make([]uint16, windows.MAX_PATH+1) volume := make([]uint16, windows.MAX_PATH+1) var h windows.Handle if h, err = windows.FindFirstVolume(&volume[0], uint32(len(volume))); err != nil { return } defer windows.FindVolumeClose(h) var result []string var size uint32 for { for { if err = windows.GetVolumePathNamesForVolumeName(&volume[0], &buffer[0], uint32(len(buffer)), &size); err != nil { if err.(syscall.Errno) != syscall.ERROR_MORE_DATA { err = errs.Wrapf(err, "Cannot obtain a list of filesystems. Volume: %s Error", windows.UTF16ToString(volume)) return } buffer = make([]uint16, size) } else { break } } buf := buffer for buf[0] != 0 { result = append(result, windows.UTF16ToString(buf)) for i, c := range buf { if c == 0 { buf = buf[i+1:] break } } } if err = windows.FindNextVolume(h, &volume[0], uint32(len(volume))); err != nil { if err.(syscall.Errno) == syscall.ERROR_NO_MORE_FILES { break } return } } return result, nil } func getFsInfo(path string) (fsname, fstype, drivetype, drivelabel string, err error) { fsname = path if len(fsname) > 0 && fsname[len(fsname)-1] == '\\' { fsname = fsname[:len(fsname)-1] } if len(path) >= windows.MAX_PATH && path[:4] != `\\?\` { path = `\\?\` + path } wpath := windows.StringToUTF16Ptr(path) bufType := make([]uint16, windows.MAX_PATH+1) bufLabel := make([]uint16, windows.MAX_PATH+1) if err = windows.GetVolumeInformation(wpath, &bufLabel[0], uint32(len(bufLabel)), nil, nil, nil, &bufType[0], uint32(len(bufType))); err != nil { fstype = "UNKNOWN" } else { fstype = windows.UTF16ToString(bufType) drivelabel = windows.UTF16ToString(bufLabel) } dt := windows.GetDriveType(wpath) switch dt { case windows.DRIVE_UNKNOWN: drivetype = "unknown" case windows.DRIVE_NO_ROOT_DIR: drivetype = "norootdir" case windows.DRIVE_REMOVABLE: drivetype = "removable" case windows.DRIVE_FIXED: drivetype = "fixed" case windows.DRIVE_REMOTE: drivetype = "remote" case windows.DRIVE_CDROM: drivetype = "cdrom" case windows.DRIVE_RAMDISK: drivetype = "ramdisk" default: drivetype = "unknown" } return } func getFsStats(path string) (stats *FsStats, err error) { var callerFree, total uint64 err = windows.GetDiskFreeSpaceEx(windows.StringToUTF16Ptr(path), &callerFree, &total, nil) if err != nil { return } totalUsed := total - callerFree stats = &FsStats{ Total: total, Free: callerFree, Used: totalUsed, } if total != 0 { stats.PFree = float64(callerFree) * 100.0 / float64(total) stats.PUsed = float64(totalUsed) * 100.0 / float64(total) } return } func (p *Plugin) getFsInfo() (data []*FsInfo, err error) { var paths []string if paths, err = getMountPaths(); err != nil { return } for _, path := range paths { if fsname, fstype, drivetype, drivelabel, fserr := getFsInfo(path); fserr == nil { data = append(data, &FsInfo{ FsName: &fsname, FsType: &fstype, DriveType: &drivetype, DriveLabel: &drivelabel, }) } else { p.Debugf(`cannot obtain file system information for "%s": %s`, path, fserr) } } return } func (p *Plugin) getFsInfoStats() (data []*FsInfoNew, err error) { var paths []string if paths, err = getMountPaths(); err != nil { return } fsmap := make(map[string]*FsInfoNew) for _, path := range paths { var info FsInfoNew if fsname, fstype, drivetype, drivelabel, fserr := getFsInfo(path); fserr == nil { info.FsName = &fsname info.FsType = &fstype info.DriveType = &drivetype info.DriveLabel = &drivelabel } else { p.Debugf(`cannot obtain file system information for "%s": %s`, path, fserr) continue } if stats, fserr := getFsStats(path); err == nil { info.Bytes = stats fsmap[path] = &info } else { p.Debugf(`cannot obtain file system statistics for "%s": %s`, path, fserr) continue } } if paths, err = getMountPaths(); err != nil { return } for _, path := range paths { if info, ok := fsmap[path]; ok { data = append(data, info) } } return } func getFsInode(string) (*FsStats, error) { return nil, plugin.UnsupportedMetricError }