github.com/sagernet/sing-box@v1.2.7/experimental/libbox/command_server.go (about) 1 package libbox 2 3 import ( 4 "encoding/binary" 5 "net" 6 "os" 7 "path/filepath" 8 "sync" 9 10 "github.com/sagernet/sing-box/log" 11 "github.com/sagernet/sing/common" 12 "github.com/sagernet/sing/common/debug" 13 E "github.com/sagernet/sing/common/exceptions" 14 "github.com/sagernet/sing/common/observable" 15 "github.com/sagernet/sing/common/x/list" 16 ) 17 18 type CommandServer struct { 19 sockPath string 20 listener net.Listener 21 handler CommandServerHandler 22 23 access sync.Mutex 24 savedLines *list.List[string] 25 subscriber *observable.Subscriber[string] 26 observer *observable.Observer[string] 27 } 28 29 type CommandServerHandler interface { 30 ServiceStop() error 31 ServiceReload() error 32 } 33 34 func NewCommandServer(sharedDirectory string, handler CommandServerHandler) *CommandServer { 35 server := &CommandServer{ 36 sockPath: filepath.Join(sharedDirectory, "command.sock"), 37 handler: handler, 38 savedLines: new(list.List[string]), 39 subscriber: observable.NewSubscriber[string](128), 40 } 41 server.observer = observable.NewObserver[string](server.subscriber, 64) 42 return server 43 } 44 45 func (s *CommandServer) Start() error { 46 os.Remove(s.sockPath) 47 listener, err := net.ListenUnix("unix", &net.UnixAddr{ 48 Name: s.sockPath, 49 Net: "unix", 50 }) 51 if err != nil { 52 return err 53 } 54 s.listener = listener 55 go s.loopConnection(listener) 56 return nil 57 } 58 59 func (s *CommandServer) Close() error { 60 return common.Close( 61 s.listener, 62 s.observer, 63 ) 64 } 65 66 func (s *CommandServer) loopConnection(listener net.Listener) { 67 for { 68 conn, err := listener.Accept() 69 if err != nil { 70 return 71 } 72 go func() { 73 hErr := s.handleConnection(conn) 74 if hErr != nil && !E.IsClosed(err) { 75 if debug.Enabled { 76 log.Warn("log-server: process connection: ", hErr) 77 } 78 } 79 }() 80 } 81 } 82 83 func (s *CommandServer) handleConnection(conn net.Conn) error { 84 defer conn.Close() 85 var command uint8 86 err := binary.Read(conn, binary.BigEndian, &command) 87 if err != nil { 88 return E.Cause(err, "read command") 89 } 90 switch int32(command) { 91 case CommandLog: 92 return s.handleLogConn(conn) 93 case CommandStatus: 94 return s.handleStatusConn(conn) 95 case CommandServiceStop: 96 return s.handleServiceStop(conn) 97 case CommandServiceReload: 98 return s.handleServiceReload(conn) 99 case CommandCloseConnections: 100 return s.handleCloseConnections(conn) 101 default: 102 return E.New("unknown command: ", command) 103 } 104 }