2022-06-12 22:39:48 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2022-07-01 21:17:19 +02:00
|
|
|
"context"
|
2022-06-12 22:39:48 +02:00
|
|
|
"encoding/json"
|
2022-06-17 07:22:47 +02:00
|
|
|
"errors"
|
2022-06-12 22:39:48 +02:00
|
|
|
"fmt"
|
2022-06-17 07:22:47 +02:00
|
|
|
"io/fs"
|
2022-10-31 20:40:45 +01:00
|
|
|
"log"
|
2022-06-12 22:39:48 +02:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2023-03-12 23:16:49 +01:00
|
|
|
"os/signal"
|
2022-07-15 10:57:45 +02:00
|
|
|
"runtime/debug"
|
2022-09-04 08:57:05 +02:00
|
|
|
"strconv"
|
2022-06-13 20:11:56 +02:00
|
|
|
"strings"
|
2022-06-15 07:39:09 +02:00
|
|
|
"sync"
|
2022-10-30 21:05:53 +01:00
|
|
|
"syscall"
|
2022-06-12 22:39:48 +02:00
|
|
|
"time"
|
|
|
|
|
2022-06-13 20:11:56 +02:00
|
|
|
"github.com/google/uuid"
|
2022-06-12 22:39:48 +02:00
|
|
|
"github.com/gorilla/mux"
|
|
|
|
|
2022-07-16 02:37:32 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/cmdrunner"
|
2023-01-18 01:02:44 +01:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/pcloud"
|
2022-07-01 19:48:14 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/remote"
|
2022-07-07 07:46:22 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
|
2022-07-02 22:31:56 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/scpacket"
|
2022-07-13 23:16:08 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/scws"
|
2022-06-13 20:11:56 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
|
2022-06-12 22:39:48 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/wsshell"
|
|
|
|
)
|
|
|
|
|
2022-12-21 01:16:46 +01:00
|
|
|
type WebFnType = func(http.ResponseWriter, *http.Request)
|
|
|
|
|
2022-06-12 22:39:48 +02:00
|
|
|
const HttpReadTimeout = 5 * time.Second
|
|
|
|
const HttpWriteTimeout = 21 * time.Second
|
|
|
|
const HttpMaxHeaderBytes = 60000
|
|
|
|
const HttpTimeoutDuration = 21 * time.Second
|
|
|
|
|
2023-01-30 20:36:02 +01:00
|
|
|
const MainServerAddr = "localhost:1619" // PromptServer, P=16, S=19, PS=1619
|
|
|
|
const WebSocketServerAddr = "localhost:1623" // PromptWebsock, P=16, W=23, PW=1623
|
2022-12-28 22:56:19 +01:00
|
|
|
const MainServerDevAddr = "localhost:8090"
|
|
|
|
const WebSocketServerDevAddr = "localhost:8091"
|
2022-06-17 07:22:47 +02:00
|
|
|
const WSStateReconnectTime = 30 * time.Second
|
|
|
|
const WSStatePacketChSize = 20
|
2022-06-16 03:13:16 +02:00
|
|
|
|
2023-01-26 07:49:50 +01:00
|
|
|
const InitialTelemetryWait = 30 * time.Second
|
|
|
|
const TelemetryTick = 30 * time.Minute
|
|
|
|
const TelemetryInterval = 8 * time.Hour
|
|
|
|
|
2022-06-17 07:22:47 +02:00
|
|
|
var GlobalLock = &sync.Mutex{}
|
2022-07-13 23:16:08 +02:00
|
|
|
var WSStateMap = make(map[string]*scws.WSState) // clientid -> WsState
|
2022-12-21 01:16:46 +01:00
|
|
|
var GlobalAuthKey string
|
2023-02-24 00:17:47 +01:00
|
|
|
var BuildTime = "0"
|
2022-06-12 22:39:48 +02:00
|
|
|
|
2023-01-17 08:36:52 +01:00
|
|
|
type ClientActiveState struct {
|
|
|
|
Fg bool `json:"fg"`
|
|
|
|
Active bool `json:"active"`
|
|
|
|
Open bool `json:"open"`
|
|
|
|
}
|
|
|
|
|
2022-07-13 23:16:08 +02:00
|
|
|
func setWSState(state *scws.WSState) {
|
2022-06-17 07:22:47 +02:00
|
|
|
GlobalLock.Lock()
|
|
|
|
defer GlobalLock.Unlock()
|
|
|
|
WSStateMap[state.ClientId] = state
|
2022-06-12 22:39:48 +02:00
|
|
|
}
|
|
|
|
|
2022-07-13 23:16:08 +02:00
|
|
|
func getWSState(clientId string) *scws.WSState {
|
2022-06-17 07:22:47 +02:00
|
|
|
GlobalLock.Lock()
|
|
|
|
defer GlobalLock.Unlock()
|
|
|
|
return WSStateMap[clientId]
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeWSStateAfterTimeout(clientId string, connectTime time.Time, waitDuration time.Duration) {
|
|
|
|
go func() {
|
|
|
|
time.Sleep(waitDuration)
|
|
|
|
GlobalLock.Lock()
|
|
|
|
defer GlobalLock.Unlock()
|
|
|
|
state := WSStateMap[clientId]
|
|
|
|
if state == nil || state.ConnectTime != connectTime {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
delete(WSStateMap, clientId)
|
2022-07-13 23:16:08 +02:00
|
|
|
state.UnWatchScreen()
|
2022-06-17 07:22:47 +02:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2022-06-12 22:39:48 +02:00
|
|
|
func HandleWs(w http.ResponseWriter, r *http.Request) {
|
|
|
|
shell, err := wsshell.StartWS(w, r)
|
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("WebSocket Upgrade Failed %T: %v\n", w, err)
|
2022-06-12 22:39:48 +02:00
|
|
|
return
|
|
|
|
}
|
2022-06-17 07:22:47 +02:00
|
|
|
defer shell.Conn.Close()
|
|
|
|
clientId := r.URL.Query().Get("clientid")
|
|
|
|
if clientId == "" {
|
|
|
|
close(shell.WriteChan)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
state := getWSState(clientId)
|
|
|
|
if state == nil {
|
2022-12-21 01:16:46 +01:00
|
|
|
state = scws.MakeWSState(clientId, GlobalAuthKey)
|
2022-07-13 23:16:08 +02:00
|
|
|
state.ReplaceShell(shell)
|
2022-06-17 07:22:47 +02:00
|
|
|
setWSState(state)
|
|
|
|
} else {
|
2022-07-13 23:16:08 +02:00
|
|
|
state.UpdateConnectTime()
|
|
|
|
state.ReplaceShell(shell)
|
2022-06-17 07:22:47 +02:00
|
|
|
}
|
2022-07-13 23:16:08 +02:00
|
|
|
stateConnectTime := state.GetConnectTime()
|
2022-06-15 07:39:09 +02:00
|
|
|
defer func() {
|
2022-06-17 07:22:47 +02:00
|
|
|
removeWSStateAfterTimeout(clientId, stateConnectTime, WSStateReconnectTime)
|
2022-06-12 22:39:48 +02:00
|
|
|
}()
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("WebSocket opened %s %s\n", state.ClientId, shell.RemoteAddr)
|
2022-07-13 23:16:08 +02:00
|
|
|
state.RunWSRead()
|
2022-06-12 22:39:48 +02:00
|
|
|
}
|
|
|
|
|
2022-06-18 00:30:42 +02:00
|
|
|
// todo: sync multiple writes to the same fifoName into a single go-routine and do liveness checking on fifo
|
|
|
|
// if this returns an error, likely the fifo is dead and the cmd should be marked as 'done'
|
|
|
|
func writeToFifo(fifoName string, data []byte) error {
|
|
|
|
rwfd, err := os.OpenFile(fifoName, os.O_RDWR, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rwfd.Close()
|
|
|
|
fifoWriter, err := os.OpenFile(fifoName, os.O_WRONLY, 0600) // blocking open (open won't block because of rwfd)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer fifoWriter.Close()
|
|
|
|
// this *could* block if the fifo buffer is full
|
|
|
|
// unlikely because if the reader is dead, and len(data) < pipe size, then the buffer will be empty and will clear after rwfd is closed
|
|
|
|
_, err = fifoWriter.Write(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-25 09:26:33 +02:00
|
|
|
func HandleGetClientData(w http.ResponseWriter, r *http.Request) {
|
|
|
|
cdata, err := sstore.EnsureClientData(r.Context())
|
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
WriteJsonSuccess(w, cdata)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func HandleSetWinSize(w http.ResponseWriter, r *http.Request) {
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
|
|
|
var winSize sstore.ClientWinSizeType
|
|
|
|
err := decoder.Decode(&winSize)
|
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, fmt.Errorf("error decoding json: %w", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = sstore.SetWinSize(r.Context(), winSize)
|
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, fmt.Errorf("error setting winsize: %w", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
WriteJsonSuccess(w, true)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-01-17 08:36:52 +01:00
|
|
|
// params: fg, active, open
|
|
|
|
func HandleLogActiveState(w http.ResponseWriter, r *http.Request) {
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
|
|
|
var activeState ClientActiveState
|
|
|
|
err := decoder.Decode(&activeState)
|
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, fmt.Errorf("error decoding json: %w", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
activity := sstore.ActivityUpdate{}
|
|
|
|
if activeState.Fg {
|
|
|
|
activity.FgMinutes = 1
|
|
|
|
}
|
|
|
|
if activeState.Active {
|
|
|
|
activity.ActiveMinutes = 1
|
|
|
|
}
|
|
|
|
if activeState.Open {
|
|
|
|
activity.OpenMinutes = 1
|
|
|
|
}
|
2023-02-22 07:41:56 +01:00
|
|
|
activity.NumConns = remote.NumRemotes()
|
2023-01-17 08:36:52 +01:00
|
|
|
err = sstore.UpdateCurrentActivity(r.Context(), activity)
|
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, fmt.Errorf("error updating activity: %w", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
WriteJsonSuccess(w, true)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-02 22:31:56 +02:00
|
|
|
// params: sessionid, windowid
|
2022-07-08 22:23:45 +02:00
|
|
|
func HandleGetWindow(w http.ResponseWriter, r *http.Request) {
|
2022-07-02 22:31:56 +02:00
|
|
|
qvals := r.URL.Query()
|
|
|
|
sessionId := qvals.Get("sessionid")
|
|
|
|
windowId := qvals.Get("windowid")
|
|
|
|
if _, err := uuid.Parse(sessionId); err != nil {
|
|
|
|
WriteJsonError(w, fmt.Errorf("invalid sessionid: %w", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := uuid.Parse(windowId); err != nil {
|
|
|
|
WriteJsonError(w, fmt.Errorf("invalid windowid: %w", err))
|
|
|
|
return
|
|
|
|
}
|
2022-07-08 22:23:45 +02:00
|
|
|
window, err := sstore.GetWindowById(r.Context(), sessionId, windowId)
|
2022-07-02 22:31:56 +02:00
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, err)
|
|
|
|
return
|
|
|
|
}
|
2022-07-08 22:23:45 +02:00
|
|
|
WriteJsonSuccess(w, window)
|
2022-07-02 22:31:56 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-10-27 09:33:50 +02:00
|
|
|
func HandleRtnState(w http.ResponseWriter, r *http.Request) {
|
2022-11-29 03:03:02 +01:00
|
|
|
defer func() {
|
|
|
|
r := recover()
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.Printf("[error] in handlertnstate: %v\n", r)
|
|
|
|
debug.PrintStack()
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("panic: %v", r)))
|
|
|
|
return
|
|
|
|
}()
|
2022-10-27 09:33:50 +02:00
|
|
|
qvals := r.URL.Query()
|
|
|
|
sessionId := qvals.Get("sessionid")
|
|
|
|
cmdId := qvals.Get("cmdid")
|
|
|
|
if sessionId == "" || cmdId == "" {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("must specify sessionid and cmdid")))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := uuid.Parse(sessionId); err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("invalid sessionid: %v", err)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := uuid.Parse(cmdId); err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("invalid cmdid: %v", err)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
data, err := cmdrunner.GetRtnStateDiff(r.Context(), sessionId, cmdId)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("cannot get rtnstate diff: %v", err)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write(data)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-09-15 08:10:35 +02:00
|
|
|
func HandleRemotePty(w http.ResponseWriter, r *http.Request) {
|
|
|
|
qvals := r.URL.Query()
|
|
|
|
remoteId := qvals.Get("remoteid")
|
|
|
|
if remoteId == "" {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("must specify remoteid")))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := uuid.Parse(remoteId); err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("invalid remoteid: %v", err)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
realOffset, data, err := remote.ReadRemotePty(r.Context(), remoteId)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("error reading ptyout file: %v", err)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Header().Set("X-PtyDataOffset", strconv.FormatInt(realOffset, 10))
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write(data)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-03 21:01:56 +02:00
|
|
|
func HandleGetPtyOut(w http.ResponseWriter, r *http.Request) {
|
2022-06-12 22:39:48 +02:00
|
|
|
qvals := r.URL.Query()
|
|
|
|
sessionId := qvals.Get("sessionid")
|
|
|
|
cmdId := qvals.Get("cmdid")
|
|
|
|
if sessionId == "" || cmdId == "" {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("must specify sessionid and cmdid")))
|
|
|
|
return
|
|
|
|
}
|
2022-07-07 07:46:22 +02:00
|
|
|
if _, err := uuid.Parse(sessionId); err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("invalid sessionid: %v", err)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := uuid.Parse(cmdId); err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("invalid cmdid: %v", err)))
|
|
|
|
return
|
|
|
|
}
|
2022-09-04 08:57:05 +02:00
|
|
|
realOffset, data, err := sstore.ReadFullPtyOutFile(r.Context(), sessionId, cmdId)
|
2022-06-12 22:39:48 +02:00
|
|
|
if err != nil {
|
2022-06-17 07:22:47 +02:00
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
return
|
|
|
|
}
|
2022-06-12 22:39:48 +02:00
|
|
|
w.WriteHeader(500)
|
2022-08-19 22:23:00 +02:00
|
|
|
w.Write([]byte(fmt.Sprintf("error reading ptyout file: %v", err)))
|
2022-06-12 22:39:48 +02:00
|
|
|
return
|
|
|
|
}
|
2022-09-04 08:57:05 +02:00
|
|
|
w.Header().Set("X-PtyDataOffset", strconv.FormatInt(realOffset, 10))
|
2022-06-12 22:39:48 +02:00
|
|
|
w.WriteHeader(http.StatusOK)
|
2022-08-19 22:23:00 +02:00
|
|
|
w.Write(data)
|
2022-06-12 22:39:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func WriteJsonError(w http.ResponseWriter, errVal error) {
|
2022-06-13 20:11:56 +02:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2022-07-16 02:37:32 +02:00
|
|
|
w.WriteHeader(200)
|
2022-06-12 22:39:48 +02:00
|
|
|
errMap := make(map[string]interface{})
|
|
|
|
errMap["error"] = errVal.Error()
|
|
|
|
barr, _ := json.Marshal(errMap)
|
|
|
|
w.Write(barr)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func WriteJsonSuccess(w http.ResponseWriter, data interface{}) {
|
2022-06-13 20:11:56 +02:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2022-06-12 22:39:48 +02:00
|
|
|
rtnMap := make(map[string]interface{})
|
|
|
|
rtnMap["success"] = true
|
|
|
|
if data != nil {
|
|
|
|
rtnMap["data"] = data
|
|
|
|
}
|
|
|
|
barr, err := json.Marshal(rtnMap)
|
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(200)
|
|
|
|
w.Write(barr)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func HandleRunCommand(w http.ResponseWriter, r *http.Request) {
|
2022-07-15 10:57:45 +02:00
|
|
|
defer func() {
|
|
|
|
r := recover()
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] in run-command: %v\n", r)
|
2022-07-15 10:57:45 +02:00
|
|
|
debug.PrintStack()
|
2022-08-24 22:21:54 +02:00
|
|
|
WriteJsonError(w, fmt.Errorf("panic: %v", r))
|
2022-07-15 10:57:45 +02:00
|
|
|
return
|
|
|
|
}()
|
2022-06-12 22:39:48 +02:00
|
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
2022-07-02 22:31:56 +02:00
|
|
|
var commandPk scpacket.FeCommandPacketType
|
|
|
|
err := decoder.Decode(&commandPk)
|
2022-06-12 22:39:48 +02:00
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, fmt.Errorf("error decoding json: %w", err))
|
|
|
|
return
|
|
|
|
}
|
2022-07-16 02:37:32 +02:00
|
|
|
update, err := cmdrunner.HandleCommand(r.Context(), &commandPk)
|
2022-07-03 21:01:56 +02:00
|
|
|
if err != nil {
|
|
|
|
WriteJsonError(w, err)
|
2022-06-13 20:11:56 +02:00
|
|
|
return
|
|
|
|
}
|
2022-07-16 02:37:32 +02:00
|
|
|
WriteJsonSuccess(w, update)
|
2022-07-03 21:01:56 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-12-21 01:16:46 +01:00
|
|
|
func AuthKeyWrap(fn WebFnType) WebFnType {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
reqAuthKey := r.Header.Get("X-AuthKey")
|
|
|
|
if reqAuthKey == "" {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte("no x-authkey header"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if reqAuthKey != GlobalAuthKey {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte("x-authkey header is invalid"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|
|
|
fn(w, r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-16 03:13:16 +02:00
|
|
|
func runWebSocketServer() {
|
|
|
|
gr := mux.NewRouter()
|
|
|
|
gr.HandleFunc("/ws", HandleWs)
|
2022-12-29 02:47:12 +01:00
|
|
|
serverAddr := WebSocketServerAddr
|
|
|
|
if scbase.IsDevMode() {
|
|
|
|
serverAddr = WebSocketServerDevAddr
|
|
|
|
}
|
2022-06-16 03:13:16 +02:00
|
|
|
server := &http.Server{
|
2022-12-29 02:47:12 +01:00
|
|
|
Addr: serverAddr,
|
2022-06-16 03:13:16 +02:00
|
|
|
ReadTimeout: HttpReadTimeout,
|
|
|
|
WriteTimeout: HttpWriteTimeout,
|
|
|
|
MaxHeaderBytes: HttpMaxHeaderBytes,
|
|
|
|
Handler: gr,
|
|
|
|
}
|
|
|
|
server.SetKeepAlivesEnabled(false)
|
2022-12-29 02:47:12 +01:00
|
|
|
log.Printf("Running websocket server on %s\n", serverAddr)
|
2022-06-16 03:13:16 +02:00
|
|
|
err := server.ListenAndServe()
|
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] trying to run websocket server: %v\n", err)
|
2022-06-16 03:13:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-06 01:31:22 +02:00
|
|
|
func test() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-23 08:10:18 +01:00
|
|
|
func sendTelemetryWrapper() {
|
2023-01-23 21:54:32 +01:00
|
|
|
defer func() {
|
|
|
|
r := recover()
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.Printf("[error] in sendTelemetryWrapper: %v\n", r)
|
|
|
|
debug.PrintStack()
|
|
|
|
return
|
|
|
|
}()
|
2023-01-23 08:10:18 +01:00
|
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancelFn()
|
2023-01-23 21:54:32 +01:00
|
|
|
err := pcloud.SendTelemetry(ctx, false)
|
2023-01-23 08:10:18 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("[error] sending telemetry: %v\n", err)
|
|
|
|
}
|
2023-01-18 01:02:44 +01:00
|
|
|
}
|
|
|
|
|
2023-01-26 07:49:50 +01:00
|
|
|
func telemetryLoop() {
|
|
|
|
var lastSent time.Time
|
|
|
|
time.Sleep(InitialTelemetryWait)
|
|
|
|
for {
|
|
|
|
dur := time.Now().Sub(lastSent)
|
|
|
|
if lastSent.IsZero() || dur >= TelemetryInterval {
|
|
|
|
lastSent = time.Now()
|
|
|
|
sendTelemetryWrapper()
|
|
|
|
}
|
|
|
|
time.Sleep(TelemetryTick)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-30 21:05:53 +01:00
|
|
|
// watch stdin, kill server if stdin is closed
|
|
|
|
func stdinReadWatch() {
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
for {
|
|
|
|
_, err := os.Stdin.Read(buf)
|
|
|
|
if err != nil {
|
2023-03-12 22:42:18 +01:00
|
|
|
log.Printf("[prompt] stdin closed/error, shutting down: %v\n", err)
|
2023-01-23 08:10:18 +01:00
|
|
|
sendTelemetryWrapper()
|
2023-03-12 23:16:49 +01:00
|
|
|
log.Printf("[prompt] closing db connection\n")
|
|
|
|
sstore.CloseDB()
|
2023-03-12 22:42:18 +01:00
|
|
|
log.Printf("[prompt] *** shutting down local server\n")
|
2023-03-12 23:16:49 +01:00
|
|
|
time.Sleep(1 * time.Second)
|
2022-10-30 21:05:53 +01:00
|
|
|
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
|
2023-01-24 00:48:54 +01:00
|
|
|
break
|
2022-10-30 21:05:53 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-24 00:48:54 +01:00
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
syscall.Kill(syscall.Getpid(), syscall.SIGKILL)
|
2022-10-30 21:05:53 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 23:16:49 +01:00
|
|
|
// ignore SIGHUP
|
|
|
|
func installSignalHandlers() {
|
|
|
|
sigCh := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigCh, syscall.SIGHUP)
|
|
|
|
go func() {
|
|
|
|
for sig := range sigCh {
|
|
|
|
fmt.Printf("[prompt] got signal %v (ignoring)\n", sig)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2022-06-12 22:39:48 +02:00
|
|
|
func main() {
|
2023-02-24 00:17:47 +01:00
|
|
|
scbase.BuildTime = BuildTime
|
|
|
|
|
2022-09-06 01:31:22 +02:00
|
|
|
if len(os.Args) >= 2 && os.Args[1] == "--test" {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("running test fn\n")
|
2022-09-06 01:31:22 +02:00
|
|
|
err := test()
|
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] %v\n", err)
|
2022-09-06 01:31:22 +02:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-12-20 02:36:19 +01:00
|
|
|
scHomeDir := scbase.GetPromptHomeDir()
|
2023-03-12 22:42:18 +01:00
|
|
|
log.Printf("[prompt] *** starting local server\n")
|
2023-02-24 00:17:47 +01:00
|
|
|
log.Printf("[prompt] local server version %s+%s\n", scbase.PromptVersion, scbase.BuildTime)
|
2022-12-20 02:36:19 +01:00
|
|
|
log.Printf("[prompt] homedir = %q\n", scHomeDir)
|
2022-10-31 20:40:45 +01:00
|
|
|
|
2022-12-20 03:52:08 +01:00
|
|
|
scLock, err := scbase.AcquirePromptLock()
|
2022-07-08 01:29:14 +02:00
|
|
|
if err != nil || scLock == nil {
|
2022-12-21 01:16:46 +01:00
|
|
|
log.Printf("[error] cannot acquire prompt lock: %v\n", err)
|
2022-07-08 01:29:14 +02:00
|
|
|
return
|
|
|
|
}
|
2022-07-01 19:48:14 +02:00
|
|
|
if len(os.Args) >= 2 && strings.HasPrefix(os.Args[1], "--migrate") {
|
|
|
|
err := sstore.MigrateCommandOpts(os.Args[1:])
|
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] migrate cmd: %v\n", err)
|
2022-07-01 19:48:14 +02:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2022-12-21 01:16:46 +01:00
|
|
|
authKey, err := scbase.ReadPromptAuthKey()
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[error] %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
GlobalAuthKey = authKey
|
2022-07-08 01:29:14 +02:00
|
|
|
err = sstore.TryMigrateUp()
|
2022-07-01 19:48:14 +02:00
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] migrate up: %v\n", err)
|
2022-07-01 19:48:14 +02:00
|
|
|
return
|
|
|
|
}
|
2022-09-25 09:26:33 +02:00
|
|
|
clientData, err := sstore.EnsureClientData(context.Background())
|
2022-08-27 01:21:19 +02:00
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] ensuring client data: %v\n", err)
|
2022-08-27 01:21:19 +02:00
|
|
|
return
|
|
|
|
}
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("userid = %s\n", clientData.UserId)
|
2022-07-01 21:17:19 +02:00
|
|
|
err = sstore.EnsureLocalRemote(context.Background())
|
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] ensuring local remote: %v\n", err)
|
2022-07-01 21:17:19 +02:00
|
|
|
return
|
|
|
|
}
|
2022-07-08 06:39:25 +02:00
|
|
|
_, err = sstore.EnsureDefaultSession(context.Background())
|
2022-07-01 23:45:33 +02:00
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] ensuring default session: %v\n", err)
|
2022-07-01 23:45:33 +02:00
|
|
|
return
|
2022-07-01 21:17:19 +02:00
|
|
|
}
|
2022-07-02 02:38:36 +02:00
|
|
|
err = remote.LoadRemotes(context.Background())
|
2022-06-12 22:39:48 +02:00
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] loading remotes: %v\n", err)
|
2022-06-12 22:39:48 +02:00
|
|
|
return
|
|
|
|
}
|
2022-07-07 04:01:00 +02:00
|
|
|
|
2022-07-08 01:29:14 +02:00
|
|
|
err = sstore.HangupAllRunningCmds(context.Background())
|
|
|
|
if err != nil {
|
2023-01-12 05:53:46 +01:00
|
|
|
log.Printf("[error] calling HUP on all running commands: %v\n", err)
|
|
|
|
}
|
|
|
|
err = sstore.ReInitFocus(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[error] resetting window focus: %v\n", err)
|
2022-07-08 01:29:14 +02:00
|
|
|
}
|
2022-07-07 04:01:00 +02:00
|
|
|
|
2023-01-26 07:49:50 +01:00
|
|
|
log.Printf("PCLOUD_ENDPOINT=%s\n", pcloud.GetEndpoint())
|
2023-02-24 00:17:47 +01:00
|
|
|
err = sstore.UpdateCurrentActivity(context.Background(), sstore.ActivityUpdate{NumConns: remote.NumRemotes()}) // set at least one record into activity
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[error] updating activity: %v\n", err)
|
|
|
|
}
|
2023-03-12 23:16:49 +01:00
|
|
|
installSignalHandlers()
|
2023-01-26 07:49:50 +01:00
|
|
|
go telemetryLoop()
|
2022-10-30 21:05:53 +01:00
|
|
|
go stdinReadWatch()
|
2022-06-16 03:13:16 +02:00
|
|
|
go runWebSocketServer()
|
2022-06-12 22:39:48 +02:00
|
|
|
gr := mux.NewRouter()
|
2022-12-21 01:16:46 +01:00
|
|
|
gr.HandleFunc("/api/ptyout", AuthKeyWrap(HandleGetPtyOut))
|
|
|
|
gr.HandleFunc("/api/remote-pty", AuthKeyWrap(HandleRemotePty))
|
|
|
|
gr.HandleFunc("/api/rtnstate", AuthKeyWrap(HandleRtnState))
|
|
|
|
gr.HandleFunc("/api/get-window", AuthKeyWrap(HandleGetWindow))
|
|
|
|
gr.HandleFunc("/api/run-command", AuthKeyWrap(HandleRunCommand)).Methods("POST")
|
|
|
|
gr.HandleFunc("/api/get-client-data", AuthKeyWrap(HandleGetClientData))
|
|
|
|
gr.HandleFunc("/api/set-winsize", AuthKeyWrap(HandleSetWinSize))
|
2023-01-17 08:36:52 +01:00
|
|
|
gr.HandleFunc("/api/log-active-state", AuthKeyWrap(HandleLogActiveState))
|
2022-12-29 02:47:12 +01:00
|
|
|
serverAddr := MainServerAddr
|
|
|
|
if scbase.IsDevMode() {
|
|
|
|
serverAddr = MainServerDevAddr
|
|
|
|
}
|
2022-06-12 22:39:48 +02:00
|
|
|
server := &http.Server{
|
2022-12-29 02:47:12 +01:00
|
|
|
Addr: serverAddr,
|
2022-06-12 22:39:48 +02:00
|
|
|
ReadTimeout: HttpReadTimeout,
|
|
|
|
WriteTimeout: HttpWriteTimeout,
|
|
|
|
MaxHeaderBytes: HttpMaxHeaderBytes,
|
|
|
|
Handler: http.TimeoutHandler(gr, HttpTimeoutDuration, "Timeout"),
|
|
|
|
}
|
|
|
|
server.SetKeepAlivesEnabled(false)
|
2022-12-29 02:47:12 +01:00
|
|
|
log.Printf("Running main server on %s\n", serverAddr)
|
2022-06-12 22:39:48 +02:00
|
|
|
err = server.ListenAndServe()
|
|
|
|
if err != nil {
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("ERROR: %v\n", err)
|
2022-06-12 22:39:48 +02:00
|
|
|
}
|
|
|
|
}
|