sandbox/master.go
Star f9dcf07ba4 first version
supported macOS、linux
2026-03-23 00:35:27 +08:00

168 lines
3.4 KiB
Go

// master.go v1.0
package sandbox
import (
"errors"
"fmt"
"math"
"os"
"path/filepath"
"syscall"
"time"
"github.com/ssgo/log"
"github.com/ssgo/u"
)
type SandboxStore struct {
Id string
Config *Config
}
var sandboxStoreList = map[string]SandboxStore{}
var sandboxStoreFile = ""
func Restore(id string, cfg *Config) *Sandbox {
root := filepath.Join(pluginConfig.Root, id)
stateFile := filepath.Join(root, ".state.json")
st := State{}
if u.FileExists(stateFile) && u.Load(stateFile, &st) == nil {
sb := &Sandbox{
id: id,
root: root,
workDir: st.WorkDir,
config: cfg,
pid: st.Pid,
status: "running",
startTime: st.StartTime,
mountedList: st.MountedList,
}
// 检查进程是否存在
if sb.Alive() {
log.DefaultLogger.Info("[Sandbox] resumed existing process", "id", id, "pid", st.Pid, "name", cfg.Name)
return sb
} else {
log.DefaultLogger.Info("[Sandbox] process dead, cleaning up residue", "id", id, "pid", st.Pid, "name", cfg.Name)
sb.Cleanup()
}
}
if cfg.AutoStart {
log.DefaultLogger.Info("[Sandbox] auto starting process", "name", cfg.Name)
sb, err := Start(cfg)
if err != nil {
log.DefaultLogger.Error("[Sandbox] auto-start failed", "err", err)
return nil
}
return sb
} else {
// 不自动启动,返回 nil
return nil
}
}
func Query(name string) []*Sandbox {
sandboxLock.RLock()
defer sandboxLock.RUnlock()
var list []*Sandbox
for _, sb := range sandboxList {
if sb.config.Name == name {
list = append(list, sb)
}
}
return list
}
func Fetch(id string) *Sandbox {
sandboxLock.RLock()
defer sandboxLock.RUnlock()
for _, sb := range sandboxList {
if sb.id == id {
return sb
}
}
return nil
}
func RegisterSandbox(sb *Sandbox) {
sandboxLock.Lock()
defer sandboxLock.Unlock()
sandboxList[sb.id] = sb
sandboxStoreList[sb.id] = SandboxStore{
Id: sb.id,
Config: sb.config,
}
u.Save(sandboxStoreFile, sandboxStoreList)
}
func ReleaseSandbox(id string) {
sandboxLock.Lock()
defer sandboxLock.Unlock()
delete(sandboxList, id)
delete(sandboxStoreList, id)
u.Save(sandboxStoreFile, sandboxStoreList)
}
func checkSandboxStatusTask() {
for isRunning.Load() {
for i := 0; i < 2*60; i++ {
time.Sleep(time.Millisecond * 500)
if !isRunning.Load() {
return
}
}
sblist := map[string]*Sandbox{}
sandboxLock.RLock()
for id, sb := range sandboxList {
sblist[id] = sb
}
sandboxLock.RUnlock()
for _, sb := range sblist {
if !sb.Alive() && sb.status == "running" {
sb.Cleanup()
}
if !isRunning.Load() {
return
}
}
}
}
func (sb *Sandbox) _kill(sig os.Signal) error {
proc, err := os.FindProcess(sb.pid)
if err == nil {
err = proc.Signal(sig)
}
return err
}
func (sb *Sandbox) Alive() bool {
sb.lock.Lock()
defer sb.lock.Unlock()
return sb._alive()
}
func (sb *Sandbox) _alive() bool {
proc, err := os.FindProcess(sb.pid)
if err == nil && proc.Signal(syscall.Signal(0)) == nil {
// 精确比对启动时间,防止 PID 回绕
if fi := u.GetFileInfo(fmt.Sprintf("/proc/%d", sb.pid)); fi != nil {
if math.Abs(float64(fi.ModTime.Unix()-sb.startTime)) > 5 {
// 确认启动时间差大于 5 秒,断言并非同一个进程
return false
}
}
return true
}
return false
}
func (sb *Sandbox) _waitProcess() (*os.ProcessState, error) {
if proc, err := os.FindProcess(sb.pid); err == nil {
return proc.Wait()
}
return nil, errors.New("pid not found")
}