137 lines
2.5 KiB
Go
137 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
|
|
}
|