git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/sysinfo/storage.go (about)

     1  // Copyright © 2016 Zlatko Čalušić
     2  //
     3  // Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
     4  
     5  package sysinfo
     6  
     7  import (
     8  	"bufio"
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"strconv"
    13  	"strings"
    14  )
    15  
    16  // StorageDevice information.
    17  type StorageDevice struct {
    18  	Name   string `json:"name,omitempty"`
    19  	Driver string `json:"driver,omitempty"`
    20  	Vendor string `json:"vendor,omitempty"`
    21  	Model  string `json:"model,omitempty"`
    22  	Serial string `json:"serial,omitempty"`
    23  	Size   uint   `json:"size,omitempty"` // device size in GB
    24  }
    25  
    26  func getSerial(name, fullpath string) (serial string) {
    27  	var f *os.File
    28  	var err error
    29  
    30  	// Modern location/format of the udev database.
    31  	if dev := slurpFile(path.Join(fullpath, "dev")); dev != "" {
    32  		if f, err = os.Open(path.Join("/run/udev/data", "b"+dev)); err == nil {
    33  			goto scan
    34  		}
    35  	}
    36  
    37  	// Legacy location/format of the udev database.
    38  	if f, err = os.Open(path.Join("/dev/.udev/db", "block:"+name)); err == nil {
    39  		goto scan
    40  	}
    41  
    42  	// No serial :(
    43  	return
    44  
    45  scan:
    46  	defer f.Close()
    47  
    48  	s := bufio.NewScanner(f)
    49  	for s.Scan() {
    50  		if sl := strings.Split(s.Text(), "="); len(sl) == 2 {
    51  			if sl[0] == "E:ID_SERIAL_SHORT" {
    52  				serial = sl[1]
    53  				break
    54  			}
    55  		}
    56  	}
    57  
    58  	return
    59  }
    60  
    61  func (si *SysInfo) getStorageInfo() {
    62  	sysBlock := "/sys/block"
    63  	devices, err := ioutil.ReadDir(sysBlock)
    64  	if err != nil {
    65  		return
    66  	}
    67  
    68  	si.Storage = make([]StorageDevice, 0)
    69  	for _, link := range devices {
    70  		fullpath := path.Join(sysBlock, link.Name())
    71  		dev, err := os.Readlink(fullpath)
    72  		if err != nil {
    73  			continue
    74  		}
    75  
    76  		if strings.HasPrefix(dev, "../devices/virtual/") {
    77  			continue
    78  		}
    79  
    80  		// We could filter all removable devices here, but some systems boot from USB flash disks, and then we
    81  		// would filter them, too. So, let's filter only floppies and CD/DVD devices, and see how it pans out.
    82  		if strings.HasPrefix(dev, "../devices/platform/floppy") || slurpFile(path.Join(fullpath, "device", "type")) == "5" {
    83  			continue
    84  		}
    85  
    86  		device := StorageDevice{
    87  			Name:   link.Name(),
    88  			Model:  slurpFile(path.Join(fullpath, "device", "model")),
    89  			Serial: getSerial(link.Name(), fullpath),
    90  		}
    91  
    92  		if driver, err := os.Readlink(path.Join(fullpath, "device", "driver")); err == nil {
    93  			device.Driver = path.Base(driver)
    94  		}
    95  
    96  		if vendor := slurpFile(path.Join(fullpath, "device", "vendor")); !strings.HasPrefix(vendor, "0x") {
    97  			device.Vendor = vendor
    98  		}
    99  
   100  		size, _ := strconv.ParseUint(slurpFile(path.Join(fullpath, "size")), 10, 64)
   101  		device.Size = uint(size) / 1953125 // GiB
   102  
   103  		si.Storage = append(si.Storage, device)
   104  	}
   105  }