govmtools/socket.go
2021-11-10 09:06:53 +01:00

138 lines
2.5 KiB
Go

package govmtools
import (
"fmt"
"github.com/ptrcnull/vsock"
"golang.org/x/sys/unix"
)
type PacketType uint32
const (
PacketTypeData PacketType = iota + 1
PacketTypePing
)
type FieldID uint32
const (
FieldIDType FieldID = iota + 1
FieldIDPayload
FieldIDFastClose
)
type FieldType uint32
const (
FieldEmpty FieldType = iota
FieldInt64
FieldString
FieldInt64List
FieldStringList
FieldMax
)
type GuestInfoType uint32
const (
GuestInfoError GuestInfoType = iota
GuestInfoDnsName
GuestInfoIpAddress
GuestInfoDiskFreeSpace
GuestInfoBuildNumber
GuestInfoOsNameFull
GuestInfoOsName
GuestInfoUptime
GuestInfoMemory
GuestInfoIpAddressV2
GuestInfoIpAddressV3
GuestInfoOsDetailed
GuestInfoMax
)
const GuestConnectPort = 976
const ContextID = unix.VMADDR_CID_HYPERVISOR
type Socket struct {
conn *vsock.Conn
}
func NewSocket() (*Socket, error) {
conn, err := vsock.Dial(ContextID, GuestConnectPort)
if err != nil {
return nil, fmt.Errorf("connect: %w", err)
}
return &Socket{
conn: conn,
}, nil
}
func (s *Socket) SendCommand(cmd string) error {
err := s.WritePacket([]Field{
{
Type: FieldInt64,
ID: FieldIDType,
Value: PacketTypeData,
},
{
Type: FieldEmpty,
},
{
Type: FieldString,
ID: FieldIDPayload,
Value: cmd,
},
})
if err != nil {
return fmt.Errorf("write packet: %w", err)
}
res, err := s.ReadPacket()
if err != nil {
return fmt.Errorf("read packet: %w", err)
}
if len(res) != 1 {
return fmt.Errorf("unexpected response length: %d", len(res))
}
field := res[0]
if field.Type != FieldString {
return fmt.Errorf("unexpected response type: %d", field.Type)
}
if field.ID != FieldIDPayload {
return fmt.Errorf("unexpected response id: %d", field.ID)
}
response := field.Value.(string)
if response != "1 " {
return fmt.Errorf("unexpected response: %s", response)
}
return nil
}
func (s *Socket) SetGuestInfo(messageType GuestInfoType, message string) {
s.MustSendCommand(fmt.Sprintf("SetGuestInfo %d %s", messageType, message))
}
func (s *Socket) MustSendCommand(cmd string) {
err := s.SendCommand(cmd)
if err != nil {
panic(err)
}
}
func (s *Socket) ReportVersionData() error {
data := []string{
"description " + Name + " " + Version + " build " + BuildNumber,
"versionString " + Version,
"versionNumber " + VersionNumber,
"buildNumber " + BuildNumber,
}
for _, value := range data {
err := s.SendCommand("info-set guestinfo.vmtools." + value + "\x00")
if err != nil {
return fmt.Errorf("send: %w", err)
}
}
return nil
}