// 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") }