| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 | package ptyimport (	"encoding/json"	"github.com/0xJacky/Nginx-UI/settings"	"github.com/creack/pty"	"github.com/gorilla/websocket"	"github.com/pkg/errors"	"github.com/uozi-tech/cosy/logger"	"os"	"os/exec"	"time"	"unicode/utf8")type Pipeline struct {	Pty *os.File	cmd *exec.Cmd	ws  *websocket.Conn}type Message struct {	Type MsgType	Data json.RawMessage}const bufferSize = 2048func NewPipeLine(conn *websocket.Conn) (p *Pipeline, err error) {	c := exec.Command(settings.TerminalSettings.StartCmd)	ptmx, err := pty.StartWithSize(c, &pty.Winsize{Cols: 90, Rows: 60})	if err != nil {		return nil, errors.Wrap(err, "start pty error")	}	p = &Pipeline{		Pty: ptmx,		cmd: c,		ws:  conn,	}	return}func (p *Pipeline) ReadWsAndWritePty(errorChan chan error) {	for {		msgType, payload, err := p.ws.ReadMessage()		if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNoStatusReceived,			websocket.CloseNormalClosure) {			errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty unexpected close")			return		}		if msgType != websocket.TextMessage {			errorChan <- errors.Errorf("Error ReadWsAndWritePty Invalid msgType: %v", msgType)			return		}		var msg Message		err = json.Unmarshal(payload, &msg)		if err != nil {			errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty json.Unmarshal")			return		}		switch msg.Type {		case TypeData:			var data string			err = json.Unmarshal(msg.Data, &data)			if err != nil {				errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty json.Unmarshal msg.Data")				return			}			_, err = p.Pty.Write([]byte(data))			if err != nil {				errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty write pty")				return			}		case TypeResize:			var win struct {				Cols uint16				Rows uint16			}			err = json.Unmarshal(msg.Data, &win)			if err != nil {				errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty Invalid resize message")				return			}			err = pty.Setsize(p.Pty, &pty.Winsize{Rows: win.Rows, Cols: win.Cols})			if err != nil {				errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty set pty size")				return			}		case TypePing:			err = p.ws.WriteControl(websocket.PongMessage, []byte{}, time.Now().Add(time.Second))			if err != nil {				errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty write pong")				return			}		default:			errorChan <- errors.Errorf("Error ReadWsAndWritePty unknown msg.Type %v", msg.Type)			return		}	}}func (p *Pipeline) ReadPtyAndWriteWs(errorChan chan error) {	buf := make([]byte, bufferSize)	for {		n, err := p.Pty.Read(buf)		if err != nil {			errorChan <- errors.Wrap(err, "Error ReadPtyAndWriteWs read pty")			return		}		processedOutput := validString(string(buf[:n]))		err = p.ws.WriteMessage(websocket.TextMessage, []byte(processedOutput))		if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) {			errorChan <- errors.Wrap(err, "Error ReadPtyAndWriteWs websocket write")			return		}	}}func (p *Pipeline) Close() {	err := p.Pty.Close()	if err != nil {		logger.Error(err)	}	err = p.cmd.Process.Kill()	if err != nil {		logger.Error(err)	}	_, err = p.cmd.Process.Wait()	if err != nil {		logger.Error(err)	}}func validString(s string) string {	if !utf8.ValidString(s) {		v := make([]rune, 0, len(s))		for i, r := range s {			if r == utf8.RuneError {				_, size := utf8.DecodeRuneInString(s[i:])				if size == 1 {					continue				}			}			v = append(v, r)		}		s = string(v)	}	return s}
 |