mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-31 09:37:21 +00:00 
			
		
		
		
	Some hypervisors (e.g. hyperkit / xhyve) don't provide a good way to keep the VM's clock in sync with the Host's clock. NTP will usually keep the clocks together, but after a the host or VM is suspended and resumed the clocks can be suddenly too far apart for NTP to work properly. This simple daemon listens on an AF_VSOCK port and resynchronises the VM clock from the virtualised hardware clock. This is a Go conversion of original C code written by Magnus Skjegstad <magnus@skjegstad.com> Signed-off-by: David Scott <dave.scott@docker.com>
		
			
				
	
	
		
			121 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"syscall"
 | |
| 	"time"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	"github.com/linuxkit/virtsock/pkg/vsock"
 | |
| )
 | |
| 
 | |
| // Listen for connections on an AF_VSOCK address and update the system time
 | |
| // from the hardware clock when a connection is received.
 | |
| 
 | |
| // From <linux/rtc.h> struct rtc_time
 | |
| type rtcTime struct {
 | |
| 	tmSec   uint32
 | |
| 	tmMin   uint32
 | |
| 	tmHour  uint32
 | |
| 	tmMday  uint32
 | |
| 	tmMon   uint32
 | |
| 	tmYear  uint32
 | |
| 	tmWday  uint32
 | |
| 	tmYday  uint32
 | |
| 	tmIsdst uint32
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	// iocREAD and friends are from <linux/asm-generic/ioctl.h>
 | |
| 	iocREAD      = uintptr(2)
 | |
| 	iocNRBITS    = uintptr(8)
 | |
| 	iocNRSHIFT   = uintptr(0)
 | |
| 	iocTYPEBITS  = uintptr(8)
 | |
| 	iocTYPESHIFT = iocNRSHIFT + iocNRBITS
 | |
| 	iocSIZEBITS  = uintptr(14)
 | |
| 	iocSIZESHIFT = iocTYPESHIFT + iocTYPEBITS
 | |
| 	iocDIRSHIFT  = iocSIZESHIFT + iocSIZEBITS
 | |
| 	// rtcRDTIMENR and friends are from <linux/rtc.h>
 | |
| 	rtcRDTIMENR   = uintptr(0x09)
 | |
| 	rtcRDTIMETYPE = uintptr(112)
 | |
| )
 | |
| 
 | |
| func rtcReadTime() rtcTime {
 | |
| 	f, err := os.Open("/dev/rtc0")
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("Failed to open /dev/rtc0: %v", err)
 | |
| 	}
 | |
| 	defer f.Close()
 | |
| 	result := rtcTime{}
 | |
| 	arg := uintptr(0)
 | |
| 	arg |= (iocREAD << iocDIRSHIFT)
 | |
| 	arg |= (rtcRDTIMETYPE << iocTYPESHIFT)
 | |
| 	arg |= (rtcRDTIMENR << iocNRSHIFT)
 | |
| 	arg |= (unsafe.Sizeof(result) << iocSIZESHIFT)
 | |
| 	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), arg, uintptr(unsafe.Pointer(&result)))
 | |
| 	if errno != 0 {
 | |
| 		log.Fatalf("RTC_RD_TIME failed: %v", errno)
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 	// host-timesync-daemon -cid <cid> -port <port>
 | |
| 
 | |
| 	cid := flag.Int("cid", 0, "AF_VSOCK CID to listen on")
 | |
| 	port := flag.Int("port", 0, "AF_VSOCK port to listen on")
 | |
| 	flag.Usage = func() {
 | |
| 		fmt.Fprintf(os.Stderr, "%s: set the time after an AH_VSOCK connection is received.\n\n", os.Args[0])
 | |
| 		fmt.Fprintf(os.Stderr, "Example usage:\n")
 | |
| 		fmt.Fprintf(os.Stderr, "%s -port 0xf3a4\n", os.Args[0])
 | |
| 		fmt.Fprintf(os.Stderr, "   -- when a connection is received on port 0xf3a4, query the hardware clock and\n")
 | |
| 		fmt.Fprintf(os.Stderr, "      set the system time. The connection will be closed after the clock has\n")
 | |
| 		fmt.Fprintf(os.Stderr, "      been changed.\n\n")
 | |
| 		fmt.Fprintf(os.Stderr, "Arguments:\n")
 | |
| 		flag.PrintDefaults()
 | |
| 	}
 | |
| 
 | |
| 	flag.Parse()
 | |
| 	if *port == 0 {
 | |
| 		log.Fatalf("Please supply a -port argument")
 | |
| 	}
 | |
| 	if *cid == 0 {
 | |
| 		// by default allow connections from anywhere on the local machine
 | |
| 		*cid = vsock.CIDAny
 | |
| 	}
 | |
| 
 | |
| 	l, err := vsock.Listen(uint32(*cid), uint32(*port))
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("Failed to bind to vsock port %x:%x: %s", *cid, *port, err)
 | |
| 	}
 | |
| 	log.Printf("Listening on port %x:%x", *cid, *port)
 | |
| 
 | |
| 	for {
 | |
| 		conn, err := l.Accept()
 | |
| 
 | |
| 		if err != nil {
 | |
| 			log.Fatalf("Error accepting connection: %s", err)
 | |
| 		}
 | |
| 		log.Printf("Connection to: %x:%x from: %s\n", *cid, *port, conn.RemoteAddr())
 | |
| 
 | |
| 		t := rtcReadTime()
 | |
| 		// Assume the RTC is set to UTC. This may not be true on a Windows host but in
 | |
| 		// that case we assume the platform is capable of providing a PV clock and
 | |
| 		// we don't use this code anyway.
 | |
| 		d := time.Date(int(t.tmYear+1900), time.Month(t.tmMon+1), int(t.tmMday), int(t.tmHour), int(t.tmMin), int(t.tmSec), 0, time.UTC)
 | |
| 		log.Printf("Setting system clock to %s", d)
 | |
| 		tv := syscall.Timeval{
 | |
| 			Sec:  int64(d.Unix()),
 | |
| 			Usec: 0, // the RTC only has second granularity
 | |
| 		}
 | |
| 		if err = syscall.Settimeofday(&tv); err != nil {
 | |
| 			log.Printf("Unexpected failure from Settimeofday: %v", err)
 | |
| 		}
 | |
| 		// Close after the command terminates. The caller can use this as a notification.
 | |
| 		conn.Close()
 | |
| 	}
 | |
| }
 |