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  }