Fix platform wrapper

This commit is contained in:
世界 2023-02-22 20:52:57 +08:00
parent 60094884cd
commit 140ed9a4cb
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
9 changed files with 339 additions and 27 deletions

3
.gitignore vendored
View file

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

42
box.go
View file

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

View file

@ -12,16 +12,32 @@ import (
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
) )
var debugEnabled bool var (
debugEnabled bool
target string
)
func init() { func init() {
flag.BoolVar(&debugEnabled, "debug", false, "enable debug") flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
flag.StringVar(&target, "target", "android", "target platform")
} }
func main() { func main() {
build_shared.FindSDK() flag.Parse()
build_shared.FindMobile() build_shared.FindMobile()
switch target {
case "android":
buildAndroid()
case "ios":
buildiOS()
}
}
func buildAndroid() {
build_shared.FindSDK()
args := []string{ args := []string{
"bind", "bind",
"-v", "-v",
@ -32,10 +48,10 @@ func main() {
if !debugEnabled { if !debugEnabled {
args = append(args, args = append(args,
"-trimpath", "-ldflags=-s -w -buildid=", "-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 { } 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") args = append(args, "./experimental/libbox")
@ -59,3 +75,38 @@ func main() {
log.Info("copied to ", copyPath) 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)
}
}

View file

@ -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
}
}
}

View file

@ -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))
}
}

View file

@ -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
}

View file

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

View file

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

View file

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