Add filemanager api

This commit is contained in:
世界 2023-04-21 17:29:00 +08:00
parent b6068cea6b
commit 98c2c439aa
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
11 changed files with 77 additions and 54 deletions

3
box.go
View file

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

View file

@ -3,40 +3,13 @@ package constant
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
) )
const dirName = "sing-box" const dirName = "sing-box"
var ( var resourcePaths []string
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
}
func FindPath(name string) (string, bool) { func FindPath(name string) (string, bool) {
name = os.ExpandEnv(name) name = os.ExpandEnv(name)

View file

@ -1,6 +1,7 @@
package experimental package experimental
import ( import (
"context"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@ -8,7 +9,7 @@ import (
"github.com/sagernet/sing-box/option" "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 var clashServerConstructor ClashServerConstructor
@ -16,9 +17,9 @@ func RegisterClashServerConstructor(constructor ClashServerConstructor) {
clashServerConstructor = constructor 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 { if clashServerConstructor == nil {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
return clashServerConstructor(router, logFactory, options) return clashServerConstructor(ctx, router, logFactory, options)
} }

View file

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

View file

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

View file

@ -27,3 +27,27 @@ func RedirectStderr(path string) error {
stderrFile = outputFile stderrFile = outputFile
return nil 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
}

View file

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

View file

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

View file

@ -3,6 +3,8 @@
package include package include
import ( import (
"context"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental" "github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
@ -11,7 +13,7 @@ import (
) )
func init() { 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`) return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`)
}) })
} }

View file

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

View file

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