134 lines
4.4 KiB
Go
134 lines
4.4 KiB
Go
// Command xui-agent is the standalone Linux build of the 3x-ui node
|
|
// agent (agent/panel split, phase 1). Until the split, nodes ran the
|
|
// panel binary under the "agent" subcommand, shipping the whole web
|
|
// panel and the embedded frontend to every node. This entry links only
|
|
// the agent's own dependency closure (internal/agent plus config,
|
|
// logger, selfupdate, xray-api types and a few util leaves), so the node
|
|
// artifact is a fraction of the panel binary while remaining a drop-in
|
|
// replacement for it:
|
|
//
|
|
// - invocation is unchanged: service units run
|
|
// "xui-agent agent [-config /etc/x-ui/agent.json]" (the bare
|
|
// "-config" form is also accepted for manual runs);
|
|
// - agent.json, the traffic baseline and the other state files keep
|
|
// their names, locations and formats;
|
|
// - the wire protocol with the master (register / heartbeat /
|
|
// config / traffic / probe-report / update intent) is byte-for-
|
|
// byte the internal/agent implementation the dual-mode binary ran.
|
|
//
|
|
// The agent lifecycle wiring below mirrors main.go's agent branch
|
|
// exactly — the same internal/agent package does the work either way,
|
|
// and cmd/xui-agent-windows is the Windows sibling of this entry.
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
_ "net/http/pprof"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/agent"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/config"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/memtuning"
|
|
|
|
"github.com/op/go-logging"
|
|
)
|
|
|
|
func main() {
|
|
args := os.Args[1:]
|
|
|
|
// Version self-report: the agent's own display version (A-form),
|
|
// from the same build-time displayVersion injection the panel
|
|
// uses. Kept ahead of the subcommand so it works however the
|
|
// binary was invoked.
|
|
if len(args) > 0 {
|
|
switch args[0] {
|
|
case "-v", "--version", "version":
|
|
fmt.Println(config.GetAgentDisplayVersion())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Service units (systemd / OpenRC / procd / nohup fallback in
|
|
// install-agent.sh) invoke the binary with the historical "agent"
|
|
// subcommand; consume it so those units keep working unchanged.
|
|
if len(args) > 0 && args[0] == "agent" {
|
|
args = args[1:]
|
|
} else if len(args) > 0 && args[0][0] != '-' {
|
|
fmt.Fprintf(os.Stderr, "xui-agent: unknown subcommand %q (this binary only runs the node agent)\n", args[0])
|
|
os.Exit(2)
|
|
}
|
|
|
|
runAgent(args)
|
|
}
|
|
|
|
// runAgent is main.go's agent branch, verbatim in behaviour: same
|
|
// flags, same defaults, same shutdown wiring.
|
|
func runAgent(args []string) {
|
|
agentCmd := flag.NewFlagSet("agent", flag.ExitOnError)
|
|
var configPath string
|
|
agentCmd.StringVar(&configPath, "config", "/etc/x-ui/agent.json", "Path to agent config file")
|
|
agentCmd.Parse(args)
|
|
|
|
// Agent mode always uses the panel's directory layout for the
|
|
// xray binary, geo files and config.json. Scoped to agent mode
|
|
// only so the panel's own relative "bin" default is untouched.
|
|
if os.Getenv("XUI_BIN_FOLDER") == "" {
|
|
_ = os.Setenv("XUI_BIN_FOLDER", agent.DefaultBinFolder)
|
|
}
|
|
|
|
cfg, err := agent.LoadConfig(configPath)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load agent config from %s: %v", configPath, err)
|
|
}
|
|
|
|
// The acknowledged traffic baseline persists next to the agent
|
|
// config (its state home, e.g. /etc/x-ui), unless the config
|
|
// names a path.
|
|
if cfg.BaselinePath == "" {
|
|
cfg.BaselinePath = filepath.Join(filepath.Dir(configPath), "agent-traffic-baseline.json")
|
|
}
|
|
|
|
// Initialize logger
|
|
logger.InitLogger(logging.INFO)
|
|
log.Printf("Starting 3x-ui agent (node: %s)", cfg.NodeName)
|
|
|
|
// Agent memory profile (ITEM-P3 A1/M5): the same runtime tuning
|
|
// family as the panel, with the agent's tighter budget, plus the
|
|
// periodic release the panel runs through its job scheduler (the
|
|
// agent has no scheduler), plus the panel's XUI_PPROF gate.
|
|
for _, line := range memtuning.ApplyAgentMemoryTuning() {
|
|
log.Println(line)
|
|
}
|
|
memtuning.StartPeriodicMemoryRelease()
|
|
if os.Getenv("XUI_PPROF") == "true" {
|
|
go func() {
|
|
log.Println("pprof profiling server listening on 127.0.0.1:6060")
|
|
if err := http.ListenAndServe("127.0.0.1:6060", nil); err != nil {
|
|
log.Printf("pprof server stopped: %v", err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
ag := agent.New(cfg)
|
|
|
|
// Handle shutdown signals
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
|
go func() {
|
|
<-sigCh
|
|
log.Println("Shutting down agent...")
|
|
ag.Stop()
|
|
}()
|
|
|
|
if err := ag.Run(); err != nil {
|
|
log.Fatalf("Agent error: %v", err)
|
|
}
|
|
}
|