You've already forked agentic-coding-workflow
47 lines
1.2 KiB
Go
47 lines
1.2 KiB
Go
//go:build !windows
|
|
|
|
package terminal
|
|
|
|
import (
|
|
"os/exec"
|
|
|
|
"github.com/creack/pty"
|
|
|
|
procgrp "github.com/yan1h/agent-coding-workflow/internal/infra/proc"
|
|
)
|
|
|
|
// startPTY 在真 PTY 中拉起 shell(Unix)。
|
|
//
|
|
// 关于进程组:pty.StartWithSize 内部会设置 SysProcAttr.Setsid,使 shell 成为新
|
|
// 会话的首进程,其进程组 pgid == pid。因此这里**不**调用 group.Prepare(它设的
|
|
// Setpgid 与 Setsid 同时设置在 Go exec 下是非法/冗余的);group.Adopt 直接记录
|
|
// pgid = cmd.Process.Pid,TerminateGroup 向 -pgid 发信号即可整树终止 shell 及其
|
|
// 派生的全部子孙进程。
|
|
func startPTY(shell string, env []string, rows, cols uint16) (*ptySession, error) {
|
|
cmd := exec.Command(shell)
|
|
cmd.Env = env
|
|
|
|
ptmx, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: rows, Cols: cols})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
group := procgrp.NewGroup()
|
|
if err := group.Adopt(cmd); err != nil {
|
|
_ = ptmx.Close()
|
|
if cmd.Process != nil {
|
|
_ = cmd.Process.Kill()
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return &ptySession{
|
|
cmd: cmd,
|
|
group: group,
|
|
rw: ptmx,
|
|
resizeFn: func(r, c uint16) error {
|
|
return pty.Setsize(ptmx, &pty.Winsize{Rows: r, Cols: c})
|
|
},
|
|
}, nil
|
|
}
|