net-predictable/loc.go

155 lines
3.4 KiB
Go

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
}