Przeglądaj źródła

Add filemanager api

世界 2 lat temu
rodzic
commit
98c2c439aa

+ 2 - 1
box.go

@@ -62,6 +62,7 @@ func New(options Options) (*Box, error) {
 		defaultLogWriter = io.Discard
 	}
 	logFactory, err := log.New(log.Options{
+		Context:        ctx,
 		Options:        common.PtrValueOrDefault(options.Log),
 		Observable:     needClashAPI,
 		DefaultWriter:  defaultLogWriter,
@@ -142,7 +143,7 @@ func New(options Options) (*Box, error) {
 	preServices := make(map[string]adapter.Service)
 	postServices := make(map[string]adapter.Service)
 	if needClashAPI {
-		clashServer, err := experimental.NewClashServer(router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
+		clashServer, err := experimental.NewClashServer(ctx, router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
 		if err != nil {
 			return nil, E.Cause(err, "create clash api server")
 		}

+ 1 - 28
constant/path.go

@@ -3,40 +3,13 @@ package constant
 import (
 	"os"
 	"path/filepath"
-	"strings"
 
 	"github.com/sagernet/sing/common/rw"
 )
 
 const dirName = "sing-box"
 
-var (
-	basePath      string
-	tempPath      string
-	resourcePaths []string
-)
-
-func BasePath(name string) string {
-	if basePath == "" || strings.HasPrefix(name, "/") {
-		return name
-	}
-	return filepath.Join(basePath, name)
-}
-
-func CreateTemp(pattern string) (*os.File, error) {
-	if tempPath == "" {
-		tempPath = os.TempDir()
-	}
-	return os.CreateTemp(tempPath, pattern)
-}
-
-func SetBasePath(path string) {
-	basePath = path
-}
-
-func SetTempPath(path string) {
-	tempPath = path
-}
+var resourcePaths []string
 
 func FindPath(name string) (string, bool) {
 	name = os.ExpandEnv(name)

+ 4 - 3
experimental/clashapi.go

@@ -1,6 +1,7 @@
 package experimental
 
 import (
+	"context"
 	"os"
 
 	"github.com/sagernet/sing-box/adapter"
@@ -8,7 +9,7 @@ import (
 	"github.com/sagernet/sing-box/option"
 )
 
-type ClashServerConstructor = func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
+type ClashServerConstructor = func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
 
 var clashServerConstructor ClashServerConstructor
 
@@ -16,9 +17,9 @@ func RegisterClashServerConstructor(constructor ClashServerConstructor) {
 	clashServerConstructor = constructor
 }
 
-func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
+func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
 	if clashServerConstructor == nil {
 		return nil, os.ErrInvalid
 	}
-	return clashServerConstructor(router, logFactory, options)
+	return clashServerConstructor(ctx, router, logFactory, options)
 }

+ 6 - 3
experimental/clashapi/server.go

@@ -23,6 +23,7 @@ import (
 	E "github.com/sagernet/sing/common/exceptions"
 	F "github.com/sagernet/sing/common/format"
 	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/service/filemanager"
 	"github.com/sagernet/websocket"
 
 	"github.com/go-chi/chi/v5"
@@ -37,6 +38,7 @@ func init() {
 var _ adapter.ClashServer = (*Server)(nil)
 
 type Server struct {
+	ctx            context.Context
 	router         adapter.Router
 	logger         log.Logger
 	httpServer     *http.Server
@@ -53,10 +55,11 @@ type Server struct {
 	externalUIDownloadDetour string
 }
 
-func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
+func NewServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
 	trafficManager := trafficontrol.NewManager()
 	chiRouter := chi.NewRouter()
 	server := &Server{
+		ctx:    ctx,
 		router: router,
 		logger: logFactory.NewLogger("clash-api"),
 		httpServer: &http.Server{
@@ -82,7 +85,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
 		if foundPath, loaded := C.FindPath(cachePath); loaded {
 			cachePath = foundPath
 		} else {
-			cachePath = C.BasePath(cachePath)
+			cachePath = filemanager.BasePath(ctx, cachePath)
 		}
 		server.cacheFilePath = cachePath
 	}
@@ -113,7 +116,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
 		server.setupMetaAPI(r)
 	})
 	if options.ExternalUI != "" {
-		server.externalUI = C.BasePath(os.ExpandEnv(options.ExternalUI))
+		server.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
 		chiRouter.Group(func(r chi.Router) {
 			fs := http.StripPrefix("/ui", http.FileServer(http.Dir(server.externalUI)))
 			r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)

+ 5 - 5
experimental/clashapi/server_resources.go

@@ -12,11 +12,11 @@ import (
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
-	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/service/filemanager"
 )
 
 func (s *Server) checkAndDownloadExternalUI() {
@@ -79,7 +79,7 @@ func (s *Server) downloadExternalUI() error {
 }
 
 func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
-	tempFile, err := C.CreateTemp(name)
+	tempFile, err := filemanager.CreateTemp(s.ctx, name)
 	if err != nil {
 		return err
 	}
@@ -112,7 +112,7 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
 			return err
 		}
 		savePath := filepath.Join(saveDirectory, pathElements[len(pathElements)-1])
-		err = downloadZIPEntry(file, savePath)
+		err = downloadZIPEntry(s.ctx, file, savePath)
 		if err != nil {
 			return err
 		}
@@ -120,8 +120,8 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
 	return nil
 }
 
-func downloadZIPEntry(zipFile *zip.File, savePath string) error {
-	saveFile, err := os.Create(savePath)
+func downloadZIPEntry(ctx context.Context, zipFile *zip.File, savePath string) error {
+	saveFile, err := filemanager.Create(ctx, savePath)
 	if err != nil {
 		return err
 	}

+ 24 - 0
experimental/libbox/log.go

@@ -27,3 +27,27 @@ func RedirectStderr(path string) error {
 	stderrFile = outputFile
 	return nil
 }
+
+func RedirectStderrAsUser(path string, uid, gid int) error {
+	if stats, err := os.Stat(path); err == nil && stats.Size() > 0 {
+		_ = os.Rename(path, path+".old")
+	}
+	outputFile, err := os.Create(path)
+	if err != nil {
+		return err
+	}
+	err = outputFile.Chown(uid, gid)
+	if err != nil {
+		outputFile.Close()
+		os.Remove(outputFile.Name())
+		return err
+	}
+	err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))
+	if err != nil {
+		outputFile.Close()
+		os.Remove(outputFile.Name())
+		return err
+	}
+	stderrFile = outputFile
+	return nil
+}

+ 2 - 0
experimental/libbox/service.go

@@ -16,6 +16,7 @@ import (
 	"github.com/sagernet/sing/common/control"
 	E "github.com/sagernet/sing/common/exceptions"
 	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/service/filemanager"
 )
 
 type BoxService struct {
@@ -30,6 +31,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
 		return nil, err
 	}
 	ctx, cancel := context.WithCancel(context.Background())
+	ctx = filemanager.WithDefault(ctx, sBasePath, sTempPath, sUserID, sGroupID)
 	instance, err := box.New(box.Options{
 		Context:           ctx,
 		Options:           options,

+ 19 - 5
experimental/libbox/setup.go

@@ -1,17 +1,31 @@
 package libbox
 
 import (
+	"os"
+
 	C "github.com/sagernet/sing-box/constant"
 
 	"github.com/dustin/go-humanize"
 )
 
-func SetBasePath(path string) {
-	C.SetBasePath(path)
-}
+var (
+	sBasePath string
+	sTempPath string
+	sUserID   int
+	sGroupID  int
+)
 
-func SetTempPath(path string) {
-	C.SetTempPath(path)
+func Setup(basePath string, tempPath string, userID int, groupID int) {
+	sBasePath = basePath
+	sTempPath = tempPath
+	sUserID = userID
+	sGroupID = groupID
+	if sUserID == -1 {
+		sUserID = os.Getuid()
+	}
+	if sGroupID == -1 {
+		sGroupID = os.Getgid()
+	}
 }
 
 func Version() string {

+ 3 - 1
include/clashapi_stub.go

@@ -3,6 +3,8 @@
 package include
 
 import (
+	"context"
+
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/experimental"
 	"github.com/sagernet/sing-box/log"
@@ -11,7 +13,7 @@ import (
 )
 
 func init() {
-	experimental.RegisterClashServerConstructor(func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
+	experimental.RegisterClashServerConstructor(func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
 		return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`)
 	})
 }

+ 4 - 2
log/log.go

@@ -1,14 +1,15 @@
 package log
 
 import (
+	"context"
 	"io"
 	"os"
 	"time"
 
-	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
+	"github.com/sagernet/sing/service/filemanager"
 )
 
 type factoryWithFile struct {
@@ -36,6 +37,7 @@ func (f *observableFactoryWithFile) Close() error {
 }
 
 type Options struct {
+	Context        context.Context
 	Options        option.LogOptions
 	Observable     bool
 	DefaultWriter  io.Writer
@@ -65,7 +67,7 @@ func New(options Options) (Factory, error) {
 		logWriter = os.Stdout
 	default:
 		var err error
-		logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
+		logFile, err = filemanager.OpenFile(options.Context, logOptions.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
 		if err != nil {
 			return nil, err
 		}

+ 7 - 6
route/router_geo_resources.go

@@ -18,6 +18,7 @@ import (
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	"github.com/sagernet/sing/common/rw"
+	"github.com/sagernet/sing/service/filemanager"
 )
 
 func (r *Router) GeoIPReader() *geoip.Reader {
@@ -51,7 +52,7 @@ func (r *Router) prepareGeoIPDatabase() error {
 			geoPath = foundPath
 		}
 	}
-	geoPath = C.BasePath(geoPath)
+	geoPath = filemanager.BasePath(r.ctx, geoPath)
 	if rw.FileExists(geoPath) {
 		geoReader, codes, err := geoip.Open(geoPath)
 		if err == nil {
@@ -95,7 +96,7 @@ func (r *Router) prepareGeositeDatabase() error {
 			geoPath = foundPath
 		}
 	}
-	geoPath = C.BasePath(geoPath)
+	geoPath = filemanager.BasePath(r.ctx, geoPath)
 	if !rw.FileExists(geoPath) {
 		r.logger.Warn("geosite database not exists: ", geoPath)
 		var err error
@@ -142,10 +143,10 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
 	}
 
 	if parentDir := filepath.Dir(savePath); parentDir != "" {
-		os.MkdirAll(parentDir, 0o755)
+		filemanager.MkdirAll(r.ctx, parentDir, 0o755)
 	}
 
-	saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644)
+	saveFile, err := filemanager.Create(r.ctx, savePath)
 	if err != nil {
 		return E.Cause(err, "open output file: ", downloadURL)
 	}
@@ -190,10 +191,10 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
 	}
 
 	if parentDir := filepath.Dir(savePath); parentDir != "" {
-		os.MkdirAll(parentDir, 0o755)
+		filemanager.MkdirAll(r.ctx, parentDir, 0o755)
 	}
 
-	saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644)
+	saveFile, err := filemanager.Create(r.ctx, savePath)
 	if err != nil {
 		return E.Cause(err, "open output file: ", downloadURL)
 	}