mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-26 09:22:50 +08:00
284 lines
5.9 KiB
Go
284 lines
5.9 KiB
Go
package service
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/websocket"
|
|
"github.com/shirou/gopsutil/v4/process"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type ProcessService struct{}
|
|
|
|
type IProcessService interface {
|
|
StopProcess(req request.ProcessReq) error
|
|
GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error)
|
|
}
|
|
|
|
func NewIProcessService() IProcessService {
|
|
return &ProcessService{}
|
|
}
|
|
|
|
func (ps *ProcessService) StopProcess(req request.ProcessReq) error {
|
|
proc, err := process.NewProcess(req.PID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := proc.Kill(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error) {
|
|
p, err := process.NewProcess(pid)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get process info by pid %v: %v", pid, err)
|
|
}
|
|
|
|
exists, err := p.IsRunning()
|
|
if err != nil || !exists {
|
|
return nil, fmt.Errorf("process %v is not running", pid)
|
|
}
|
|
|
|
data := &websocket.PsProcessData{
|
|
PID: pid,
|
|
}
|
|
|
|
if name, err := p.Name(); err == nil {
|
|
data.Name = name
|
|
}
|
|
|
|
if ppid, err := p.Ppid(); err == nil {
|
|
data.PPID = ppid
|
|
}
|
|
|
|
if username, err := p.Username(); err == nil {
|
|
data.Username = username
|
|
}
|
|
|
|
if status, err := p.Status(); err == nil {
|
|
if len(status) > 0 {
|
|
data.Status = status[0]
|
|
}
|
|
}
|
|
|
|
if createTime, err := p.CreateTime(); err == nil {
|
|
data.StartTime = time.Unix(createTime/1000, 0).Format("2006-01-02 15:04:05")
|
|
}
|
|
|
|
if numThreads, err := p.NumThreads(); err == nil {
|
|
data.NumThreads = numThreads
|
|
}
|
|
|
|
if connections, err := p.Connections(); err == nil {
|
|
data.NumConnections = len(connections)
|
|
|
|
var connects []websocket.ProcessConnect
|
|
for _, conn := range connections {
|
|
pc := websocket.ProcessConnect{
|
|
Status: conn.Status,
|
|
Laddr: conn.Laddr,
|
|
Raddr: conn.Raddr,
|
|
PID: pid,
|
|
Name: data.Name,
|
|
}
|
|
connects = append(connects, pc)
|
|
}
|
|
data.Connects = connects
|
|
}
|
|
|
|
if cpuPercent, err := p.CPUPercent(); err == nil {
|
|
data.CpuValue = cpuPercent
|
|
data.CpuPercent = fmt.Sprintf("%.2f%%", cpuPercent)
|
|
}
|
|
|
|
if ioCounters, err := p.IOCounters(); err == nil {
|
|
data.DiskRead = common.FormatBytes(ioCounters.ReadBytes)
|
|
data.DiskWrite = common.FormatBytes(ioCounters.WriteBytes)
|
|
}
|
|
|
|
if cmdline, err := p.Cmdline(); err == nil {
|
|
data.CmdLine = cmdline
|
|
}
|
|
|
|
if memDetail, err := getMemoryDetail(p.Pid); err == nil {
|
|
data.Rss = common.FormatBytes(memDetail.RSS)
|
|
data.VMS = common.FormatBytes(memDetail.VMS)
|
|
data.HWM = common.FormatBytes(memDetail.HWM)
|
|
data.Data = common.FormatBytes(memDetail.Data)
|
|
data.Stack = common.FormatBytes(memDetail.Stack)
|
|
data.Locked = common.FormatBytes(memDetail.Locked)
|
|
data.Swap = common.FormatBytes(memDetail.Swap)
|
|
data.Dirty = common.FormatBytes(memDetail.Dirty)
|
|
data.RssValue = memDetail.RSS
|
|
data.PSS = common.FormatBytes(memDetail.PSS)
|
|
data.USS = common.FormatBytes(memDetail.USS)
|
|
data.Shared = common.FormatBytes(memDetail.Shared)
|
|
data.Text = common.FormatBytes(memDetail.Text)
|
|
}
|
|
|
|
if envs, err := p.Environ(); err == nil {
|
|
data.Envs = envs
|
|
}
|
|
|
|
if openFiles, err := p.OpenFiles(); err == nil {
|
|
data.OpenFiles = openFiles
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
type MemoryDetail struct {
|
|
RSS uint64
|
|
VMS uint64
|
|
HWM uint64
|
|
Data uint64
|
|
Stack uint64
|
|
Locked uint64
|
|
Swap uint64
|
|
|
|
PSS uint64
|
|
USS uint64
|
|
Shared uint64
|
|
Text uint64
|
|
Dirty uint64
|
|
}
|
|
|
|
func getMemoryDetail(pid int32) (*MemoryDetail, error) {
|
|
mem := &MemoryDetail{}
|
|
|
|
if err := readStatus(pid, mem); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := readSmapsRollup(pid, mem); err != nil {
|
|
if err := readSmaps(pid, mem); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return mem, nil
|
|
}
|
|
|
|
func readStatus(pid int32, mem *MemoryDetail) error {
|
|
filePath := fmt.Sprintf("/proc/%d/status", pid)
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
|
|
key := strings.TrimSuffix(fields[0], ":")
|
|
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
|
value *= 1024
|
|
|
|
switch key {
|
|
case "VmRSS":
|
|
mem.RSS = value
|
|
case "VmSize":
|
|
mem.VMS = value
|
|
case "VmData":
|
|
mem.Data = value
|
|
case "VmSwap":
|
|
mem.Swap = value
|
|
case "VmExe":
|
|
mem.Text = value
|
|
case "RssShmem":
|
|
mem.Shared = value
|
|
case "VmHWM":
|
|
mem.HWM = value
|
|
case "VmStk":
|
|
mem.Stack = value
|
|
case "VmLck":
|
|
mem.Locked = value
|
|
}
|
|
}
|
|
|
|
return scanner.Err()
|
|
}
|
|
|
|
func readSmapsRollup(pid int32, mem *MemoryDetail) error {
|
|
filePath := fmt.Sprintf("/proc/%d/smaps_rollup", pid)
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
|
|
key := strings.TrimSuffix(fields[0], ":")
|
|
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
|
value *= 1024
|
|
|
|
switch key {
|
|
case "Pss":
|
|
mem.PSS = value
|
|
case "Private_Clean", "Private_Dirty":
|
|
mem.USS += value
|
|
case "Shared_Clean", "Shared_Dirty":
|
|
if mem.Shared == 0 {
|
|
mem.Shared = value
|
|
}
|
|
}
|
|
}
|
|
|
|
return scanner.Err()
|
|
}
|
|
|
|
func readSmaps(pid int32, mem *MemoryDetail) error {
|
|
filePath := fmt.Sprintf("/proc/%d/smaps", pid)
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
|
|
key := strings.TrimSuffix(fields[0], ":")
|
|
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
|
value *= 1024
|
|
|
|
switch key {
|
|
case "Pss":
|
|
mem.PSS += value
|
|
case "Private_Clean", "Private_Dirty":
|
|
mem.USS += value
|
|
case "Shared_Clean", "Shared_Dirty":
|
|
if mem.Shared == 0 {
|
|
mem.Shared += value
|
|
}
|
|
}
|
|
}
|
|
|
|
return scanner.Err()
|
|
}
|