mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-10-12 01:58:46 +02:00
feat(cli): port the log journal and network journal events to master
This commit is contained in:
1 parent
488065f6e8
commit
37b835c791
109 files changed
+17258
-905
No files matched your search
@@ -2,8 +2,38 @@ package cli
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The kernel gives an Ethernet-like link the type ARPHRD_ETHER.
|
||||
const arphrdEther = 1
|
||||
|
||||
// Tests point the readers at a temporary directory.
|
||||
var (
|
||||
sysfsNetRoot = "/sys/class/net"
|
||||
sysfsVirtualNetRoot = "/sys/devices/virtual/net"
|
||||
)
|
||||
|
||||
// noiseInterfacePrefixes name the interfaces of a container host. They change
|
||||
// every few seconds, and no traffic of the host goes through them.
|
||||
var noiseInterfacePrefixes = []string{"docker", "veth", "virbr", "br-"}
|
||||
|
||||
// usbTetherDrivers name the kernel modules of a phone that shares its data
|
||||
// connection over the cable.
|
||||
var usbTetherDrivers = []string{"ipheth", "rndis_host"}
|
||||
|
||||
// sysfsInfo holds what sysfs knows about one interface.
|
||||
type sysfsInfo struct {
|
||||
Type int
|
||||
DevType string
|
||||
Driver string
|
||||
Wireless bool
|
||||
}
|
||||
|
||||
// patchNetIfaceName patches network interface names on Linux
|
||||
// This is a no-op on Linux as interface names don't need special handling
|
||||
func patchNetIfaceName(iface *net.Interface) (bool, error) { return true, nil }
|
||||
@@ -15,3 +45,118 @@ func validInterface(iface *net.Interface, validIfacesMap map[string]struct{}) bo
|
||||
_, ok := validIfacesMap[iface.Name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// sysfsVirtualInterfaces returns the virtual interfaces of the host. The
|
||||
// nameserver code has its own reader of the fixed path; this one reads the
|
||||
// root that a test can point elsewhere.
|
||||
func sysfsVirtualInterfaces() map[string]struct{} {
|
||||
s := make(map[string]struct{})
|
||||
entries, _ := os.ReadDir(sysfsVirtualNetRoot)
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
s[strings.TrimSpace(entry.Name())] = struct{}{}
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// sysfsInterfaceInfo reads the sysfs entry of an interface. A missing file
|
||||
// leaves its field empty.
|
||||
func sysfsInterfaceInfo(name string) sysfsInfo {
|
||||
dir := filepath.Join(sysfsNetRoot, name)
|
||||
info := sysfsInfo{DevType: sysfsUeventValue(dir, "DEVTYPE"), Driver: sysfsDriver(dir)}
|
||||
info.Type, _ = strconv.Atoi(readSysfsAttribute(dir, "type"))
|
||||
if entry, err := os.Stat(filepath.Join(dir, "wireless")); err == nil && entry.IsDir() {
|
||||
info.Wireless = true
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// readSysfsAttribute reads one attribute without its trailing newline.
|
||||
func readSysfsAttribute(dir, file string) string {
|
||||
b, err := os.ReadFile(filepath.Join(dir, file))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(b))
|
||||
}
|
||||
|
||||
// sysfsUeventValue reads one entry of the uevent file.
|
||||
func sysfsUeventValue(dir, key string) string {
|
||||
for _, line := range strings.Split(readSysfsAttribute(dir, "uevent"), "\n") {
|
||||
after, ok := strings.CutPrefix(strings.TrimSpace(line), key+"=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
return strings.TrimSpace(after)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// sysfsDriver names the kernel module behind an interface. The driver link of
|
||||
// the device points at the module, and the uevent of the device names it too.
|
||||
func sysfsDriver(dir string) string {
|
||||
device := filepath.Join(dir, "device")
|
||||
if target, err := os.Readlink(filepath.Join(device, "driver")); err == nil {
|
||||
return filepath.Base(target)
|
||||
}
|
||||
if driver := sysfsUeventValue(device, "DRIVER"); driver != "" {
|
||||
return driver
|
||||
}
|
||||
return sysfsUeventValue(dir, "DRIVER")
|
||||
}
|
||||
|
||||
// sysfsHardwarePort names the link in the words that the link type mapping
|
||||
// reads. The port names of macOS use the same words.
|
||||
func sysfsHardwarePort(info sysfsInfo) string {
|
||||
switch {
|
||||
case info.Wireless:
|
||||
return "Wi-Fi"
|
||||
case slices.Contains(usbTetherDrivers, info.Driver):
|
||||
return hardwarePortIPhoneUSB
|
||||
case info.DevType != "":
|
||||
return info.DevType
|
||||
case info.Type == arphrdEther:
|
||||
return "Ethernet"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// sysfsClass names the kind of an interface. The name rule comes before the
|
||||
// device type, so an interface that went away keeps its class. A bridge, a
|
||||
// bond, and a VLAN carry the traffic of the host, so they stay hardware.
|
||||
func sysfsClass(name string, info sysfsInfo) string {
|
||||
switch {
|
||||
case info.DevType == "tun" || info.DevType == "tap":
|
||||
return "tunnel"
|
||||
case hasInterfacePrefix(name, noiseInterfacePrefixes...):
|
||||
return "virtual"
|
||||
case info.Wireless || info.Type == arphrdEther:
|
||||
return "hardware"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// platformInterfaceMeta returns the Linux class and link name of an interface.
|
||||
// Linux has no service name.
|
||||
func platformInterfaceMeta(name string) (class, hardwarePort, service string) {
|
||||
info := sysfsInterfaceInfo(name)
|
||||
return sysfsClass(name, info), sysfsHardwarePort(info), ""
|
||||
}
|
||||
|
||||
// platformVirtualInterfaces returns the container interfaces of the host. The
|
||||
// kernel lists the uplink bridges and the bonds beside them, and those carry
|
||||
// the traffic of the host.
|
||||
func platformVirtualInterfaces() map[string]struct{} {
|
||||
noise := make(map[string]struct{})
|
||||
for name := range sysfsVirtualInterfaces() {
|
||||
if hasInterfacePrefix(name, noiseInterfacePrefixes...) {
|
||||
noise[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
return noise
|
||||
}
|
||||
|
||||
// refreshInterfaceMeta does no work. Each sysfs read answers from the kernel
|
||||
// of this moment, so there is no cache to refresh.
|
||||
func refreshInterfaceMeta() {}
|
||||
Reference in new issue
Block a user