package main import ( "errors" "fmt" "io/fs" "os" "path/filepath" "strings" ) const ( PathProcPCI = iota PathProcUSB ) func (i *iface) ProcLoc() (err error) { spath := strings.Join([]string{i.netpath, "device"}, "/") var real string if real, err = filepath.EvalSymlinks(spath); err != nil { if errors.Is(err, os.ErrNotExist) { err = fmt.Errorf("Interface %s isn't a hardware device", i.names[IfNameKern]) return } err = fmt.Errorf("Failed to resolve symlink to device: %w", err) return } var devpath string var ok bool sysdev := strings.Join([]string{SYSFS, "devices/"}, "/") if devpath, ok = strings.CutPrefix(real, sysdev); !ok { err = fmt.Errorf("Symlink doesn't point to %s\n", sysdev) return } var ( pci, usb string procmode int path = strings.Split(devpath, "/") cdir = strings.Join([]string{ SYSFS, "devices", }, "/") ) for i, v := range path { if i >= 1 { // Append to currend directory cdir = strings.Join([]string{cdir, path[i-1]}, "/") } if strings.HasPrefix(v, "pci") { procmode = PathProcPCI continue } else if strings.HasPrefix(v, "usb") { procmode = PathProcUSB continue } switch procmode { case PathProcPCI: { var ( domain, bus, device, function int ) if _, err = fmt.Sscanf(v, "%x:%x:%x.%x", &domain, &bus, &device, &function); err != nil { err = ErrInvalidPCIFormat continue } var name strings.Builder if domain != 0 { // Hey, if you're reading this you're cool. // Do you know any machine which has a PCI domain other than 0? // Mail me at caskd@redxen.eu about it. name.WriteString(fmt.Sprintf("P%d", domain)) } name.WriteString(fmt.Sprintf("p%ds%d", bus, device)) // List PCI path to see if there's any other functions available under the same device var matches []string dir := os.DirFS(cdir) glob := fmt.Sprintf("%04x:%02x:%02x.?", domain, bus, device) matches, _ = fs.Glob(dir, glob) if len(matches) > 1 { // Multi-function device name.WriteString(fmt.Sprintf("f%d", function)) } pci = name.String() } case PathProcUSB: { var ( bus, conf, iface int port_path []int work string ) res := strings.Split(v, ":") if len(res) != 2 { err = ErrInvalidUSBFormat continue } // Bus and ports if _, err = fmt.Sscanf(res[0], "%d-%s", &bus, &work); err != nil { err = ErrInvalidUSBFormat continue } for _, v := range strings.Split(work, ".") { var cp int if _, err = fmt.Sscanf(v, "%d", &cp); err != nil { err = ErrInvalidUSBFormat continue } port_path = append(port_path, cp) } // Configuration and interface if _, err = fmt.Sscanf(res[1], "%d.%d", &conf, &iface); err != nil { err = ErrInvalidUSBFormat continue } var name strings.Builder for _, v := range port_path { name.WriteString(fmt.Sprintf("u%d", v)) } if conf != 1 { name.WriteString(fmt.Sprintf("c%d", conf)) } if iface != 0 { name.WriteString(fmt.Sprintf("i%d", iface)) } usb = name.String() } } } // Discard path errors if something was found if pci != "" && errors.Is(err, ErrInvalidPCIFormat) { err = nil } if usb != "" && errors.Is(err, ErrInvalidUSBFormat) { err = nil } i.names[IfNameLoc] = strings.Join([]string{i.pfx, pci, usb}, "") return }