Files
Muse 6a5ee8f77b rename: 3x-ui/x-ui -> L-UI/l-ui/lui-agent (paths, services, env, assets)
Display L-UI; panel binary/process l-ui, agent lui-agent.
Dirs /usr/local/l-ui, /etc/l-ui, /var/log/l-ui; DB /etc/l-ui/l-ui.db
(name file drives it via GetName), probe.db beside it, cert
/etc/l-ui/cert, bin /usr/local/l-ui/bin. Services l-ui.service /
lui-agent.service, OpenRC lui-agent, l-ui.sh, cmd/lui-agent.
Env prefix XUI_ -> LUI_ (code reads new prefix only). Release
assets l-ui-linux-* / lui-agent-linux-*; Gitea repo rogue/l-ui is
a placeholder pending user confirmation (see selfupdate.go note).
Go module path github.com/mhsanaei/3x-ui/v3 intentionally kept.
Versions unchanged: panel V2.6.0 / agent A2.5.0.
2026-10-24 12:00:55 +00:00

134 lines
4.4 KiB
Go

// Command lui-agent is the standalone Linux build of the L-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
// "lui-agent agent [-config /etc/l-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, "lui-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/l-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("LUI_BIN_FOLDER") == "" {
_ = os.Setenv("LUI_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/l-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 L-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 LUI_PPROF gate.
for _, line := range memtuning.ApplyAgentMemoryTuning() {
log.Println(line)
}
memtuning.StartPeriodicMemoryRelease()
if os.Getenv("LUI_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)
}
}