浏览代码

Fix platform wrapper

世界 2 年之前
父节点
当前提交
140ed9a4cb

+ 2 - 1
.gitignore

@@ -8,4 +8,5 @@
 /sing-box
 /build/
 /*.jar
-/*.aar
+/*.aar
+/*.xcframework/

+ 21 - 21
box.go

@@ -78,28 +78,28 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
 			}
 			logWriter = logFile
 		}
-	}
-	logFormatter := log.Formatter{
-		BaseTime:         createdAt,
-		DisableColors:    logOptions.DisableColor || logFile != nil,
-		DisableTimestamp: !logOptions.Timestamp && logFile != nil,
-		FullTimestamp:    logOptions.Timestamp,
-		TimestampFormat:  "-0700 2006-01-02 15:04:05",
-	}
-	if needClashAPI {
-		observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, options.PlatformInterface)
-		logFactory = observableLogFactory
-	} else {
-		logFactory = log.NewFactory(logFormatter, logWriter, options.PlatformInterface)
-	}
-	if logOptions.Level != "" {
-		logLevel, err := log.ParseLevel(logOptions.Level)
-		if err != nil {
-			return nil, E.Cause(err, "parse log level")
+		logFormatter := log.Formatter{
+			BaseTime:         createdAt,
+			DisableColors:    logOptions.DisableColor || logFile != nil,
+			DisableTimestamp: !logOptions.Timestamp && logFile != nil,
+			FullTimestamp:    logOptions.Timestamp,
+			TimestampFormat:  "-0700 2006-01-02 15:04:05",
+		}
+		if needClashAPI {
+			observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, options.PlatformInterface)
+			logFactory = observableLogFactory
+		} else {
+			logFactory = log.NewFactory(logFormatter, logWriter, options.PlatformInterface)
+		}
+		if logOptions.Level != "" {
+			logLevel, err := log.ParseLevel(logOptions.Level)
+			if err != nil {
+				return nil, E.Cause(err, "parse log level")
+			}
+			logFactory.SetLevel(logLevel)
+		} else {
+			logFactory.SetLevel(log.LevelTrace)
 		}
-		logFactory.SetLevel(logLevel)
-	} else {
-		logFactory.SetLevel(log.LevelTrace)
 	}
 
 	router, err := route.NewRouter(

+ 55 - 4
cmd/internal/build_libbox/main.go

@@ -12,16 +12,32 @@ import (
 	"github.com/sagernet/sing/common/rw"
 )
 
-var debugEnabled bool
+var (
+	debugEnabled bool
+	target       string
+)
 
 func init() {
 	flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
+	flag.StringVar(&target, "target", "android", "target platform")
 }
 
 func main() {
-	build_shared.FindSDK()
+	flag.Parse()
+
 	build_shared.FindMobile()
 
+	switch target {
+	case "android":
+		buildAndroid()
+	case "ios":
+		buildiOS()
+	}
+}
+
+func buildAndroid() {
+	build_shared.FindSDK()
+
 	args := []string{
 		"bind",
 		"-v",
@@ -32,10 +48,10 @@ func main() {
 	if !debugEnabled {
 		args = append(args,
 			"-trimpath", "-ldflags=-s -w -buildid=",
-			"-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,debug",
+			"-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api",
 		)
 	} else {
-		args = append(args, "-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api")
+		args = append(args, "-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,debug")
 	}
 
 	args = append(args, "./experimental/libbox")
@@ -59,3 +75,38 @@ func main() {
 		log.Info("copied to ", copyPath)
 	}
 }
+
+func buildiOS() {
+	args := []string{
+		"bind",
+		"-v",
+		"-target", "ios,iossimulator,macos",
+		"-libname=box",
+	}
+	if !debugEnabled {
+		args = append(args,
+			"-trimpath", "-ldflags=-s -w -buildid=",
+		)
+	} else {
+		args = append(args, "-tags", "debug")
+	}
+
+	args = append(args, "./experimental/libbox")
+
+	command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
+	command.Stdout = os.Stdout
+	command.Stderr = os.Stderr
+	err := command.Run()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	copyPath := filepath.Join("..", "sfi")
+	if rw.FileExists(copyPath) {
+		targetDir := filepath.Join(copyPath, "Libbox.xcframework")
+		targetDir, _ = filepath.Abs(targetDir)
+		os.RemoveAll(targetDir)
+		os.Rename("Libbox.xcframework", targetDir)
+		log.Info("copied to ", targetDir)
+	}
+}

+ 54 - 0
experimental/libbox/log.go

@@ -0,0 +1,54 @@
+package libbox
+
+import (
+	"bufio"
+	"log"
+	"os"
+)
+
+type StandardOutput interface {
+	WriteOutput(message string)
+	WriteErrorOutput(message string)
+}
+
+func SetOutput(output StandardOutput) {
+	log.SetOutput(logWriter{output})
+	pipeIn, pipeOut, err := os.Pipe()
+	if err != nil {
+		panic(err)
+	}
+	os.Stdout = os.NewFile(pipeOut.Fd(), "stdout")
+	go lineLog(pipeIn, output.WriteOutput)
+
+	pipeIn, pipeOut, err = os.Pipe()
+	if err != nil {
+		panic(err)
+	}
+	os.Stderr = os.NewFile(pipeOut.Fd(), "srderr")
+	go lineLog(pipeIn, output.WriteErrorOutput)
+}
+
+type logWriter struct {
+	output StandardOutput
+}
+
+func (w logWriter) Write(p []byte) (n int, err error) {
+	w.output.WriteOutput(string(p))
+	return len(p), nil
+}
+
+func lineLog(f *os.File, output func(string)) {
+	const logSize = 1024 // matches android/log.h.
+	r := bufio.NewReaderSize(f, logSize)
+	for {
+		line, _, err := r.ReadLine()
+		str := string(line)
+		if err != nil {
+			str += " " + err.Error()
+		}
+		output(str)
+		if err != nil {
+			break
+		}
+	}
+}

+ 59 - 0
experimental/libbox/log_client.go

@@ -0,0 +1,59 @@
+//go:build ios
+
+package libbox
+
+import (
+	"net"
+	"path/filepath"
+
+	"github.com/sagernet/sing/common"
+)
+
+type LogClient struct {
+	sockPath string
+	handler  LogClientHandler
+	conn     net.Conn
+}
+
+type LogClientHandler interface {
+	Connected()
+	Disconnected()
+	WriteLog(message string)
+}
+
+func NewLogClient(sharedDirectory string, handler LogClientHandler) *LogClient {
+	return &LogClient{
+		sockPath: filepath.Join(sharedDirectory, "log.sock"),
+		handler:  handler,
+	}
+}
+
+func (c *LogClient) Connect() error {
+	conn, err := net.DialUnix("unix", nil, &net.UnixAddr{
+		Name: c.sockPath,
+		Net:  "unix",
+	})
+	if err != nil {
+		return err
+	}
+	c.conn = conn
+	go c.loopConnection(&messageConn{conn})
+	return nil
+}
+
+func (c *LogClient) Disconnect() error {
+	return common.Close(c.conn)
+}
+
+func (c *LogClient) loopConnection(conn *messageConn) {
+	c.handler.Connected()
+	defer c.handler.Disconnected()
+	for {
+		message, err := conn.Read()
+		if err != nil {
+			c.handler.WriteLog("(log client error) " + err.Error())
+			return
+		}
+		c.handler.WriteLog(string(message))
+	}
+}

+ 139 - 0
experimental/libbox/log_server.go

@@ -0,0 +1,139 @@
+//go:build ios
+
+package libbox
+
+import (
+	"encoding/binary"
+	"io"
+	"net"
+	"os"
+	"path/filepath"
+	"sync"
+
+	"github.com/sagernet/sing-box/log"
+	E "github.com/sagernet/sing/common/exceptions"
+	"github.com/sagernet/sing/common/observable"
+	"github.com/sagernet/sing/common/x/list"
+)
+
+type LogServer struct {
+	sockPath string
+	listener net.Listener
+
+	access     sync.Mutex
+	savedLines *list.List[string]
+	subscriber *observable.Subscriber[string]
+	observer   *observable.Observer[string]
+}
+
+func NewLogServer(sharedDirectory string) *LogServer {
+	server := &LogServer{
+		sockPath:   filepath.Join(sharedDirectory, "log.sock"),
+		savedLines: new(list.List[string]),
+		subscriber: observable.NewSubscriber[string](128),
+	}
+	server.observer = observable.NewObserver[string](server.subscriber, 64)
+	return server
+}
+
+func (s *LogServer) Start() error {
+	os.Remove(s.sockPath)
+	listener, err := net.ListenUnix("unix", &net.UnixAddr{
+		Name: s.sockPath,
+		Net:  "unix",
+	})
+	if err != nil {
+		return err
+	}
+	go s.loopConnection(listener)
+	return nil
+}
+
+func (s *LogServer) Close() error {
+	return s.listener.Close()
+}
+
+func (s *LogServer) WriteMessage(message string) {
+	s.subscriber.Emit(message)
+	s.access.Lock()
+	s.savedLines.PushBack(message)
+	if s.savedLines.Len() > 100 {
+		s.savedLines.Remove(s.savedLines.Front())
+	}
+	s.access.Unlock()
+}
+
+func (s *LogServer) loopConnection(listener net.Listener) {
+	for {
+		conn, err := listener.Accept()
+		if err != nil {
+			return
+		}
+		go func() {
+			hErr := s.handleConnection(&messageConn{conn})
+			if hErr != nil && !E.IsClosed(err) {
+				log.Warn("log-server: process connection: ", hErr)
+			}
+		}()
+	}
+}
+
+func (s *LogServer) handleConnection(conn *messageConn) error {
+	var savedLines []string
+	s.access.Lock()
+	savedLines = make([]string, 0, s.savedLines.Len())
+	for element := s.savedLines.Front(); element != nil; element = element.Next() {
+		savedLines = append(savedLines, element.Value)
+	}
+	s.access.Unlock()
+	subscription, done, err := s.observer.Subscribe()
+	if err != nil {
+		return err
+	}
+	defer s.observer.UnSubscribe(subscription)
+	for _, line := range savedLines {
+		err = conn.Write([]byte(line))
+		if err != nil {
+			return err
+		}
+	}
+	for {
+		select {
+		case message := <-subscription:
+			err = conn.Write([]byte(message))
+			if err != nil {
+				return err
+			}
+		case <-done:
+			conn.Close()
+			return nil
+		}
+	}
+}
+
+type messageConn struct {
+	net.Conn
+}
+
+func (c *messageConn) Read() ([]byte, error) {
+	var messageLength uint16
+	err := binary.Read(c.Conn, binary.BigEndian, &messageLength)
+	if err != nil {
+		return nil, err
+	}
+	data := make([]byte, messageLength)
+	_, err = io.ReadFull(c.Conn, data)
+	if err != nil {
+		return nil, err
+	}
+	return data, nil
+}
+
+func (c *messageConn) Write(message []byte) error {
+	err := binary.Write(c.Conn, binary.BigEndian, uint16(len(message)))
+	if err != nil {
+		return err
+	}
+	_, err = c.Conn.Write(message)
+	return err
+}

+ 2 - 0
experimental/libbox/service.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"net/netip"
 	"os"
+	"runtime"
 	"syscall"
 
 	"github.com/sagernet/sing-box"
@@ -27,6 +28,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
 	if err != nil {
 		return nil, err
 	}
+	platformInterface.WriteLog("Hello " + runtime.GOOS + "/" + runtime.GOARCH)
 	options.PlatformInterface = &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()}
 	ctx, cancel := context.WithCancel(context.Background())
 	instance, err := box.New(ctx, options)

+ 4 - 0
experimental/libbox/setup.go

@@ -5,3 +5,7 @@ import C "github.com/sagernet/sing-box/constant"
 func SetBasePath(path string) {
 	C.SetBasePath(path)
 }
+
+func Version() string {
+	return C.Version
+}

+ 3 - 1
log/default.go

@@ -6,6 +6,7 @@ import (
 	"os"
 	"time"
 
+	C "github.com/sagernet/sing-box/constant"
 	F "github.com/sagernet/sing/common/format"
 )
 
@@ -23,7 +24,8 @@ func NewFactory(formatter Formatter, writer io.Writer, platformWriter io.Writer)
 	return &simpleFactory{
 		formatter: formatter,
 		platformFormatter: Formatter{
-			BaseTime: formatter.BaseTime,
+			BaseTime:      formatter.BaseTime,
+			DisableColors: C.IsIos,
 		},
 		writer:         writer,
 		platformWriter: platformWriter,