2023-03-01 02:37:47 +00:00
|
|
|
package libbox
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/sagernet/sing-box/log"
|
2023-04-04 20:38:56 +00:00
|
|
|
"github.com/sagernet/sing/common"
|
2023-04-10 00:48:58 +00:00
|
|
|
"github.com/sagernet/sing/common/debug"
|
2023-03-01 02:37:47 +00:00
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
|
|
"github.com/sagernet/sing/common/observable"
|
|
|
|
"github.com/sagernet/sing/common/x/list"
|
|
|
|
)
|
|
|
|
|
|
|
|
type CommandServer struct {
|
|
|
|
sockPath string
|
|
|
|
listener net.Listener
|
2023-03-03 11:26:54 +00:00
|
|
|
handler CommandServerHandler
|
2023-03-01 02:37:47 +00:00
|
|
|
|
|
|
|
access sync.Mutex
|
|
|
|
savedLines *list.List[string]
|
|
|
|
subscriber *observable.Subscriber[string]
|
|
|
|
observer *observable.Observer[string]
|
|
|
|
}
|
|
|
|
|
2023-03-03 11:26:54 +00:00
|
|
|
type CommandServerHandler interface {
|
2023-03-03 16:40:47 +00:00
|
|
|
ServiceStop() error
|
2023-03-03 11:26:54 +00:00
|
|
|
ServiceReload() error
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCommandServer(sharedDirectory string, handler CommandServerHandler) *CommandServer {
|
2023-03-01 02:37:47 +00:00
|
|
|
server := &CommandServer{
|
|
|
|
sockPath: filepath.Join(sharedDirectory, "command.sock"),
|
2023-03-03 11:26:54 +00:00
|
|
|
handler: handler,
|
2023-03-01 02:37:47 +00:00
|
|
|
savedLines: new(list.List[string]),
|
|
|
|
subscriber: observable.NewSubscriber[string](128),
|
|
|
|
}
|
|
|
|
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
|
|
|
return server
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *CommandServer) Start() error {
|
|
|
|
os.Remove(s.sockPath)
|
|
|
|
listener, err := net.ListenUnix("unix", &net.UnixAddr{
|
|
|
|
Name: s.sockPath,
|
|
|
|
Net: "unix",
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-03-03 16:40:47 +00:00
|
|
|
s.listener = listener
|
2023-03-01 02:37:47 +00:00
|
|
|
go s.loopConnection(listener)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *CommandServer) Close() error {
|
2023-04-04 20:38:56 +00:00
|
|
|
return common.Close(
|
|
|
|
s.listener,
|
|
|
|
s.observer,
|
|
|
|
)
|
2023-03-01 02:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *CommandServer) loopConnection(listener net.Listener) {
|
|
|
|
for {
|
|
|
|
conn, err := listener.Accept()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
hErr := s.handleConnection(conn)
|
|
|
|
if hErr != nil && !E.IsClosed(err) {
|
2023-04-10 00:48:58 +00:00
|
|
|
if debug.Enabled {
|
|
|
|
log.Warn("log-server: process connection: ", hErr)
|
|
|
|
}
|
2023-03-01 02:37:47 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *CommandServer) handleConnection(conn net.Conn) error {
|
|
|
|
defer conn.Close()
|
|
|
|
var command uint8
|
|
|
|
err := binary.Read(conn, binary.BigEndian, &command)
|
|
|
|
if err != nil {
|
|
|
|
return E.Cause(err, "read command")
|
|
|
|
}
|
|
|
|
switch int32(command) {
|
|
|
|
case CommandLog:
|
|
|
|
return s.handleLogConn(conn)
|
|
|
|
case CommandStatus:
|
|
|
|
return s.handleStatusConn(conn)
|
2023-03-03 16:40:47 +00:00
|
|
|
case CommandServiceStop:
|
|
|
|
return s.handleServiceStop(conn)
|
2023-03-03 11:26:54 +00:00
|
|
|
case CommandServiceReload:
|
|
|
|
return s.handleServiceReload(conn)
|
|
|
|
case CommandCloseConnections:
|
|
|
|
return s.handleCloseConnections(conn)
|
2023-03-01 02:37:47 +00:00
|
|
|
default:
|
|
|
|
return E.New("unknown command: ", command)
|
|
|
|
}
|
|
|
|
}
|