270 lines
6.3 KiB
Go
270 lines
6.3 KiB
Go
package gocmdDaemon
|
||
|
||
import (
|
||
"fmt"
|
||
"net"
|
||
"os"
|
||
"strings"
|
||
"syscall"
|
||
"time"
|
||
|
||
"flag"
|
||
|
||
"github.com/google/uuid"
|
||
)
|
||
|
||
type CmdDaemon struct {
|
||
Name string // app cli command name
|
||
PipePath string // named pipe path
|
||
cmds map[string]CmdHandler
|
||
}
|
||
type CmdRequest struct {
|
||
Id string `json:"id"`
|
||
Cmd string `json:"cmd"`
|
||
Args string `json:"args"`
|
||
IsDebug bool `json:"debug"`
|
||
}
|
||
type CmdResponse struct {
|
||
Id string `json:"id"`
|
||
Data string `json:"data"`
|
||
Error string `json:"error"`
|
||
Continue bool `json:"continue"`
|
||
}
|
||
|
||
type CmdConn struct {
|
||
net.Conn
|
||
Id string
|
||
}
|
||
|
||
// pipeConnection 是一个适配器,将 *os.File 转换为 net.Conn 接口
|
||
type pipeConnection struct {
|
||
*os.File
|
||
}
|
||
|
||
// 实现 net.Conn 接口所需的方法
|
||
func (p *pipeConnection) LocalAddr() net.Addr { return &pipeAddr{p.Name()} }
|
||
func (p *pipeConnection) RemoteAddr() net.Addr { return &pipeAddr{p.Name()} }
|
||
func (p *pipeConnection) SetDeadline(t time.Time) error { return nil }
|
||
func (p *pipeConnection) SetReadDeadline(t time.Time) error { return nil }
|
||
func (p *pipeConnection) SetWriteDeadline(t time.Time) error { return nil }
|
||
|
||
// pipeAddr 实现 net.Addr 接口
|
||
type pipeAddr struct {
|
||
name string
|
||
}
|
||
|
||
func (a *pipeAddr) Network() string { return "pipe" }
|
||
func (a *pipeAddr) String() string { return a.name }
|
||
|
||
func (c *CmdConn) Write(d string) error {
|
||
resp := CmdResponse{
|
||
Id: c.Id,
|
||
Data: d,
|
||
Continue: true,
|
||
}
|
||
return Write(c.Conn, resp)
|
||
}
|
||
func (c *CmdConn) WriteError(err error, isContinue bool) error {
|
||
resp := CmdResponse{
|
||
Id: c.Id,
|
||
Error: err.Error(),
|
||
Continue: isContinue,
|
||
}
|
||
return Write(c.Conn, resp)
|
||
}
|
||
func (c *CmdConn) End(d string) error {
|
||
resp := CmdResponse{
|
||
Id: c.Id,
|
||
Data: d,
|
||
Continue: false,
|
||
}
|
||
return Write(c.Conn, resp)
|
||
}
|
||
|
||
type CmdHandler interface {
|
||
Handle(conn *CmdConn, req *CmdRequest) error
|
||
Description() string
|
||
Usage() string
|
||
}
|
||
|
||
// Listen 启动守护进程并监听命名管道上的连接
|
||
// 参数:
|
||
//
|
||
// c: CmdDaemon 实例指针
|
||
//
|
||
// 返回:
|
||
//
|
||
// error: 监听过程中发生的错误(如果有)
|
||
func (c *CmdDaemon) Listen() error {
|
||
// 删除已存在的命名管道(如果存在)
|
||
if err := os.Remove(c.PipePath); err != nil && !os.IsNotExist(err) {
|
||
return fmt.Errorf("failed to remove existing pipe: %v", err)
|
||
}
|
||
|
||
// 创建命名管道
|
||
if err := syscall.Mkfifo(c.PipePath, 0666); err != nil {
|
||
return fmt.Errorf("failed to create named pipe: %v", err)
|
||
}
|
||
|
||
// 设置管道文件权限
|
||
if err := os.Chmod(c.PipePath, 0666); err != nil {
|
||
return fmt.Errorf("failed to set pipe permissions: %v", err)
|
||
}
|
||
pipe, err := os.OpenFile(c.PipePath, os.O_RDWR, 0)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to open pipe for reading: %v", err)
|
||
}
|
||
defer pipe.Close()
|
||
|
||
// 将文件转换为io.ReadWriter接口,以便与现有的Read/Write函数兼容
|
||
pipeConn := &pipeConnection{pipe}
|
||
for {
|
||
// 打开命名管道进行读取
|
||
|
||
// 处理请求
|
||
// go func(pipe *os.File) {
|
||
|
||
req, err := Read[CmdRequest](pipeConn)
|
||
if err != nil {
|
||
_ = Write(pipeConn, CmdResponse{
|
||
Error: "failed to read request: " + err.Error(),
|
||
Continue: false,
|
||
})
|
||
break
|
||
}
|
||
|
||
cmdHandler, ok := c.cmds[req.Cmd]
|
||
if !ok {
|
||
_ = Write(pipeConn, CmdResponse{
|
||
Error: "unknown command: " + req.Cmd + "\n" + c.Usage(),
|
||
Continue: false,
|
||
})
|
||
continue
|
||
}
|
||
|
||
// 执行命令处理程序
|
||
cmdConn := &CmdConn{Conn: pipeConn, Id: req.Id}
|
||
err = cmdHandler.Handle(cmdConn, req)
|
||
if err != nil {
|
||
_ = cmdConn.End(err.Error() + cmdHandler.Usage())
|
||
}
|
||
// }(pipe)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Usage 生成命令使用说明
|
||
// 参数:
|
||
//
|
||
// c: CmdDaemon 实例指针
|
||
//
|
||
// 返回:
|
||
//
|
||
// string: 命令使用说明字符串
|
||
func (c *CmdDaemon) Usage() string {
|
||
usage := fmt.Sprintf("Usage: %s [options] <command> [args...]\n\n", c.Name)
|
||
usage += "Options:\n"
|
||
usage += " -d, --debug Run command in debug mode\n\n"
|
||
usage += "Commands:\n"
|
||
for cmd, handler := range c.cmds {
|
||
usage += fmt.Sprintf(" %-10s %s\n", cmd, handler.Description())
|
||
}
|
||
return usage
|
||
}
|
||
|
||
func (c *CmdDaemon) RegisterCmd(cmd string, handler CmdHandler) {
|
||
c.cmds[cmd] = handler
|
||
}
|
||
|
||
// isDebug 检查给定的命令是否是调试标志
|
||
// 参数:
|
||
//
|
||
// c: CmdDaemon 实例指针
|
||
// cmd: 要检查的命令字符串
|
||
//
|
||
// 返回:
|
||
//
|
||
// bool: 如果是调试标志返回true,否则返回false
|
||
func (c *CmdDaemon) isDebug(cmd string) bool {
|
||
|
||
return cmd == "--debug" || cmd == "-d"
|
||
}
|
||
|
||
// Run 执行客户端命令并通过Unix socket与守护进程通信
|
||
// 参数:
|
||
//
|
||
// c: CmdDaemon 实例指针
|
||
//
|
||
// 返回:
|
||
//
|
||
// error: 执行过程中发生的错误(如果有)
|
||
func (c *CmdDaemon) Run() (*CmdResponse, error) {
|
||
// 定义flag: -debug 和 -daemon
|
||
// flag := flag.NewFlagSet(c.Name, flag.ContinueOnError)
|
||
var isDebug bool
|
||
flag.BoolVar(&isDebug, "debug", false, "Run command in debug mode")
|
||
flag.BoolVar(&isDebug, "d", false, "Run command in debug mode")
|
||
var daemon bool
|
||
flag.BoolVar(&daemon, "daemon", false, "Run command in daemon mode")
|
||
flag.BoolVar(&daemon, "D", false, "Run command in daemon mode")
|
||
flag.Parse()
|
||
// 从命令参数中解析出是否debug 子命令和剩余参数字符串
|
||
var remainingArgs []string
|
||
cmd := ""
|
||
// if isDebug {
|
||
// if len(args) > 1 {
|
||
// cmd = args[1]
|
||
// remainingArgs = args[2:]
|
||
// } else {
|
||
// cmd = "help"
|
||
// isDebug = false
|
||
// remainingArgs = []string{}
|
||
// }
|
||
// } else {
|
||
// if len(args) > 0 {
|
||
// cmd = args[0]
|
||
// remainingArgs = args[1:]
|
||
// } else {
|
||
// cmd = "help"
|
||
// isDebug = false
|
||
// remainingArgs = []string{}
|
||
// }
|
||
// }
|
||
cmdReq := CmdRequest{
|
||
Args: strings.Join(remainingArgs, " "),
|
||
Cmd: cmd,
|
||
Id: uuid.New().String(),
|
||
IsDebug: isDebug,
|
||
}
|
||
// 打开命名管道进行写入
|
||
pipe, err := os.OpenFile(c.PipePath, os.O_RDWR, 0)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to open pipe for writing: %v", err)
|
||
}
|
||
defer pipe.Close()
|
||
|
||
// 将文件转换为io.ReadWriter接口,以便与现有的Read/Write函数兼容
|
||
pipeConn := &pipeConnection{pipe}
|
||
|
||
// 发送命令请求
|
||
err = Write(pipeConn, cmdReq)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to write request to pipe: %v", err)
|
||
}
|
||
|
||
for {
|
||
resp, err := Read[CmdResponse](pipeConn)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to read response from pipe: %v", err)
|
||
}
|
||
if resp.Error != "" {
|
||
fmt.Println(resp.Error)
|
||
}
|
||
if !resp.Continue {
|
||
break
|
||
}
|
||
}
|
||
return nil
|
||
|
||
}
|