123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- package pty
- import (
- "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 = 2048
- func 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
- }
|