command_server.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package libbox
  2. import (
  3. "encoding/binary"
  4. "net"
  5. "os"
  6. "path/filepath"
  7. "sync"
  8. "github.com/sagernet/sing-box/common/urltest"
  9. "github.com/sagernet/sing-box/log"
  10. "github.com/sagernet/sing/common"
  11. "github.com/sagernet/sing/common/debug"
  12. E "github.com/sagernet/sing/common/exceptions"
  13. "github.com/sagernet/sing/common/observable"
  14. "github.com/sagernet/sing/common/x/list"
  15. "github.com/sagernet/sing/service"
  16. )
  17. type CommandServer struct {
  18. listener net.Listener
  19. handler CommandServerHandler
  20. access sync.Mutex
  21. savedLines *list.List[string]
  22. maxLines int
  23. subscriber *observable.Subscriber[string]
  24. observer *observable.Observer[string]
  25. service *BoxService
  26. urlTestListener *list.Element[func()]
  27. urlTestUpdate chan struct{}
  28. }
  29. type CommandServerHandler interface {
  30. ServiceReload() error
  31. }
  32. func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServer {
  33. server := &CommandServer{
  34. handler: handler,
  35. savedLines: new(list.List[string]),
  36. maxLines: int(maxLines),
  37. subscriber: observable.NewSubscriber[string](128),
  38. urlTestUpdate: make(chan struct{}, 1),
  39. }
  40. server.observer = observable.NewObserver[string](server.subscriber, 64)
  41. return server
  42. }
  43. func (s *CommandServer) SetService(newService *BoxService) {
  44. if s.service != nil && s.listener != nil {
  45. service.PtrFromContext[urltest.HistoryStorage](s.service.ctx).RemoveListener(s.urlTestListener)
  46. s.urlTestListener = nil
  47. }
  48. s.service = newService
  49. if newService != nil {
  50. s.urlTestListener = service.PtrFromContext[urltest.HistoryStorage](newService.ctx).AddListener(s.notifyURLTestUpdate)
  51. }
  52. s.notifyURLTestUpdate()
  53. }
  54. func (s *CommandServer) notifyURLTestUpdate() {
  55. select {
  56. case s.urlTestUpdate <- struct{}{}:
  57. default:
  58. }
  59. }
  60. func (s *CommandServer) Start() error {
  61. if !sTVOS {
  62. return s.listenUNIX()
  63. } else {
  64. return s.listenTCP()
  65. }
  66. }
  67. func (s *CommandServer) listenUNIX() error {
  68. sockPath := filepath.Join(sBasePath, "command.sock")
  69. os.Remove(sockPath)
  70. listener, err := net.ListenUnix("unix", &net.UnixAddr{
  71. Name: sockPath,
  72. Net: "unix",
  73. })
  74. if err != nil {
  75. return E.Cause(err, "listen ", sockPath)
  76. }
  77. if sUserID > 0 {
  78. err = os.Chown(sockPath, sUserID, sGroupID)
  79. if err != nil {
  80. listener.Close()
  81. os.Remove(sockPath)
  82. return E.Cause(err, "chown")
  83. }
  84. }
  85. s.listener = listener
  86. go s.loopConnection(listener)
  87. return nil
  88. }
  89. func (s *CommandServer) listenTCP() error {
  90. listener, err := net.Listen("tcp", "127.0.0.1:8964")
  91. if err != nil {
  92. return E.Cause(err, "listen")
  93. }
  94. s.listener = listener
  95. go s.loopConnection(listener)
  96. return nil
  97. }
  98. func (s *CommandServer) Close() error {
  99. return common.Close(
  100. s.listener,
  101. s.observer,
  102. )
  103. }
  104. func (s *CommandServer) loopConnection(listener net.Listener) {
  105. for {
  106. conn, err := listener.Accept()
  107. if err != nil {
  108. return
  109. }
  110. go func() {
  111. hErr := s.handleConnection(conn)
  112. if hErr != nil && !E.IsClosed(err) {
  113. if debug.Enabled {
  114. log.Warn("log-server: process connection: ", hErr)
  115. }
  116. }
  117. }()
  118. }
  119. }
  120. func (s *CommandServer) handleConnection(conn net.Conn) error {
  121. defer conn.Close()
  122. var command uint8
  123. err := binary.Read(conn, binary.BigEndian, &command)
  124. if err != nil {
  125. return E.Cause(err, "read command")
  126. }
  127. switch int32(command) {
  128. case CommandLog:
  129. return s.handleLogConn(conn)
  130. case CommandStatus:
  131. return s.handleStatusConn(conn)
  132. case CommandServiceReload:
  133. return s.handleServiceReload(conn)
  134. case CommandCloseConnections:
  135. return s.handleCloseConnections(conn)
  136. case CommandGroup:
  137. return s.handleGroupConn(conn)
  138. case CommandSelectOutbound:
  139. return s.handleSelectOutbound(conn)
  140. case CommandURLTest:
  141. return s.handleURLTest(conn)
  142. default:
  143. return E.New("unknown command: ", command)
  144. }
  145. }