Remind users when a VM is still running after hanger vm ssh exits instead of silently dropping them back to the host shell.\n\nAttach a Firecracker vsock device to each VM, persist the host vsock path/CID,\nadd a new guest-side banger-vsock-pingd responder to the runtime bundle and both\nimage-build paths, and expose a vm.ping RPC that the CLI and TUI call after SSH\nreturns. Doctor and start/build preflight now validate the helper plus\n/dev/vhost-vsock so the feature fails early and clearly.\n\nValidated with go mod tidy, bash -n customize.sh, git diff --check, make build,\nand GOCACHE=/tmp/banger-gocache go test ./... outside the sandbox because the\ndaemon tests need real Unix/UDP sockets. Rebuild the image/rootfs used for new\nVMs so the guest ping service is present.
105 lines
2.4 KiB
Go
105 lines
2.4 KiB
Go
package vsockping
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
sdkvsock "github.com/firecracker-microvm/firecracker-go-sdk/vsock"
|
|
)
|
|
|
|
const (
|
|
Port uint32 = 42070
|
|
RequestLine = "PING\n"
|
|
ResponseLine = "PONG\n"
|
|
GuestBinaryName = "banger-vsock-pingd"
|
|
GuestInstallPath = "/usr/local/bin/" + GuestBinaryName
|
|
ServiceName = "banger-vsock-pingd.service"
|
|
serviceUnit = `[Unit]
|
|
Description=Banger vsock ping responder
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/usr/local/bin/banger-vsock-pingd
|
|
Restart=on-failure
|
|
RestartSec=1
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
`
|
|
modulesLoadConfig = "vsock\nvmw_vsock_virtio_transport\n"
|
|
)
|
|
|
|
func Ping(ctx context.Context, logger *slog.Logger, socketPath string) error {
|
|
conn, err := sdkvsock.DialContext(
|
|
ctx,
|
|
socketPath,
|
|
Port,
|
|
sdkvsock.WithRetryTimeout(3*time.Second),
|
|
sdkvsock.WithRetryInterval(100*time.Millisecond),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
|
|
if deadline, ok := ctx.Deadline(); ok {
|
|
_ = conn.SetDeadline(deadline)
|
|
} else {
|
|
_ = conn.SetDeadline(time.Now().Add(3 * time.Second))
|
|
}
|
|
|
|
if _, err := io.WriteString(conn, RequestLine); err != nil {
|
|
return err
|
|
}
|
|
line, err := bufio.NewReader(conn).ReadString('\n')
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.TrimSpace(line) != strings.TrimSpace(ResponseLine) {
|
|
return fmt.Errorf("unexpected vsock ping response %q", strings.TrimSpace(line))
|
|
}
|
|
if logger != nil {
|
|
logger.Debug("vsock ping ok", "vsock_path", socketPath, "vsock_port", Port)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ServiceUnit() string {
|
|
return serviceUnit
|
|
}
|
|
|
|
func ModulesLoadConfig() string {
|
|
return modulesLoadConfig
|
|
}
|
|
|
|
func ReminderMessage(name string) string {
|
|
return fmt.Sprintf("session ended; %s is still running (stop it with 'banger vm stop %s')", name, name)
|
|
}
|
|
|
|
func WarningMessage(name string, err error) string {
|
|
if err == nil {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("warning: failed to check whether %s is still running: %v", name, err)
|
|
}
|
|
|
|
func ServeConn(conn net.Conn) error {
|
|
defer conn.Close()
|
|
_ = conn.SetDeadline(time.Now().Add(5 * time.Second))
|
|
line, err := bufio.NewReader(conn).ReadString('\n')
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.TrimSpace(line) != strings.TrimSpace(RequestLine) {
|
|
return fmt.Errorf("unexpected request %q", strings.TrimSpace(line))
|
|
}
|
|
_, err = io.WriteString(conn, ResponseLine)
|
|
return err
|
|
}
|