Add Darwin FFI for macOS in-process embedding
- ffi_darwin.go: C-exported functions (minising_start/stop/is_running/ get_peers/get_build_info/free) for c-archive buildmode - main.go: exclude darwin+cgo from CLI build - go.mod: fix sing-tun replace path after directory reorganization
This commit is contained in:
160
ffi_darwin.go
Normal file
160
ffi_darwin.go
Normal file
@@ -0,0 +1,160 @@
|
||||
//go:build darwin && cgo
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/netkits-dev/mini-sing/option"
|
||||
)
|
||||
|
||||
var (
|
||||
ffiMu sync.Mutex
|
||||
ffiBox *Box
|
||||
ffiCtx context.Context
|
||||
ffiCancel context.CancelFunc
|
||||
|
||||
// Set via -ldflags at build time
|
||||
buildTime = "unknown"
|
||||
buildHash = "unknown"
|
||||
)
|
||||
|
||||
//export minising_start
|
||||
func minising_start(cconfig *C.char) *C.char {
|
||||
config := C.GoString(cconfig)
|
||||
|
||||
// Setup logging from config
|
||||
if logPath := extractLogPath(config); logPath != "" {
|
||||
setupLog(logPath)
|
||||
}
|
||||
log.Printf("[ffi] mini-sing build=%s hash=%s", buildTime, buildHash)
|
||||
log.Println("[ffi] start called, config bytes:", len(config))
|
||||
|
||||
var opts option.Options
|
||||
if err := json.Unmarshal([]byte(config), &opts); err != nil {
|
||||
return C.CString("parse: " + err.Error())
|
||||
}
|
||||
|
||||
ffiMu.Lock()
|
||||
defer ffiMu.Unlock()
|
||||
|
||||
if ffiBox != nil {
|
||||
log.Println("[ffi] already running, rejecting start")
|
||||
return C.CString("already running")
|
||||
}
|
||||
|
||||
log.Println("[ffi] creating box")
|
||||
ffiCtx, ffiCancel = context.WithCancel(context.Background())
|
||||
box, err := NewBox(ffiCtx, opts)
|
||||
if err != nil {
|
||||
log.Println("[ffi] create box failed:", err)
|
||||
ffiCancel()
|
||||
return C.CString("create: " + err.Error())
|
||||
}
|
||||
|
||||
log.Println("[ffi] box created, starting")
|
||||
if err := box.Start(); err != nil {
|
||||
log.Println("[ffi] start failed:", err)
|
||||
box.Close()
|
||||
ffiCancel()
|
||||
return C.CString("start: " + err.Error())
|
||||
}
|
||||
ffiBox = box
|
||||
log.Println("[ffi] started successfully")
|
||||
return nil // nil = success
|
||||
}
|
||||
|
||||
//export minising_stop
|
||||
func minising_stop() {
|
||||
log.Println("[ffi] stop called")
|
||||
ffiMu.Lock()
|
||||
defer ffiMu.Unlock()
|
||||
if ffiBox != nil {
|
||||
ffiCancel()
|
||||
ffiBox.Close()
|
||||
ffiBox = nil
|
||||
log.Println("[ffi] stopped")
|
||||
}
|
||||
}
|
||||
|
||||
//export minising_is_running
|
||||
func minising_is_running() C.int {
|
||||
ffiMu.Lock()
|
||||
defer ffiMu.Unlock()
|
||||
if ffiBox != nil {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//export minising_get_peers
|
||||
func minising_get_peers() *C.char {
|
||||
ffiMu.Lock()
|
||||
box := ffiBox
|
||||
ffiMu.Unlock()
|
||||
|
||||
if box == nil {
|
||||
return C.CString(`{"error":"not running"}`)
|
||||
}
|
||||
ts := box.findTailscale()
|
||||
if ts == nil {
|
||||
return C.CString(`{"error":"tailscale not enabled"}`)
|
||||
}
|
||||
self, peers, err := ts.Peers()
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf(`{"error":"%s"}`, err.Error()))
|
||||
}
|
||||
data, _ := json.Marshal(map[string]any{"self": self, "peers": peers})
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export minising_get_build_info
|
||||
func minising_get_build_info() *C.char {
|
||||
info := fmt.Sprintf(`{"build_time":"%s","build_hash":"%s"}`, buildTime, buildHash)
|
||||
return C.CString(info)
|
||||
}
|
||||
|
||||
//export minising_free
|
||||
func minising_free(p *C.char) {
|
||||
C.free(unsafe.Pointer(p))
|
||||
}
|
||||
|
||||
func extractLogPath(config string) string {
|
||||
var raw map[string]json.RawMessage
|
||||
if err := json.Unmarshal([]byte(config), &raw); err != nil {
|
||||
return ""
|
||||
}
|
||||
logRaw, ok := raw["log"]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
var logObj struct {
|
||||
Output string `json:"output"`
|
||||
}
|
||||
if err := json.Unmarshal(logRaw, &logObj); err != nil {
|
||||
return ""
|
||||
}
|
||||
return logObj.Output
|
||||
}
|
||||
|
||||
func setupLog(logPath string) {
|
||||
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.SetOutput(f)
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||
}
|
||||
|
||||
func main() {} // required for c-archive buildmode
|
||||
2
go.mod
2
go.mod
@@ -23,7 +23,7 @@ require (
|
||||
|
||||
replace github.com/netkits-dev/mini-dns => ../mini-dns
|
||||
|
||||
replace github.com/sagernet/sing-tun => ../sing-tun
|
||||
replace github.com/sagernet/sing-tun => ../reference/sing-tun
|
||||
|
||||
replace github.com/netkits-dev/tailnet => ../tailnet
|
||||
|
||||
|
||||
Reference in New Issue
Block a user