|
@@ -58,6 +58,9 @@ type managed struct {
|
|
|
type Manager struct {
|
|
type Manager struct {
|
|
|
mu sync.Mutex
|
|
mu sync.Mutex
|
|
|
procs map[int]*managed
|
|
procs map[int]*managed
|
|
|
|
|
+ // swept records that the one-time startup cleanup of orphaned mtg
|
|
|
|
|
+ // processes (survivors of a previous x-ui run) has already run.
|
|
|
|
|
+ swept bool
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
var (
|
|
@@ -107,9 +110,24 @@ func InstanceFromInbound(ib *model.Inbound) (Instance, bool) {
|
|
|
func (m *Manager) Ensure(inst Instance) error {
|
|
func (m *Manager) Ensure(inst Instance) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
|
|
+ m.sweepOrphansLocked()
|
|
|
return m.ensureLocked(inst)
|
|
return m.ensureLocked(inst)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// sweepOrphansLocked kills mtg processes left running by a previous x-ui run,
|
|
|
|
|
+// exactly once per process lifetime and before any of our own mtg are started.
|
|
|
|
|
+// Because x-ui owns every mtg process, anything alive at this point is an orphan
|
|
|
|
|
+// that would otherwise keep holding an inbound port with a stale secret.
|
|
|
|
|
+func (m *Manager) sweepOrphansLocked() {
|
|
|
|
|
+ if m.swept {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ m.swept = true
|
|
|
|
|
+ if n := killStrayMtgProcesses(GetBinaryPath()); n > 0 {
|
|
|
|
|
+ logger.Warningf("mtproto: terminated %d orphaned mtg process(es) from a previous run", n)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (m *Manager) ensureLocked(inst Instance) error {
|
|
func (m *Manager) ensureLocked(inst Instance) error {
|
|
|
fp := inst.fingerprint()
|
|
fp := inst.fingerprint()
|
|
|
if cur, ok := m.procs[inst.Id]; ok {
|
|
if cur, ok := m.procs[inst.Id]; ok {
|
|
@@ -128,7 +146,7 @@ func (m *Manager) ensureLocked(inst Instance) error {
|
|
|
if err := writeConfig(cfgPath, inst.Secret, inst.bindTo(), metricsPort); err != nil {
|
|
if err := writeConfig(cfgPath, inst.Secret, inst.bindTo(), metricsPort); err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
- proc := newProcess(cfgPath)
|
|
|
|
|
|
|
+ proc := newProcess(cfgPath, fmt.Sprintf("inbound %d", inst.Id))
|
|
|
if err := proc.Start(); err != nil {
|
|
if err := proc.Start(); err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
@@ -138,7 +156,7 @@ func (m *Manager) ensureLocked(inst Instance) error {
|
|
|
fingerprint: fp,
|
|
fingerprint: fp,
|
|
|
metricsPort: metricsPort,
|
|
metricsPort: metricsPort,
|
|
|
}
|
|
}
|
|
|
- logger.Info("mtproto: started mtg for inbound", inst.Id, "on", inst.bindTo())
|
|
|
|
|
|
|
+ logger.Infof("mtproto: started mtg for inbound %d on %s", inst.Id, inst.bindTo())
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -150,7 +168,7 @@ func (m *Manager) Remove(id int) {
|
|
|
cur.proc.Stop()
|
|
cur.proc.Stop()
|
|
|
delete(m.procs, id)
|
|
delete(m.procs, id)
|
|
|
_ = os.Remove(configPathForID(id))
|
|
_ = os.Remove(configPathForID(id))
|
|
|
- logger.Info("mtproto: stopped mtg for inbound", id)
|
|
|
|
|
|
|
+ logger.Infof("mtproto: stopped mtg for inbound %d", id)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -160,6 +178,7 @@ func (m *Manager) Remove(id int) {
|
|
|
func (m *Manager) Reconcile(desired []Instance) {
|
|
func (m *Manager) Reconcile(desired []Instance) {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
|
|
+ m.sweepOrphansLocked()
|
|
|
want := make(map[int]struct{}, len(desired))
|
|
want := make(map[int]struct{}, len(desired))
|
|
|
for _, inst := range desired {
|
|
for _, inst := range desired {
|
|
|
want[inst.Id] = struct{}{}
|
|
want[inst.Id] = struct{}{}
|
|
@@ -173,7 +192,7 @@ func (m *Manager) Reconcile(desired []Instance) {
|
|
|
}
|
|
}
|
|
|
for _, inst := range desired {
|
|
for _, inst := range desired {
|
|
|
if err := m.ensureLocked(inst); err != nil {
|
|
if err := m.ensureLocked(inst); err != nil {
|
|
|
- logger.Warning("mtproto: reconcile failed for inbound", inst.Id, ":", err)
|
|
|
|
|
|
|
+ logger.Warningf("mtproto: reconcile failed for inbound %d: %v", inst.Id, err)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|