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,239 @@
|
||||
package executor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// runFwStatus implements `kopsc fw status`: show current firewall rules.
|
||||
func runFwStatus(ctx context.Context, req BuiltinRequest) error {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return fwStatusLinux(ctx, req)
|
||||
case "darwin":
|
||||
return fwStatusDarwin(ctx, req)
|
||||
case "windows":
|
||||
return fwStatusWindows(ctx, req)
|
||||
default:
|
||||
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
func fwStatusLinux(ctx context.Context, req BuiltinRequest) error {
|
||||
// Try ufw first, then firewalld, then iptables.
|
||||
if path, err := exec.LookPath("ufw"); err == nil {
|
||||
cmd := exec.CommandContext(ctx, path, "status", "verbose")
|
||||
cmd.Stdout = req.Stdout
|
||||
cmd.Stderr = req.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
if path, err := exec.LookPath("firewall-cmd"); err == nil {
|
||||
fmt.Fprintln(req.Stdout, "=== firewalld 状态 ===")
|
||||
cmd := exec.CommandContext(ctx, path, "--state")
|
||||
cmd.Stdout = req.Stdout
|
||||
cmd.Stderr = req.Stderr
|
||||
_ = cmd.Run()
|
||||
|
||||
fmt.Fprintln(req.Stdout, "\n=== 已开放端口 ===")
|
||||
listCmd := exec.CommandContext(ctx, path, "--list-ports")
|
||||
listCmd.Stdout = req.Stdout
|
||||
listCmd.Stderr = req.Stderr
|
||||
_ = listCmd.Run()
|
||||
|
||||
fmt.Fprintln(req.Stdout, "\n=== 已允许服务 ===")
|
||||
svcCmd := exec.CommandContext(ctx, path, "--list-services")
|
||||
svcCmd.Stdout = req.Stdout
|
||||
svcCmd.Stderr = req.Stderr
|
||||
return svcCmd.Run()
|
||||
}
|
||||
if path, err := exec.LookPath("iptables"); err == nil {
|
||||
cmd := exec.CommandContext(ctx, path, "-L", "-n", "--line-numbers")
|
||||
cmd.Stdout = req.Stdout
|
||||
cmd.Stderr = req.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
return fmt.Errorf("未找到 ufw、firewall-cmd 或 iptables")
|
||||
}
|
||||
|
||||
func fwStatusDarwin(ctx context.Context, req BuiltinRequest) error {
|
||||
fmt.Fprintln(req.Stdout, "macOS 使用内置应用防火墙(ALF)")
|
||||
cmd := exec.CommandContext(ctx, "/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate")
|
||||
cmd.Stdout = req.Stdout
|
||||
cmd.Stderr = req.Stderr
|
||||
_ = cmd.Run()
|
||||
|
||||
fmt.Fprintln(req.Stdout, "\n=== 已允许的应用 ===")
|
||||
listCmd := exec.CommandContext(ctx, "/usr/libexec/ApplicationFirewall/socketfilterfw", "--listapps")
|
||||
listCmd.Stdout = req.Stdout
|
||||
listCmd.Stderr = req.Stderr
|
||||
return listCmd.Run()
|
||||
}
|
||||
|
||||
func fwStatusWindows(ctx context.Context, req BuiltinRequest) error {
|
||||
cmd := exec.CommandContext(ctx, "netsh", "advfirewall", "firewall", "show", "rule", "name=all", "dir=in", "status=enabled")
|
||||
cmd.Stdout = req.Stdout
|
||||
cmd.Stderr = req.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// runFwAllow implements `kopsc fw allow <port>/<protocol>`: open a port.
|
||||
func runFwAllow(ctx context.Context, req BuiltinRequest) error {
|
||||
opts, err := parseFwPortArgs(req.Args, "allow")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return fwAllowLinux(ctx, opts, req)
|
||||
case "darwin":
|
||||
return fmt.Errorf("macOS 不支持通过命令行开放端口,请在系统偏好设置中配置")
|
||||
case "windows":
|
||||
return fwAllowWindows(ctx, opts, req)
|
||||
default:
|
||||
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
func fwAllowLinux(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
|
||||
portProto := fmt.Sprintf("%d/%s", opts.port, opts.protocol)
|
||||
if path, err := exec.LookPath("ufw"); err == nil {
|
||||
fmt.Fprintf(req.Stdout, "开放端口: %s (ufw)\n", portProto)
|
||||
return runCommand(ctx, path, []string{"allow", portProto}, req)
|
||||
}
|
||||
if path, err := exec.LookPath("firewall-cmd"); err == nil {
|
||||
fmt.Fprintf(req.Stdout, "开放端口: %s (firewalld)\n", portProto)
|
||||
if err := runCommand(ctx, path, []string{"--add-port=" + portProto, "--permanent"}, req); err != nil {
|
||||
return err
|
||||
}
|
||||
return runCommand(ctx, path, []string{"--reload"}, req)
|
||||
}
|
||||
return fmt.Errorf("未找到 ufw 或 firewall-cmd")
|
||||
}
|
||||
|
||||
func fwAllowWindows(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
|
||||
name := fmt.Sprintf("kopsc-allow-%d-%s", opts.port, opts.protocol)
|
||||
fmt.Fprintf(req.Stdout, "开放端口: %d/%s (Windows Firewall)\n", opts.port, opts.protocol)
|
||||
return runCommand(ctx, "netsh", []string{
|
||||
"advfirewall", "firewall", "add", "rule",
|
||||
"name=" + name, "dir=in", "action=allow",
|
||||
"protocol=" + opts.protocol, "localport=" + fmt.Sprintf("%d", opts.port),
|
||||
}, req)
|
||||
}
|
||||
|
||||
// runFwDeny implements `kopsc fw deny <port>/<protocol>`: close a port.
|
||||
func runFwDeny(ctx context.Context, req BuiltinRequest) error {
|
||||
opts, err := parseFwPortArgs(req.Args, "deny")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return fwDenyLinux(ctx, opts, req)
|
||||
case "darwin":
|
||||
return fmt.Errorf("macOS 不支持通过命令行关闭端口,请在系统偏好设置中配置")
|
||||
case "windows":
|
||||
return fwDenyWindows(ctx, opts, req)
|
||||
default:
|
||||
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
func fwDenyLinux(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
|
||||
portProto := fmt.Sprintf("%d/%s", opts.port, opts.protocol)
|
||||
if path, err := exec.LookPath("ufw"); err == nil {
|
||||
fmt.Fprintf(req.Stdout, "关闭端口: %s (ufw)\n", portProto)
|
||||
return runCommand(ctx, path, []string{"deny", portProto}, req)
|
||||
}
|
||||
if path, err := exec.LookPath("firewall-cmd"); err == nil {
|
||||
fmt.Fprintf(req.Stdout, "关闭端口: %s (firewalld)\n", portProto)
|
||||
if err := runCommand(ctx, path, []string{"--remove-port=" + portProto, "--permanent"}, req); err != nil {
|
||||
return err
|
||||
}
|
||||
return runCommand(ctx, path, []string{"--reload"}, req)
|
||||
}
|
||||
return fmt.Errorf("未找到 ufw 或 firewall-cmd")
|
||||
}
|
||||
|
||||
func fwDenyWindows(ctx context.Context, opts fwPortOptions, req BuiltinRequest) error {
|
||||
name := fmt.Sprintf("kopsc-allow-%d-%s", opts.port, opts.protocol)
|
||||
fmt.Fprintf(req.Stdout, "关闭端口: %d/%s (Windows Firewall)\n", opts.port, opts.protocol)
|
||||
return runCommand(ctx, "netsh", []string{
|
||||
"advfirewall", "firewall", "delete", "rule",
|
||||
"name=" + name,
|
||||
}, req)
|
||||
}
|
||||
|
||||
type fwPortOptions struct {
|
||||
port int
|
||||
protocol string
|
||||
}
|
||||
|
||||
func parseFwPortArgs(args []string, action string) (fwPortOptions, error) {
|
||||
opts := fwPortOptions{protocol: "tcp"}
|
||||
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 == "-p" || arg == "--protocol":
|
||||
if i+1 >= len(args) {
|
||||
return opts, fmt.Errorf("%s 缺少协议", arg)
|
||||
}
|
||||
i++
|
||||
proto := strings.ToLower(args[i])
|
||||
if proto != "tcp" && proto != "udp" {
|
||||
return opts, fmt.Errorf("协议必须是 tcp 或 udp,收到 %q", args[i])
|
||||
}
|
||||
opts.protocol = proto
|
||||
case strings.HasPrefix(arg, "--protocol="):
|
||||
proto := strings.ToLower(strings.TrimPrefix(arg, "--protocol="))
|
||||
if proto != "tcp" && proto != "udp" {
|
||||
return opts, fmt.Errorf("协议必须是 tcp 或 udp,收到 %q", strings.TrimPrefix(arg, "--protocol="))
|
||||
}
|
||||
opts.protocol = proto
|
||||
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 fw %s <端口>[/<协议>]", action)
|
||||
}
|
||||
|
||||
portStr := positional[0]
|
||||
if idx := strings.Index(portStr, "/"); idx >= 0 {
|
||||
proto := strings.ToLower(portStr[idx+1:])
|
||||
if proto != "tcp" && proto != "udp" {
|
||||
return opts, fmt.Errorf("协议必须是 tcp 或 udp,收到 %q", proto)
|
||||
}
|
||||
opts.protocol = proto
|
||||
portStr = portStr[:idx]
|
||||
}
|
||||
|
||||
n, err := parseInt(portStr)
|
||||
if err != nil || n < 1 || n > 65535 {
|
||||
return opts, fmt.Errorf("端口必须是 1-65535 的整数,收到 %q", positional[0])
|
||||
}
|
||||
opts.port = n
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func parseInt(s string) (int, error) {
|
||||
n := 0
|
||||
for _, c := range s {
|
||||
if c < '0' || c > '9' {
|
||||
return 0, fmt.Errorf("不是整数: %q", s)
|
||||
}
|
||||
n = n*10 + int(c-'0')
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
Reference in New Issue
Block a user