summaryrefslogtreecommitdiff
path: root/doDrives.go
blob: 6ded27a8b17a0a466a9e55506964c3dcdaa060ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package main

import (
	"bufio"
	"io/fs"
	"os"
	"path/filepath"
	"strings"

	"go.wit.com/log"
)

func doDrives() {
	parts, err := parseProcPartitions()
	if err != nil {
		panic(err)
	}

	for _, p := range parts {
		log.Printf("RAW Device: /dev/%s\tSize: %d KiB\n", p.Name, p.Blocks)
		devname := "/dev/" + p.Name
		if blkpb := me.pb.FindByName(devname); blkpb != nil {
			blkpb.SizeBytes = uint64(p.Blocks) * 1024
		}
	}

	blockDir := "/sys/block"
	mounts := getMountedDevices()

	log.Info("doDrives()", mounts)

	err = filepath.WalkDir(blockDir, func(path string, d fs.DirEntry, err error) error {
		if path == blockDir {
			return nil // skip root
		}
		if d.IsDir() && filepath.Dir(path) == blockDir {
			dev := d.Name()
			devPath := "/dev/" + dev
			log.Printf("Drive: %s\n", devPath)

			// Look for partitions
			partitions, err := filepath.Glob(filepath.Join(blockDir, dev, dev+"*"))
			if err != nil {
				return err
			}

			hasPartition := false
			used := false
			for _, part := range partitions {
				partName := filepath.Base(part)
				partDev := "/dev/" + partName
				if partName == dev {
					log.Printf("  SKIP MAIN Partition: %s\n", partDev)
					continue // skip the main device
				}
				hasPartition = true
				isMounted := mounts[partDev]
				log.Printf("  Partition: %s [%v]\n", partDev, isMounted)
				if isMounted {
					used = true
				}
			}

			if !hasPartition {
				log.Println("  No partitions found.")
			}

			if !used {
				log.Println("  * Drive appears to be unused.")
			}
		}
		return nil
	})

	if err != nil {
		panic(err)
	}
}

// getMountedDevices reads /proc/self/mountinfo and returns a map of mounted device paths
func getMountedDevices() map[string]bool {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	mounted := make(map[string]bool)
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		fields := strings.Fields(line)
		if len(fields) < 10 {
			continue
		}
		// Device path is after the last '-' token
		for i, field := range fields {
			if field == "-" && i+2 < len(fields) {
				dev := fields[i+2]
				if strings.HasPrefix(dev, "/dev/") {
					mounted[dev] = true
				}
				break
			}
		}
	}
	return mounted
}

type Partition struct {
	Major  int
	Minor  int
	Blocks int64
	Name   string
}

func parseProcPartitions() ([]Partition, error) {
	f, err := os.Open("/proc/partitions")
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var partitions []Partition
	scanner := bufio.NewScanner(f)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "major") || line == "" {
			continue // Skip header and empty lines
		}

		var p Partition
		_, err := log.Sscanf(line, "%d %d %d %s", &p.Major, &p.Minor, &p.Blocks, &p.Name)
		if err != nil {
			return nil, log.Errorf("error parsing line: %q: %w", line, err)
		}
		partitions = append(partitions, p)
	}

	return partitions, scanner.Err()
}