Files
2026-06-18 22:56:52 +08:00

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
}