package storage import ( "bufio" "fmt" "strconv" "strings" "github.com/edgebox-iot/edgeboxctl/internal/utils" "github.com/shirou/gopsutil/disk" ) // Device : Struct representing a storage device in the system type Device struct { ID string `json:"id"` Name string `json:"name"` Size string `json:"size"` InUse bool `json:"in_use"` MainDevice bool `json:"main_device"` MAJ string `json:"maj"` MIN string `json:"min"` RM string `json:"rm"` RO string `json:"ro"` Partitions []Partition `json:"partitions"` Status DeviceStatus `json:"status"` } // DeviceStatus : Struct representing possible storage device statuses (code + description) type DeviceStatus struct { ID int `json:"id"` Description string `json:"description"` } // MaybeDevice : Boolean flag for validation of device existance type MaybeDevice struct { Device Device `json:"device"` Valid bool `json:"valid"` } type UsageStat struct { Total string `json:"total"` Used string `json:"used"` Free string `json:"free"` Percent string `json:"percent"` } // Partition : Struct representing a partition / filesystem (Empty Mountpoint means it is not mounted) type Partition struct { ID string `json:"id"` Size string `json:"size"` MAJ string `json:"maj"` MIN string `json:"min"` RM string `json:"rm"` RO string `json:"ro"` Filesystem string `json:"filesystem"` Mountpoint string `json:"mountpoint"` UsageStat UsageStat `json:"usage_stat"` } const mainDiskID = "mmcblk0" // GetDevices : Returns a list of all available sotrage devices in structs filled with information func GetDevices() []Device { var devices []Device cmdArgs := []string{"--raw", "--noheadings"} cmdOutput := utils.Exec("lsblk", cmdArgs) cmdOutputReader := strings.NewReader(cmdOutput) scanner := bufio.NewScanner(cmdOutputReader) scanner.Split(bufio.ScanLines) var currentDevice Device var currentPartitions []Partition firstDevice := true currentDeviceInUseFlag := false for scanner.Scan() { // 1 Device is represented here. Extract words in order for filling a Device struct // Example deviceRawInfo: "mmcblk0 179:0 0 29.7G 0 disk" deviceRawInfo := strings.Fields(scanner.Text()) majMin := strings.SplitN(deviceRawInfo[1], ":", 2) isDevice := true if deviceRawInfo[5] == "part" { isDevice = false } if isDevice { // Clean up on the latest device being prepared. Append all partitions found and delete the currentPartitions list afterwards. // The first device found should not run the cleanup lines below if !firstDevice { currentDevice.Partitions = currentPartitions if !currentDeviceInUseFlag { currentDevice.Status.ID = 0 currentDevice.Status.Description = "not configured" } currentDevice.InUse = currentDeviceInUseFlag currentDeviceInUseFlag = false currentPartitions = []Partition{} devices = append(devices, currentDevice) } else { firstDevice = false } mainDevice := false device := Device{ ID: deviceRawInfo[0], Name: deviceRawInfo[0], Size: deviceRawInfo[3], MainDevice: mainDevice, MAJ: majMin[0], MIN: majMin[1], RM: deviceRawInfo[2], RO: deviceRawInfo[4], Status: DeviceStatus{ID: 1, Description: "healthy"}, } if device.ID == mainDiskID { device.MainDevice = true } currentDevice = device } else { mountpoint := "" if len(deviceRawInfo) >= 7 { mountpoint = deviceRawInfo[6] currentDeviceInUseFlag = true } // It is a partition, part of the last device read. partition := Partition{ ID: deviceRawInfo[0], Size: deviceRawInfo[3], MAJ: majMin[0], MIN: majMin[1], RM: deviceRawInfo[2], RO: deviceRawInfo[4], Filesystem: "", Mountpoint: mountpoint, } currentPartitions = append(currentPartitions, partition) } } currentDevice.Partitions = currentPartitions if !currentDeviceInUseFlag { currentDevice.Status.ID = 0 currentDevice.Status.Description = "Not configured" } currentDevice.InUse = currentDeviceInUseFlag devices = append([]Device{currentDevice}, devices...) // Prepending the first device... getDevicesSpaceUsage(devices) return devices } func getDevicesSpaceUsage(devices []Device) []Device { for deviceIndex, device := range devices { if device.InUse { for partitionIndex, partition := range device.Partitions { s, _ := disk.Usage(partition.Mountpoint) if s.Total == 0 { continue } partitionUsagePercent := fmt.Sprintf("%2.f%%", s.UsedPercent) devices[deviceIndex].Partitions[partitionIndex].UsageStat = UsageStat{Total: strconv.FormatUint(s.Total, 10), Used: strconv.FormatUint(s.Used, 10), Free: strconv.FormatUint(s.Free, 10), Percent: partitionUsagePercent} } } fmt.Println(device) } return devices }