- 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 同步更新
290 lines
7.6 KiB
Go
290 lines
7.6 KiB
Go
package executor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os/exec"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// runNetPing implements `kopsc net ping <host>`: connectivity check.
|
|
func runNetPing(ctx context.Context, req BuiltinRequest) error {
|
|
opts, err := parseNetPingArgs(req.Args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
args := []string{}
|
|
if opts.count > 0 {
|
|
if runtime.GOOS == "windows" {
|
|
args = append(args, "-n", strconv.Itoa(opts.count))
|
|
} else {
|
|
args = append(args, "-c", strconv.Itoa(opts.count))
|
|
}
|
|
}
|
|
if opts.interval > 0 {
|
|
if runtime.GOOS != "windows" {
|
|
args = append(args, "-i", strconv.Itoa(opts.interval))
|
|
}
|
|
}
|
|
args = append(args, opts.host)
|
|
|
|
cmd := exec.CommandContext(ctx, "ping", args...)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
|
|
type netPingOptions struct {
|
|
host string
|
|
count int
|
|
interval int
|
|
}
|
|
|
|
func parseNetPingArgs(args []string) (netPingOptions, error) {
|
|
opts := netPingOptions{count: 4}
|
|
var positional []string
|
|
|
|
for i := 0; i < len(args); i++ {
|
|
arg := args[i]
|
|
switch {
|
|
case arg == "--":
|
|
positional = append(positional, args[i+1:]...)
|
|
i = len(args)
|
|
case arg == "-c" || arg == "-n" || arg == "--count":
|
|
if i+1 >= len(args) {
|
|
return opts, fmt.Errorf("%s 缺少次数", arg)
|
|
}
|
|
i++
|
|
n, err := strconv.Atoi(args[i])
|
|
if err != nil || n < 1 {
|
|
return opts, fmt.Errorf("次数需要正整数,收到 %q", args[i])
|
|
}
|
|
opts.count = n
|
|
case arg == "-i" || arg == "--interval":
|
|
if i+1 >= len(args) {
|
|
return opts, fmt.Errorf("%s 缺少间隔秒数", arg)
|
|
}
|
|
i++
|
|
n, err := strconv.Atoi(args[i])
|
|
if err != nil || n < 1 {
|
|
return opts, fmt.Errorf("间隔需要正整数,收到 %q", args[i])
|
|
}
|
|
opts.interval = n
|
|
case strings.HasPrefix(arg, "-") && arg != "-":
|
|
return opts, fmt.Errorf("未知选项 %q", arg)
|
|
default:
|
|
positional = append(positional, arg)
|
|
}
|
|
}
|
|
if len(positional) == 0 {
|
|
return opts, fmt.Errorf("缺少目标主机;用法: kopsc net ping <主机>")
|
|
}
|
|
opts.host = positional[0]
|
|
return opts, nil
|
|
}
|
|
|
|
// runNetTrace implements `kopsc net trace <host>`: route tracing.
|
|
func runNetTrace(ctx context.Context, req BuiltinRequest) error {
|
|
if len(req.Args) == 0 {
|
|
return fmt.Errorf("缺少目标主机;用法: kopsc net trace <主机>")
|
|
}
|
|
host := req.Args[0]
|
|
|
|
// Try mtr first (best output), then traceroute, then tracert on Windows.
|
|
if path, err := exec.LookPath("mtr"); err == nil {
|
|
cmd := exec.CommandContext(ctx, path, "--report", "--report-wide", host)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
if runtime.GOOS == "windows" {
|
|
cmd := exec.CommandContext(ctx, "tracert", host)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
if path, err := exec.LookPath("traceroute"); err == nil {
|
|
cmd := exec.CommandContext(ctx, path, host)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
return fmt.Errorf("未找到 traceroute 或 mtr,请安装其中之一")
|
|
}
|
|
|
|
// runNetDNS implements `kopsc net dns <host>`: DNS lookup.
|
|
func runNetDNS(ctx context.Context, req BuiltinRequest) error {
|
|
opts, err := parseNetDNSArgs(req.Args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Try dig first, then nslookup, then host.
|
|
if path, err := exec.LookPath("dig"); err == nil {
|
|
args := []string{opts.host}
|
|
if opts.recordType != "" {
|
|
args = []string{opts.host, opts.recordType}
|
|
}
|
|
if opts.server != "" {
|
|
args = append(args, "@"+opts.server)
|
|
}
|
|
args = append(args, "+noall", "+answer")
|
|
cmd := exec.CommandContext(ctx, path, args...)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
if path, err := exec.LookPath("nslookup"); err == nil {
|
|
args := []string{opts.host}
|
|
if opts.server != "" {
|
|
args = []string{opts.host, opts.server}
|
|
}
|
|
cmd := exec.CommandContext(ctx, path, args...)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
if path, err := exec.LookPath("host"); err == nil {
|
|
args := []string{opts.host}
|
|
if opts.server != "" {
|
|
args = append(args, opts.server)
|
|
}
|
|
cmd := exec.CommandContext(ctx, path, args...)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
return fmt.Errorf("未找到 dig、nslookup 或 host,请安装其中之一")
|
|
}
|
|
|
|
type netDNSOptions struct {
|
|
host string
|
|
recordType string
|
|
server string
|
|
}
|
|
|
|
func parseNetDNSArgs(args []string) (netDNSOptions, error) {
|
|
var opts netDNSOptions
|
|
var positional []string
|
|
|
|
for i := 0; i < len(args); i++ {
|
|
arg := args[i]
|
|
switch {
|
|
case arg == "--":
|
|
positional = append(positional, args[i+1:]...)
|
|
i = len(args)
|
|
case arg == "-t" || arg == "--type":
|
|
if i+1 >= len(args) {
|
|
return opts, fmt.Errorf("%s 缺少记录类型", arg)
|
|
}
|
|
i++
|
|
opts.recordType = strings.ToUpper(args[i])
|
|
case strings.HasPrefix(arg, "--type="):
|
|
opts.recordType = strings.ToUpper(strings.TrimPrefix(arg, "--type="))
|
|
case arg == "-s" || arg == "--server":
|
|
if i+1 >= len(args) {
|
|
return opts, fmt.Errorf("%s 缺少 DNS 服务器", arg)
|
|
}
|
|
i++
|
|
opts.server = args[i]
|
|
case strings.HasPrefix(arg, "--server="):
|
|
opts.server = strings.TrimPrefix(arg, "--server=")
|
|
case strings.HasPrefix(arg, "-") && arg != "-":
|
|
return opts, fmt.Errorf("未知选项 %q", arg)
|
|
default:
|
|
positional = append(positional, arg)
|
|
}
|
|
}
|
|
if len(positional) == 0 {
|
|
return opts, fmt.Errorf("缺少域名;用法: kopsc net dns <域名>")
|
|
}
|
|
opts.host = positional[0]
|
|
return opts, nil
|
|
}
|
|
|
|
// runNetCurl implements `kopsc net curl <url>`: HTTP diagnostics showing
|
|
// status code, time breakdown, and redirect chain.
|
|
func runNetCurl(ctx context.Context, req BuiltinRequest) error {
|
|
opts, err := parseNetCurlArgs(req.Args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := exec.LookPath("curl"); err != nil {
|
|
return fmt.Errorf("net curl 需要 curl,请先安装")
|
|
}
|
|
|
|
args := []string{"-sS", "-o", "/dev/null", "-w",
|
|
"HTTP 状态码: %{http_code}\n" +
|
|
"重定向次数: %{num_redirects}\n" +
|
|
"DNS 解析: %{time_namelookup}s\n" +
|
|
"TCP 连接: %{time_connect}s\n" +
|
|
"TLS 握手: %{time_appconnect}s\n" +
|
|
"首字节: %{time_starttransfer}s\n" +
|
|
"总耗时: %{time_total}s\n" +
|
|
"下载大小: %{size_download} bytes\n" +
|
|
"下载速度: %{speed_download} bytes/s\n",
|
|
}
|
|
if opts.follow {
|
|
args = append(args, "-L")
|
|
}
|
|
if opts.method != "" {
|
|
args = append(args, "-X", opts.method)
|
|
}
|
|
for _, h := range opts.headers {
|
|
args = append(args, "-H", h)
|
|
}
|
|
args = append(args, opts.url)
|
|
|
|
cmd := exec.CommandContext(ctx, "curl", args...)
|
|
cmd.Stdout = req.Stdout
|
|
cmd.Stderr = req.Stderr
|
|
return cmd.Run()
|
|
}
|
|
|
|
type netCurlOptions struct {
|
|
url string
|
|
follow bool
|
|
method string
|
|
headers []string
|
|
}
|
|
|
|
func parseNetCurlArgs(args []string) (netCurlOptions, error) {
|
|
var opts netCurlOptions
|
|
var positional []string
|
|
|
|
for i := 0; i < len(args); i++ {
|
|
arg := args[i]
|
|
switch {
|
|
case arg == "--":
|
|
positional = append(positional, args[i+1:]...)
|
|
i = len(args)
|
|
case arg == "-L" || arg == "--follow":
|
|
opts.follow = true
|
|
case arg == "-X" || arg == "--method":
|
|
if i+1 >= len(args) {
|
|
return opts, fmt.Errorf("%s 缺少 HTTP 方法", arg)
|
|
}
|
|
i++
|
|
opts.method = strings.ToUpper(args[i])
|
|
case arg == "-H" || arg == "--header":
|
|
if i+1 >= len(args) {
|
|
return opts, fmt.Errorf("%s 缺少 header 值", arg)
|
|
}
|
|
i++
|
|
opts.headers = append(opts.headers, args[i])
|
|
case strings.HasPrefix(arg, "-") && arg != "-":
|
|
return opts, fmt.Errorf("未知选项 %q", arg)
|
|
default:
|
|
positional = append(positional, arg)
|
|
}
|
|
}
|
|
if len(positional) == 0 {
|
|
return opts, fmt.Errorf("缺少 URL;用法: kopsc net curl <URL>")
|
|
}
|
|
opts.url = positional[0]
|
|
return opts, nil
|
|
}
|