mirror of https://github.com/jetkvm/kvm.git
chore(reorganization): part two
Initially tried to condense each part of the kvm software into individual groups. The code is so dependent on each other file it doesn't work without causing a crap ton of import cycles.
This commit is contained in:
parent
9eced85559
commit
1508701927
|
@ -0,0 +1,22 @@
|
|||
; https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
||||
eclint_indent_style = unset
|
||||
|
||||
[Dockerfile]
|
||||
indent_size = 4
|
28
cmd/main.go
28
cmd/main.go
|
@ -10,24 +10,22 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/config"
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
"github.com/jetkvm/kvm/internal/kvm"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
"github.com/jetkvm/kvm/internal/network"
|
||||
"github.com/jetkvm/kvm/internal/server"
|
||||
|
||||
"github.com/gwatts/rootcerts"
|
||||
)
|
||||
|
||||
var appCtx context.Context
|
||||
var ctx context.Context
|
||||
|
||||
func main() {
|
||||
var cancel context.CancelFunc
|
||||
appCtx, cancel = context.WithCancel(context.Background())
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
logging.Logger.Info("Starting JetKvm")
|
||||
go hardware.RunWatchdog()
|
||||
go network.ConfirmCurrentSystem()
|
||||
go kvm.RunWatchdog(ctx)
|
||||
go kvm.ConfirmCurrentSystem()
|
||||
|
||||
http.DefaultClient.Timeout = 1 * time.Minute
|
||||
cfg := config.LoadConfig()
|
||||
|
@ -38,13 +36,13 @@ func main() {
|
|||
logging.Logger.Errorf("failed to load CA certs: %v", err)
|
||||
}
|
||||
|
||||
go network.TimeSyncLoop()
|
||||
go kvm.TimeSyncLoop()
|
||||
|
||||
hardware.StartNativeCtrlSocketServer()
|
||||
hardware.StartNativeVideoSocketServer()
|
||||
kvm.StartNativeCtrlSocketServer()
|
||||
kvm.StartNativeVideoSocketServer()
|
||||
|
||||
go func() {
|
||||
err = hardware.ExtractAndRunNativeBin()
|
||||
err = kvm.ExtractAndRunNativeBin(ctx)
|
||||
if err != nil {
|
||||
logging.Logger.Errorf("failed to extract and run native bin: %v", err)
|
||||
//TODO: prepare an error message screen buffer to show on kvm screen
|
||||
|
@ -58,13 +56,13 @@ func main() {
|
|||
if cfg.AutoUpdateEnabled == false {
|
||||
return
|
||||
}
|
||||
if server.CurrentSession != nil {
|
||||
if kvm.CurrentSession != nil {
|
||||
logging.Logger.Debugf("skipping update since a session is active")
|
||||
time.Sleep(1 * time.Minute)
|
||||
continue
|
||||
}
|
||||
includePreRelease := cfg.IncludePreRelease
|
||||
err = network.TryUpdate(context.Background(), hardware.GetDeviceID(), includePreRelease)
|
||||
err = kvm.TryUpdate(context.Background(), kvm.GetDeviceID(), includePreRelease)
|
||||
if err != nil {
|
||||
logging.Logger.Errorf("failed to auto update: %v", err)
|
||||
}
|
||||
|
@ -72,8 +70,8 @@ func main() {
|
|||
}
|
||||
}()
|
||||
//go RunFuseServer()
|
||||
go server.RunWebServer()
|
||||
go server.RunWebsocketClient()
|
||||
go kvm.RunWebServer()
|
||||
go kvm.RunWebsocketClient()
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigs
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package hardware
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -17,15 +17,15 @@ type remoteImageBackend struct {
|
|||
}
|
||||
|
||||
func (r remoteImageBackend) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
virtualMediaStateMutex.RLock()
|
||||
logging.Logger.Debugf("currentVirtualMediaState is %v", currentVirtualMediaState)
|
||||
VirtualMediaStateMutex.RLock()
|
||||
logging.Logger.Debugf("currentVirtualMediaState is %v", CurrentVirtualMediaState)
|
||||
logging.Logger.Debugf("read size: %d, off: %d", len(p), off)
|
||||
if currentVirtualMediaState == nil {
|
||||
if CurrentVirtualMediaState == nil {
|
||||
return 0, errors.New("image not mounted")
|
||||
}
|
||||
source := currentVirtualMediaState.Source
|
||||
mountedImageSize := currentVirtualMediaState.Size
|
||||
virtualMediaStateMutex.RUnlock()
|
||||
source := CurrentVirtualMediaState.Source
|
||||
mountedImageSize := CurrentVirtualMediaState.Size
|
||||
VirtualMediaStateMutex.RUnlock()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
@ -36,14 +36,14 @@ func (r remoteImageBackend) ReadAt(p []byte, off int64) (n int, err error) {
|
|||
}
|
||||
var data []byte
|
||||
if source == WebRTC {
|
||||
data, err = webRTCDiskReader.Read(ctx, off, readLen)
|
||||
data, err = WebRTCDiskReader.Read(ctx, off, readLen)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n = copy(p, data)
|
||||
return n, nil
|
||||
} else if source == HTTP {
|
||||
return httpRangeReader.ReadAt(p, off)
|
||||
return HttpRangeReader.ReadAt(p, off)
|
||||
} else {
|
||||
return 0, errors.New("unknown image source")
|
||||
}
|
||||
|
@ -54,12 +54,12 @@ func (r remoteImageBackend) WriteAt(p []byte, off int64) (n int, err error) {
|
|||
}
|
||||
|
||||
func (r remoteImageBackend) Size() (int64, error) {
|
||||
virtualMediaStateMutex.Lock()
|
||||
defer virtualMediaStateMutex.Unlock()
|
||||
if currentVirtualMediaState == nil {
|
||||
VirtualMediaStateMutex.Lock()
|
||||
defer VirtualMediaStateMutex.Unlock()
|
||||
if CurrentVirtualMediaState == nil {
|
||||
return 0, errors.New("no virtual media state")
|
||||
}
|
||||
return currentVirtualMediaState.Size, nil
|
||||
return CurrentVirtualMediaState.Size, nil
|
||||
}
|
||||
|
||||
func (r remoteImageBackend) Sync() error {
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/coder/websocket/wsjson"
|
||||
"github.com/jetkvm/kvm/internal/config"
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
|
@ -121,7 +120,7 @@ func runWebsocketClient() error {
|
|||
wsURL.Scheme = "wss"
|
||||
}
|
||||
header := http.Header{}
|
||||
header.Set("X-Device-ID", hardware.GetDeviceID())
|
||||
header.Set("X-Device-ID", GetDeviceID())
|
||||
header.Set("Authorization", "Bearer "+cfg.CloudToken)
|
||||
dialCtx, cancelDial := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancelDial()
|
||||
|
@ -248,7 +247,7 @@ func RPCDeregisterDevice() error {
|
|||
return fmt.Errorf("cloud token or URL is not set")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodDelete, cfg.CloudURL+"/devices/"+hardware.GetDeviceID(), nil)
|
||||
req, err := http.NewRequest(http.MethodDelete, cfg.CloudURL+"/devices/"+GetDeviceID(), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create deregister request: %w", err)
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package hardware
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
)
|
||||
|
||||
var currentScreen = "ui_Boot_Screen"
|
||||
|
@ -34,23 +35,23 @@ func switchToScreenIfDifferent(screenName string) {
|
|||
}
|
||||
|
||||
func updateDisplay() {
|
||||
updateLabelIfChanged("ui_Home_Content_Ip", networkState.IPv4)
|
||||
if usbState == "configured" {
|
||||
updateLabelIfChanged("ui_Home_Content_Ip", NetworkState.IPv4)
|
||||
if UsbState == "configured" {
|
||||
updateLabelIfChanged("ui_Home_Footer_Usb_Status_Label", "Connected")
|
||||
_, _ = CallCtrlAction("lv_obj_set_state", map[string]interface{}{"obj": "ui_Home_Footer_Usb_Status_Label", "state": "LV_STATE_DEFAULT"})
|
||||
} else {
|
||||
updateLabelIfChanged("ui_Home_Footer_Usb_Status_Label", "Disconnected")
|
||||
_, _ = CallCtrlAction("lv_obj_set_state", map[string]interface{}{"obj": "ui_Home_Footer_Usb_Status_Label", "state": "LV_STATE_USER_2"})
|
||||
}
|
||||
if lastVideoState.Ready {
|
||||
if LastVideoState.Ready {
|
||||
updateLabelIfChanged("ui_Home_Footer_Hdmi_Status_Label", "Connected")
|
||||
_, _ = CallCtrlAction("lv_obj_set_state", map[string]interface{}{"obj": "ui_Home_Footer_Hdmi_Status_Label", "state": "LV_STATE_DEFAULT"})
|
||||
} else {
|
||||
updateLabelIfChanged("ui_Home_Footer_Hdmi_Status_Label", "Disconnected")
|
||||
_, _ = CallCtrlAction("lv_obj_set_state", map[string]interface{}{"obj": "ui_Home_Footer_Hdmi_Status_Label", "state": "LV_STATE_USER_2"})
|
||||
}
|
||||
updateLabelIfChanged("ui_Home_Header_Cloud_Status_Label", fmt.Sprintf("%d active", actionSessions))
|
||||
if networkState.Up {
|
||||
updateLabelIfChanged("ui_Home_Header_Cloud_Status_Label", fmt.Sprintf("%d active", ActionSessions))
|
||||
if NetworkState.Up {
|
||||
switchToScreenIfDifferent("ui_Home_Screen")
|
||||
} else {
|
||||
switchToScreenIfDifferent("ui_No_Network_Screen")
|
||||
|
@ -73,7 +74,7 @@ func RequestDisplayUpdate() {
|
|||
|
||||
func updateStaticContents() {
|
||||
//contents that never change
|
||||
updateLabelIfChanged("ui_Home_Content_Mac", networkState.MAC)
|
||||
updateLabelIfChanged("ui_Home_Content_Mac", NetworkState.MAC)
|
||||
systemVersion, appVersion, err := GetLocalVersion()
|
||||
if err == nil {
|
||||
updateLabelIfChanged("ui_About_Content_Operating_System_Version_ContentLabel", systemVersion.String())
|
||||
|
@ -85,7 +86,7 @@ func updateStaticContents() {
|
|||
|
||||
func init() {
|
||||
go func() {
|
||||
waitCtrlClientConnected()
|
||||
WaitCtrlClientConnected()
|
||||
fmt.Println("setting initial display contents")
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
updateStaticContents()
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -58,10 +58,10 @@ type DiskReadRequest struct {
|
|||
End uint64 `json:"end"`
|
||||
}
|
||||
|
||||
var diskReadChan = make(chan []byte, 1)
|
||||
var DiskReadChan = make(chan []byte, 1)
|
||||
|
||||
func (f *WebRTCStreamFile) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
|
||||
buf, err := webRTCDiskReader.Read(ctx, off, int64(len(dest)))
|
||||
buf, err := WebRTCDiskReader.Read(ctx, off, int64(len(dest)))
|
||||
if err != nil {
|
||||
return nil, syscall.EIO
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
package hardware
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
)
|
||||
|
||||
func extractSerialNumber() (string, error) {
|
||||
|
@ -42,7 +47,7 @@ func GetDeviceID() string {
|
|||
deviceIDOnce.Do(func() {
|
||||
serial, err := extractSerialNumber()
|
||||
if err != nil {
|
||||
logger.Warn("unknown serial number, the program likely not running on RV1106")
|
||||
logging.Logger.Warn("unknown serial number, the program likely not running on RV1106")
|
||||
deviceID = "unknown_device_id"
|
||||
} else {
|
||||
deviceID = serial
|
||||
|
@ -51,10 +56,10 @@ func GetDeviceID() string {
|
|||
return deviceID
|
||||
}
|
||||
|
||||
func RunWatchdog() {
|
||||
func RunWatchdog(ctx context.Context) {
|
||||
file, err := os.OpenFile("/dev/watchdog", os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
logger.Warnf("unable to open /dev/watchdog: %v, skipping watchdog reset", err)
|
||||
logging.Logger.Warnf("unable to open /dev/watchdog: %v, skipping watchdog reset", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
@ -65,15 +70,36 @@ func RunWatchdog() {
|
|||
case <-ticker.C:
|
||||
_, err = file.Write([]byte{0})
|
||||
if err != nil {
|
||||
logger.Errorf("error writing to /dev/watchdog, system may reboot: %v", err)
|
||||
logging.Logger.Errorf("error writing to /dev/watchdog, system may reboot: %v", err)
|
||||
}
|
||||
case <-appCtx.Done():
|
||||
case <-ctx.Done():
|
||||
//disarm watchdog with magic value
|
||||
_, err := file.Write([]byte("V"))
|
||||
if err != nil {
|
||||
logger.Errorf("failed to disarm watchdog, system may reboot: %v", err)
|
||||
logging.Logger.Errorf("failed to disarm watchdog, system may reboot: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var builtAppVersion = "0.1.0+dev"
|
||||
|
||||
func GetLocalVersion() (systemVersion *semver.Version, appVersion *semver.Version, err error) {
|
||||
appVersion, err = semver.NewVersion(builtAppVersion)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid built-in app version: %w", err)
|
||||
}
|
||||
|
||||
systemVersionBytes, err := os.ReadFile("/version")
|
||||
if err != nil {
|
||||
return nil, appVersion, fmt.Errorf("error reading system version: %w", err)
|
||||
}
|
||||
|
||||
systemVersion, err = semver.NewVersion(strings.TrimSpace(string(systemVersionBytes)))
|
||||
if err != nil {
|
||||
return nil, appVersion, fmt.Errorf("invalid system version: %w", err)
|
||||
}
|
||||
|
||||
return systemVersion, appVersion, nil
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
package jiggler
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
)
|
||||
|
||||
|
@ -31,11 +30,11 @@ func runJiggler() {
|
|||
if jigglerEnabled {
|
||||
if time.Since(lastUserInput) > 20*time.Second {
|
||||
//TODO: change to rel mouse
|
||||
err := hardware.RPCAbsMouseReport(1, 1, 0)
|
||||
err := RPCAbsMouseReport(1, 1, 0)
|
||||
if err != nil {
|
||||
logging.Logger.Warnf("Failed to jiggle mouse: %v", err)
|
||||
}
|
||||
err = hardware.RPCAbsMouseReport(0, 0, 0)
|
||||
err = RPCAbsMouseReport(0, 0, 0)
|
||||
if err != nil {
|
||||
logging.Logger.Warnf("Failed to reset mouse position: %v", err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -12,10 +12,7 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/config"
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
"github.com/jetkvm/kvm/internal/jiggler"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
"github.com/jetkvm/kvm/internal/network"
|
||||
"github.com/jetkvm/kvm/internal/wol"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
@ -134,7 +131,7 @@ func rpcPing() (string, error) {
|
|||
}
|
||||
|
||||
func rpcGetDeviceID() (string, error) {
|
||||
return hardware.GetDeviceID(), nil
|
||||
return GetDeviceID(), nil
|
||||
}
|
||||
|
||||
var streamFactor = 1.0
|
||||
|
@ -145,7 +142,7 @@ func rpcGetStreamQualityFactor() (float64, error) {
|
|||
|
||||
func rpcSetStreamQualityFactor(factor float64) error {
|
||||
log.Printf("Setting stream quality factor to: %f", factor)
|
||||
var _, err = hardware.CallCtrlAction("set_video_quality_factor", map[string]interface{}{"quality_factor": factor})
|
||||
var _, err = CallCtrlAction("set_video_quality_factor", map[string]interface{}{"quality_factor": factor})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -169,7 +166,7 @@ func rpcSetAutoUpdateState(enabled bool) (bool, error) {
|
|||
}
|
||||
|
||||
func rpcGetEDID() (string, error) {
|
||||
resp, err := hardware.CallCtrlAction("get_edid", nil)
|
||||
resp, err := CallCtrlAction("get_edid", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -187,7 +184,7 @@ func rpcSetEDID(edid string) error {
|
|||
} else {
|
||||
log.Printf("Setting EDID to: %s", edid)
|
||||
}
|
||||
_, err := hardware.CallCtrlAction("set_edid", map[string]interface{}{"edid": edid})
|
||||
_, err := CallCtrlAction("set_edid", map[string]interface{}{"edid": edid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -208,10 +205,10 @@ func rpcSetDevChannelState(enabled bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func rpcGetUpdateStatus() (*network.UpdateStatus, error) {
|
||||
func rpcGetUpdateStatus() (*UpdateStatus, error) {
|
||||
cfg := config.LoadConfig()
|
||||
includePreRelease := cfg.IncludePreRelease
|
||||
updateStatus, err := network.GetUpdateStatus(context.Background(), hardware.GetDeviceID(), includePreRelease)
|
||||
updateStatus, err := GetUpdateStatus(context.Background(), GetDeviceID(), includePreRelease)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error checking for updates: %w", err)
|
||||
}
|
||||
|
@ -223,7 +220,7 @@ func rpcTryUpdate() error {
|
|||
cfg := config.LoadConfig()
|
||||
includePreRelease := cfg.IncludePreRelease
|
||||
go func() {
|
||||
err := network.TryUpdate(context.Background(), hardware.GetDeviceID(), includePreRelease)
|
||||
err := TryUpdate(context.Background(), GetDeviceID(), includePreRelease)
|
||||
if err != nil {
|
||||
logging.Logger.Warnf("failed to try update: %v", err)
|
||||
}
|
||||
|
@ -441,7 +438,7 @@ func rpcSetMassStorageMode(mode string) (string, error) {
|
|||
|
||||
log.Printf("[jsonrpc.go:rpcSetMassStorageMode] Setting mass storage mode to: %s", mode)
|
||||
|
||||
err := hardware.SetMassStorageMode(cdrom)
|
||||
err := SetMassStorageMode(cdrom)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to set mass storage mode: %w", err)
|
||||
}
|
||||
|
@ -453,7 +450,7 @@ func rpcSetMassStorageMode(mode string) (string, error) {
|
|||
}
|
||||
|
||||
func rpcGetMassStorageMode() (string, error) {
|
||||
cdrom, err := hardware.GetMassStorageMode()
|
||||
cdrom, err := GetMassStorageMode()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get mass storage mode: %w", err)
|
||||
}
|
||||
|
@ -466,10 +463,10 @@ func rpcGetMassStorageMode() (string, error) {
|
|||
}
|
||||
|
||||
func rpcIsUpdatePending() (bool, error) {
|
||||
return network.IsUpdatePending(), nil
|
||||
return IsUpdatePending(), nil
|
||||
}
|
||||
|
||||
var udcFilePath = filepath.Join("/sys/bus/platform/drivers/dwc3", udc)
|
||||
var udcFilePath = filepath.Join("/sys/bus/platform/drivers/dwc3", Udc)
|
||||
|
||||
func rpcGetUsbEmulationState() (bool, error) {
|
||||
_, err := os.Stat(udcFilePath)
|
||||
|
@ -484,9 +481,9 @@ func rpcGetUsbEmulationState() (bool, error) {
|
|||
|
||||
func rpcSetUsbEmulationState(enabled bool) error {
|
||||
if enabled {
|
||||
return os.WriteFile("/sys/bus/platform/drivers/dwc3/bind", []byte(udc), 0644)
|
||||
return os.WriteFile("/sys/bus/platform/drivers/dwc3/bind", []byte(Udc), 0644)
|
||||
} else {
|
||||
return os.WriteFile("/sys/bus/platform/drivers/dwc3/unbind", []byte(udc), 0644)
|
||||
return os.WriteFile("/sys/bus/platform/drivers/dwc3/unbind", []byte(Udc), 0644)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -523,15 +520,15 @@ var rpcHandlers = map[string]RPCHandler{
|
|||
"getDeviceID": {Func: rpcGetDeviceID},
|
||||
"deregisterDevice": {Func: RPCDeregisterDevice},
|
||||
"getCloudState": {Func: RPCGetCloudState},
|
||||
"keyboardReport": {Func: hardware.RPCKeyboardReport, Params: []string{"modifier", "keys"}},
|
||||
"absMouseReport": {Func: hardware.RPCAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
||||
"wheelReport": {Func: hardware.RPCWheelReport, Params: []string{"wheelY"}},
|
||||
"keyboardReport": {Func: RPCKeyboardReport, Params: []string{"modifier", "keys"}},
|
||||
"absMouseReport": {Func: RPCAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
||||
"wheelReport": {Func: RPCWheelReport, Params: []string{"wheelY"}},
|
||||
"getVideoState": {Func: rpcGetVideoState},
|
||||
"getUSBState": {Func: hardware.RPCGetUSBState},
|
||||
"unmountImage": {Func: hardware.RPCUnmountImage},
|
||||
"rpcMountBuiltInImage": {Func: hardware.RPCMountBuiltInImage, Params: []string{"filename"}},
|
||||
"setJigglerState": {Func: jiggler.RPCSetJigglerState, Params: []string{"enabled"}},
|
||||
"getJigglerState": {Func: jiggler.RPCGetJigglerState},
|
||||
"getUSBState": {Func: RPCGetUSBState},
|
||||
"unmountImage": {Func: RPCUnmountImage},
|
||||
"rpcMountBuiltInImage": {Func: RPCMountBuiltInImage, Params: []string{"filename"}},
|
||||
"setJigglerState": {Func: RPCSetJigglerState, Params: []string{"enabled"}},
|
||||
"getJigglerState": {Func: RPCGetJigglerState},
|
||||
"sendWOLMagicPacket": {Func: wol.RPCSendWolMagicPacket, Params: []string{"macAddress"}},
|
||||
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
|
||||
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
|
||||
|
@ -552,15 +549,15 @@ var rpcHandlers = map[string]RPCHandler{
|
|||
"isUpdatePending": {Func: rpcIsUpdatePending},
|
||||
"getUsbEmulationState": {Func: rpcGetUsbEmulationState},
|
||||
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
|
||||
"checkMountUrl": {Func: hardware.RPCCheckMountUrl, Params: []string{"url"}},
|
||||
"getVirtualMediaState": {Func: hardware.RPCGetVirtualMediaState},
|
||||
"getStorageSpace": {Func: hardware.RPCGetStorageSpace},
|
||||
"mountWithHTTP": {Func: hardware.RPCMountWithHTTP, Params: []string{"url", "mode"}},
|
||||
"mountWithWebRTC": {Func: hardware.RPCMountWithWebRTC, Params: []string{"filename", "size", "mode"}},
|
||||
"mountWithStorage": {Func: hardware.RPCMountWithStorage, Params: []string{"filename", "mode"}},
|
||||
"listStorageFiles": {Func: hardware.RPCListStorageFiles},
|
||||
"deleteStorageFile": {Func: hardware.RPCDeleteStorageFile, Params: []string{"filename"}},
|
||||
"startStorageFileUpload": {Func: hardware.RPCStartStorageFileUpload, Params: []string{"filename", "size"}},
|
||||
"checkMountUrl": {Func: RPCCheckMountUrl, Params: []string{"url"}},
|
||||
"getVirtualMediaState": {Func: RPCGetVirtualMediaState},
|
||||
"getStorageSpace": {Func: RPCGetStorageSpace},
|
||||
"mountWithHTTP": {Func: RPCMountWithHTTP, Params: []string{"url", "mode"}},
|
||||
"mountWithWebRTC": {Func: RPCMountWithWebRTC, Params: []string{"filename", "size", "mode"}},
|
||||
"mountWithStorage": {Func: RPCMountWithStorage, Params: []string{"filename", "mode"}},
|
||||
"listStorageFiles": {Func: RPCListStorageFiles},
|
||||
"deleteStorageFile": {Func: RPCDeleteStorageFile, Params: []string{"filename"}},
|
||||
"startStorageFileUpload": {Func: RPCStartStorageFileUpload, Params: []string{"filename", "size"}},
|
||||
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
|
||||
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
|
||||
"resetConfig": {Func: rpcResetConfig},
|
|
@ -1,11 +1,11 @@
|
|||
package hardware
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"kvm/resource"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -13,6 +13,9 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/resource"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
"github.com/pion/webrtc/v4/pkg/media"
|
||||
)
|
||||
|
||||
|
@ -95,7 +98,7 @@ var nativeVideoSocketListener net.Listener
|
|||
|
||||
var ctrlClientConnected = make(chan struct{})
|
||||
|
||||
func waitCtrlClientConnected() {
|
||||
func WaitCtrlClientConnected() {
|
||||
<-ctrlClientConnected
|
||||
}
|
||||
|
||||
|
@ -118,11 +121,11 @@ func StartNativeSocketServer(socketPath string, handleClient func(net.Conn), isC
|
|||
conn, err := listener.Accept()
|
||||
listener.Close()
|
||||
if err != nil {
|
||||
logger.Errorf("failed to accept sock: %v", err)
|
||||
logging.Logger.Errorf("failed to accept sock: %v", err)
|
||||
}
|
||||
if isCtrl {
|
||||
close(ctrlClientConnected)
|
||||
logger.Debug("first native ctrl socket client connected")
|
||||
logging.Logger.Debug("first native ctrl socket client connected")
|
||||
}
|
||||
handleClient(conn)
|
||||
}()
|
||||
|
@ -132,20 +135,20 @@ func StartNativeSocketServer(socketPath string, handleClient func(net.Conn), isC
|
|||
|
||||
func StartNativeCtrlSocketServer() {
|
||||
nativeCtrlSocketListener = StartNativeSocketServer("/var/run/jetkvm_ctrl.sock", handleCtrlClient, true)
|
||||
logger.Debug("native app ctrl sock started")
|
||||
logging.Logger.Debug("native app ctrl sock started")
|
||||
}
|
||||
|
||||
func StartNativeVideoSocketServer() {
|
||||
nativeVideoSocketListener = StartNativeSocketServer("/var/run/jetkvm_video.sock", handleVideoClient, false)
|
||||
logger.Debug("native app video sock started")
|
||||
logging.Logger.Debug("native app video sock started")
|
||||
}
|
||||
|
||||
func handleCtrlClient(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
logger.Debug("native socket client connected")
|
||||
logging.Logger.Debug("native socket client connected")
|
||||
if ctrlSocketConn != nil {
|
||||
logger.Debugf("closing existing native socket connection")
|
||||
logging.Logger.Debugf("closing existing native socket connection")
|
||||
ctrlSocketConn.Close()
|
||||
}
|
||||
|
||||
|
@ -155,15 +158,15 @@ func handleCtrlClient(conn net.Conn) {
|
|||
for {
|
||||
n, err := conn.Read(readBuf)
|
||||
if err != nil {
|
||||
logger.Errorf("error reading from ctrl sock: %v", err)
|
||||
logging.Logger.Errorf("error reading from ctrl sock: %v", err)
|
||||
break
|
||||
}
|
||||
readMsg := string(readBuf[:n])
|
||||
logger.Tracef("ctrl sock msg: %v", readMsg)
|
||||
logging.Logger.Tracef("ctrl sock msg: %v", readMsg)
|
||||
ctrlResp := CtrlResponse{}
|
||||
err = json.Unmarshal([]byte(readMsg), &ctrlResp)
|
||||
if err != nil {
|
||||
logger.Warnf("error parsing ctrl sock msg: %v", err)
|
||||
logging.Logger.Warnf("error parsing ctrl sock msg: %v", err)
|
||||
continue
|
||||
}
|
||||
if ctrlResp.Seq != 0 {
|
||||
|
@ -178,7 +181,7 @@ func handleCtrlClient(conn net.Conn) {
|
|||
}
|
||||
}
|
||||
|
||||
logger.Debug("ctrl sock disconnected")
|
||||
logging.Logger.Debug("ctrl sock disconnected")
|
||||
}
|
||||
|
||||
func handleVideoClient(conn net.Conn) {
|
||||
|
@ -186,7 +189,7 @@ func handleVideoClient(conn net.Conn) {
|
|||
|
||||
log.Printf("Native video socket client connected: %v", conn.RemoteAddr())
|
||||
|
||||
inboundPacket := make([]byte, maxFrameSize)
|
||||
inboundPacket := make([]byte, MaxFrameSize)
|
||||
lastFrame := time.Now()
|
||||
for {
|
||||
n, err := conn.Read(inboundPacket)
|
||||
|
@ -207,7 +210,7 @@ func handleVideoClient(conn net.Conn) {
|
|||
}
|
||||
}
|
||||
|
||||
func ExtractAndRunNativeBin() error {
|
||||
func ExtractAndRunNativeBin(ctx context.Context) error {
|
||||
binaryPath := "/userdata/jetkvm/bin/jetkvm_native"
|
||||
if err := ensureBinaryUpdated(binaryPath); err != nil {
|
||||
return fmt.Errorf("failed to extract binary: %w", err)
|
||||
|
@ -231,11 +234,11 @@ func ExtractAndRunNativeBin() error {
|
|||
|
||||
//TODO: add auto restart
|
||||
go func() {
|
||||
<-appCtx.Done()
|
||||
logger.Infof("killing process PID: %d", cmd.Process.Pid)
|
||||
<-ctx.Done()
|
||||
logging.Logger.Infof("killing process PID: %d", cmd.Process.Pid)
|
||||
err := cmd.Process.Kill()
|
||||
if err != nil {
|
||||
logger.Errorf("failed to kill process: %v", err)
|
||||
logging.Logger.Errorf("failed to kill process: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
@ -247,13 +250,13 @@ func ExtractAndRunNativeBin() error {
|
|||
|
||||
func shouldOverwrite(destPath string, srcHash []byte) bool {
|
||||
if srcHash == nil {
|
||||
logger.Debug("error reading embedded jetkvm_native.sha256, doing overwriting")
|
||||
logging.Logger.Debug("error reading embedded jetkvm_native.sha256, doing overwriting")
|
||||
return true
|
||||
}
|
||||
|
||||
dstHash, err := os.ReadFile(destPath + ".sha256")
|
||||
if err != nil {
|
||||
logger.Debug("error reading existing jetkvm_native.sha256, doing overwriting")
|
||||
logging.Logger.Debug("error reading existing jetkvm_native.sha256, doing overwriting")
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -269,13 +272,13 @@ func ensureBinaryUpdated(destPath string) error {
|
|||
|
||||
srcHash, err := resource.ResourceFS.ReadFile("jetkvm_native.sha256")
|
||||
if err != nil {
|
||||
logger.Debug("error reading embedded jetkvm_native.sha256, proceeding with update")
|
||||
logging.Logger.Debug("error reading embedded jetkvm_native.sha256, proceeding with update")
|
||||
srcHash = nil
|
||||
}
|
||||
|
||||
_, err = os.Stat(destPath)
|
||||
if shouldOverwrite(destPath, srcHash) || err != nil {
|
||||
logger.Info("writing jetkvm_native")
|
||||
logging.Logger.Info("writing jetkvm_native")
|
||||
_ = os.Remove(destPath)
|
||||
destFile, err := os.OpenFile(destPath, os.O_CREATE|os.O_RDWR, 0755)
|
||||
if err != nil {
|
||||
|
@ -292,7 +295,7 @@ func ensureBinaryUpdated(destPath string) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
logger.Info("jetkvm_native updated")
|
||||
logging.Logger.Info("jetkvm_native updated")
|
||||
}
|
||||
|
||||
return nil
|
|
@ -1,20 +1,18 @@
|
|||
package network
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
"github.com/pion/mdns/v2"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
var networkState struct {
|
||||
var NetworkState struct {
|
||||
Up bool
|
||||
IPv4 string
|
||||
IPv6 string
|
||||
|
@ -57,10 +55,10 @@ func checkNetworkState() {
|
|||
}
|
||||
}
|
||||
|
||||
if newState != networkState {
|
||||
networkState = newState
|
||||
if newState != NetworkState {
|
||||
NetworkState = newState
|
||||
fmt.Println("network state changed")
|
||||
hardware.RequestDisplayUpdate()
|
||||
RequestDisplayUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +103,7 @@ func init() {
|
|||
}
|
||||
|
||||
go func() {
|
||||
waitCtrlClientConnected()
|
||||
WaitCtrlClientConnected()
|
||||
checkNetworkState()
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
|
@ -1,4 +1,4 @@
|
|||
package network
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package network
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -13,10 +13,10 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
)
|
||||
|
||||
type UpdateMetadata struct {
|
||||
|
@ -43,27 +43,6 @@ type UpdateStatus struct {
|
|||
|
||||
const UpdateMetadataUrl = "https://api.jetkvm.com/releases"
|
||||
|
||||
var builtAppVersion = "0.1.0+dev"
|
||||
|
||||
func GetLocalVersion() (systemVersion *semver.Version, appVersion *semver.Version, err error) {
|
||||
appVersion, err = semver.NewVersion(builtAppVersion)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid built-in app version: %w", err)
|
||||
}
|
||||
|
||||
systemVersionBytes, err := os.ReadFile("/version")
|
||||
if err != nil {
|
||||
return nil, appVersion, fmt.Errorf("error reading system version: %w", err)
|
||||
}
|
||||
|
||||
systemVersion, err = semver.NewVersion(strings.TrimSpace(string(systemVersionBytes)))
|
||||
if err != nil {
|
||||
return nil, appVersion, fmt.Errorf("invalid system version: %w", err)
|
||||
}
|
||||
|
||||
return systemVersion, appVersion, nil
|
||||
}
|
||||
|
||||
func fetchUpdateMetadata(ctx context.Context, deviceId string, includePreRelease bool) (*UpdateMetadata, error) {
|
||||
metadata := &UpdateMetadata{}
|
||||
|
||||
|
@ -498,6 +477,6 @@ func IsUpdatePending() bool {
|
|||
func ConfirmCurrentSystem() {
|
||||
output, err := exec.Command("rk_ota", "--misc=now").CombinedOutput()
|
||||
if err != nil {
|
||||
logger.Warnf("failed to set current partition in A/B setup: %s", string(output))
|
||||
logging.Logger.Warnf("failed to set current partition in A/B setup: %s", string(output))
|
||||
}
|
||||
}
|
|
@ -1,32 +1,34 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
)
|
||||
|
||||
type RemoteImageReader interface {
|
||||
Read(ctx context.Context, offset int64, size int64) ([]byte, error)
|
||||
}
|
||||
|
||||
type WebRTCDiskReader struct {
|
||||
type WebRTCDiskReaderStruct struct {
|
||||
}
|
||||
|
||||
var webRTCDiskReader WebRTCDiskReader
|
||||
var WebRTCDiskReader WebRTCDiskReaderStruct
|
||||
|
||||
func (w *WebRTCDiskReader) Read(ctx context.Context, offset int64, size int64) ([]byte, error) {
|
||||
virtualMediaStateMutex.RLock()
|
||||
if currentVirtualMediaState == nil {
|
||||
virtualMediaStateMutex.RUnlock()
|
||||
func (w *WebRTCDiskReaderStruct) Read(ctx context.Context, offset int64, size int64) ([]byte, error) {
|
||||
VirtualMediaStateMutex.RLock()
|
||||
if CurrentVirtualMediaState == nil {
|
||||
VirtualMediaStateMutex.RUnlock()
|
||||
return nil, errors.New("image not mounted")
|
||||
}
|
||||
if currentVirtualMediaState.Source != WebRTC {
|
||||
virtualMediaStateMutex.RUnlock()
|
||||
if CurrentVirtualMediaState.Source != WebRTC {
|
||||
VirtualMediaStateMutex.RUnlock()
|
||||
return nil, errors.New("image not mounted from webrtc")
|
||||
}
|
||||
mountedImageSize := currentVirtualMediaState.Size
|
||||
virtualMediaStateMutex.RUnlock()
|
||||
mountedImageSize := CurrentVirtualMediaState.Size
|
||||
VirtualMediaStateMutex.RUnlock()
|
||||
end := offset + size
|
||||
if end > mountedImageSize {
|
||||
end = mountedImageSize
|
||||
|
@ -44,7 +46,7 @@ func (w *WebRTCDiskReader) Read(ctx context.Context, offset int64, size int64) (
|
|||
return nil, errors.New("not active session")
|
||||
}
|
||||
|
||||
logger.Debugf("reading from webrtc %v", string(jsonBytes))
|
||||
logging.Logger.Debugf("reading from webrtc %v", string(jsonBytes))
|
||||
err = CurrentSession.DiskChannel.SendText(string(jsonBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -52,7 +54,7 @@ func (w *WebRTCDiskReader) Read(ctx context.Context, offset int64, size int64) (
|
|||
buf := make([]byte, 0)
|
||||
for {
|
||||
select {
|
||||
case data := <-diskReadChan:
|
||||
case data := <-DiskReadChan:
|
||||
buf = data[16:]
|
||||
case <-ctx.Done():
|
||||
return nil, context.Canceled
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
|||
package hardware
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -41,7 +41,7 @@ func init() {
|
|||
logging.UsbLogger.Error("no udc found, skipping USB stack init")
|
||||
return
|
||||
}
|
||||
udc = udcs[0]
|
||||
Udc = udcs[0]
|
||||
_, err := os.Stat(kvmGadgetPath)
|
||||
if err == nil {
|
||||
logging.Logger.Info("usb gadget already exists, skipping usb gadget initialization")
|
||||
|
@ -208,7 +208,7 @@ func writeGadgetConfig() error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(path.Join(kvmGadgetPath, "UDC"), []byte(udc), 0644)
|
||||
err = os.WriteFile(path.Join(kvmGadgetPath, "UDC"), []byte(Udc), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -217,11 +217,11 @@ func writeGadgetConfig() error {
|
|||
}
|
||||
|
||||
func rebindUsb() error {
|
||||
err := os.WriteFile("/sys/bus/platform/drivers/dwc3/unbind", []byte(udc), 0644)
|
||||
err := os.WriteFile("/sys/bus/platform/drivers/dwc3/unbind", []byte(Udc), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile("/sys/bus/platform/drivers/dwc3/bind", []byte(udc), 0644)
|
||||
err = os.WriteFile("/sys/bus/platform/drivers/dwc3/bind", []byte(Udc), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ func RPCKeyboardReport(modifier uint8, keys []uint8) error {
|
|||
keyboardHidFile = nil
|
||||
return err
|
||||
}
|
||||
kvm.ResetUserInputTime()
|
||||
ResetUserInputTime()
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ func RPCAbsMouseReport(x, y int, buttons uint8) error {
|
|||
return fmt.Errorf("failed to open hidg1: %w", err)
|
||||
}
|
||||
}
|
||||
kvm.ResetUserInputTime()
|
||||
ResetUserInputTime()
|
||||
_, err := mouseHidFile.Write([]byte{
|
||||
1, // Report ID 1
|
||||
buttons, // Buttons
|
||||
|
@ -308,7 +308,7 @@ func RPCWheelReport(wheelY int8) error {
|
|||
// Reset the accumulator, keeping any remainder
|
||||
accumulatedWheelY -= float64(scaledWheelY)
|
||||
|
||||
kvm.ResetUserInputTime()
|
||||
ResetUserInputTime()
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -323,7 +323,7 @@ func abs(x float64) float64 {
|
|||
return x
|
||||
}
|
||||
|
||||
var usbState = "unknown"
|
||||
var UsbState = "unknown"
|
||||
|
||||
func RPCGetUSBState() (state string) {
|
||||
stateBytes, err := os.ReadFile("/sys/class/udc/ffb00000.usb/state")
|
||||
|
@ -335,24 +335,24 @@ func RPCGetUSBState() (state string) {
|
|||
|
||||
func TriggerUSBStateUpdate() {
|
||||
go func() {
|
||||
if kvm.CurrentSession == nil {
|
||||
if CurrentSession == nil {
|
||||
log.Println("No active RPC session, skipping update state update")
|
||||
return
|
||||
}
|
||||
WriteJSONRPCEvent("usbState", usbState, kvm.CurrentSession)
|
||||
WriteJSONRPCEvent("usbState", UsbState, CurrentSession)
|
||||
}()
|
||||
}
|
||||
|
||||
var udc string
|
||||
var Udc string
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
for {
|
||||
newState := RPCGetUSBState()
|
||||
if newState != usbState {
|
||||
log.Printf("USB state changed from %s to %s", usbState, newState)
|
||||
usbState = newState
|
||||
requestDisplayUpdate()
|
||||
if newState != UsbState {
|
||||
log.Printf("USB state changed from %s to %s", UsbState, newState)
|
||||
UsbState = newState
|
||||
RequestDisplayUpdate()
|
||||
TriggerUSBStateUpdate()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
|
@ -1,11 +1,10 @@
|
|||
package hardware
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"kvm/resource"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -16,6 +15,8 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/resource"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
|
||||
|
@ -55,7 +56,7 @@ func SetMassStorageMode(cdrom bool) error {
|
|||
|
||||
func OnDiskMessage(msg webrtc.DataChannelMessage) {
|
||||
fmt.Println("Disk Message, len:", len(msg.Data))
|
||||
diskReadChan <- msg.Data
|
||||
DiskReadChan <- msg.Data
|
||||
}
|
||||
|
||||
func mountImage(imagePath string) error {
|
||||
|
@ -153,18 +154,18 @@ type VirtualMediaState struct {
|
|||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
var currentVirtualMediaState *VirtualMediaState
|
||||
var virtualMediaStateMutex sync.RWMutex
|
||||
var CurrentVirtualMediaState *VirtualMediaState
|
||||
var VirtualMediaStateMutex sync.RWMutex
|
||||
|
||||
func RPCGetVirtualMediaState() (*VirtualMediaState, error) {
|
||||
virtualMediaStateMutex.RLock()
|
||||
defer virtualMediaStateMutex.RUnlock()
|
||||
return currentVirtualMediaState, nil
|
||||
VirtualMediaStateMutex.RLock()
|
||||
defer VirtualMediaStateMutex.RUnlock()
|
||||
return CurrentVirtualMediaState, nil
|
||||
}
|
||||
|
||||
func RPCUnmountImage() error {
|
||||
virtualMediaStateMutex.Lock()
|
||||
defer virtualMediaStateMutex.Unlock()
|
||||
VirtualMediaStateMutex.Lock()
|
||||
defer VirtualMediaStateMutex.Unlock()
|
||||
err := setMassStorageImage("\n")
|
||||
if err != nil {
|
||||
fmt.Println("Remove Mass Storage Image Error", err)
|
||||
|
@ -175,32 +176,32 @@ func RPCUnmountImage() error {
|
|||
nbdDevice.Close()
|
||||
nbdDevice = nil
|
||||
}
|
||||
currentVirtualMediaState = nil
|
||||
CurrentVirtualMediaState = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
var httpRangeReader *httpreadat.RangeReader
|
||||
var HttpRangeReader *httpreadat.RangeReader
|
||||
|
||||
func RPCMountWithHTTP(url string, mode VirtualMediaMode) error {
|
||||
virtualMediaStateMutex.Lock()
|
||||
if currentVirtualMediaState != nil {
|
||||
virtualMediaStateMutex.Unlock()
|
||||
VirtualMediaStateMutex.Lock()
|
||||
if CurrentVirtualMediaState != nil {
|
||||
VirtualMediaStateMutex.Unlock()
|
||||
return fmt.Errorf("another virtual media is already mounted")
|
||||
}
|
||||
httpRangeReader = httpreadat.New(url)
|
||||
n, err := httpRangeReader.Size()
|
||||
HttpRangeReader = httpreadat.New(url)
|
||||
n, err := HttpRangeReader.Size()
|
||||
if err != nil {
|
||||
virtualMediaStateMutex.Unlock()
|
||||
VirtualMediaStateMutex.Unlock()
|
||||
return fmt.Errorf("failed to use http url: %w", err)
|
||||
}
|
||||
logging.Logger.Infof("using remote url %s with size %d", url, n)
|
||||
currentVirtualMediaState = &VirtualMediaState{
|
||||
CurrentVirtualMediaState = &VirtualMediaState{
|
||||
Source: HTTP,
|
||||
Mode: mode,
|
||||
URL: url,
|
||||
Size: n,
|
||||
}
|
||||
virtualMediaStateMutex.Unlock()
|
||||
VirtualMediaStateMutex.Unlock()
|
||||
|
||||
logging.Logger.Debug("Starting nbd device")
|
||||
nbdDevice = NewNBDDevice()
|
||||
|
@ -221,19 +222,19 @@ func RPCMountWithHTTP(url string, mode VirtualMediaMode) error {
|
|||
}
|
||||
|
||||
func RPCMountWithWebRTC(filename string, size int64, mode VirtualMediaMode) error {
|
||||
virtualMediaStateMutex.Lock()
|
||||
if currentVirtualMediaState != nil {
|
||||
virtualMediaStateMutex.Unlock()
|
||||
VirtualMediaStateMutex.Lock()
|
||||
if CurrentVirtualMediaState != nil {
|
||||
VirtualMediaStateMutex.Unlock()
|
||||
return fmt.Errorf("another virtual media is already mounted")
|
||||
}
|
||||
currentVirtualMediaState = &VirtualMediaState{
|
||||
CurrentVirtualMediaState = &VirtualMediaState{
|
||||
Source: WebRTC,
|
||||
Mode: mode,
|
||||
Filename: filename,
|
||||
Size: size,
|
||||
}
|
||||
virtualMediaStateMutex.Unlock()
|
||||
logging.Logger.Debugf("currentVirtualMediaState is %v", currentVirtualMediaState)
|
||||
VirtualMediaStateMutex.Unlock()
|
||||
logging.Logger.Debugf("currentVirtualMediaState is %v", CurrentVirtualMediaState)
|
||||
logging.Logger.Debug("Starting nbd device")
|
||||
nbdDevice = NewNBDDevice()
|
||||
err := nbdDevice.Start()
|
||||
|
@ -258,9 +259,9 @@ func RPCMountWithStorage(filename string, mode VirtualMediaMode) error {
|
|||
return err
|
||||
}
|
||||
|
||||
virtualMediaStateMutex.Lock()
|
||||
defer virtualMediaStateMutex.Unlock()
|
||||
if currentVirtualMediaState != nil {
|
||||
VirtualMediaStateMutex.Lock()
|
||||
defer VirtualMediaStateMutex.Unlock()
|
||||
if CurrentVirtualMediaState != nil {
|
||||
return fmt.Errorf("another virtual media is already mounted")
|
||||
}
|
||||
|
||||
|
@ -274,7 +275,7 @@ func RPCMountWithStorage(filename string, mode VirtualMediaMode) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to set mass storage image: %w", err)
|
||||
}
|
||||
currentVirtualMediaState = &VirtualMediaState{
|
||||
CurrentVirtualMediaState = &VirtualMediaState{
|
||||
Source: Storage,
|
||||
Mode: mode,
|
||||
Filename: filename,
|
|
@ -1,14 +1,12 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
)
|
||||
|
||||
// max frame size for 1080p video, specified in mpp venc setting
|
||||
const maxFrameSize = 1920 * 1080 / 2
|
||||
const MaxFrameSize = 1920 * 1080 / 2
|
||||
|
||||
func writeCtrlAction(action string) error {
|
||||
actionMessage := map[string]string{
|
||||
|
@ -18,7 +16,7 @@ func writeCtrlAction(action string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = hardware.WriteCtrlMessage(jsonMessage)
|
||||
err = WriteCtrlMessage(jsonMessage)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -30,25 +28,25 @@ type VideoInputState struct {
|
|||
FramePerSecond float64 `json:"fps"`
|
||||
}
|
||||
|
||||
var lastVideoState VideoInputState
|
||||
var LastVideoState VideoInputState
|
||||
|
||||
func TriggerVideoStateUpdate() {
|
||||
go func() {
|
||||
WriteJSONRPCEvent("videoInputState", lastVideoState, CurrentSession)
|
||||
WriteJSONRPCEvent("videoInputState", LastVideoState, CurrentSession)
|
||||
}()
|
||||
}
|
||||
func HandleVideoStateMessage(event hardware.CtrlResponse) {
|
||||
func HandleVideoStateMessage(event CtrlResponse) {
|
||||
videoState := VideoInputState{}
|
||||
err := json.Unmarshal(event.Data, &videoState)
|
||||
if err != nil {
|
||||
log.Println("Error parsing video state json:", err)
|
||||
return
|
||||
}
|
||||
lastVideoState = videoState
|
||||
LastVideoState = videoState
|
||||
TriggerVideoStateUpdate()
|
||||
hardware.RequestDisplayUpdate()
|
||||
RequestDisplayUpdate()
|
||||
}
|
||||
|
||||
func rpcGetVideoState() (VideoInputState, error) {
|
||||
return lastVideoState, nil
|
||||
return LastVideoState, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"embed"
|
||||
|
@ -11,13 +11,11 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jetkvm/kvm/internal/config"
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
"github.com/jetkvm/kvm/internal/server"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
//go:embed all:static
|
||||
var staticFiles embed.FS
|
||||
//go:embed static
|
||||
var StaticFiles embed.FS
|
||||
|
||||
type WebRTCSessionRequest struct {
|
||||
Sd string `json:"sd"`
|
||||
|
@ -56,7 +54,7 @@ func setupRouter() *gin.Engine {
|
|||
gin.DisableConsoleColor()
|
||||
r := gin.Default()
|
||||
|
||||
staticFS, _ := fs.Sub(staticFiles, "static")
|
||||
staticFS, _ := fs.Sub(StaticFiles, "static")
|
||||
|
||||
// Add a custom middleware to set cache headers for images
|
||||
// This is crucial for optimizing the initial welcome screen load time
|
||||
|
@ -86,14 +84,14 @@ func setupRouter() *gin.Engine {
|
|||
protected.Use(protectedMiddleware())
|
||||
{
|
||||
protected.POST("/webrtc/session", handleWebRTCSession)
|
||||
protected.POST("/cloud/register", server.HandleCloudRegister)
|
||||
protected.POST("/cloud/register", HandleCloudRegister)
|
||||
protected.GET("/device", handleDevice)
|
||||
protected.POST("/auth/logout", handleLogout)
|
||||
|
||||
protected.POST("/auth/password-local", handleCreatePassword)
|
||||
protected.PUT("/auth/password-local", handleUpdatePassword)
|
||||
protected.DELETE("/auth/local-password", handleDeletePassword)
|
||||
protected.POST("/storage/upload", hardware.HandleUploadHttp)
|
||||
protected.POST("/storage/upload", HandleUploadHttp)
|
||||
}
|
||||
|
||||
// Catch-all route for SPA
|
||||
|
@ -119,7 +117,7 @@ func handleWebRTCSession(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
session, err := server.NewSession()
|
||||
session, err := NewSession()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err})
|
||||
return
|
||||
|
@ -220,7 +218,7 @@ func handleDevice(c *gin.Context) {
|
|||
|
||||
response := LocalDevice{
|
||||
AuthMode: &cfg.LocalAuthMode,
|
||||
DeviceID: hardware.GetDeviceID(),
|
||||
DeviceID: GetDeviceID(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
@ -6,9 +6,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/hardware"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
"github.com/jetkvm/kvm/internal/server"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
|
@ -79,19 +77,19 @@ func NewSession() (*Session, error) {
|
|||
case "rpc":
|
||||
session.RPCChannel = d
|
||||
d.OnMessage(func(msg webrtc.DataChannelMessage) {
|
||||
go server.OnRPCMessage(msg, session)
|
||||
go OnRPCMessage(msg, session)
|
||||
})
|
||||
server.TriggerOTAStateUpdate()
|
||||
server.TriggerVideoStateUpdate()
|
||||
hardware.TriggerUSBStateUpdate()
|
||||
TriggerOTAStateUpdate()
|
||||
TriggerVideoStateUpdate()
|
||||
TriggerUSBStateUpdate()
|
||||
case "disk":
|
||||
session.DiskChannel = d
|
||||
d.OnMessage(hardware.OnDiskMessage)
|
||||
d.OnMessage(OnDiskMessage)
|
||||
case "terminal":
|
||||
server.HandleTerminalChannel(d)
|
||||
HandleTerminalChannel(d)
|
||||
default:
|
||||
if strings.HasPrefix(d.Label(), hardware.UploadIdPrefix) {
|
||||
go hardware.HandleUploadChannel(d)
|
||||
if strings.HasPrefix(d.Label(), UploadIdPrefix) {
|
||||
go HandleUploadChannel(d)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -124,9 +122,9 @@ func NewSession() (*Session, error) {
|
|||
if connectionState == webrtc.ICEConnectionStateConnected {
|
||||
if !isConnected {
|
||||
isConnected = true
|
||||
actionSessions++
|
||||
ActionSessions++
|
||||
onActiveSessionsChanged()
|
||||
if actionSessions == 1 {
|
||||
if ActionSessions == 1 {
|
||||
onFirstSessionConnected()
|
||||
}
|
||||
}
|
||||
|
@ -140,14 +138,14 @@ func NewSession() (*Session, error) {
|
|||
CurrentSession = nil
|
||||
}
|
||||
if session.shouldUmountVirtualMedia {
|
||||
err := hardware.RPCUnmountImage()
|
||||
err := RPCUnmountImage()
|
||||
logging.Logger.Debugf("unmount image failed on connection close %v", err)
|
||||
}
|
||||
if isConnected {
|
||||
isConnected = false
|
||||
actionSessions--
|
||||
ActionSessions--
|
||||
onActiveSessionsChanged()
|
||||
if actionSessions == 0 {
|
||||
if ActionSessions == 0 {
|
||||
onLastSessionDisconnected()
|
||||
}
|
||||
}
|
||||
|
@ -156,10 +154,10 @@ func NewSession() (*Session, error) {
|
|||
return session, nil
|
||||
}
|
||||
|
||||
var actionSessions = 0
|
||||
var ActionSessions = 0
|
||||
|
||||
func onActiveSessionsChanged() {
|
||||
hardware.RequestDisplayUpdate()
|
||||
RequestDisplayUpdate()
|
||||
}
|
||||
|
||||
func onFirstSessionConnected() {
|
|
@ -6,3 +6,5 @@ import "github.com/pion/logging"
|
|||
// ref: https://github.com/pion/webrtc/wiki/Debugging-WebRTC
|
||||
var Logger = logging.NewDefaultLoggerFactory().NewLogger("jetkvm")
|
||||
var UsbLogger = logging.NewDefaultLoggerFactory().NewLogger("usb")
|
||||
|
||||
// Ideally you would implement some kind of logging system here with our own custom logging functions
|
||||
|
|
Loading…
Reference in New Issue