Detect interfaces per platform; add macOS and single-interface support (closes #2)
Linux runs exactly as before when both bridge interfaces exist. When they do not, the lone default-route interface is monitored, and a single interface now draws a single UI pane instead of an empty second one. macOS is newly supported: a running VPN tunnel (utun) is monitored as the primary pane alongside the physical default-route interface, or the physical interface alone when no VPN is up. Detection lives in internal/netdetect: interface/route data types, pure selection logic keyed on OS name, and route parsers, all unit-tested on Linux for both platforms. Only the real route query and the per-platform TCP dial binding (source address on Linux, IP_BOUND_IF on macOS) are build-tagged. Ping argument construction is a pure, OS-keyed function. NewMonitor now takes a list of interfaces. Model: opus-4-8
This commit is contained in:
+47
-6
@@ -1,18 +1,17 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"git.eeqj.de/sneak/rtnetmon/internal/monitor"
|
||||
"git.eeqj.de/sneak/rtnetmon/internal/netdetect"
|
||||
)
|
||||
|
||||
// Config holds the application configuration
|
||||
@@ -73,12 +72,17 @@ func init() {
|
||||
_ = viper.BindPFlag("logfile", rootCmd.Flags().Lookup("logfile"))
|
||||
}
|
||||
|
||||
func runMonitor(cmd *cobra.Command, args []string) error {
|
||||
func runMonitor(cmd *cobra.Command, _ []string) error {
|
||||
monitor.Logf(cfg.LogFile, "Starting rtnetmon")
|
||||
monitor.Logf(cfg.LogFile, "Monitoring interfaces %s and %s", cfg.IfaceA, cfg.IfaceB)
|
||||
|
||||
// Detect which interfaces to monitor for this platform.
|
||||
specs, err := detectInterfaces(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the monitor
|
||||
mon := monitor.NewMonitor(cfg.IfaceA, cfg.LabelA, cfg.IfaceB, cfg.LabelB, cfg.LogFile)
|
||||
mon := monitor.NewMonitor(specs, cfg.LogFile)
|
||||
|
||||
// Add reachability hosts
|
||||
for _, host := range cfg.Hosts {
|
||||
@@ -112,6 +116,43 @@ func runMonitor(cmd *cobra.Command, args []string) error {
|
||||
return mon.Run(ctx)
|
||||
}
|
||||
|
||||
// detectInterfaces enumerates the host, reads its default routes, and applies
|
||||
// the platform selection rules to decide which interfaces to monitor.
|
||||
func detectInterfaces(cmd *cobra.Command) ([]monitor.IfaceSpec, error) {
|
||||
ifaces, err := netdetect.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routes, err := netdetect.DefaultRoutes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flags := netdetect.Flags{
|
||||
IfaceA: cfg.IfaceA,
|
||||
LabelA: cfg.LabelA,
|
||||
IfaceB: cfg.IfaceB,
|
||||
LabelB: cfg.LabelB,
|
||||
LabelASet: cmd.Flags().Changed("labelA"),
|
||||
LabelBSet: cmd.Flags().Changed("labelB"),
|
||||
}
|
||||
|
||||
panes, err := netdetect.Select(runtime.GOOS, ifaces, routes, flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
specs := make([]monitor.IfaceSpec, len(panes))
|
||||
for i, p := range panes {
|
||||
specs[i] = monitor.IfaceSpec{Name: p.Name, Label: p.Label}
|
||||
}
|
||||
|
||||
monitor.Logf(cfg.LogFile, "Monitoring %d interface(s)", len(specs))
|
||||
|
||||
return specs, nil
|
||||
}
|
||||
|
||||
// Execute runs the root command
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
|
||||
Reference in New Issue
Block a user