#![allow(dead_code)]
use std::mem::size_of;
use std::str::FromStr;
use std::sync::atomic;
use clock_bound_shm::{syserror, ShmError};
use tracing::{debug, error};
pub const VMCLOCK_SHM_DEFAULT_PATH: &str = "/dev/vmclock0";
pub const VMCLOCK_SHM_MAGIC: u32 = 0x4B4C4356;
#[repr(C)]
#[derive(Debug)]
pub struct VMClockShmHeader {
pub magic: atomic::AtomicU32,
pub size: atomic::AtomicU32,
pub version: atomic::AtomicU16,
pub counter_id: atomic::AtomicU8,
pub time_type: atomic::AtomicU8,
pub seq_count: atomic::AtomicU32,
}
impl VMClockShmHeader {
pub fn read(vector: &Vec<u8>) -> Result<Self, ShmError> {
if vector.len() < size_of::<VMClockShmHeader>() {
return syserror!("Insufficient bytes to create a VMClockShmHeader.");
}
let slice = vector.as_slice();
let magic = u32::from_le_bytes(slice[0..4].try_into().unwrap());
let size = u32::from_le_bytes(slice[4..8].try_into().unwrap());
let version = u16::from_le_bytes(slice[8..10].try_into().unwrap());
let counter_id = u8::from_le_bytes(slice[10..11].try_into().unwrap());
let time_type = u8::from_le_bytes(slice[11..12].try_into().unwrap());
let seq_count = u32::from_le_bytes(slice[12..16].try_into().unwrap());
let header = VMClockShmHeader {
magic: atomic::AtomicU32::new(magic),
size: atomic::AtomicU32::new(size),
version: atomic::AtomicU16::new(version),
counter_id: atomic::AtomicU8::new(counter_id),
time_type: atomic::AtomicU8::new(time_type),
seq_count: atomic::AtomicU32::new(seq_count),
};
header.is_valid()?;
Ok(header)
}
fn matches_magic(&self) -> bool {
let magic = self.magic.load(atomic::Ordering::Relaxed);
debug!("VMClockShmHeader has magic: {:?}", magic);
magic == VMCLOCK_SHM_MAGIC
}
fn has_valid_version(&self) -> bool {
let version = self.version.load(atomic::Ordering::Relaxed);
debug!("VMClockShmHeader has version: {:?}", version);
version > 0
}
fn is_well_formed(&self) -> bool {
let size = self.size.load(atomic::Ordering::Relaxed);
debug!("VMClockShmHeader has size: {:?}", size);
size as usize >= size_of::<Self>()
}
fn is_valid(&self) -> Result<(), ShmError> {
if !self.matches_magic() {
error!("VMClockShmHeader does not have a matching magic number.");
return Err(ShmError::SegmentMalformed);
}
if !self.has_valid_version() {
error!("VMClockShmHeader does not have a valid version number.");
return Err(ShmError::SegmentNotInitialized);
}
if !self.is_well_formed() {
error!("VMClockShmHeader is not well formed.");
return Err(ShmError::SegmentMalformed);
}
Ok(())
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum VMClockClockStatus {
Unknown = 0,
Initializing = 1,
Synchronized = 2,
FreeRunning = 3,
Unreliable = 4,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct ParseError;
impl FromStr for VMClockClockStatus {
type Err = ParseError;
fn from_str(input: &str) -> Result<VMClockClockStatus, Self::Err> {
match input {
"Unknown" => Ok(VMClockClockStatus::Unknown),
"Initializing" => Ok(VMClockClockStatus::Initializing),
"Synchronized" => Ok(VMClockClockStatus::Synchronized),
"FreeRunning" => Ok(VMClockClockStatus::FreeRunning),
"Unreliable" => Ok(VMClockClockStatus::Unreliable),
_ => Err(ParseError),
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct VMClockShmBody {
pub disruption_marker: u64,
pub flags: u64,
pub _padding: [u8; 2],
pub clock_status: VMClockClockStatus,
pub leap_second_smearing_hint: u8,
pub tai_offset_sec: i16,
pub leap_indicator: u8,
pub counter_period_shift: u8,
pub counter_value: u64,
pub counter_period_frac_sec: u64,
pub counter_period_esterror_rate_frac_sec: u64,
pub counter_period_maxerror_rate_frac_sec: u64,
pub time_sec: u64,
pub time_frac_sec: u64,
pub time_esterror_nanosec: u64,
pub time_maxerror_nanosec: u64,
}
impl Default for VMClockShmBody {
fn default() -> Self {
VMClockShmBody {
disruption_marker: 0,
flags: 0,
_padding: [0x00, 0x00],
clock_status: VMClockClockStatus::Unknown,
leap_second_smearing_hint: 0,
tai_offset_sec: 0,
leap_indicator: 0,
counter_period_shift: 0,
counter_value: 0,
counter_period_frac_sec: 0,
counter_period_esterror_rate_frac_sec: 0,
counter_period_maxerror_rate_frac_sec: 0,
time_sec: 0,
time_frac_sec: 0,
time_esterror_nanosec: 0,
time_maxerror_nanosec: 0,
}
}
}
#[cfg(test)]
mod t_vmclock_shm_header {
use super::*;
use byteorder::{LittleEndian, WriteBytesExt};
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::path::Path;
use tempfile::NamedTempFile;
macro_rules! write_vmclock_shm_header {
($file:ident,
$magic:literal,
$size:literal,
$version:literal,
$counter_id:literal,
$time_type:literal,
$seq_count:literal) => {
$file
.write_u32::<LittleEndian>($magic)
.expect("Write failed magic");
$file
.write_u32::<LittleEndian>($size)
.expect("Write failed size");
$file
.write_u16::<LittleEndian>($version)
.expect("Write failed version");
$file
.write_u8($counter_id)
.expect("Write failed counter_id");
$file.write_u8($time_type).expect("Write failed time_type");
$file
.write_u32::<LittleEndian>($seq_count)
.expect("Write failed seq_count");
$file.sync_all().expect("Sync to disk failed");
};
}
fn remove_path_if_exists(path_shm: &str) {
let path = Path::new(path_shm);
if path.exists() {
if path.is_dir() {
std::fs::remove_dir_all(path_shm).expect("failed to remove file");
} else {
std::fs::remove_file(path_shm).expect("failed to remove file");
}
}
}
#[test]
fn test_header_valid() {
let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
let mut vmclock_shm_file = OpenOptions::new()
.write(true)
.open(vmclock_shm_path)
.expect("open vmclock file failed");
write_vmclock_shm_header!(
vmclock_shm_file,
0x4B4C4356,
104_u32,
1_u16,
0_u8,
0_u8,
99_u32
);
let mut file = File::open(vmclock_shm_path).expect("failed to open file");
let mut buffer = vec![];
file.read_to_end(&mut buffer)
.expect("failed to read to end of the file");
let header = VMClockShmHeader::read(&buffer).expect("SHM Reader read");
assert_eq!(header.magic.into_inner(), 0x4B4C4356);
assert_eq!(header.size.into_inner(), 104_u32);
assert_eq!(header.version.into_inner(), 1_u16);
assert_eq!(header.counter_id.into_inner(), 0_u8);
assert_eq!(header.time_type.into_inner(), 0_u8);
assert_eq!(header.seq_count.into_inner(), 99_u32);
}
#[test]
fn test_header_bad_magic() {
let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
let mut vmclock_shm_file = OpenOptions::new()
.write(true)
.open(vmclock_shm_path)
.expect("open vmclock file failed");
write_vmclock_shm_header!(
vmclock_shm_file,
0xdeadbeef,
16_u32,
1_u16,
0_u8,
0_u8,
99_u32
);
let mut file = File::open(vmclock_shm_path).expect("failed to open file");
let mut buffer = vec![];
file.read_to_end(&mut buffer)
.expect("failed to read to end of the file");
assert!(VMClockShmHeader::read(&buffer).is_err());
}
#[test]
fn test_header_bad_size() {
let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
let mut vmclock_shm_file = OpenOptions::new()
.write(true)
.open(vmclock_shm_path)
.expect("open vmclock file failed");
write_vmclock_shm_header!(
vmclock_shm_file,
0x4B4C4356,
4_u32,
1_u16,
0_u8,
0_u8,
99_u32
);
let mut file = File::open(vmclock_shm_path).expect("failed to open file");
let mut buffer = vec![];
file.read_to_end(&mut buffer)
.expect("failed to read to end of the file");
assert!(VMClockShmHeader::read(&buffer).is_err());
}
#[test]
fn test_header_bad_version() {
let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
let mut vmclock_shm_file = OpenOptions::new()
.write(true)
.open(vmclock_shm_path)
.expect("open vmclock file failed");
write_vmclock_shm_header!(
vmclock_shm_file,
0x4B4C4356,
16_u32,
0_u16,
0_u8,
0_u8,
99_u32
);
let mut file = File::open(vmclock_shm_path).expect("failed to open file");
let mut buffer = vec![];
file.read_to_end(&mut buffer)
.expect("failed to read to end of the file");
assert!(VMClockShmHeader::read(&buffer).is_err());
}
}