168 lines
3.4 KiB
Go
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")
|
|
}
|