From f03124bf8618adfc75dd87ff7d95df29fd309e42 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Fri, 1 Jun 2018 13:57:14 +0200 Subject: [PATCH] Read disk model and serial from udev data or sysfs on Linux It reads model and serial data directly from files instead of using `udevadm` command. This way obtaining the disk serial number doesn't depend on command execution, and can be also possible even if udev or udevadm are not available. --- disk/disk_linux.go | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 4033b1ac..2e861471 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -3,10 +3,11 @@ package disk import ( + "bufio" + "bytes" "context" "fmt" "io/ioutil" - "os/exec" "path/filepath" "strconv" "strings" @@ -385,26 +386,33 @@ func GetDiskSerialNumber(name string) string { } func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { - n := fmt.Sprintf("--name=%s", name) - udevadm, err := exec.LookPath("/sbin/udevadm") + var stat unix.Stat_t + err := unix.Stat(name, &stat) if err != nil { return "" } + major := unix.Major(stat.Rdev) + minor := unix.Minor(stat.Rdev) - out, err := invoke.CommandWithContext(ctx, udevadm, "info", "--query=property", n) - - // does not return error, just an empty string - if err != nil { - return "" - } - lines := strings.Split(string(out), "\n") - for _, line := range lines { - values := strings.Split(line, "=") - if len(values) < 2 || values[0] != "ID_SERIAL" { - // only get ID_SERIAL, not ID_SERIAL_SHORT - continue + // Try to get the serial from udev data + udevDataPath := fmt.Sprintf("/run/udev/data/b%d:%d", major, minor) + if udevdata, err := ioutil.ReadFile(udevDataPath); err == nil { + scanner := bufio.NewScanner(bytes.NewReader(udevdata)) + for scanner.Scan() { + values := strings.Split(scanner.Text(), "=") + if len(values) == 2 && values[0] == "E:ID_SERIAL" { + return values[1] + } } - return values[1] + } + + // Try to get the serial from sysfs, look at the disk device (minor 0) directly + // because if it is a partition it is not going to contain any device information + devicePath := fmt.Sprintf("/sys/dev/block/%d:0/device", major) + model, _ := ioutil.ReadFile(filepath.Join(devicePath, "model")) + serial, _ := ioutil.ReadFile(filepath.Join(devicePath, "serial")) + if len(model) > 0 && len(serial) > 0 { + return fmt.Sprintf("%s_%s", string(model), string(serial)) } return "" }