ntp/shm/ntpshm.go (76 lines of code) (raw):
/*
Copyright (c) Facebook, Inc. and its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package shm
import (
"bytes"
"encoding/binary"
"fmt"
"time"
"unsafe"
"golang.org/x/sys/unix"
)
// SHMKEY is a key of the first NTPD SHM segment
// http://doc.ntp.org/current-stable/drivers/driver28.html
const SHMKEY = 0x4e545030
// IPCCREAT create if key is nonexistent
// https://man7.org/linux/man-pages/man0/sys_ipc.h.0p.html
const IPCCREAT = 00001000
// NTPSHMSize is a size of NTPSHM struct
const NTPSHMSize = 96
// NTPSHM Declaration of the SHM segment from ntp (ntpd/refclock_shm.c)
type NTPSHM struct {
Mode int32
Count int32
ClockTimeStampSec int64
ClockTimeStampUSec int32
ReceiveTimeStampSec int64
ReceiveTimeStampUSec int32
Leap int32
Precision int32
Nsamples int32
Valid int32
ClockTimeStampNSec int32
ReceiveTimeStampNSec int32
Dummy [8]int32
}
// Create a segment in SHM and return the ID
func Create() (uintptr, error) {
shmID, _, errno := unix.Syscall(unix.SYS_SHMGET, uintptr(SHMKEY), 0, uintptr(IPCCREAT|0600))
if errno != 0 {
return 0, fmt.Errorf("failed get shm: %s", unix.ErrnoName(errno))
}
return shmID, nil
}
func ptrToBytes(shmptr uintptr) []byte {
// Runtime representation of a slice in Go
var sl = struct {
addr uintptr
len int
cap int
}{shmptr, NTPSHMSize, NTPSHMSize}
return *(*[]byte)(unsafe.Pointer(&sl))
}
func ptrToNTPSHM(shmptr uintptr) (*NTPSHM, error) {
b := ptrToBytes(shmptr)
s := &NTPSHM{}
r := bytes.NewReader(b)
err := binary.Read(r, binary.LittleEndian, s)
return s, err
}
// ReadID reads SHM segment by ID
func ReadID(id uintptr) (*NTPSHM, error) {
shmptr, _, errno := unix.Syscall(unix.SYS_SHMAT, id, 0, 0)
if errno != 0 {
return nil, fmt.Errorf("failed to attach to shm: %s", unix.ErrnoName(errno))
}
return ptrToNTPSHM(shmptr)
}
// Read SHM segment
func Read() (*NTPSHM, error) {
shmID, _, errno := unix.Syscall(unix.SYS_SHMGET, uintptr(SHMKEY), 0, uintptr(0400))
if errno != 0 {
return nil, fmt.Errorf("failed get shm: %s", unix.ErrnoName(errno))
}
return ReadID(shmID)
}
// Time returns time from SHM
func Time() (time.Time, error) {
shm, err := Read()
if err != nil {
return time.Time{}, err
}
return shm.ClockTimeStamp(), nil
}
// ClockTimeStamp returns the clock time
func (n *NTPSHM) ClockTimeStamp() time.Time {
return time.Unix(n.ClockTimeStampSec, int64(n.ClockTimeStampNSec))
}
// ReceiveTimeStamp returns the receive time
func (n *NTPSHM) ReceiveTimeStamp() time.Time {
return time.Unix(n.ReceiveTimeStampSec, int64(n.ReceiveTimeStampNSec))
}