mirror of https://github.com/jetkvm/kvm.git
Compare commits
2 Commits
5f617079ab
...
f34d97eedf
| Author | SHA1 | Date |
|---|---|---|
|
|
f34d97eedf | |
|
|
f735f57c3d |
32
hw.go
32
hw.go
|
|
@ -3,6 +3,7 @@ package kvm
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -36,6 +37,37 @@ func readOtpEntropy() ([]byte, error) { //nolint:unused
|
||||||
return content[0x17:0x1C], nil
|
return content[0x17:0x1C], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hwReboot(force bool, postRebootAction *PostRebootAction, delay time.Duration) error {
|
||||||
|
logger.Info().Msgf("Reboot requested, rebooting in %d seconds...", delay)
|
||||||
|
|
||||||
|
writeJSONRPCEvent("willReboot", postRebootAction, currentSession)
|
||||||
|
time.Sleep(1 * time.Second) // Wait for the JSONRPCEvent to be sent
|
||||||
|
|
||||||
|
nativeInstance.SwitchToScreenIfDifferent("rebooting_screen")
|
||||||
|
time.Sleep(delay - (1 * time.Second)) // wait requested extra settle time
|
||||||
|
|
||||||
|
args := []string{}
|
||||||
|
if force {
|
||||||
|
args = append(args, "-f")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("reboot", args...)
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Msg("failed to reboot")
|
||||||
|
switchToMainScreen()
|
||||||
|
return fmt.Errorf("failed to reboot: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the reboot command is successful, exit the program after 5 seconds
|
||||||
|
go func() {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var deviceID string
|
var deviceID string
|
||||||
var deviceIDOnce sync.Once
|
var deviceIDOnce sync.Once
|
||||||
|
|
||||||
|
|
|
||||||
30
jsonrpc.go
30
jsonrpc.go
|
|
@ -173,34 +173,8 @@ func rpcGetDeviceID() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcReboot(force bool) error {
|
func rpcReboot(force bool) error {
|
||||||
logger.Info().Msg("Got reboot request from JSONRPC, rebooting...")
|
logger.Info().Msg("Got reboot request via RPC")
|
||||||
|
return hwReboot(force, nil, 0)
|
||||||
writeJSONRPCEvent("willReboot", nil, currentSession)
|
|
||||||
|
|
||||||
// Wait for the JSONRPCEvent to be sent
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
nativeInstance.SwitchToScreenIfDifferent("rebooting_screen")
|
|
||||||
|
|
||||||
args := []string{}
|
|
||||||
if force {
|
|
||||||
args = append(args, "-f")
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("reboot", args...)
|
|
||||||
err := cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
logger.Error().Err(err).Msg("failed to reboot")
|
|
||||||
switchToMainScreen()
|
|
||||||
return fmt.Errorf("failed to reboot: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the reboot command is successful, exit the program after 5 seconds
|
|
||||||
go func() {
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
os.Exit(0)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var streamFactor = 1.0
|
var streamFactor = 1.0
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,17 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
|
||||||
nativeLogger.Trace().Str("event", event).Msg("rpc event received")
|
nativeLogger.Trace().Str("event", event).Msg("rpc event received")
|
||||||
switch event {
|
switch event {
|
||||||
case "resetConfig":
|
case "resetConfig":
|
||||||
|
nativeLogger.Info().Msg("Reset configuration request via native rpc event")
|
||||||
err := rpcResetConfig()
|
err := rpcResetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
nativeLogger.Warn().Err(err).Msg("error resetting config")
|
nativeLogger.Warn().Err(err).Msg("error resetting config")
|
||||||
}
|
}
|
||||||
_ = rpcReboot(true)
|
_ = rpcReboot(true)
|
||||||
case "reboot":
|
case "reboot":
|
||||||
|
nativeLogger.Info().Msg("Reboot request via native rpc event")
|
||||||
_ = rpcReboot(true)
|
_ = rpcReboot(true)
|
||||||
case "toggleDHCPClient":
|
case "toggleDHCPClient":
|
||||||
|
nativeLogger.Info().Msg("Toggle DHCP request via native rpc event")
|
||||||
_ = rpcToggleDHCPClient()
|
_ = rpcToggleDHCPClient()
|
||||||
default:
|
default:
|
||||||
nativeLogger.Warn().Str("event", event).Msg("unknown rpc event received")
|
nativeLogger.Warn().Str("event", event).Msg("unknown rpc event received")
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,7 @@ func shouldRebootForNetworkChange(oldConfig, newConfig *types.NetworkConfig) (re
|
||||||
|
|
||||||
oldIPv4Mode := oldConfig.IPv4Mode.String
|
oldIPv4Mode := oldConfig.IPv4Mode.String
|
||||||
newIPv4Mode := newConfig.IPv4Mode.String
|
newIPv4Mode := newConfig.IPv4Mode.String
|
||||||
|
|
||||||
// IPv4 mode change requires reboot
|
// IPv4 mode change requires reboot
|
||||||
if newIPv4Mode != oldIPv4Mode {
|
if newIPv4Mode != oldIPv4Mode {
|
||||||
rebootRequired = true
|
rebootRequired = true
|
||||||
|
|
@ -284,7 +285,8 @@ func rpcSetNetworkSettings(settings RpcNetworkSettings) (*RpcNetworkSettings, er
|
||||||
}
|
}
|
||||||
|
|
||||||
if rebootRequired {
|
if rebootRequired {
|
||||||
if err := rpcReboot(false); err != nil {
|
l.Info().Msg("Rebooting due to network changes")
|
||||||
|
if err := hwReboot(true, postRebootAction, 0); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
ota.go
24
ota.go
|
|
@ -487,25 +487,15 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rebootNeeded {
|
if rebootNeeded {
|
||||||
scopedLogger.Info().Msg("System Rebooting in 10s")
|
scopedLogger.Info().Msg("System Rebooting due to OTA update")
|
||||||
|
|
||||||
// TODO: Future enhancement - send postRebootAction to redirect to release notes
|
postRebootAction := &PostRebootAction{
|
||||||
// Example:
|
HealthCheck: "/device/status",
|
||||||
// postRebootAction := &PostRebootAction{
|
RedirectUrl: "/settings/general/update?version=" + remote.SystemVersion,
|
||||||
// HealthCheck: "[..]/device/status",
|
}
|
||||||
// RedirectUrl: "[..]/settings/general/update?version=X.Y.Z",
|
|
||||||
// }
|
|
||||||
// writeJSONRPCEvent("willReboot", postRebootAction, currentSession)
|
|
||||||
|
|
||||||
time.Sleep(10 * time.Second)
|
if err := hwReboot(true, postRebootAction, 10*time.Second); err != nil {
|
||||||
cmd := exec.Command("reboot")
|
return fmt.Errorf("error requesting reboot: %w", err)
|
||||||
err := cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
otaState.Error = fmt.Sprintf("Failed to start reboot: %v", err)
|
|
||||||
scopedLogger.Error().Err(err).Msg("Failed to start reboot")
|
|
||||||
return fmt.Errorf("failed to start reboot: %w", err)
|
|
||||||
} else {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -476,6 +476,7 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
// Device is available, redirect to the specified URL
|
// Device is available, redirect to the specified URL
|
||||||
console.log('Device is available, redirecting to:', postRebootAction.redirectUrl);
|
console.log('Device is available, redirecting to:', postRebootAction.redirectUrl);
|
||||||
window.location.href = postRebootAction.redirectUrl;
|
window.location.href = postRebootAction.redirectUrl;
|
||||||
|
window.location.reload();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore errors - they're expected while device is rebooting
|
// Ignore errors - they're expected while device is rebooting
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
try {
|
try {
|
||||||
data = message.marshal();
|
data = message.marshal();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to send HID RPC message", e);
|
console.error("Failed to marshal HID RPC message", e);
|
||||||
}
|
}
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
|
|
@ -223,13 +223,19 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
setRpcHidProtocolVersion(null);
|
setRpcHidProtocolVersion(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const errorHandler = (e: Event) => {
|
||||||
|
console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${e}`)
|
||||||
|
};
|
||||||
|
|
||||||
rpcHidChannel.addEventListener("message", messageHandler);
|
rpcHidChannel.addEventListener("message", messageHandler);
|
||||||
rpcHidChannel.addEventListener("close", closeHandler);
|
rpcHidChannel.addEventListener("close", closeHandler);
|
||||||
|
rpcHidChannel.addEventListener("error", errorHandler);
|
||||||
rpcHidChannel.addEventListener("open", openHandler);
|
rpcHidChannel.addEventListener("open", openHandler);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
rpcHidChannel.removeEventListener("message", messageHandler);
|
rpcHidChannel.removeEventListener("message", messageHandler);
|
||||||
rpcHidChannel.removeEventListener("close", closeHandler);
|
rpcHidChannel.removeEventListener("close", closeHandler);
|
||||||
|
rpcHidChannel.removeEventListener("error", errorHandler);
|
||||||
rpcHidChannel.removeEventListener("open", openHandler);
|
rpcHidChannel.removeEventListener("open", openHandler);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
|
|
|
||||||
|
|
@ -485,13 +485,15 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
const rpcDataChannel = pc.createDataChannel("rpc");
|
const rpcDataChannel = pc.createDataChannel("rpc");
|
||||||
rpcDataChannel.onclose = () => console.log("rpcDataChannel has closed");
|
rpcDataChannel.onclose = () => console.log("rpcDataChannel has closed");
|
||||||
rpcDataChannel.onerror = (e: Event) => console.error(`Error on DataChannel '${rpcDataChannel.label}': ${e}`);
|
rpcDataChannel.onerror = (ev: Event) => console.error(`Error on DataChannel '${rpcDataChannel.label}': ${ev}`);
|
||||||
rpcDataChannel.onopen = () => {
|
rpcDataChannel.onopen = () => {
|
||||||
setRpcDataChannel(rpcDataChannel);
|
setRpcDataChannel(rpcDataChannel);
|
||||||
};
|
};
|
||||||
|
|
||||||
const rpcHidChannel = pc.createDataChannel("hidrpc");
|
const rpcHidChannel = pc.createDataChannel("hidrpc");
|
||||||
rpcHidChannel.binaryType = "arraybuffer";
|
rpcHidChannel.binaryType = "arraybuffer";
|
||||||
|
rpcHidChannel.onclose = () => console.log("rpcHidChannel has closed");
|
||||||
|
rpcHidChannel.onerror = (ev: Event) => console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${ev}`);
|
||||||
rpcHidChannel.onopen = () => {
|
rpcHidChannel.onopen = () => {
|
||||||
setRpcHidChannel(rpcHidChannel);
|
setRpcHidChannel(rpcHidChannel);
|
||||||
};
|
};
|
||||||
|
|
@ -501,6 +503,8 @@ export default function KvmIdRoute() {
|
||||||
maxRetransmits: 0,
|
maxRetransmits: 0,
|
||||||
});
|
});
|
||||||
rpcHidUnreliableChannel.binaryType = "arraybuffer";
|
rpcHidUnreliableChannel.binaryType = "arraybuffer";
|
||||||
|
rpcHidUnreliableChannel.onclose = () => console.log("rpcHidUnreliableChannel has closed");
|
||||||
|
rpcHidUnreliableChannel.onerror = (ev: Event) => console.error(`Error on rpcHidUnreliableChannel '${rpcHidUnreliableChannel.label}': ${ev}`);
|
||||||
rpcHidUnreliableChannel.onopen = () => {
|
rpcHidUnreliableChannel.onopen = () => {
|
||||||
setRpcHidUnreliableChannel(rpcHidUnreliableChannel);
|
setRpcHidUnreliableChannel(rpcHidUnreliableChannel);
|
||||||
};
|
};
|
||||||
|
|
@ -510,6 +514,8 @@ export default function KvmIdRoute() {
|
||||||
maxRetransmits: 0,
|
maxRetransmits: 0,
|
||||||
});
|
});
|
||||||
rpcHidUnreliableNonOrderedChannel.binaryType = "arraybuffer";
|
rpcHidUnreliableNonOrderedChannel.binaryType = "arraybuffer";
|
||||||
|
rpcHidUnreliableNonOrderedChannel.onclose = () => console.log("rpcHidUnreliableNonOrderedChannel has closed");
|
||||||
|
rpcHidUnreliableNonOrderedChannel.onerror = (ev: Event) => console.error(`Error on rpcHidUnreliableNonOrderedChannel '${rpcHidUnreliableNonOrderedChannel.label}': ${ev}`);
|
||||||
rpcHidUnreliableNonOrderedChannel.onopen = () => {
|
rpcHidUnreliableNonOrderedChannel.onopen = () => {
|
||||||
setRpcHidUnreliableNonOrderedChannel(rpcHidUnreliableNonOrderedChannel);
|
setRpcHidUnreliableNonOrderedChannel(rpcHidUnreliableNonOrderedChannel);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue