Compare commits

...

6 Commits

Author SHA1 Message Date
Jack M. ee8e88fb56
Merge 7b023a3e8e into 2a99c2db9d 2025-02-13 15:59:14 +01:00
Cameron Fleming 2a99c2db9d
fix(net): stop dhcp client and release all v4 addr on linkdown (#16)
This commit fixes jetkvm/kvm#12 by disabling the udhcpc client when the
link goes down, it then removes all the active IPv4 addresses from the
deivce.

Once the link comes back up, it re-activates the udhcpc client so it can
fetch a new IPv4 address for the device.

This doesn't make any changes to the IPv6 side of things yet.
2025-02-13 15:41:10 +01:00
Cameron Fleming 0b5033f798
feat: restore EDID on reboot (#34)
This commit adds the config entry "EdidString" and saves the EDID string
when it's modified via the RPC.

The EDID is restored when the jetkvm_native control socket connects
(usually at boot)

Signed-off-by: Cameron Fleming <cameron@nevexo.space>
2025-02-13 14:42:07 +01:00
Scai d07bedb323
Invert colors on Icons (#123)
* feat(ui): invert colors on icons

* feat(ui): fix tailwindcss class for invert
2025-02-13 14:33:03 +01:00
Dominik Heidler aa0f38bc0b
Add openSUSE ISOs (#151) 2025-02-13 14:05:07 +01:00
Jack M. 7b023a3e8e
fix: NTP requests sleep for 1 hour between attempts.
Fixes #74 - When the device is unable to connect to the time servers, it
was looping every two seconds.  This lead to unecessary processing, and
if the device was able to connect to DNS but not the time servers, it
was causing undue load on the DNS server as well.
2025-01-25 06:42:39 -07:00
7 changed files with 74 additions and 3 deletions

View File

@ -22,6 +22,7 @@ type Config struct {
LocalAuthToken string `json:"local_auth_token"` LocalAuthToken string `json:"local_auth_token"`
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"` WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
EdidString string `json:"hdmi_edid_string"`
} }
const configPath = "/userdata/kvm_config.json" const configPath = "/userdata/kvm_config.json"

View File

@ -183,6 +183,12 @@ func rpcSetEDID(edid string) error {
if err != nil { if err != nil {
return err return err
} }
// Save EDID to config, allowing it to be restored on reboot.
LoadConfig()
config.EdidString = edid
SaveConfig()
return nil return nil
} }

View File

@ -152,6 +152,9 @@ func handleCtrlClient(conn net.Conn) {
ctrlSocketConn = conn ctrlSocketConn = conn
// Restore HDMI EDID if applicable
go restoreHdmiEdid()
readBuf := make([]byte, 4096) readBuf := make([]byte, 4096)
for { for {
n, err := conn.Read(readBuf) n, err := conn.Read(readBuf)
@ -304,3 +307,16 @@ func ensureBinaryUpdated(destPath string) error {
return nil return nil
} }
// Restore the HDMI EDID value from the config.
// Called after successful connection to jetkvm_native.
func restoreHdmiEdid() {
LoadConfig()
if config.EdidString != "" {
logger.Infof("Restoring HDMI EDID to %v", config.EdidString)
_, err := CallCtrlAction("set_edid", map[string]interface{}{"edid": config.EdidString})
if err != nil {
logger.Errorf("Failed to restore HDMI EDID: %v", err)
}
}
}

View File

@ -6,6 +6,7 @@ import (
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"net" "net"
"os/exec"
"time" "time"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
@ -25,6 +26,23 @@ type LocalIpInfo struct {
MAC string MAC string
} }
// setDhcpClientState sends signals to udhcpc to change it's current mode
// of operation. Setting active to true will force udhcpc to renew the DHCP lease.
// Setting active to false will put udhcpc into idle mode.
func setDhcpClientState(active bool) {
var signal string;
if active {
signal = "-SIGUSR1"
} else {
signal = "-SIGUSR2"
}
cmd := exec.Command("/usr/bin/killall", signal, "udhcpc");
if err := cmd.Run(); err != nil {
fmt.Printf("network: setDhcpClientState: failed to change udhcpc state: %s\n", err)
}
}
func checkNetworkState() { func checkNetworkState() {
iface, err := netlink.LinkByName("eth0") iface, err := netlink.LinkByName("eth0")
if err != nil { if err != nil {
@ -47,9 +65,26 @@ func checkNetworkState() {
fmt.Printf("failed to get addresses for eth0: %v\n", err) fmt.Printf("failed to get addresses for eth0: %v\n", err)
} }
// If the link is going down, put udhcpc into idle mode.
// If the link is coming back up, activate udhcpc and force it to renew the lease.
if newState.Up != networkState.Up {
setDhcpClientState(newState.Up)
}
for _, addr := range addrs { for _, addr := range addrs {
if addr.IP.To4() != nil { if addr.IP.To4() != nil {
if !newState.Up && networkState.Up {
// If the network is going down, remove all IPv4 addresses from the interface.
fmt.Printf("network: state transitioned to down, removing IPv4 address %s\n", addr.IP.String())
err := netlink.AddrDel(iface, &addr)
if err != nil {
fmt.Printf("network: failed to delete %s", addr.IP.String())
}
newState.IPv4 = "..."
} else {
newState.IPv4 = addr.IP.String() newState.IPv4 = addr.IP.String()
}
} else if addr.IP.To16() != nil && newState.IPv6 == "" { } else if addr.IP.To16() != nil && newState.IPv6 == "" {
newState.IPv6 = addr.IP.String() newState.IPv6 = addr.IP.String()
} }

2
ntp.go
View File

@ -20,6 +20,8 @@ func TimeSyncLoop() {
err := SyncSystemTime() err := SyncSystemTime()
if err != nil { if err != nil {
log.Printf("Failed to sync system time: %v", err) log.Printf("Failed to sync system time: %v", err)
// Sync failed for all 4 endpoints, likely network issue, wait for 1 hour before retrying
time.Sleep(1 * time.Hour)
continue continue
} }
log.Printf("Time sync successful, now is: %v, time taken: %v", time.Now(), time.Since(start)) log.Printf("Time sync successful, now is: %v, time taken: %v", time.Now(), time.Since(start))

View File

@ -26,6 +26,7 @@ import { InputFieldWithLabel } from "./InputField";
import DebianIcon from "@/assets/debian-icon.png"; import DebianIcon from "@/assets/debian-icon.png";
import UbuntuIcon from "@/assets/ubuntu-icon.png"; import UbuntuIcon from "@/assets/ubuntu-icon.png";
import FedoraIcon from "@/assets/fedora-icon.png"; import FedoraIcon from "@/assets/fedora-icon.png";
import OpenSUSEIcon from "@/assets/opensuse-icon.png";
import ArchIcon from "@/assets/arch-icon.png"; import ArchIcon from "@/assets/arch-icon.png";
import NetBootIcon from "@/assets/netboot-icon.svg"; import NetBootIcon from "@/assets/netboot-icon.svg";
import { TrashIcon } from "@heroicons/react/16/solid"; import { TrashIcon } from "@heroicons/react/16/solid";
@ -542,6 +543,16 @@ function UrlView({
url: "https://download.fedoraproject.org/pub/fedora/linux/releases/41/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-41-1.4.iso", url: "https://download.fedoraproject.org/pub/fedora/linux/releases/41/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-41-1.4.iso",
icon: FedoraIcon, icon: FedoraIcon,
}, },
{
name: "openSUSE Leap 15.6",
url: "https://download.opensuse.org/distribution/leap/15.6/iso/openSUSE-Leap-15.6-NET-x86_64-Media.iso",
icon: OpenSUSEIcon,
},
{
name: "openSUSE Tumbleweed",
url: "https://download.opensuse.org/tumbleweed/iso/openSUSE-Tumbleweed-NET-x86_64-Current.iso",
icon: OpenSUSEIcon,
},
{ {
name: "Arch Linux", name: "Arch Linux",
url: "https://archlinux.doridian.net/iso/2025.02.01/archlinux-2025.02.01-x86_64.iso", url: "https://archlinux.doridian.net/iso/2025.02.01/archlinux-2025.02.01-x86_64.iso",

View File

@ -466,7 +466,7 @@ export default function SettingsSidebar() {
<GridCard> <GridCard>
<div className="flex items-center px-4 py-3 group gap-x-4"> <div className="flex items-center px-4 py-3 group gap-x-4">
<img <img
className="w-6 shrink-0" className="w-6 shrink-0 dark:invert"
src={PointingFinger} src={PointingFinger}
alt="Finger touching a screen" alt="Finger touching a screen"
/> />
@ -490,7 +490,7 @@ export default function SettingsSidebar() {
> >
<GridCard> <GridCard>
<div className="flex items-center px-4 py-3 gap-x-4"> <div className="flex items-center px-4 py-3 gap-x-4">
<img className="w-6 shrink-0" src={MouseIcon} alt="Mouse icon" /> <img className="w-6 shrink-0 dark:invert" src={MouseIcon} alt="Mouse icon" />
<div className="flex items-center justify-between grow"> <div className="flex items-center justify-between grow">
<div className="text-left"> <div className="text-left">
<h3 className="text-sm font-semibold text-black dark:text-white"> <h3 className="text-sm font-semibold text-black dark:text-white">