diff --git a/CHANGELOG.md b/CHANGELOG.md index 972b998715..d3988158c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#1058](https://github.com/nix-rust/nix/pull/1058)) - Add `renameat`. ([#1097](https://github.com/nix-rust/nix/pull/1097)) +- Add `clock_gettime`, `clock_settime`, `clock_getres` + ([#1100](https://github.com/nix-rust/nix/pull/1100)) ### Changed - Support for `ifaddrs` now present when building for Android. diff --git a/src/lib.rs b/src/lib.rs index 71485d2af1..051c688b44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,7 @@ pub mod poll; pub mod pty; pub mod sched; pub mod sys; +pub mod time; // This can be implemented for other platforms as soon as libc // provides bindings for them. #[cfg(all(target_os = "linux", diff --git a/src/sys/time.rs b/src/sys/time.rs index 3ad57543b1..12895ce022 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -67,6 +67,12 @@ impl AsRef for TimeSpec { } } +impl From for TimeSpec { + fn from(ts: timespec) -> Self { + TimeSpec(ts) + } +} + impl Ord for TimeSpec { // The implementation of cmp is simplified by assuming that the struct is // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000000..c08cdcc824 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,118 @@ +use sys::time::TimeSpec; +use {Result, Errno}; +use libc; + +libc_enum! { + #[repr(i32)] + pub enum ClockId { + #[cfg(any(target_os = "fuchsia", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] + CLOCK_BOOTTIME, + #[cfg(any(target_os = "fuchsia", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] + CLOCK_BOOTTIME_ALARM, + CLOCK_MONOTONIC, + #[cfg(any(target_os = "fuchsia", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] + CLOCK_MONOTONIC_COARSE, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_MONOTONIC_FAST, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_MONOTONIC_PRECISE, + #[cfg(any(target_os = "fuchsia", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] + CLOCK_MONOTONIC_RAW, + #[cfg(any(target_os = "fuchsia", + target_env = "uclibc", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] + CLOCK_PROCESS_CPUTIME_ID, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_PROF, + CLOCK_REALTIME, + #[cfg(any(target_os = "fuchsia", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] + CLOCK_REALTIME_ALARM, + #[cfg(any(target_os = "fuchsia", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] + CLOCK_REALTIME_COARSE, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_REALTIME_FAST, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_REALTIME_PRECISE, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_SECOND, + #[cfg(any(target_os = "fuchsia", + target_os = "emscripten", + all(target_os = "linux", target_env = "musl")))] + CLOCK_SGI_CYCLE, + #[cfg(any(target_os = "fuchsia", + target_os = "emscripten", + all(target_os = "linux", target_env = "musl")))] + CLOCK_TAI, + #[cfg(any(target_os = "fuchsia", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_env = "uclibc"))] + CLOCK_THREAD_CPUTIME_ID, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_UPTIME, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_UPTIME_FAST, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_UPTIME_PRECISE, + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly"))] + CLOCK_VIRTUAL, + } +} + +pub fn clock_getres(clk_id: ClockId) -> Result { + let mut c_time = libc::timespec {tv_sec: 0, tv_nsec: 0}; + let res = unsafe { libc::clock_getres(clk_id as libc::c_int, &mut c_time) }; + Errno::result(res)?; + Ok(TimeSpec::from(c_time)) +} + +pub fn clock_gettime(clk_id: ClockId) -> Result { + let mut c_time = libc::timespec {tv_sec: 0, tv_nsec: 0}; + let res = unsafe { libc::clock_gettime(clk_id as libc::c_int, &mut c_time) }; + Errno::result(res)?; + Ok(TimeSpec::from(c_time)) +} + +pub fn clock_settime(clk_id: ClockId, timespec: TimeSpec) -> Result<()> { + let res = unsafe { libc::clock_settime(clk_id as libc::c_int, timespec.as_ref()) }; + Errno::result(res).map(drop) +} diff --git a/test/test.rs b/test/test.rs index 6a71d261b5..8a9a1b72da 100644 --- a/test/test.rs +++ b/test/test.rs @@ -86,6 +86,7 @@ mod test_pty; target_os = "macos"))] mod test_sendfile; mod test_stat; +mod test_time; mod test_unistd; use std::os::unix::io::RawFd; diff --git a/test/test_time.rs b/test/test_time.rs new file mode 100644 index 0000000000..4eb2b61cf5 --- /dev/null +++ b/test/test_time.rs @@ -0,0 +1,32 @@ +use nix::time::{clock_getres, clock_gettime, clock_settime, ClockId}; +use nix::Error; +use nix::errno::Errno; +use nix::sys::time::TimeSpec; +use libc::timespec; + +#[test] +pub fn test_clock_getres() { + clock_getres(ClockId::CLOCK_REALTIME).unwrap(); +} + +#[test] +pub fn test_clock_gettime() { + let res1 = clock_gettime(ClockId::CLOCK_REALTIME).unwrap(); + let res2 = clock_gettime(ClockId::CLOCK_REALTIME).unwrap(); + assert!(res1 < res2); +} + +#[test] +pub fn test_clock_settime() { + require_capability!(CAP_SYS_TIME); + let ts = TimeSpec::from(timespec{tv_sec: 10000000, tv_nsec: 100}); + let res = clock_settime(ClockId::CLOCK_REALTIME, ts).unwrap(); +} + +#[test] +pub fn test_clock_settime_err() { + require_capability!(CAP_SYS_TIME); + let ts = TimeSpec::from(timespec{tv_sec: 10000000, tv_nsec: 100}); + let err = clock_settime(ClockId::CLOCK_MONOTONIC, ts).unwrap_err(); + assert_eq!(err, Error::Sys(Errno::EINVAL)); +}