mirror of https://github.com/jetkvm/kvm.git
Compare commits
4 Commits
4e3d7fc9ab
...
940b2739a0
Author | SHA1 | Date |
---|---|---|
|
940b2739a0 | |
|
3e7d8fb0f5 | |
|
a0609a5f86 | |
|
3276427e9c |
|
@ -143,15 +143,21 @@ func (u *UsbGadget) listenKeyboardEvents() {
|
||||||
default:
|
default:
|
||||||
l.Trace().Msg("reading from keyboard")
|
l.Trace().Msg("reading from keyboard")
|
||||||
if u.keyboardHidFile == nil {
|
if u.keyboardHidFile == nil {
|
||||||
l.Error().Msg("keyboardHidFile is nil")
|
u.logWithSupression("keyboardHidFileNil", 100, &l, nil, "keyboardHidFile is nil")
|
||||||
|
// show the error every 100 times to avoid spamming the logs
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// reset the counter
|
||||||
|
u.resetLogSuppressionCounter("keyboardHidFileNil")
|
||||||
|
|
||||||
n, err := u.keyboardHidFile.Read(buf)
|
n, err := u.keyboardHidFile.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Error().Err(err).Msg("failed to read")
|
u.logWithSupression("keyboardHidFileRead", 100, &l, err, "failed to read")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
u.resetLogSuppressionCounter("keyboardHidFileRead")
|
||||||
|
|
||||||
l.Trace().Int("n", n).Bytes("buf", buf).Msg("got data from keyboard")
|
l.Trace().Int("n", n).Bytes("buf", buf).Msg("got data from keyboard")
|
||||||
if n != 1 {
|
if n != 1 {
|
||||||
l.Trace().Int("n", n).Msg("expected 1 byte, got")
|
l.Trace().Int("n", n).Msg("expected 1 byte, got")
|
||||||
|
@ -195,12 +201,12 @@ func (u *UsbGadget) keyboardWriteHidFile(data []byte) error {
|
||||||
|
|
||||||
_, err := u.keyboardHidFile.Write(data)
|
_, err := u.keyboardHidFile.Write(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.log.Error().Err(err).Msg("failed to write to hidg0")
|
u.logWithSupression("keyboardWriteHidFile", 100, u.log, err, "failed to write to hidg0")
|
||||||
u.keyboardHidFile.Close()
|
u.keyboardHidFile.Close()
|
||||||
u.keyboardHidFile = nil
|
u.keyboardHidFile = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
u.resetLogSuppressionCounter("keyboardWriteHidFile")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,11 +75,12 @@ func (u *UsbGadget) absMouseWriteHidFile(data []byte) error {
|
||||||
|
|
||||||
_, err := u.absMouseHidFile.Write(data)
|
_, err := u.absMouseHidFile.Write(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.log.Error().Err(err).Msg("failed to write to hidg1")
|
u.logWithSupression("absMouseWriteHidFile", 100, u.log, err, "failed to write to hidg1")
|
||||||
u.absMouseHidFile.Close()
|
u.absMouseHidFile.Close()
|
||||||
u.absMouseHidFile = nil
|
u.absMouseHidFile = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
u.resetLogSuppressionCounter("absMouseWriteHidFile")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,11 +65,12 @@ func (u *UsbGadget) relMouseWriteHidFile(data []byte) error {
|
||||||
|
|
||||||
_, err := u.relMouseHidFile.Write(data)
|
_, err := u.relMouseHidFile.Write(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.log.Error().Err(err).Msg("failed to write to hidg2")
|
u.logWithSupression("relMouseWriteHidFile", 100, u.log, err, "failed to write to hidg2")
|
||||||
u.relMouseHidFile.Close()
|
u.relMouseHidFile.Close()
|
||||||
u.relMouseHidFile = nil
|
u.relMouseHidFile = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
u.resetLogSuppressionCounter("relMouseWriteHidFile")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,8 @@ type UsbGadget struct {
|
||||||
onKeyboardStateChange *func(state KeyboardState)
|
onKeyboardStateChange *func(state KeyboardState)
|
||||||
|
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
|
|
||||||
|
logSuppressionCounter map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
const configFSPath = "/sys/kernel/config"
|
const configFSPath = "/sys/kernel/config"
|
||||||
|
@ -126,6 +128,8 @@ func newUsbGadget(name string, configMap map[string]gadgetConfigItem, enabledDev
|
||||||
|
|
||||||
strictMode: config.strictMode,
|
strictMode: config.strictMode,
|
||||||
|
|
||||||
|
logSuppressionCounter: make(map[string]int),
|
||||||
|
|
||||||
absMouseAccumulatedWheelY: 0,
|
absMouseAccumulatedWheelY: 0,
|
||||||
}
|
}
|
||||||
if err := g.Init(); err != nil {
|
if err := g.Init(); err != nil {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func joinPath(basePath string, paths []string) string {
|
func joinPath(basePath string, paths []string) string {
|
||||||
|
@ -78,3 +80,27 @@ func compareFileContent(oldContent []byte, newContent []byte, looserMatch bool)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UsbGadget) logWithSupression(counterName string, every int, logger *zerolog.Logger, err error, msg string, args ...interface{}) {
|
||||||
|
if _, ok := u.logSuppressionCounter[counterName]; !ok {
|
||||||
|
u.logSuppressionCounter[counterName] = 0
|
||||||
|
} else {
|
||||||
|
u.logSuppressionCounter[counterName]++
|
||||||
|
}
|
||||||
|
|
||||||
|
l := logger.With().Int("counter", u.logSuppressionCounter[counterName]).Logger()
|
||||||
|
|
||||||
|
if u.logSuppressionCounter[counterName]%every == 0 {
|
||||||
|
if err != nil {
|
||||||
|
l.Error().Err(err).Msgf(msg, args...)
|
||||||
|
} else {
|
||||||
|
l.Error().Msgf(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UsbGadget) resetLogSuppressionCounter(counterName string) {
|
||||||
|
if _, ok := u.logSuppressionCounter[counterName]; !ok {
|
||||||
|
u.logSuppressionCounter[counterName] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import SettingsHardwareRoute from "./routes/devices.$id.settings.hardware";
|
||||||
import SettingsVideoRoute from "./routes/devices.$id.settings.video";
|
import SettingsVideoRoute from "./routes/devices.$id.settings.video";
|
||||||
import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance";
|
import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance";
|
||||||
import * as SettingsGeneralIndexRoute from "./routes/devices.$id.settings.general._index";
|
import * as SettingsGeneralIndexRoute from "./routes/devices.$id.settings.general._index";
|
||||||
|
import SettingsGeneralRebootRoute from "./routes/devices.$id.settings.general.reboot";
|
||||||
import SettingsGeneralUpdateRoute from "./routes/devices.$id.settings.general.update";
|
import SettingsGeneralUpdateRoute from "./routes/devices.$id.settings.general.update";
|
||||||
import SettingsNetworkRoute from "./routes/devices.$id.settings.network";
|
import SettingsNetworkRoute from "./routes/devices.$id.settings.network";
|
||||||
import SecurityAccessLocalAuthRoute from "./routes/devices.$id.settings.access.local-auth";
|
import SecurityAccessLocalAuthRoute from "./routes/devices.$id.settings.access.local-auth";
|
||||||
|
@ -140,6 +141,10 @@ if (isOnDevice) {
|
||||||
index: true,
|
index: true,
|
||||||
element: <SettingsGeneralIndexRoute.default />,
|
element: <SettingsGeneralIndexRoute.default />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "reboot",
|
||||||
|
element: <SettingsGeneralRebootRoute />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "update",
|
path: "update",
|
||||||
element: <SettingsGeneralUpdateRoute />,
|
element: <SettingsGeneralUpdateRoute />,
|
||||||
|
|
|
@ -92,6 +92,21 @@ export default function SettingsGeneralRoute() {
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 flex items-center justify-between gap-x-2">
|
||||||
|
<SettingsItem
|
||||||
|
title="Reboot Device"
|
||||||
|
description="Power cycle the JetKVM"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="light"
|
||||||
|
text="Reboot Device"
|
||||||
|
onClick={() => navigateTo("./reboot")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
|
import { Button } from "@components/Button";
|
||||||
|
|
||||||
|
export default function SettingsGeneralRebootRoute() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [send] = useJsonRpc();
|
||||||
|
|
||||||
|
const onConfirmUpdate = useCallback(() => {
|
||||||
|
// This is where we send the RPC to the golang binary
|
||||||
|
send("reboot", {force: true});
|
||||||
|
}, [send]);
|
||||||
|
|
||||||
|
{
|
||||||
|
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
|
||||||
|
}
|
||||||
|
return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Dialog({
|
||||||
|
onClose,
|
||||||
|
onConfirmUpdate,
|
||||||
|
}: {
|
||||||
|
onClose: () => void;
|
||||||
|
onConfirmUpdate: () => void;
|
||||||
|
}) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pointer-events-auto relative mx-auto text-left">
|
||||||
|
<div>
|
||||||
|
<ConfirmationBox
|
||||||
|
onYes={onConfirmUpdate}
|
||||||
|
onNo={onClose}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConfirmationBox({
|
||||||
|
onYes,
|
||||||
|
onNo,
|
||||||
|
}: {
|
||||||
|
onYes: () => void;
|
||||||
|
onNo: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-start justify-start space-y-4 text-left">
|
||||||
|
<div className="text-left">
|
||||||
|
<p className="text-base font-semibold text-black dark:text-white">
|
||||||
|
Reboot JetKVM
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-slate-600 dark:text-slate-300">
|
||||||
|
Do you want to proceed with rebooting the system?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-4 flex gap-x-2">
|
||||||
|
<Button size="SM" theme="light" text="Yes" onClick={onYes} />
|
||||||
|
<Button size="SM" theme="blank" text="No" onClick={onNo} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue