command_server.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. sockPath string
  19. listener net.Listener
  20. handler CommandServerHandler
  21. access sync.Mutex
  22. savedLines *list.List[string]
  23. maxLines int
  24. subscriber *observable.Subscriber[string]
  25. observer *observable.Observer[string]
  26. service *BoxService
  27. urlTestListener *list.Element[func()]
  28. urlTestUpdate chan struct{}
  29. }
  30. type CommandServerHandler interface {
  31. ServiceReload() error
  32. }
  33. func NewCommandServer(sharedDirectory string, handler CommandServerHandler, maxLines int32) *CommandServer {
  34. server := &CommandServer{
  35. sockPath: filepath.Join(sharedDirectory, "command.sock"),
  36. handler: handler,
  37. savedLines: new(list.List[string]),
  38. maxLines: int(maxLines),
  39. subscriber: observable.NewSubscriber[string](128),
  40. urlTestUpdate: make(chan struct{}, 1),
  41. }
  42. server.observer = observable.NewObserver[string](server.subscriber, 64)
  43. return server
  44. }
  45. func (s *CommandServer) SetService(newService *BoxService) {
  46. if s.service != nil && s.listener != nil {
  47. service.PtrFromContext[urltest.HistoryStorage](s.service.ctx).RemoveListener(s.urlTestListener)
  48. s.urlTestListener = nil
  49. }
  50. s.service = newService
  51. if newService != nil {
  52. s.urlTestListener = service.PtrFromContext[urltest.HistoryStorage](newService.ctx).AddListener(s.notifyURLTestUpdate)
  53. }
  54. s.notifyURLTestUpdate()
  55. }
  56. func (s *CommandServer) notifyURLTestUpdate() {
  57. select {
  58. case s.urlTestUpdate <- struct{}{}:
  59. default:
  60. }
  61. }
  62. func (s *CommandServer) Start() error {
  63. os.Remove(s.sockPath)
  64. listener, err := net.ListenUnix("unix", &net.UnixAddr{
  65. Name: s.sockPath,
  66. Net: "unix",
  67. })
  68. if err != nil {
  69. return err
  70. }
  71. s.listener = listener
  72. go s.loopConnection(listener)
  73. return nil
  74. }
  75. func (s *CommandServer) Close() error {
  76. return common.Close(
  77. s.listener,
  78. s.observer,
  79. )
  80. }
  81. func (s *CommandServer) loopConnection(listener net.Listener) {
  82. for {
  83. conn, err := listener.Accept()
  84. if err != nil {
  85. return
  86. }
  87. go func() {
  88. hErr := s.handleConnection(conn)
  89. if hErr != nil && !E.IsClosed(err) {
  90. if debug.Enabled {
  91. log.Warn("log-server: process connection: ", hErr)
  92. }
  93. }
  94. }()
  95. }
  96. }
  97. func (s *CommandServer) handleConnection(conn net.Conn) error {
  98. defer conn.Close()
  99. var command uint8
  100. err := binary.Read(conn, binary.BigEndian, &command)
  101. if err != nil {
  102. return E.Cause(err, "read command")
  103. }
  104. switch int32(command) {
  105. case CommandLog:
  106. return s.handleLogConn(conn)
  107. case CommandStatus:
  108. return s.handleStatusConn(conn)
  109. case CommandServiceReload:
  110. return s.handleServiceReload(conn)
  111. case CommandCloseConnections:
  112. return s.handleCloseConnections(conn)
  113. case CommandGroup:
  114. return s.handleGroupConn(conn)
  115. case CommandSelectOutbound:
  116. return s.handleSelectOutbound(conn)
  117. case CommandURLTest:
  118. return s.handleURLTest(conn)
  119. default:
  120. return E.New("unknown command: ", command)
  121. }
  122. }