mirror of
				https://github.com/1Panel-dev/1Panel.git
				synced 2025-10-26 00:36:12 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			110 lines
		
	
	
	
		
			2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
	
		
			2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| 	ptyClosed chan struct{}
 | |
| }
 | |
| 
 | |
| 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")
 | |
| 	}
 | |
| 	ptyClosed := make(chan struct{})
 | |
| 
 | |
| 	lcmd := &LocalCommand{
 | |
| 		closeSignal:  DefaultCloseSignal,
 | |
| 		closeTimeout: DefaultCloseTimeout,
 | |
| 
 | |
| 		cmd:       cmd,
 | |
| 		pty:       pty,
 | |
| 		ptyClosed: ptyClosed,
 | |
| 	}
 | |
| 
 | |
| 	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.Signal(lcmd.closeSignal)
 | |
| 	}
 | |
| 	for {
 | |
| 		select {
 | |
| 		case <-lcmd.ptyClosed:
 | |
| 			return nil
 | |
| 		case <-lcmd.closeTimeoutC():
 | |
| 			_ = lcmd.cmd.Process.Signal(syscall.SIGKILL)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (lcmd *LocalCommand) closeTimeoutC() <-chan time.Time {
 | |
| 	if lcmd.closeTimeout >= 0 {
 | |
| 		return time.After(lcmd.closeTimeout)
 | |
| 	}
 | |
| 
 | |
| 	return make(chan time.Time)
 | |
| }
 |