mirror of https://gitee.com/godoos/godoos.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
6.5 KiB
243 lines
6.5 KiB
/*
|
|
* GodoOS - A lightweight cloud desktop
|
|
* Copyright (C) 2024 https://godoos.com
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package store
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os/exec"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type ProcessSystemInfo struct {
|
|
PID int `json:"pid"`
|
|
Port int `json:"port"`
|
|
Proto string `json:"proto"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type AllProcessesResponse struct {
|
|
Processes []ProcessSystemInfo `json:"processes"`
|
|
}
|
|
|
|
//var processInfoRegex = regexp.MustCompile(`(\d+)\s+.*:\s*(\d+)\s+.*LISTEN\s+.*:(\d+)`)
|
|
|
|
func listAllProcesses() ([]ProcessSystemInfo, error) {
|
|
osType := runtime.GOOS
|
|
|
|
var cmd *exec.Cmd
|
|
var output []byte
|
|
var err error
|
|
|
|
switch osType {
|
|
case "darwin", "linux":
|
|
cmd = exec.Command("lsof", "-i")
|
|
case "windows":
|
|
cmd = exec.Command("netstat", "-ano")
|
|
cmd = SetHideConsoleCursor(cmd)
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported operating system")
|
|
}
|
|
|
|
output, err = cmd.CombinedOutput()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list all processes: %v", err)
|
|
}
|
|
//lsofRegex := regexp.MustCompile(`(\d+)\s+(\S+\s+\S+)\s+(\d+)\s+(\w+)\s+IPv4\s+(\w+)\s+(\w+)\s+TCP\s+(\S+)\->\s+(\S+)\s+\((\w+)\)`)
|
|
|
|
processes := make([]ProcessSystemInfo, 0)
|
|
// 初始化映射用于去重
|
|
seenPIDs := make(map[int]bool)
|
|
// 解析输出
|
|
switch osType {
|
|
case "darwin", "linux":
|
|
scanner := bufio.NewScanner(bytes.NewBuffer(output))
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
fields := strings.Fields(line)
|
|
if len(fields) >= 9 && fields[7] == "TCP" {
|
|
// 解析 PID 和其他字段
|
|
pid, _ := strconv.Atoi(fields[1])
|
|
name := fields[0]
|
|
// 解析本地端口
|
|
localPortStr := strings.Split(fields[8], ":")[1]
|
|
localPort, _ := strconv.Atoi(localPortStr)
|
|
if !seenPIDs[pid] {
|
|
seenPIDs[pid] = true
|
|
processes = append(processes, ProcessSystemInfo{
|
|
PID: pid,
|
|
Port: localPort,
|
|
Proto: "TCP",
|
|
Name: name,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
case "windows":
|
|
scanner := bufio.NewScanner(bytes.NewBuffer(output))
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
// 需要针对Windows的netstat输出格式进行解析
|
|
// 示例:TCP 0.0.0.0:80 0.0.0.0:* LISTENING 1234
|
|
fields := strings.Fields(line)
|
|
if len(fields) >= 4 && fields[3] == "LISTENING" {
|
|
_, port, err := net.SplitHostPort(fields[1])
|
|
if err != nil {
|
|
log.Printf("Failed to parse port: %v", err)
|
|
continue
|
|
}
|
|
pid, _ := strconv.Atoi(fields[4])
|
|
processName, err := getProcessName(osType, pid)
|
|
if err != nil {
|
|
log.Printf("Failed to get process name for PID %d: %v", pid, err)
|
|
continue
|
|
}
|
|
portInt, err := strconv.Atoi(port)
|
|
if err != nil {
|
|
log.Printf("Failed to convert port to integer: %v", err)
|
|
continue
|
|
}
|
|
if !seenPIDs[pid] {
|
|
seenPIDs[pid] = true
|
|
processes = append(processes, ProcessSystemInfo{
|
|
PID: pid,
|
|
Port: portInt,
|
|
Proto: fields[0],
|
|
Name: processName,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return processes, nil
|
|
}
|
|
|
|
func getProcessName(osType string, pid int) (string, error) {
|
|
var cmd *exec.Cmd
|
|
var output []byte
|
|
var err error
|
|
|
|
switch osType {
|
|
case "darwin", "linux":
|
|
cmd = exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "comm=")
|
|
case "windows":
|
|
cmd = exec.Command("tasklist", "/FI", fmt.Sprintf("PID eq %d", pid), "/NH")
|
|
cmd = SetHideConsoleCursor(cmd)
|
|
default:
|
|
return "", fmt.Errorf("unsupported operating system")
|
|
}
|
|
|
|
output, err = cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get process name: %v", err)
|
|
}
|
|
// log.Printf("output: %s", output)
|
|
switch osType {
|
|
case "darwin", "linux":
|
|
return strings.TrimSpace(string(output)), nil
|
|
case "windows":
|
|
parts := strings.Fields(string(output))
|
|
if len(parts) >= 1 {
|
|
return parts[0], nil
|
|
}
|
|
return "", fmt.Errorf("no process name found in output")
|
|
}
|
|
|
|
return "", fmt.Errorf("unknown error getting process name")
|
|
}
|
|
|
|
func killProcessByName(name string) error {
|
|
osType := runtime.GOOS
|
|
|
|
var cmd *exec.Cmd
|
|
var err error
|
|
|
|
switch osType {
|
|
case "darwin", "linux":
|
|
// 使用 pgrep 查找进程 PID
|
|
pgrepCmd := exec.Command("pgrep", "-f", name)
|
|
pgrepOutput, err := pgrepCmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find process with name %s: %v", name, err)
|
|
}
|
|
|
|
// 将输出转换为字符串并按行分割
|
|
pids := strings.Split(strings.TrimSpace(string(pgrepOutput)), "\n")
|
|
|
|
// 对于每个找到的 PID,使用 kill 命令杀死进程
|
|
for _, pidStr := range pids {
|
|
pid, err := strconv.Atoi(pidStr)
|
|
if err != nil {
|
|
log.Printf("Failed to convert PID to integer: %v", err)
|
|
continue
|
|
}
|
|
// 每次循环使用新的 *exec.Cmd 实例
|
|
killCmd := exec.Command("kill", "-9", strconv.Itoa(pid))
|
|
if err := killCmd.Run(); err != nil {
|
|
log.Printf("Failed to kill process with PID %d: %v", pid, err)
|
|
}
|
|
}
|
|
case "windows":
|
|
cmd = exec.Command("taskkill", "/IM", name, "/F") // /F 表示强制结束
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
log.Printf("Failed to kill process with name %s: %v", name, err)
|
|
}
|
|
default:
|
|
return fmt.Errorf("unsupported operating system")
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func KillProcessByNameHandler(w http.ResponseWriter, r *http.Request) {
|
|
name := r.URL.Query().Get("name")
|
|
if err := killProcessByName(name); err != nil {
|
|
http.Error(w, fmt.Sprintf("Failed to kill process: %v", err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
fmt.Fprintf(w, "Process '%s' has been killed", name)
|
|
}
|
|
|
|
func ListAllProcessesHandler(w http.ResponseWriter, r *http.Request) {
|
|
processes, err := listAllProcesses()
|
|
if err != nil {
|
|
http.Error(w, "Failed to list all processes", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
response := AllProcessesResponse{
|
|
Processes: processes,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|