fix: incorrect disk info on unix-like os

This commit is contained in:
XZB-1248
2022-09-17 09:31:45 +08:00
parent 98b1beb8e0
commit 9d9a9929b0
15 changed files with 369 additions and 302 deletions

View File

@@ -11,7 +11,7 @@ jobs:
strategy: strategy:
matrix: matrix:
go-version: [ 1.17 ] go-version: [ 1.18.1 ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -57,7 +57,7 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: [ 16.x ] node-version: [ 16.x ]
go-version: [ 1.17 ] go-version: [ 1.18.1 ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@@ -14,55 +14,76 @@ import (
type Conn struct { type Conn struct {
*ws.Conn *ws.Conn
Secret []byte secret []byte
secretHex string
} }
var WSConn *Conn var WSConn *Conn
var WSLock = sync.Mutex{} var Mutex = &sync.Mutex{}
var HTTP = req.C().SetUserAgent(`SPARK COMMIT: ` + config.COMMIT) var HTTP = CreateClient()
const MaxMessageSize = 32768 + 1024 const MaxMessageSize = 32768 + 1024
func SendData(data []byte, wsConn *Conn) error { func CreateConn(wsConn *ws.Conn, secret []byte) *Conn {
WSLock.Lock() return &Conn{
defer WSLock.Unlock() Conn: wsConn,
secret: secret,
secretHex: hex.EncodeToString(secret),
}
}
func CreateClient() *req.Client {
return req.C().SetUserAgent(`SPARK COMMIT: ` + config.COMMIT)
}
func (wsConn *Conn) SendData(data []byte) error {
Mutex.Lock()
defer Mutex.Unlock()
if WSConn == nil { if WSConn == nil {
return errors.New(`${i18n|wsClosed}`) return errors.New(`${i18n|wsClosed}`)
} }
wsConn.SetWriteDeadline(time.Now().Add(5 * time.Second)) wsConn.SetWriteDeadline(Now.Add(5 * time.Second))
defer wsConn.SetWriteDeadline(time.Time{}) defer wsConn.SetWriteDeadline(time.Time{})
return wsConn.WriteMessage(ws.BinaryMessage, data) return wsConn.WriteMessage(ws.BinaryMessage, data)
} }
func SendPack(pack interface{}, wsConn *Conn) error { func (wsConn *Conn) SendPack(pack interface{}) error {
WSLock.Lock() Mutex.Lock()
defer WSLock.Unlock() defer Mutex.Unlock()
data, err := utils.JSON.Marshal(pack) data, err := utils.JSON.Marshal(pack)
if err != nil { if err != nil {
return err return err
} }
data, err = utils.Encrypt(data, wsConn.Secret) data, err = utils.Encrypt(data, wsConn.secret)
if err != nil { if err != nil {
return err return err
} }
if len(data) > MaxMessageSize { if len(data) > MaxMessageSize {
_, err = HTTP.R(). _, err = HTTP.R().
SetBody(data). SetBody(data).
SetHeader(`Secret`, hex.EncodeToString(wsConn.Secret)). SetHeader(`Secret`, wsConn.secretHex).
Send(`POST`, config.GetBaseURL(false)+`/ws`) Send(`POST`, config.GetBaseURL(false)+`/ws`)
return err return err
} }
if WSConn == nil { if WSConn == nil {
return errors.New(`${i18n|wsClosed}`) return errors.New(`${i18n|wsClosed}`)
} }
wsConn.SetWriteDeadline(time.Now().Add(5 * time.Second)) wsConn.SetWriteDeadline(Now.Add(5 * time.Second))
defer wsConn.SetWriteDeadline(time.Time{}) defer wsConn.SetWriteDeadline(time.Time{})
return wsConn.WriteMessage(ws.BinaryMessage, data) return wsConn.WriteMessage(ws.BinaryMessage, data)
} }
func SendCb(pack, prev modules.Packet, wsConn *Conn) error { func (wsConn *Conn) SendCallback(pack, prev modules.Packet) error {
if len(prev.Event) > 0 { if len(prev.Event) > 0 {
pack.Event = prev.Event pack.Event = prev.Event
} }
return SendPack(pack, wsConn) return wsConn.SendPack(pack)
}
func (wsConn *Conn) GetSecret() []byte {
return wsConn.secret
}
func (wsConn *Conn) GetSecretHex() string {
return wsConn.secretHex
} }

16
client/common/time.go Normal file
View File

@@ -0,0 +1,16 @@
package common
import "time"
var Now time.Time = time.Now()
var Unix int64 = Now.Unix()
// To prevent call time.Now().Unix() too often.
func init() {
go func() {
for now := range time.NewTicker(time.Second).C {
Now = now
Unix = now.Unix()
}
}()
}

View File

@@ -26,45 +26,18 @@ var stop bool
var ( var (
errNoSecretHeader = errors.New(`can not find secret header`) errNoSecretHeader = errors.New(`can not find secret header`)
) )
var handlers = map[string]func(pack modules.Packet, wsConn *common.Conn){
`ping`: ping,
`offline`: offline,
`lock`: lock,
`logoff`: logoff,
`hibernate`: hibernate,
`suspend`: suspend,
`restart`: restart,
`shutdown`: shutdown,
`screenshot`: screenshot,
`initTerminal`: initTerminal,
`inputTerminal`: inputTerminal,
`resizeTerminal`: resizeTerminal,
`pingTerminal`: pingTerminal,
`killTerminal`: killTerminal,
`listFiles`: listFiles,
`fetchFile`: fetchFile,
`removeFiles`: removeFiles,
`uploadFiles`: uploadFiles,
`uploadTextFile`: uploadTextFile,
`listProcesses`: listProcesses,
`killProcess`: killProcess,
`initDesktop`: initDesktop,
`pingDesktop`: pingDesktop,
`killDesktop`: killDesktop,
`getDesktop`: getDesktop,
}
func Start() { func Start() {
for !stop { for !stop {
var err error var err error
if common.WSConn != nil { if common.WSConn != nil {
common.WSLock.Lock() common.Mutex.Lock()
common.WSConn.Close() common.WSConn.Close()
common.WSLock.Unlock() common.Mutex.Unlock()
} }
common.WSLock.Lock() common.Mutex.Lock()
common.WSConn, err = connectWS() common.WSConn, err = connectWS()
common.WSLock.Unlock() common.Mutex.Unlock()
if err != nil && !stop { if err != nil && !stop {
golog.Error(`Connection error: `, err) golog.Error(`Connection error: `, err)
<-time.After(3 * time.Second) <-time.After(3 * time.Second)
@@ -105,7 +78,7 @@ func connectWS() (*common.Conn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &common.Conn{Conn: wsConn, Secret: secret}, nil return common.CreateConn(wsConn, secret), nil
} }
func reportWS(wsConn *common.Conn) error { func reportWS(wsConn *common.Conn) error {
@@ -114,18 +87,18 @@ func reportWS(wsConn *common.Conn) error {
return err return err
} }
pack := modules.CommonPack{Act: `report`, Data: *device} pack := modules.CommonPack{Act: `report`, Data: *device}
err = common.SendPack(pack, wsConn) err = wsConn.SendPack(pack)
common.WSConn.SetWriteDeadline(time.Time{}) common.WSConn.SetWriteDeadline(time.Time{})
if err != nil { if err != nil {
return err return err
} }
common.WSConn.SetReadDeadline(time.Now().Add(5 * time.Second)) common.WSConn.SetReadDeadline(common.Now.Add(5 * time.Second))
_, data, err := common.WSConn.ReadMessage() _, data, err := common.WSConn.ReadMessage()
common.WSConn.SetReadDeadline(time.Time{}) common.WSConn.SetReadDeadline(time.Time{})
if err != nil { if err != nil {
return err return err
} }
data, err = utils.Decrypt(data, common.WSConn.Secret) data, err = utils.Decrypt(data, common.WSConn.GetSecret())
if err != nil { if err != nil {
return err return err
} }
@@ -148,7 +121,7 @@ func checkUpdate(wsConn *common.Conn) error {
SetQueryParam(`os`, runtime.GOOS). SetQueryParam(`os`, runtime.GOOS).
SetQueryParam(`arch`, runtime.GOARCH). SetQueryParam(`arch`, runtime.GOARCH).
SetQueryParam(`commit`, config.COMMIT). SetQueryParam(`commit`, config.COMMIT).
SetHeader(`Secret`, hex.EncodeToString(wsConn.Secret)). SetHeader(`Secret`, wsConn.GetSecretHex()).
Send(`POST`, config.GetBaseURL(false)+`/api/client/update`) Send(`POST`, config.GetBaseURL(false)+`/api/client/update`)
if err != nil { if err != nil {
return err return err
@@ -189,7 +162,7 @@ func handleWS(wsConn *common.Conn) error {
golog.Error(err) golog.Error(err)
return nil return nil
} }
data, err = utils.Decrypt(data, wsConn.Secret) data, err = utils.Decrypt(data, wsConn.GetSecret())
if err != nil { if err != nil {
golog.Error(err) golog.Error(err)
errCount++ errCount++
@@ -220,7 +193,7 @@ func handleWS(wsConn *common.Conn) error {
func handleAct(pack modules.Packet, wsConn *common.Conn) { func handleAct(pack modules.Packet, wsConn *common.Conn) {
if act, ok := handlers[pack.Act]; !ok { if act, ok := handlers[pack.Act]; !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|actionNotImplemented}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|actionNotImplemented}`}, pack)
} else { } else {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {

View File

@@ -147,17 +147,20 @@ func GetRAMInfo() (modules.IO, error) {
} }
func GetDiskInfo() (modules.IO, error) { func GetDiskInfo() (modules.IO, error) {
devices := map[string]struct{}{}
result := modules.IO{} result := modules.IO{}
disk.IOCounters() disks, err := disk.Partitions(false)
disks, err := disk.Partitions(true)
if err != nil { if err != nil {
return result, nil return result, nil
} }
for i := 0; i < len(disks); i++ { for i := 0; i < len(disks); i++ {
stat, err := disk.Usage(disks[i].Mountpoint) if _, ok := devices[disks[i].Device]; !ok {
if err == nil { devices[disks[i].Device] = struct{}{}
result.Total += stat.Total stat, err := disk.Usage(disks[i].Mountpoint)
result.Used += stat.Used if err == nil {
result.Total += stat.Total
result.Used += stat.Used
}
} }
} }
result.Usage = float64(result.Used) / float64(result.Total) * 100 result.Usage = float64(result.Used) / float64(result.Total) * 100

View File

@@ -12,21 +12,48 @@ import (
"github.com/kataras/golog" "github.com/kataras/golog"
"os" "os"
"reflect" "reflect"
"strconv"
) )
var handlers = map[string]func(pack modules.Packet, wsConn *common.Conn){
`ping`: ping,
`offline`: offline,
`lock`: lock,
`logoff`: logoff,
`hibernate`: hibernate,
`suspend`: suspend,
`restart`: restart,
`shutdown`: shutdown,
`screenshot`: screenshot,
`initTerminal`: initTerminal,
`inputTerminal`: inputTerminal,
`resizeTerminal`: resizeTerminal,
`pingTerminal`: pingTerminal,
`killTerminal`: killTerminal,
`listFiles`: listFiles,
`fetchFile`: fetchFile,
`removeFiles`: removeFiles,
`uploadFiles`: uploadFiles,
`uploadTextFile`: uploadTextFile,
`listProcesses`: listProcesses,
`killProcess`: killProcess,
`initDesktop`: initDesktop,
`pingDesktop`: pingDesktop,
`killDesktop`: killDesktop,
`getDesktop`: getDesktop,
}
func ping(pack modules.Packet, wsConn *common.Conn) { func ping(pack modules.Packet, wsConn *common.Conn) {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
device, err := GetPartialInfo() device, err := GetPartialInfo()
if err != nil { if err != nil {
golog.Error(err) golog.Error(err)
return return
} }
common.SendPack(modules.CommonPack{Act: `setDevice`, Data: *device}, wsConn) wsConn.SendPack(modules.CommonPack{Act: `setDevice`, Data: *device})
} }
func offline(pack modules.Packet, wsConn *common.Conn) { func offline(pack modules.Packet, wsConn *common.Conn) {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
stop = true stop = true
wsConn.Close() wsConn.Close()
os.Exit(0) os.Exit(0)
@@ -35,75 +62,75 @@ func offline(pack modules.Packet, wsConn *common.Conn) {
func lock(pack modules.Packet, wsConn *common.Conn) { func lock(pack modules.Packet, wsConn *common.Conn) {
err := basic.Lock() err := basic.Lock()
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func logoff(pack modules.Packet, wsConn *common.Conn) { func logoff(pack modules.Packet, wsConn *common.Conn) {
err := basic.Logoff() err := basic.Logoff()
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func hibernate(pack modules.Packet, wsConn *common.Conn) { func hibernate(pack modules.Packet, wsConn *common.Conn) {
err := basic.Hibernate() err := basic.Hibernate()
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func suspend(pack modules.Packet, wsConn *common.Conn) { func suspend(pack modules.Packet, wsConn *common.Conn) {
err := basic.Suspend() err := basic.Suspend()
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func restart(pack modules.Packet, wsConn *common.Conn) { func restart(pack modules.Packet, wsConn *common.Conn) {
err := basic.Restart() err := basic.Restart()
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func shutdown(pack modules.Packet, wsConn *common.Conn) { func shutdown(pack modules.Packet, wsConn *common.Conn) {
err := basic.Shutdown() err := basic.Shutdown()
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func screenshot(pack modules.Packet, wsConn *common.Conn) { func screenshot(pack modules.Packet, wsConn *common.Conn) {
var bridge string var bridge string
if val, ok := pack.GetData(`bridge`, reflect.String); !ok { if val, ok := pack.GetData(`bridge`, reflect.String); !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack)
return return
} else { } else {
bridge = val.(string) bridge = val.(string)
} }
err := Screenshot.GetScreenshot(bridge) err := Screenshot.GetScreenshot(bridge)
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} }
} }
func initTerminal(pack modules.Packet, wsConn *common.Conn) { func initTerminal(pack modules.Packet, wsConn *common.Conn) {
err := terminal.InitTerminal(pack) err := terminal.InitTerminal(pack)
if err != nil { if err != nil {
common.SendCb(modules.Packet{Act: `initTerminal`, Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Act: `initTerminal`, Code: 1, Msg: err.Error()}, pack)
} }
} }
@@ -130,42 +157,42 @@ func listFiles(pack modules.Packet, wsConn *common.Conn) {
} }
files, err := file.ListFiles(path) files, err := file.ListFiles(path)
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0, Data: smap{`files`: files}}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0, Data: smap{`files`: files}}, pack)
} }
} }
func fetchFile(pack modules.Packet, wsConn *common.Conn) { func fetchFile(pack modules.Packet, wsConn *common.Conn) {
var path, filename, bridge string var path, filename, bridge string
if val, ok := pack.GetData(`path`, reflect.String); !ok { if val, ok := pack.GetData(`path`, reflect.String); !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack)
return return
} else { } else {
path = val.(string) path = val.(string)
} }
if val, ok := pack.GetData(`file`, reflect.String); !ok { if val, ok := pack.GetData(`file`, reflect.String); !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack)
return return
} else { } else {
filename = val.(string) filename = val.(string)
} }
if val, ok := pack.GetData(`bridge`, reflect.String); !ok { if val, ok := pack.GetData(`bridge`, reflect.String); !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack)
return return
} else { } else {
bridge = val.(string) bridge = val.(string)
} }
err := file.FetchFile(path, filename, bridge) err := file.FetchFile(path, filename, bridge)
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} }
} }
func removeFiles(pack modules.Packet, wsConn *common.Conn) { func removeFiles(pack modules.Packet, wsConn *common.Conn) {
var files []string var files []string
if val, ok := pack.Data[`files`]; !ok { if val, ok := pack.Data[`files`]; !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack)
return return
} else { } else {
slice := val.([]interface{}) slice := val.([]interface{})
@@ -176,24 +203,26 @@ func removeFiles(pack modules.Packet, wsConn *common.Conn) {
} }
} }
if len(files) == 0 { if len(files) == 0 {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack)
return return
} }
} }
err := file.RemoveFiles(files) err := file.RemoveFiles(files)
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func uploadFiles(pack modules.Packet, wsConn *common.Conn) { func uploadFiles(pack modules.Packet, wsConn *common.Conn) {
var start, end int64 var (
var files []string start, end int64
var bridge string files []string
bridge string
)
if val, ok := pack.Data[`files`]; !ok { if val, ok := pack.Data[`files`]; !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack)
return return
} else { } else {
slice := val.([]interface{}) slice := val.([]interface{})
@@ -204,12 +233,12 @@ func uploadFiles(pack modules.Packet, wsConn *common.Conn) {
} }
} }
if len(files) == 0 { if len(files) == 0 {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack)
return return
} }
} }
if val, ok := pack.GetData(`bridge`, reflect.String); !ok { if val, ok := pack.GetData(`bridge`, reflect.String); !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack)
return return
} else { } else {
bridge = val.(string) bridge = val.(string)
@@ -225,28 +254,27 @@ func uploadFiles(pack modules.Packet, wsConn *common.Conn) {
} }
} }
if end > 0 && end < start { if end > 0 && end < start {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|invalidFileRange}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|invalidFileRange}`}, pack)
return return
} }
} }
err := file.UploadFiles(files, bridge, start, end) err := file.UploadFiles(files, bridge, start, end)
if err != nil { if err != nil {
golog.Error(err) golog.Error(err)
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} }
} }
func uploadTextFile(pack modules.Packet, wsConn *common.Conn) { func uploadTextFile(pack modules.Packet, wsConn *common.Conn) {
var path string var path, bridge string
var bridge string
if val, ok := pack.GetData(`file`, reflect.String); !ok { if val, ok := pack.GetData(`file`, reflect.String); !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|fileOrDirNotExist}`}, pack)
return return
} else { } else {
path = val.(string) path = val.(string)
} }
if val, ok := pack.GetData(`bridge`, reflect.String); !ok { if val, ok := pack.GetData(`bridge`, reflect.String); !ok {
common.SendCb(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack)
return return
} else { } else {
bridge = val.(string) bridge = val.(string)
@@ -254,41 +282,42 @@ func uploadTextFile(pack modules.Packet, wsConn *common.Conn) {
err := file.UploadTextFile(path, bridge) err := file.UploadTextFile(path, bridge)
if err != nil { if err != nil {
golog.Error(err) golog.Error(err)
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} }
} }
func listProcesses(pack modules.Packet, wsConn *common.Conn) { func listProcesses(pack modules.Packet, wsConn *common.Conn) {
processes, err := process.ListProcesses() processes, err := process.ListProcesses()
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0, Data: map[string]interface{}{`processes`: processes}}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0, Data: map[string]interface{}{`processes`: processes}}, pack)
} }
} }
func killProcess(pack modules.Packet, wsConn *common.Conn) { func killProcess(pack modules.Packet, wsConn *common.Conn) {
var ( var (
pid int64 pid int32
err error err error
) )
if val, ok := pack.GetData(`pid`, reflect.String); ok { if val, ok := pack.GetData(`pid`, reflect.Float64); !ok {
pid, err = strconv.ParseInt(val.(string), 10, 32) wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|invalidParameter}`}, pack)
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn)
return return
} else {
pid = int32(val.(float64))
} }
err = process.KillProcess(int32(pid)) err = process.KillProcess(int32(pid))
if err != nil { if err != nil {
common.SendCb(modules.Packet{Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 1, Msg: err.Error()}, pack)
} else { } else {
common.SendCb(modules.Packet{Code: 0}, pack, wsConn) wsConn.SendCallback(modules.Packet{Code: 0}, pack)
} }
} }
func initDesktop(pack modules.Packet, wsConn *common.Conn) { func initDesktop(pack modules.Packet, wsConn *common.Conn) {
err := desktop.InitDesktop(pack) err := desktop.InitDesktop(pack)
if err != nil { if err != nil {
common.SendCb(modules.Packet{Act: `initDesktop`, Code: 1, Msg: err.Error()}, pack, wsConn) wsConn.SendCallback(modules.Packet{Act: `initDesktop`, Code: 1, Msg: err.Error()}, pack)
} }
} }

View File

@@ -3,6 +3,7 @@ package desktop
import ( import (
"Spark/client/common" "Spark/client/common"
"Spark/modules" "Spark/modules"
"Spark/utils"
"Spark/utils/cmap" "Spark/utils/cmap"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
@@ -19,7 +20,7 @@ import (
type session struct { type session struct {
lastPack int64 lastPack int64
binEvent []byte rawEvent []byte
event string event string
escape bool escape bool
channel chan message channel chan message
@@ -28,28 +29,31 @@ type session struct {
type message struct { type message struct {
t int t int
info string info string
data *[][]byte data *[]*[]byte
} }
// +---------+---------+----------+----------+------------+---------+---------+---------+---------+-------+ // +---------+---------+----------+----------+------------+---------+---------+---------+---------+-------+
// | magic | op code | event id | img type | img length | x | y | width | height | image | // | magic | OP code | event id | img type | img length | x | y | width | height | image |
// +---------+---------+----------+----------+------------+---------+---------+---------+---------+-------+ // +---------+---------+----------+----------+------------+---------+---------+---------+---------+-------+
// | 6 bytes | 1 byte | 16 bytes | 2 bytes | 2 bytes | 2 bytes | 2 bytes | 2 bytes | 2 bytes | - | // | 5 bytes | 1 byte | 16 bytes | 2 bytes | 2 bytes | 2 bytes | 2 bytes | 2 bytes | 2 bytes | - |
// +---------+---------+----------+----------+------------+---------+---------+---------+---------+-------+ // +---------+---------+----------+----------+------------+---------+---------+---------+---------+-------+
// []byte{00, 22, 34, 19, 20}, magic bytes. // magic:
// []byte{34, 22, 19, 17, 20}
// Op code: // OP code:
// 00: first part of a frame. // 00: first part of a frame
// 01: rest parts of a frame. // 01: rest parts of a frame
// 02: set resolution of every frame. // 02: set resolution of every frame
// 03: JSON string format. (Only for server). // 03: JSON string (only for server)
// img type: 0: raw image, 1: compressed image (jpeg). // img type:
// 0: raw image
// 1: compressed image (jpeg)
const compress = true const compress = true
const blockSize = 64 const blockSize = 64
const imgQuality = 80 const imgQuality = 70
var lock = &sync.Mutex{} var lock = &sync.Mutex{}
var working = false var working = false
@@ -93,12 +97,12 @@ func worker() {
if diff != nil && len(diff) > 0 { if diff != nil && len(diff) > 0 {
prevDesktop = img prevDesktop = img
sessions.IterCb(func(uuid string, t interface{}) bool { sessions.IterCb(func(uuid string, t interface{}) bool {
desktopSession := t.(*session) desktop := t.(*session)
desktopSession.lock.Lock() desktop.lock.Lock()
if !desktopSession.escape { if !desktop.escape {
desktopSession.channel <- message{t: 0, data: &diff} desktop.channel <- message{t: 0, data: &diff}
} }
desktopSession.lock.Unlock() desktop.lock.Unlock()
return true return true
}) })
} }
@@ -117,9 +121,9 @@ func quitAll(info string) {
keys := make([]string, 0) keys := make([]string, 0)
sessions.IterCb(func(uuid string, t interface{}) bool { sessions.IterCb(func(uuid string, t interface{}) bool {
keys = append(keys, uuid) keys = append(keys, uuid)
desktopSession := t.(*session) desktop := t.(*session)
desktopSession.escape = true desktop.escape = true
desktopSession.channel <- message{t: 1, info: info} desktop.channel <- message{t: 1, info: info}
return true return true
}) })
sessions.Clear() sessions.Clear()
@@ -128,8 +132,8 @@ func quitAll(info string) {
lock.Unlock() lock.Unlock()
} }
func imageCompare(img, prev *image.RGBA, compress bool) [][]byte { func imageCompare(img, prev *image.RGBA, compress bool) []*[]byte {
result := make([][]byte, 0) result := make([]*[]byte, 0)
if prev == nil { if prev == nil {
return splitFullImage(img, compress) return splitFullImage(img, compress)
} }
@@ -150,29 +154,24 @@ func imageCompare(img, prev *image.RGBA, compress bool) [][]byte {
binary.BigEndian.PutUint16(buf[6:8], uint16(rect.Min.Y)) binary.BigEndian.PutUint16(buf[6:8], uint16(rect.Min.Y))
binary.BigEndian.PutUint16(buf[8:10], uint16(rect.Size().X)) binary.BigEndian.PutUint16(buf[8:10], uint16(rect.Size().X))
binary.BigEndian.PutUint16(buf[10:12], uint16(rect.Size().Y)) binary.BigEndian.PutUint16(buf[10:12], uint16(rect.Size().Y))
result = append(result, append(buf, block...)) buf = append(buf, block...)
result = append(result, &buf)
} }
return result return result
} }
func splitFullImage(img *image.RGBA, compress bool) [][]byte { func splitFullImage(img *image.RGBA, compress bool) []*[]byte {
if img == nil { if img == nil {
return nil return nil
} }
result := make([][]byte, 0) result := make([]*[]byte, 0)
rect := img.Bounds() rect := img.Rect
imgWidth := rect.Dx() imgWidth := rect.Dx()
imgHeight := rect.Dy() imgHeight := rect.Dy()
for y := rect.Min.Y; y < rect.Max.Y; y += blockSize { for y := rect.Min.Y; y < rect.Max.Y; y += blockSize {
height := utils.If(y+blockSize > imgHeight, imgHeight-y, blockSize)
for x := rect.Min.X; x < rect.Max.X; x += blockSize { for x := rect.Min.X; x < rect.Max.X; x += blockSize {
width := blockSize width := utils.If(x+blockSize > imgWidth, imgWidth-x, blockSize)
height := blockSize
if x+width > imgWidth {
width = imgWidth - x
}
if y+height > imgHeight {
height = imgHeight - y
}
block := getImageBlock(img, image.Rect(x, y, x+width, y+height), compress) block := getImageBlock(img, image.Rect(x, y, x+width, y+height), compress)
buf := make([]byte, 12) buf := make([]byte, 12)
if compress { if compress {
@@ -185,7 +184,8 @@ func splitFullImage(img *image.RGBA, compress bool) [][]byte {
binary.BigEndian.PutUint16(buf[6:8], uint16(y)) binary.BigEndian.PutUint16(buf[6:8], uint16(y))
binary.BigEndian.PutUint16(buf[8:10], uint16(width)) binary.BigEndian.PutUint16(buf[8:10], uint16(width))
binary.BigEndian.PutUint16(buf[10:12], uint16(height)) binary.BigEndian.PutUint16(buf[10:12], uint16(height))
result = append(result, append(buf, block...)) buf = append(buf, block...)
result = append(result, &buf)
} }
} }
return result return result
@@ -194,42 +194,35 @@ func splitFullImage(img *image.RGBA, compress bool) [][]byte {
func getImageBlock(img *image.RGBA, rect image.Rectangle, compress bool) []byte { func getImageBlock(img *image.RGBA, rect image.Rectangle, compress bool) []byte {
width := rect.Dx() width := rect.Dx()
height := rect.Dy() height := rect.Dy()
if rect.Min.X+width > img.Rect.Max.X { buf := make([]byte, width*height*4)
width = img.Rect.Max.X - rect.Min.X bufPos := 0
} imgPos := img.PixOffset(rect.Min.X, rect.Min.Y)
if rect.Min.Y+height > img.Rect.Max.Y { for y := 0; y < height; y++ {
height = img.Rect.Max.Y - rect.Min.Y copy(buf[bufPos:bufPos+width*4], img.Pix[imgPos:imgPos+width*4])
} bufPos += width * 4
buf := make([]byte, 0) imgPos += img.Stride
for y := 0; y < rect.Dy(); y++ {
pos := (rect.Min.Y+y)*img.Rect.Size().X + rect.Min.X
end := pos + width
buf = append(buf, img.Pix[pos*4:end*4]...)
} }
if !compress { if !compress {
return buf return buf
} }
newRect := image.Rect(0, 0, width, height) subImg := &image.RGBA{
newImg := image.NewRGBA(newRect) Pix: buf,
copy(newImg.Pix[:len(buf)], buf[:]) Stride: width * 4,
Rect: image.Rect(0, 0, width, height),
}
writer := new(bytes.Buffer) writer := new(bytes.Buffer)
jpeg.Encode(writer, newImg, &jpeg.Options{Quality: imgQuality}) jpeg.Encode(writer, subImg, &jpeg.Options{Quality: imgQuality})
return writer.Bytes() return writer.Bytes()
} }
func getDiff(img, prev *image.RGBA) []image.Rectangle { func getDiff(img, prev *image.RGBA) []image.Rectangle {
imgWidth := img.Rect.Dx()
imgHeight := img.Rect.Dy()
result := make([]image.Rectangle, 0) result := make([]image.Rectangle, 0)
for y := 0; y < img.Rect.Size().Y; y += blockSize { for y := 0; y < imgHeight; y += blockSize {
for x := 0; x < img.Rect.Size().X; x += blockSize { height := utils.If(y+blockSize > imgHeight, imgHeight-y, blockSize)
width := blockSize for x := 0; x < imgWidth; x += blockSize {
height := blockSize width := utils.If(x+blockSize > imgWidth, imgWidth-x, blockSize)
if x+width > img.Rect.Size().X {
width = img.Rect.Size().X - x
}
if y+height > img.Rect.Size().Y {
height = img.Rect.Size().Y - y
}
rect := image.Rect(x, y, x+width, y+height) rect := image.Rect(x, y, x+width, y+height)
if isDiff(img, prev, rect) { if isDiff(img, prev, rect) {
result = append(result, rect) result = append(result, rect)
@@ -244,8 +237,8 @@ func isDiff(img, prev *image.RGBA, rect image.Rectangle) bool {
prevHeader := (*reflect.SliceHeader)(unsafe.Pointer(&prev.Pix)) prevHeader := (*reflect.SliceHeader)(unsafe.Pointer(&prev.Pix))
imgPtr := imgHeader.Data imgPtr := imgHeader.Data
prevPtr := prevHeader.Data prevPtr := prevHeader.Data
imgWidth := img.Rect.Size().X imgWidth := img.Rect.Dx()
rectWidth := rect.Size().X rectWidth := rect.Dx()
end := 0 end := 0
if rect.Max.Y == 0 { if rect.Max.Y == 0 {
@@ -281,139 +274,141 @@ func isDiff(img, prev *image.RGBA, rect image.Rectangle) bool {
} }
func InitDesktop(pack modules.Packet) error { func InitDesktop(pack modules.Packet) error {
var desktop string var uuid string
binEvent, err := hex.DecodeString(pack.Event) rawEvent, err := hex.DecodeString(pack.Event)
if err != nil { if err != nil {
return err return err
} }
if val, ok := pack.GetData(`desktop`, reflect.String); !ok { if val, ok := pack.GetData(`desktop`, reflect.String); !ok {
return errors.New(`${i18n|invalidParameter}`) return errors.New(`${i18n|invalidParameter}`)
} else { } else {
desktop = val.(string) uuid = val.(string)
} }
desktopSession := &session{ desktop := &session{
event: pack.Event, event: pack.Event,
binEvent: binEvent, rawEvent: rawEvent,
lastPack: time.Now().Unix(), lastPack: time.Now().Unix(),
escape: false, escape: false,
channel: make(chan message, 4), channel: make(chan message, 4),
lock: &sync.Mutex{}, lock: &sync.Mutex{},
} }
{ {
// set resolution of desktop. // set resolution of desktop
if screenshot.NumActiveDisplays() == 0 { if screenshot.NumActiveDisplays() == 0 {
common.SendCb(modules.Packet{Act: `quitDesktop`, Msg: `${i18n|noDisplayFound}`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitDesktop`, Msg: `${i18n|noDisplayFound}`}, pack)
return errors.New(`${i18n|noDisplayFound}`) return errors.New(`${i18n|noDisplayFound}`)
} }
buf := append([]byte{00, 22, 34, 19, 20, 02}, binEvent...) buf := append([]byte{34, 22, 19, 17, 20, 02}, rawEvent...)
data := make([]byte, 4) data := make([]byte, 4)
rect := screenshot.GetDisplayBounds(0) rect := screenshot.GetDisplayBounds(0)
binary.BigEndian.PutUint16(data[:2], uint16(rect.Dx())) binary.BigEndian.PutUint16(data[:2], uint16(rect.Dx()))
binary.BigEndian.PutUint16(data[2:], uint16(rect.Dy())) binary.BigEndian.PutUint16(data[2:], uint16(rect.Dy()))
buf = append(buf, data...) buf = append(buf, data...)
common.SendData(buf, common.WSConn) common.WSConn.SendData(buf)
} }
go func() { go handleDesktop(pack, uuid, desktop)
for !desktopSession.escape {
select {
case msg, ok := <-desktopSession.channel:
// send error info
if msg.t == 1 || !ok {
common.SendCb(modules.Packet{Act: `quitDesktop`, Msg: msg.info}, pack, common.WSConn)
desktopSession.escape = true
sessions.Remove(desktop)
break
}
// send image
if msg.t == 0 {
buf := append([]byte{00, 22, 34, 19, 20, 00}, binEvent...)
for _, slice := range *msg.data {
if len(buf)+len(slice) >= common.MaxMessageSize {
if common.SendData(buf, common.WSConn) != nil {
break
}
buf = append([]byte{00, 22, 34, 19, 20, 01}, binEvent...)
}
buf = append(buf, slice...)
}
common.SendData(buf, common.WSConn)
buf = nil
continue
}
case <-time.After(time.Second * 5):
default:
time.Sleep(50 * time.Millisecond)
}
}
}()
if !working { if !working {
sessions.Set(desktop, desktopSession) sessions.Set(uuid, desktop)
go worker() go worker()
} else { } else {
img := splitFullImage(prevDesktop, compress) img := splitFullImage(prevDesktop, compress)
desktopSession.lock.Lock() desktop.lock.Lock()
desktopSession.channel <- message{t: 0, data: &img} desktop.channel <- message{t: 0, data: &img}
desktopSession.lock.Unlock() desktop.lock.Unlock()
sessions.Set(desktop, desktopSession) sessions.Set(uuid, desktop)
} }
return nil return nil
} }
func PingDesktop(pack modules.Packet) { func PingDesktop(pack modules.Packet) {
var desktop string var uuid string
var desktopSession *session var desktop *session
if val, ok := pack.GetData(`desktop`, reflect.String); !ok { if val, ok := pack.GetData(`desktop`, reflect.String); !ok {
return return
} else { } else {
desktop = val.(string) uuid = val.(string)
} }
if val, ok := sessions.Get(desktop); !ok { if val, ok := sessions.Get(uuid); !ok {
return return
} else { } else {
desktopSession = val.(*session) desktop = val.(*session)
desktopSession.lastPack = time.Now().Unix() desktop.lastPack = time.Now().Unix()
} }
} }
func KillDesktop(pack modules.Packet) { func KillDesktop(pack modules.Packet) {
var desktop string var uuid string
var desktopSession *session var desktop *session
if val, ok := pack.GetData(`desktop`, reflect.String); !ok { if val, ok := pack.GetData(`desktop`, reflect.String); !ok {
return return
} else { } else {
desktop = val.(string) uuid = val.(string)
} }
if val, ok := sessions.Get(desktop); !ok { if val, ok := sessions.Get(uuid); !ok {
return return
} else { } else {
desktopSession = val.(*session) desktop = val.(*session)
} }
sessions.Remove(desktop) sessions.Remove(uuid)
desktopSession.lock.Lock() desktop.lock.Lock()
desktopSession.escape = true desktop.escape = true
desktopSession.binEvent = nil desktop.rawEvent = nil
desktopSession.lock.Unlock() desktop.lock.Unlock()
common.SendCb(modules.Packet{Act: `quitDesktop`, Msg: `${i18n|desktopSessionClosed}`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitDesktop`, Msg: `${i18n|desktopClosed}`}, pack)
} }
func GetDesktop(pack modules.Packet) { func GetDesktop(pack modules.Packet) {
var desktop string var uuid string
var desktopSession *session var desktop *session
if val, ok := pack.GetData(`desktop`, reflect.String); !ok { if val, ok := pack.GetData(`desktop`, reflect.String); !ok {
return return
} else { } else {
desktop = val.(string) uuid = val.(string)
} }
if val, ok := sessions.Get(desktop); !ok { if val, ok := sessions.Get(uuid); !ok {
return return
} else { } else {
desktopSession = val.(*session) desktop = val.(*session)
} }
if !desktopSession.escape { if !desktop.escape {
img := splitFullImage(prevDesktop, compress) img := splitFullImage(prevDesktop, compress)
desktopSession.lock.Lock() desktop.lock.Lock()
desktopSession.channel <- message{t: 0, data: &img} desktop.channel <- message{t: 0, data: &img}
desktopSession.lock.Unlock() desktop.lock.Unlock()
}
}
func handleDesktop(pack modules.Packet, uuid string, desktop *session) {
for !desktop.escape {
select {
case msg, ok := <-desktop.channel:
// send error info
if msg.t == 1 || !ok {
common.WSConn.SendCallback(modules.Packet{Act: `quitDesktop`, Msg: msg.info}, pack)
desktop.escape = true
sessions.Remove(uuid)
break
}
// send image
if msg.t == 0 {
buf := append([]byte{34, 22, 19, 17, 20, 00}, desktop.rawEvent...)
for _, slice := range *msg.data {
if len(buf)+len(*slice) >= common.MaxMessageSize {
if common.WSConn.SendData(buf) != nil {
break
}
buf = append([]byte{34, 22, 19, 17, 20, 01}, desktop.rawEvent...)
}
buf = append(buf, *slice...)
}
common.WSConn.SendData(buf)
buf = nil
continue
}
case <-time.After(time.Second * 5):
default:
time.Sleep(50 * time.Millisecond)
}
} }
} }
@@ -424,8 +419,8 @@ func healthCheck() {
// stores sessions to be disconnected // stores sessions to be disconnected
keys := make([]string, 0) keys := make([]string, 0)
sessions.IterCb(func(uuid string, t interface{}) bool { sessions.IterCb(func(uuid string, t interface{}) bool {
desktopSession := t.(*session) desktop := t.(*session)
if timestamp-desktopSession.lastPack > MaxInterval { if timestamp-desktop.lastPack > MaxInterval {
keys = append(keys, uuid) keys = append(keys, uuid)
} }
return true return true

View File

@@ -33,7 +33,7 @@ func InitTerminal(pack modules.Packet) error {
termSession := &terminal{ termSession := &terminal{
pty: ptySession, pty: ptySession,
event: pack.Event, event: pack.Event,
lastPack: time.Now().Unix(), lastPack: common.Unix,
} }
terminals.Set(pack.Data[`terminal`].(string), termSession) terminals.Set(pack.Data[`terminal`].(string), termSession)
go func() { go func() {
@@ -41,12 +41,12 @@ func InitTerminal(pack modules.Packet) error {
buffer := make([]byte, 512) buffer := make([]byte, 512)
n, err := ptySession.Read(buffer) n, err := ptySession.Read(buffer)
buffer = buffer[:n] buffer = buffer[:n]
common.SendCb(modules.Packet{Act: `outputTerminal`, Data: map[string]interface{}{ common.WSConn.SendCallback(modules.Packet{Act: `outputTerminal`, Data: map[string]interface{}{
`output`: hex.EncodeToString(buffer), `output`: hex.EncodeToString(buffer),
}}, pack, common.WSConn) }}, pack)
termSession.lastPack = time.Now().Unix() termSession.lastPack = common.Unix
if err != nil { if err != nil {
common.SendCb(modules.Packet{Act: `quitTerminal`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitTerminal`}, pack)
break break
} }
} }
@@ -72,12 +72,12 @@ func InputTerminal(pack modules.Packet) error {
termUUID := val.(string) termUUID := val.(string)
val, ok = terminals.Get(termUUID) val, ok = terminals.Get(termUUID)
if !ok { if !ok {
common.SendCb(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack)
return nil return nil
} }
terminal := val.(*terminal) terminal := val.(*terminal)
terminal.pty.Write(data) terminal.pty.Write(data)
terminal.lastPack = time.Now().Unix() terminal.lastPack = common.Unix
return nil return nil
} }
@@ -100,7 +100,7 @@ func ResizeTerminal(pack modules.Packet) error {
termUUID := val.(string) termUUID := val.(string)
val, ok = terminals.Get(termUUID) val, ok = terminals.Get(termUUID)
if !ok { if !ok {
common.SendCb(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack)
return nil return nil
} }
terminal := val.(*terminal) terminal := val.(*terminal)
@@ -119,7 +119,7 @@ func KillTerminal(pack modules.Packet) error {
termUUID := val.(string) termUUID := val.(string)
val, ok = terminals.Get(termUUID) val, ok = terminals.Get(termUUID)
if !ok { if !ok {
common.SendCb(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack)
return nil return nil
} }
terminal := val.(*terminal) terminal := val.(*terminal)
@@ -140,7 +140,7 @@ func PingTerminal(pack modules.Packet) {
return return
} else { } else {
termSession = val.(*terminal) termSession = val.(*terminal)
termSession.lastPack = time.Now().Unix() termSession.lastPack = common.Unix
} }
} }

View File

@@ -53,7 +53,7 @@ func InitTerminal(pack modules.Packet) error {
stdout: &stdout, stdout: &stdout,
stderr: &stderr, stderr: &stderr,
stdin: &stdin, stdin: &stdin,
lastPack: time.Now().Unix(), lastPack: common.Unix,
} }
terminals.Set(pack.Data[`terminal`].(string), termSession) terminals.Set(pack.Data[`terminal`].(string), termSession)
@@ -63,18 +63,18 @@ func InitTerminal(pack modules.Packet) error {
n, err := rc.Read(buffer) n, err := rc.Read(buffer)
buffer = buffer[:n] buffer = buffer[:n]
// Clear screen. // clear screen
if len(buffer) == 1 && buffer[0] == 12 { if len(buffer) == 1 && buffer[0] == 12 {
buffer = []byte{27, 91, 72, 27, 91, 50, 74} buffer = []byte{27, 91, 72, 27, 91, 50, 74}
} }
buffer, _ = encodeUTF8(buffer) buffer, _ = encodeUTF8(buffer)
common.SendCb(modules.Packet{Act: `outputTerminal`, Data: map[string]interface{}{ common.WSConn.SendCallback(modules.Packet{Act: `outputTerminal`, Data: map[string]interface{}{
`output`: hex.EncodeToString(buffer), `output`: hex.EncodeToString(buffer),
}}, pack, common.WSConn) }}, pack)
termSession.lastPack = time.Now().Unix() termSession.lastPack = common.Unix
if err != nil { if err != nil {
common.SendCb(modules.Packet{Act: `quitTerminal`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitTerminal`}, pack)
break break
} }
} }
@@ -103,7 +103,7 @@ func InputTerminal(pack modules.Packet) error {
termUUID := val.(string) termUUID := val.(string)
val, ok = terminals.Get(termUUID) val, ok = terminals.Get(termUUID)
if !ok { if !ok {
common.SendCb(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack)
return nil return nil
} }
terminal := val.(*terminal) terminal := val.(*terminal)
@@ -113,7 +113,7 @@ func InputTerminal(pack modules.Packet) error {
} }
data, _ = decodeUTF8(data) data, _ = decodeUTF8(data)
(*terminal.stdin).Write(data) (*terminal.stdin).Write(data)
terminal.lastPack = time.Now().Unix() terminal.lastPack = common.Unix
return nil return nil
} }
@@ -129,7 +129,7 @@ func KillTerminal(pack modules.Packet) error {
termUUID := val.(string) termUUID := val.(string)
val, ok = terminals.Get(termUUID) val, ok = terminals.Get(termUUID)
if !ok { if !ok {
common.SendCb(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack, common.WSConn) common.WSConn.SendCallback(modules.Packet{Act: `quitTerminal`, Msg: `${i18n|terminalSessionClosed}`}, pack)
return nil return nil
} }
terminal := val.(*terminal) terminal := val.(*terminal)
@@ -150,7 +150,7 @@ func PingTerminal(pack modules.Packet) {
return return
} else { } else {
termSession = val.(*terminal) termSession = val.(*terminal)
termSession.lastPack = time.Now().Unix() termSession.lastPack = common.Unix
} }
} }

2
go.mod
View File

@@ -1,6 +1,6 @@
module Spark module Spark
go 1.17 go 1.18
require ( require (
github.com/creack/pty v1.1.18 github.com/creack/pty v1.1.18

View File

@@ -11,9 +11,7 @@ import (
"encoding/hex" "encoding/hex"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net" "net"
"reflect"
"strings" "strings"
"unsafe"
) )
var Melody = melody.New() var Melody = melody.New()
@@ -187,23 +185,3 @@ func DecAES(data []byte, key []byte) ([]byte, error) {
} }
return decBuffer[:dataLen-16], nil return decBuffer[:dataLen-16], nil
} }
func RemoveBytesPrefix(data *[]byte, n int) *[]byte {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(data))
header := &reflect.SliceHeader{
Data: sliceHeader.Data + uintptr(n),
Len: sliceHeader.Len - n,
Cap: sliceHeader.Cap - n,
}
return (*[]byte)(unsafe.Pointer(header))
}
func RemoveBytesSuffix(data *[]byte, n int) *[]byte {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(data))
header := &reflect.SliceHeader{
Data: sliceHeader.Data,
Len: sliceHeader.Len - n,
Cap: sliceHeader.Cap - n,
}
return (*[]byte)(unsafe.Pointer(header))
}

View File

@@ -8,7 +8,6 @@ import (
"Spark/utils/melody" "Spark/utils/melody"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"strconv"
"time" "time"
) )
@@ -43,7 +42,7 @@ func KillDeviceProcess(ctx *gin.Context) {
return return
} }
trigger := utils.GetStrUUID() trigger := utils.GetStrUUID()
common.SendPackByUUID(modules.Packet{Code: 0, Act: `killProcess`, Data: gin.H{`pid`: strconv.FormatInt(int64(form.Pid), 10)}, Event: trigger}, target) common.SendPackByUUID(modules.Packet{Code: 0, Act: `killProcess`, Data: gin.H{`pid`: form.Pid}, Event: trigger}, target)
ok = common.AddEventOnce(func(p modules.Packet, _ *melody.Session) { ok = common.AddEventOnce(func(p modules.Packet, _ *melody.Session) {
if p.Code != 0 { if p.Code != 0 {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, modules.Packet{Code: 1, Msg: p.Msg}) ctx.AbortWithStatusJSON(http.StatusInternalServerError, modules.Packet{Code: 1, Msg: p.Msg})

View File

@@ -178,14 +178,15 @@ func wsOnMessageBinary(session *melody.Session, data []byte) {
var pack modules.Packet var pack modules.Packet
{ {
if len(data) >= 22 { dataLen := len(data)
if bytes.Equal(data[:5], []byte{00, 22, 34, 19, 20}) { if dataLen >= 22 {
if bytes.Equal(data[:5], []byte{34, 22, 19, 17, 20}) {
event := hex.EncodeToString(data[6:22]) event := hex.EncodeToString(data[6:22])
copy(data[6:], data[22:]) copy(data[6:], data[22:])
common.CallEvent(modules.Packet{ common.CallEvent(modules.Packet{
Event: event, Event: event,
Data: gin.H{ Data: gin.H{
`data`: common.RemoveBytesSuffix(&data, 16), `data`: utils.GetSlicePrefix(&data, dataLen-16),
}, },
}, session) }, session)
return return
@@ -205,7 +206,7 @@ func wsOnMessageBinary(session *melody.Session, data []byte) {
return return
} }
if !common.Devices.Has(session.UUID) { if !common.Devices.Has(session.UUID) {
session.CloseWithMsg(melody.FormatCloseMessage(1001, `invalid identifier`)) session.CloseWithMsg(melody.FormatCloseMessage(1001, `invalid device id`))
return return
} }
common.CallEvent(pack, session) common.CallEvent(pack, session)

View File

@@ -9,6 +9,8 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"reflect"
"unsafe"
) )
var ( var (
@@ -17,6 +19,27 @@ var (
JSON = jsoniter.ConfigCompatibleWithStandardLibrary JSON = jsoniter.ConfigCompatibleWithStandardLibrary
) )
func If[T any](b bool, t, f T) T {
if b {
return t
}
return f
}
func Min[T int | int32 | int64 | uint | uint32 | uint64 | float32 | float64](a, b T) T {
if a < b {
return a
}
return b
}
func Max[T int | int32 | int64 | uint | uint32 | uint64 | float32 | float64](a, b T) T {
if a > b {
return a
}
return b
}
func GenRandByte(n int) []byte { func GenRandByte(n int) []byte {
secBuffer := make([]byte, n) secBuffer := make([]byte, n)
rand.Reader.Read(secBuffer) rand.Reader.Read(secBuffer)
@@ -83,3 +106,30 @@ func Decrypt(data []byte, key []byte) ([]byte, error) {
//fmt.Println(`Recv: `, string(decBuffer[:dataLen-16-64])) //fmt.Println(`Recv: `, string(decBuffer[:dataLen-16-64]))
return decBuffer, nil return decBuffer, nil
} }
func GetSlicePrefix[T any](data *[]T, n int) *[]T {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(data))
return (*[]T)(unsafe.Pointer(&reflect.SliceHeader{
Data: sliceHeader.Data,
Len: n,
Cap: n,
}))
}
func GetSliceSuffix[T any](data *[]T, n int) *[]T {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(data))
return (*[]T)(unsafe.Pointer(&reflect.SliceHeader{
Data: sliceHeader.Data + uintptr(sliceHeader.Len-n),
Len: n,
Cap: n,
}))
}
func GetSliceChunk[T any](data *[]T, start, end int) *[]T {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(data))
return (*[]T)(unsafe.Pointer(&reflect.SliceHeader{
Data: sliceHeader.Data + uintptr(start),
Len: end - start,
Cap: end - start,
}))
}

View File

@@ -142,6 +142,7 @@ function ScreenModal(props) {
updateImage(ab.slice(offset, offset + len + 12), canvasCtx); updateImage(ab.slice(offset, offset + len + 12), canvasCtx);
offset += len + 12; offset += len + 12;
} }
dv = null;
} }
function updateImage(ab, canvasCtx) { function updateImage(ab, canvasCtx) {
let dv = new DataView(ab); let dv = new DataView(ab);
@@ -153,12 +154,13 @@ function ScreenModal(props) {
let bh = dv.getUint16(10, false); let bh = dv.getUint16(10, false);
ab = ab.slice(12); ab = ab.slice(12);
if (it === 0) { if (it === 0) {
canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy); canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh);
dv = null;
} else { } else {
createImageBitmap(new Blob([ab]), 0, 0, bw, bh) createImageBitmap(new Blob([ab]), 0, 0, bw, bh)
.then((ib) => { .then((ib) => {
canvasCtx.drawImage(ib, dx, dy); canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh);
}); }).finally(() => dv = null);
} }
} }
function handleJSON(ab) { function handleJSON(ab) {