// 远程进程管理:进程列表 / 结束进程。 // gopsutil 跨平台实现,与 file/shell 同为被控 HTTP 能力——BS 与客户机被控端共用, // 经 zonat 隧道 + web-gateway 反代出网(typ=process,页面 www/process.html)。 package process import ( "encoding/json" "net/http" "os/exec" "runtime" "sort" "strconv" "github.com/shirou/gopsutil/v3/process" "git.zeroonesoft.cn/golib/remote-core/webhttp" ) // ProcItem 进程列表行。 type ProcItem struct { Pid int32 `json:"pid"` Name string `json:"name"` Status string `json:"status"` User string `json:"user"` CPUPercent float64 `json:"cpuPercent"` // 进程存活期累计 CPU 占用率(gopsutil 口径) MemRSS uint64 `json:"memBytes"` // 物理内存占用(字节) MemPercent float32 `json:"memPercent"` CreateTime int64 `json:"createTime"` // 启动时间(Unix 毫秒) Exe string `json:"exe"` Cmdline string `json:"cmdline"` } // RegisterRoutersProcess 装配进程管理路由: GET /process/list, POST /process/kill。 func RegisterRoutersProcess(r webhttp.Router) { r.GET("/process/list", listProcesses) r.POST("/process/kill", killProcess) } func fail(w http.ResponseWriter, msg string) { webhttp.WriteJSON(w, http.StatusOK, map[string]any{"code": 1, "msg": msg, "data": nil}) } // listProcesses 全量进程列表(按内存占用降序)。 func listProcesses(w http.ResponseWriter, _ *http.Request) { procs, err := process.Processes() if err != nil { fail(w, "枚举进程失败: "+err.Error()) return } items := make([]ProcItem, 0, len(procs)) for _, p := range procs { item := ProcItem{Pid: p.Pid} if name, e := p.Name(); e == nil { item.Name = name } if st, e := p.Status(); e == nil && len(st) > 0 { item.Status = st[0] } if u, e := p.Username(); e == nil { item.User = u } if cpu, e := p.CPUPercent(); e == nil { item.CPUPercent = cpu } if mem, e := p.MemoryInfo(); e == nil { item.MemRSS = mem.RSS } if mp, e := p.MemoryPercent(); e == nil { item.MemPercent = mp } if ct, e := p.CreateTime(); e == nil { item.CreateTime = ct } if exe, e := p.Exe(); e == nil { item.Exe = exe } if cmd, e := p.Cmdline(); e == nil { item.Cmdline = cmd } items = append(items, item) } sort.Slice(items, func(i, j int) bool { return items[i].MemPercent > items[j].MemPercent }) webhttp.WriteJSON(w, http.StatusOK, map[string]any{"code": 0, "msg": "OK", "data": map[string]any{"list": items}}) } // killProcess 结束进程: {pid: 必填, name: 可选校验(防 pid 复用误杀)}。 // Windows 上 gopsutil 权限不足时回退 taskkill /F /T(连子进程树强杀)。 func killProcess(w http.ResponseWriter, r *http.Request) { var req struct { Pid int32 `json:"pid"` Name string `json:"name"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Pid <= 0 { fail(w, "参数错误: pid 必填且为正整数") return } p, err := process.NewProcess(req.Pid) if err != nil { fail(w, "进程不存在: "+err.Error()) return } if req.Name != "" { if name, e := p.Name(); e == nil && name != "" && name != req.Name { fail(w, "进程名不匹配(实际 "+name+"), 已拒绝结束") return } } if err := p.Kill(); err != nil { if runtime.GOOS != "windows" { fail(w, "结束进程失败: "+err.Error()) return } if out, e := exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(int(req.Pid))).CombinedOutput(); e != nil { fail(w, "结束进程失败: "+err.Error()+"; taskkill: "+string(out)) return } } webhttp.WriteJSON(w, http.StatusOK, map[string]any{"code": 0, "msg": "OK", "data": map[string]any{"pid": req.Pid}}) }