Reticulum-Go/pkg/cli/debug.go

122 lines
3.5 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2024-2026 Quad4.io
package cli
import (
"encoding/json"
"flag"
"fmt"
"runtime"
"time"
"quad4/reticulum-go/pkg/debug"
"quad4/reticulum-go/pkg/rnsutil"
)
// RunDebug prints effective runtime diagnostics via shared-instance RPC.
func RunDebug(args []string, opt ...Options) int {
stdout, stderr := cliIO(opt)
fs := flag.NewFlagSet("debug", flag.ContinueOnError)
fs.SetOutput(stderr)
configDir := fs.String("config", "", "path to config directory")
jsonOut := fs.Bool("json", false, "emit JSON")
rates := fs.Bool("rates", false, "include rate table")
timeout := fs.Duration("timeout", 10*time.Second, "RPC timeout")
if err := fs.Parse(args); err != nil {
return 2
}
cfg, err := rnsutil.LoadConfigDir(*configDir)
if err != nil {
fmt.Fprintf(stderr, "config: %v\n", err)
return 1
}
type dump struct {
ConfigPath string `json:"config_path"`
LogLevel int `json:"log_level"`
DebugLevel int `json:"debug_level"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
RPCAddr string `json:"rpc_addr,omitempty"`
TransportID string `json:"transport_id,omitempty"`
UptimeSec float64 `json:"uptime_sec,omitempty"`
InterfaceN int `json:"interfaces,omitempty"`
RateTableLen int `json:"rate_table_len,omitempty"`
Error string `json:"error,omitempty"`
}
out := dump{
ConfigPath: cfg.ConfigPath,
LogLevel: cfg.LogLevel,
DebugLevel: debug.GetDebugLevel(),
GOOS: runtime.GOOS,
GOARCH: runtime.GOARCH,
}
client, err := rnsutil.DialRPC(cfg, nil)
if err != nil {
out.Error = err.Error()
if *jsonOut {
enc := json.NewEncoder(stdout)
enc.SetIndent("", " ")
_ = enc.Encode(out)
return 1
}
fmt.Fprintf(stderr, "rpc: %v\n", err)
fmt.Fprintf(stdout, "config_path=%s log_level=%d goos=%s goarch=%s\n",
out.ConfigPath, out.LogLevel, out.GOOS, out.GOARCH)
return 1
}
client.SetTimeout(*timeout)
out.RPCAddr = client.Addr()
stats, err := client.GetInterfaceStats()
if err != nil {
out.Error = err.Error()
} else {
out.InterfaceN = len(stats.Interfaces)
out.UptimeSec = stats.TransportUptime
out.TransportID = rnsutil.PrettyHex(stats.TransportID)
}
if *rates {
var raw any
if err := client.Call(map[string]any{"get": "rate_table"}, &raw); err == nil {
switch v := raw.(type) {
case []any:
out.RateTableLen = len(v)
case []map[string]any:
out.RateTableLen = len(v)
}
}
}
if *jsonOut {
enc := json.NewEncoder(stdout)
enc.SetIndent("", " ")
if err := enc.Encode(out); err != nil {
fmt.Fprintf(stderr, "json: %v\n", err)
return 1
}
return 0
}
fmt.Fprintf(stdout, "%s : %s\n", infoMsg(stdout, "config_path "), out.ConfigPath)
fmt.Fprintf(stdout, "%s : %d\n", infoMsg(stdout, "log_level "), out.LogLevel)
fmt.Fprintf(stdout, "%s : %d\n", infoMsg(stdout, "debug_level "), out.DebugLevel)
fmt.Fprintf(stdout, "%s : %s/%s\n", infoMsg(stdout, "platform "), out.GOOS, out.GOARCH)
fmt.Fprintf(stdout, "%s : %s\n", infoMsg(stdout, "rpc_addr "), out.RPCAddr)
if out.Error != "" {
fmt.Fprintf(stdout, "%s : %s\n", errMsg(stdout, "rpc_error "), out.Error)
return 1
}
fmt.Fprintf(stdout, "%s : %s\n", infoMsg(stdout, "transport_id"), out.TransportID)
fmt.Fprintf(stdout, "%s : %.1f\n", infoMsg(stdout, "uptime_sec "), out.UptimeSec)
fmt.Fprintf(stdout, "%s : %d\n", infoMsg(stdout, "interfaces "), out.InterfaceN)
if *rates {
fmt.Fprintf(stdout, "%s : %d entries\n", infoMsg(stdout, "rate_table "), out.RateTableLen)
}
return 0
}