package collector import ( "os/exec" "strconv" "strings" "syscall" ) type DiskInfo struct { Filesystem string `json:"filesystem"` TotalBytes int64 `json:"total_bytes"` UsedBytes int64 `json:"used_bytes"` FreeBytes int64 `json:"free_bytes"` MountPoint string `json:"mount_point"` } func CollectDisks() []DiskInfo { var disks []DiskInfo // df -T zur Anzeige aller mounted filesystems output, err := exec.Command("/usr/bin/df", "-T", "-P").CombinedOutput() if err != nil { return disks } lines := strings.Split(string(output), "\n") if len(lines) < 2 { return disks } // Erste Zeile ist Header, überspringen for _, line := range lines[1:] { line = strings.TrimSpace(line) if line == "" { continue } fields := strings.Fields(line) if len(fields) < 7 { continue } filesystem := fields[0] fstype := fields[1] totalKB := fields[2] usedKB := fields[3] availKB := fields[4] mountpoint := fields[6] // Überspringe spezielle Filesysteme if shouldSkipFilesystem(filesystem, fstype, mountpoint) { continue } // Konvertiere kB zu Bytes total, _ := strconv.ParseInt(totalKB, 10, 64) used, _ := strconv.ParseInt(usedKB, 10, 64) avail, _ := strconv.ParseInt(availKB, 10, 64) // Für bessere Genauigkeit: Versuche statvfs syscall if stat := getStatvfs(mountpoint); stat != nil { total = int64(stat.Blocks) * int64(stat.Bsize) avail = int64(stat.Bavail) * int64(stat.Bsize) used = total - avail } else { // Fallback: df-Werte in Bytes konvertieren total *= 1024 used *= 1024 avail *= 1024 } disks = append(disks, DiskInfo{ Filesystem: filesystem, TotalBytes: total, UsedBytes: used, FreeBytes: avail, MountPoint: mountpoint, }) } return disks } // Bestimmt ob ein Filesystem übersprungen werden soll func shouldSkipFilesystem(filesystem, fstype, mountpoint string) bool { // Überspringe virtuelle Filesysteme skipTypes := []string{ "proc", "sysfs", "devfs", "devtmpfs", "tmpfs", "debugfs", "securityfs", "cgroup", "cgroup2", "pstore", "bpf", "tracefs", "configfs", "fusectl", "binfmt_misc", "autofs", "mqueue", "hugetlbfs", "sunrpc", } for _, skipType := range skipTypes { if fstype == skipType { return true } } // Überspringe spezielle Mount-Points skipMounts := []string{ "/dev", "/proc", "/sys", "/run", "/tmp", "/dev/shm", "/run/lock", "/run/user", } for _, skipMount := range skipMounts { if strings.HasPrefix(mountpoint, skipMount) { return true } } // Überspringe Loop-Devices außer root if strings.HasPrefix(filesystem, "/dev/loop") && mountpoint != "/" { return true } // Überspringe Bind-Mounts (heuristic) if strings.Contains(mountpoint, "/snap/") { return true } return false } // Holt präzise Filesystem-Statistiken via statvfs syscall func getStatvfs(path string) *syscall.Statfs_t { var stat syscall.Statfs_t if err := syscall.Statfs(path, &stat); err != nil { return nil } return &stat }