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] [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 }