mirror of https://github.com/jetkvm/kvm.git
Revamp the OTA and reboot processing
OTA supplies port-reboot action to handle the rebooting device. Make sure we force-reload the page after redirection. Move reboot logic into hw.go and make it set the willReboot message with parameters if provided. Improve logging consistency.
This commit is contained in:
parent
d3b0f1bebc
commit
f735f57c3d
32
hw.go
32
hw.go
|
|
@ -3,6 +3,7 @@ package kvm
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -36,6 +37,37 @@ func readOtpEntropy() ([]byte, error) { //nolint:unused
|
|||
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 deviceIDOnce sync.Once
|
||||
|
||||
|
|
|
|||
30
jsonrpc.go
30
jsonrpc.go
|
|
@ -173,34 +173,8 @@ func rpcGetDeviceID() (string, error) {
|
|||
}
|
||||
|
||||
func rpcReboot(force bool) error {
|
||||
logger.Info().Msg("Got reboot request from JSONRPC, rebooting...")
|
||||
|
||||
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
|
||||
logger.Info().Msg("Got reboot request via RPC")
|
||||
return hwReboot(force, nil, 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")
|
||||
switch event {
|
||||
case "resetConfig":
|
||||
nativeLogger.Info().Msg("Reset configuration request via native rpc event")
|
||||
err := rpcResetConfig()
|
||||
if err != nil {
|
||||
nativeLogger.Warn().Err(err).Msg("error resetting config")
|
||||
}
|
||||
_ = rpcReboot(true)
|
||||
case "reboot":
|
||||
nativeLogger.Info().Msg("Reboot request via native rpc event")
|
||||
_ = rpcReboot(true)
|
||||
case "toggleDHCPClient":
|
||||
nativeLogger.Info().Msg("Toggle DHCP request via native rpc event")
|
||||
_ = rpcToggleDHCPClient()
|
||||
default:
|
||||
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
|
||||
newIPv4Mode := newConfig.IPv4Mode.String
|
||||
|
||||
// IPv4 mode change requires reboot
|
||||
if newIPv4Mode != oldIPv4Mode {
|
||||
rebootRequired = true
|
||||
|
|
@ -284,7 +285,8 @@ func rpcSetNetworkSettings(settings RpcNetworkSettings) (*RpcNetworkSettings, er
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
24
ota.go
24
ota.go
|
|
@ -487,25 +487,15 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
|||
}
|
||||
|
||||
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
|
||||
// Example:
|
||||
// postRebootAction := &PostRebootAction{
|
||||
// HealthCheck: "[..]/device/status",
|
||||
// RedirectUrl: "[..]/settings/general/update?version=X.Y.Z",
|
||||
// }
|
||||
// writeJSONRPCEvent("willReboot", postRebootAction, currentSession)
|
||||
postRebootAction := &PostRebootAction{
|
||||
HealthCheck: "/device/status",
|
||||
RedirectUrl: "/settings/general/update?version=" + remote.SystemVersion,
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
cmd := exec.Command("reboot")
|
||||
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)
|
||||
if err := hwReboot(true, postRebootAction, 10*time.Second); err != nil {
|
||||
return fmt.Errorf("error requesting reboot: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -476,6 +476,7 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
|||
// Device is available, redirect to the specified URL
|
||||
console.log('Device is available, redirecting to:', postRebootAction.redirectUrl);
|
||||
window.location.href = postRebootAction.redirectUrl;
|
||||
window.location.reload();
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore errors - they're expected while device is rebooting
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
try {
|
||||
data = message.marshal();
|
||||
} catch (e) {
|
||||
console.error("Failed to send HID RPC message", e);
|
||||
console.error("Failed to marshal HID RPC message", e);
|
||||
}
|
||||
if (!data) return;
|
||||
|
||||
|
|
@ -223,13 +223,19 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
setRpcHidProtocolVersion(null);
|
||||
};
|
||||
|
||||
const errorHandler = (e: Event) => {
|
||||
console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${e}`)
|
||||
};
|
||||
|
||||
rpcHidChannel.addEventListener("message", messageHandler);
|
||||
rpcHidChannel.addEventListener("close", closeHandler);
|
||||
rpcHidChannel.addEventListener("error", errorHandler);
|
||||
rpcHidChannel.addEventListener("open", openHandler);
|
||||
|
||||
return () => {
|
||||
rpcHidChannel.removeEventListener("message", messageHandler);
|
||||
rpcHidChannel.removeEventListener("close", closeHandler);
|
||||
rpcHidChannel.removeEventListener("error", errorHandler);
|
||||
rpcHidChannel.removeEventListener("open", openHandler);
|
||||
};
|
||||
}, [
|
||||
|
|
|
|||
|
|
@ -485,13 +485,15 @@ export default function KvmIdRoute() {
|
|||
|
||||
const rpcDataChannel = pc.createDataChannel("rpc");
|
||||
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 = () => {
|
||||
setRpcDataChannel(rpcDataChannel);
|
||||
};
|
||||
|
||||
const rpcHidChannel = pc.createDataChannel("hidrpc");
|
||||
rpcHidChannel.binaryType = "arraybuffer";
|
||||
rpcHidChannel.onclose = () => console.log("rpcHidChannel has closed");
|
||||
rpcHidChannel.onerror = (ev: Event) => console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${ev}`);
|
||||
rpcHidChannel.onopen = () => {
|
||||
setRpcHidChannel(rpcHidChannel);
|
||||
};
|
||||
|
|
@ -501,6 +503,8 @@ export default function KvmIdRoute() {
|
|||
maxRetransmits: 0,
|
||||
});
|
||||
rpcHidUnreliableChannel.binaryType = "arraybuffer";
|
||||
rpcHidUnreliableChannel.onclose = () => console.log("rpcHidUnreliableChannel has closed");
|
||||
rpcHidUnreliableChannel.onerror = (ev: Event) => console.error(`Error on rpcHidUnreliableChannel '${rpcHidUnreliableChannel.label}': ${ev}`);
|
||||
rpcHidUnreliableChannel.onopen = () => {
|
||||
setRpcHidUnreliableChannel(rpcHidUnreliableChannel);
|
||||
};
|
||||
|
|
@ -510,6 +514,8 @@ export default function KvmIdRoute() {
|
|||
maxRetransmits: 0,
|
||||
});
|
||||
rpcHidUnreliableNonOrderedChannel.binaryType = "arraybuffer";
|
||||
rpcHidUnreliableNonOrderedChannel.onclose = () => console.log("rpcHidUnreliableNonOrderedChannel has closed");
|
||||
rpcHidUnreliableNonOrderedChannel.onerror = (ev: Event) => console.error(`Error on rpcHidUnreliableNonOrderedChannel '${rpcHidUnreliableNonOrderedChannel.label}': ${ev}`);
|
||||
rpcHidUnreliableNonOrderedChannel.onopen = () => {
|
||||
setRpcHidUnreliableNonOrderedChannel(rpcHidUnreliableNonOrderedChannel);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue