feat: 新增 file/svc/archive/sys/log/fw 分组并扩充 net、proc
- file:findbig 按大小、findrecent 按修改时间、findname 按名称查找文件 - svc:systemctl / launchctl / sc 统一接口,status、enable、disable、logs - archive:按扩展名自动识别格式并解压,支持 tar.gz/tgz/tar.bz2/tar.xz/tar/zip/gz - sysinfo:OS、CPU、内存、磁盘一览 - log:journalctl / log show / wevtutil 搜索关键字与查看最近日志 - fw:ufw / firewalld / iptables / ALF / netsh 统一接口,status、allow、deny - net 新增 ping、trace(mtr 优先)、dns(dig 优先)、curl HTTP 诊断 - proc 新增 topmem、topcpu、killname - 危险操作(svc enable/disable、proc killname、fw allow/deny)需 --yes 确认 - 注册表、executor 派发表、cli 分组说明与 README 同步更新
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package executor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.odboy.cn/odboy/kenaito-kopsc/internal/platform"
|
||||
)
|
||||
|
||||
// runSysInfo implements `kopsc sysinfo`: show OS, CPU, memory, disk overview.
|
||||
func runSysInfo(ctx context.Context, req BuiltinRequest) error {
|
||||
w := req.Stdout
|
||||
res := platform.DetectResources()
|
||||
|
||||
fmt.Fprintf(w, "=== 系统信息 ===\n\n")
|
||||
|
||||
fmt.Fprintf(w, "操作系统: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
if runtime.GOOS == "linux" {
|
||||
if data, err := os.ReadFile("/etc/os-release"); err == nil {
|
||||
distro, _ := platform.ParseOSRelease(data)
|
||||
if distro != "" {
|
||||
fmt.Fprintf(w, "发行版: %s\n", distro)
|
||||
}
|
||||
}
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
fmt.Fprintf(w, "主机名: %s\n", hostname)
|
||||
}
|
||||
if data, err := os.ReadFile("/proc/version"); err == nil {
|
||||
fields := strings.Fields(string(data))
|
||||
if len(fields) >= 3 {
|
||||
fmt.Fprintf(w, "内核: %s\n", fields[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "\n=== CPU ===\n\n")
|
||||
fmt.Fprintf(w, "逻辑核心: %d\n", res.CPUs)
|
||||
if runtime.GOOS == "linux" {
|
||||
if model := cpuModelLinux(); model != "" {
|
||||
fmt.Fprintf(w, "型号: %s\n", model)
|
||||
}
|
||||
} else if runtime.GOOS == "darwin" {
|
||||
if model := cpuModelDarwin(ctx); model != "" {
|
||||
fmt.Fprintf(w, "型号: %s\n", model)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "\n=== 内存 ===\n\n")
|
||||
if res.Memory > 0 {
|
||||
fmt.Fprintf(w, "总计: %s\n", humanSize(res.Memory))
|
||||
fmt.Fprintf(w, "检测方式: %s\n", res.Detected)
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
if free := memFreeLinux(); free > 0 {
|
||||
fmt.Fprintf(w, "可用: %s\n", humanSize(free))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "\n=== 磁盘 ===\n\n")
|
||||
printDiskInfo(ctx, w)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cpuModelLinux() string {
|
||||
data, err := os.ReadFile("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
if strings.HasPrefix(line, "model name") {
|
||||
_, value, ok := strings.Cut(line, ":")
|
||||
if ok {
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cpuModelDarwin(ctx context.Context) string {
|
||||
out, err := exec.CommandContext(ctx, "sysctl", "-n", "machdep.cpu.brand_string").Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
func memFreeLinux() int64 {
|
||||
data, err := os.ReadFile("/proc/meminfo")
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
if fields[0] == "MemAvailable:" || fields[0] == "MemFree:" {
|
||||
n, err := strconv.ParseInt(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return n * 1024
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func printDiskInfo(ctx context.Context, w interface{ Write([]byte) (int, error) }) {
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd := exec.CommandContext(ctx, "wmic", "logicaldisk", "get", "size,freespace,caption", "/format:list")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "无法获取磁盘信息")
|
||||
return
|
||||
}
|
||||
parseWMICDisk(string(out), w)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "df", "-h")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "无法获取磁盘信息")
|
||||
return
|
||||
}
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
if len(lines) == 0 {
|
||||
return
|
||||
}
|
||||
for _, line := range lines {
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
}
|
||||
|
||||
func parseWMICDisk(out string, w interface{ Write([]byte) (int, error) }) {
|
||||
var caption, freeStr, sizeStr string
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "Caption=") {
|
||||
caption = strings.TrimPrefix(line, "Caption=")
|
||||
} else if strings.HasPrefix(line, "FreeSpace=") {
|
||||
freeStr = strings.TrimPrefix(line, "FreeSpace=")
|
||||
} else if strings.HasPrefix(line, "Size=") {
|
||||
sizeStr = strings.TrimPrefix(line, "Size=")
|
||||
if caption != "" && sizeStr != "" {
|
||||
size, _ := strconv.ParseInt(sizeStr, 10, 64)
|
||||
free, _ := strconv.ParseInt(freeStr, 10, 64)
|
||||
fmt.Fprintf(w, "%-6s 总计 %-10s 可用 %-10s\n", caption, humanSize(size), humanSize(free))
|
||||
caption, freeStr, sizeStr = "", "", ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user