kvm/block_device.go
Aveline 189b84380b
network enhanecment / refactor (#361)
* chore(network): improve connectivity check

* refactor(network): rewrite network and timesync component

* feat(display): show cloud connection status

* chore: change logging verbosity

* chore(websecure): update log message

* fix(ota): validate root certificate when downloading update

* feat(ui): add network settings tab

* fix(display): cloud connecting animation

* fix: golintci issues

* feat: add network settings tab

* feat(timesync): query servers in parallel

* refactor(network): move to internal/network package

* feat(timesync): add metrics

* refactor(log): move log to internal/logging package

* refactor(mdms): move mdns to internal/mdns package

* feat(developer): add pprof endpoint

* feat(logging): add a simple logging streaming endpoint

* fix(mdns): do not start mdns until network is up

* feat(network): allow users to update network settings from ui

* fix(network): handle errors when net.IPAddr is nil

* fix(mdns): scopedLogger SIGSEGV

* fix(dhcp): watch directory instead of file to catch fsnotify.Create event

* refactor(nbd): move platform-specific code to different files

* refactor(native): move platform-specific code to different files

* chore: fix linter issues

* chore(dev_deploy): allow to override PION_LOG_TRACE
2025-04-16 01:39:23 +02:00

151 lines
3.2 KiB
Go

package kvm
import (
"context"
"errors"
"net"
"os"
"time"
"github.com/pojntfx/go-nbd/pkg/server"
"github.com/rs/zerolog"
)
type remoteImageBackend struct {
}
func (r remoteImageBackend) ReadAt(p []byte, off int64) (n int, err error) {
virtualMediaStateMutex.RLock()
logger.Debug().Interface("currentVirtualMediaState", currentVirtualMediaState).Msg("currentVirtualMediaState")
logger.Debug().Int64("read size", int64(len(p))).Int64("off", off).Msg("read size and off")
if currentVirtualMediaState == nil {
return 0, errors.New("image not mounted")
}
source := currentVirtualMediaState.Source
mountedImageSize := currentVirtualMediaState.Size
virtualMediaStateMutex.RUnlock()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
readLen := int64(len(p))
if off+readLen > mountedImageSize {
readLen = mountedImageSize - off
}
var data []byte
switch source {
case WebRTC:
data, err = webRTCDiskReader.Read(ctx, off, readLen)
if err != nil {
return 0, err
}
n = copy(p, data)
return n, nil
case HTTP:
return httpRangeReader.ReadAt(p, off)
default:
return 0, errors.New("unknown image source")
}
}
func (r remoteImageBackend) WriteAt(p []byte, off int64) (n int, err error) {
return 0, errors.New("not supported")
}
func (r remoteImageBackend) Size() (int64, error) {
virtualMediaStateMutex.Lock()
defer virtualMediaStateMutex.Unlock()
if currentVirtualMediaState == nil {
return 0, errors.New("no virtual media state")
}
return currentVirtualMediaState.Size, nil
}
func (r remoteImageBackend) Sync() error {
return nil
}
const nbdSocketPath = "/var/run/nbd.socket"
const nbdDevicePath = "/dev/nbd0"
type NBDDevice struct {
listener net.Listener
serverConn net.Conn
clientConn net.Conn
dev *os.File
l *zerolog.Logger
}
func NewNBDDevice() *NBDDevice {
return &NBDDevice{}
}
func (d *NBDDevice) Start() error {
var err error
if _, err := os.Stat(nbdDevicePath); os.IsNotExist(err) {
return errors.New("NBD device does not exist")
}
d.dev, err = os.Open(nbdDevicePath)
if err != nil {
return err
}
if d.l == nil {
scopedLogger := nbdLogger.With().
Str("socket_path", nbdSocketPath).
Str("device_path", nbdDevicePath).
Logger()
d.l = &scopedLogger
}
// Remove the socket file if it already exists
if _, err := os.Stat(nbdSocketPath); err == nil {
if err := os.Remove(nbdSocketPath); err != nil {
d.l.Error().Err(err).Msg("failed to remove existing socket file")
os.Exit(1)
}
}
d.listener, err = net.Listen("unix", nbdSocketPath)
if err != nil {
return err
}
d.clientConn, err = net.Dial("unix", nbdSocketPath)
if err != nil {
return err
}
d.serverConn, err = d.listener.Accept()
if err != nil {
return err
}
go d.runServerConn()
go d.runClientConn()
return nil
}
func (d *NBDDevice) runServerConn() {
err := server.Handle(
d.serverConn,
[]*server.Export{
{
Name: "jetkvm",
Description: "",
Backend: &remoteImageBackend{},
},
},
&server.Options{
ReadOnly: true,
MinimumBlockSize: uint32(1024),
PreferredBlockSize: uint32(4 * 1024),
MaximumBlockSize: uint32(16 * 1024),
SupportsMultiConn: false,
})
d.l.Info().Err(err).Msg("nbd server exited")
}