package terminal import ( "os" "os/exec" "syscall" "time" "unsafe" "github.com/1Panel-dev/1Panel/backend/global" "github.com/creack/pty" "github.com/pkg/errors" ) const ( DefaultCloseSignal = syscall.SIGINT DefaultCloseTimeout = 10 * time.Second ) type LocalCommand struct { closeSignal syscall.Signal closeTimeout time.Duration cmd *exec.Cmd pty *os.File } func NewCommand(commands string) (*LocalCommand, error) { cmd := exec.Command("sh", "-c", commands) pty, err := pty.Start(cmd) if err != nil { return nil, errors.Wrapf(err, "failed to start command") } lcmd := &LocalCommand{ closeSignal: DefaultCloseSignal, closeTimeout: DefaultCloseTimeout, cmd: cmd, pty: pty, } return lcmd, nil } func (lcmd *LocalCommand) Read(p []byte) (n int, err error) { return lcmd.pty.Read(p) } func (lcmd *LocalCommand) Write(p []byte) (n int, err error) { return lcmd.pty.Write(p) } func (lcmd *LocalCommand) Close() error { if lcmd.cmd != nil && lcmd.cmd.Process != nil { _ = lcmd.cmd.Process.Kill() } _ = lcmd.pty.Close() return nil } func (lcmd *LocalCommand) ResizeTerminal(width int, height int) error { window := struct { row uint16 col uint16 x uint16 y uint16 }{ uint16(height), uint16(width), 0, 0, } _, _, errno := syscall.Syscall( syscall.SYS_IOCTL, lcmd.pty.Fd(), syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(&window)), ) if errno != 0 { return errno } else { return nil } } func (lcmd *LocalCommand) Wait(quitChan chan bool) { if err := lcmd.cmd.Wait(); err != nil { global.LOG.Errorf("ssh session wait failed, err: %v", err) setQuit(quitChan) } }