summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs1
-rw-r--r--src/sys/ptrace/linux.rs47
-rw-r--r--src/sys/time.rs26
-rw-r--r--src/time.rs258
-rw-r--r--src/unistd.rs6
5 files changed, 334 insertions, 4 deletions
diff --git a/src/lib.rs b/src/lib.rs
index bb2db873..da517b59 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -61,6 +61,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/ptrace/linux.rs b/src/sys/ptrace/linux.rs
index a0bae14f..8d1dd16e 100644
--- a/src/sys/ptrace/linux.rs
+++ b/src/sys/ptrace/linux.rs
@@ -109,6 +109,12 @@ libc_enum!{
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_PEEKSIGINFO,
+ #[cfg(all(target_os = "linux", target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")))]
+ PTRACE_SYSEMU,
+ #[cfg(all(target_os = "linux", target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")))]
+ PTRACE_SYSEMU_SINGLESTEP,
}
}
@@ -278,7 +284,7 @@ pub fn traceme() -> Result<()> {
}
}
-/// Ask for next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
+/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
///
/// Arranges for the tracee to be stopped at the next entry to or exit from a system call,
/// optionally delivering a signal specified by `sig`.
@@ -297,6 +303,23 @@ pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
}
}
+/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)`
+///
+/// In contrast to the `syscall` function, the syscall stopped at will not be executed.
+/// Thus the the tracee will only be stopped once per syscall,
+/// optionally delivering a signal specified by `sig`.
+#[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))]
+pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data).map(drop)
+ // ignore the useless return value
+ }
+}
+
/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
///
/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
@@ -402,6 +425,28 @@ pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
}
}
+/// Move the stopped tracee process forward by a single step or stop at the next syscall
+/// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)`
+///
+/// Advances the execution by a single step or until the next syscall.
+/// In case the tracee is stopped at a syscall, the syscall will not be executed.
+/// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation.
+#[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))]
+pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_SYSEMU_SINGLESTEP,
+ pid,
+ ptr::null_mut(),
+ data,
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
/// Reads a word from a processes memory at the given address
pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
diff --git a/src/sys/time.rs b/src/sys/time.rs
index 973a3526..269b4253 100644
--- a/src/sys/time.rs
+++ b/src/sys/time.rs
@@ -1,4 +1,5 @@
use std::{cmp, fmt, ops};
+use std::time::Duration;
use std::convert::From;
use libc::{c_long, timespec, timeval};
pub use libc::{time_t, suseconds_t};
@@ -66,6 +67,21 @@ impl From<timespec> for TimeSpec {
}
}
+impl From<Duration> for TimeSpec {
+ fn from(duration: Duration) -> Self {
+ TimeSpec(timespec {
+ tv_sec: duration.as_secs() as time_t,
+ tv_nsec: duration.subsec_nanos() as c_long
+ })
+ }
+}
+
+impl From<TimeSpec> for Duration {
+ fn from(timespec: TimeSpec) -> Self {
+ Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
+ }
+}
+
impl AsRef<timespec> for TimeSpec {
fn as_ref(&self) -> &timespec {
&self.0
@@ -484,6 +500,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
#[cfg(test)]
mod test {
use super::{TimeSpec, TimeVal, TimeValLike};
+ use std::time::Duration;
#[test]
pub fn test_timespec() {
@@ -495,6 +512,15 @@ mod test {
}
#[test]
+ pub fn test_timespec_from() {
+ let duration = Duration::new(123, 123_456_789);
+ let timespec = TimeSpec::nanoseconds(123_123_456_789);
+
+ assert_eq!(TimeSpec::from(duration), timespec);
+ assert_eq!(Duration::from(timespec), duration);
+ }
+
+ #[test]
pub fn test_timespec_neg() {
let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
diff --git a/src/time.rs b/src/time.rs
new file mode 100644
index 00000000..54989c2e
--- /dev/null
+++ b/src/time.rs
@@ -0,0 +1,258 @@
+use crate::sys::time::TimeSpec;
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+use crate::{unistd::Pid, Error};
+use crate::{Errno, Result};
+use libc::{self, clockid_t};
+use std::mem::MaybeUninit;
+
+/// Clock identifier
+///
+/// Newtype pattern around `clockid_t` (which is just alias). It pervents bugs caused by
+/// accidentally passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct ClockId(clockid_t);
+
+impl ClockId {
+ /// Creates `ClockId` from raw `clockid_t`
+ pub fn from_raw(clk_id: clockid_t) -> Self {
+ ClockId(clk_id)
+ }
+
+ /// Returns `ClockId` of a `pid` CPU-time clock
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ ))]
+ pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
+ clock_getcpuclockid(pid)
+ }
+
+ /// Returns resolution of the clock id
+ pub fn res(self) -> Result<TimeSpec> {
+ clock_getres(self)
+ }
+
+ /// Returns the current time on the clock id
+ pub fn now(self) -> Result<TimeSpec> {
+ clock_gettime(self)
+ }
+
+ /// Sets time to `timespec` on the clock id
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlibc")),
+ any(target_os = "redox", target_os = "hermit",),
+ ),
+ )))]
+ pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
+ clock_settime(self, timespec)
+ }
+
+ /// Gets the raw `clockid_t` wrapped by `self`
+ pub fn as_raw(self) -> clockid_t {
+ self.0
+ }
+
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten"),
+ )
+ ))]
+ pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten")
+ )
+ ))]
+ pub const CLOCK_BOOTTIME_ALARM: ClockId = ClockId(libc::CLOCK_BOOTTIME_ALARM);
+ pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten")
+ )
+ ))]
+ pub const CLOCK_MONOTONIC_COARSE: ClockId = ClockId(libc::CLOCK_MONOTONIC_COARSE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_MONOTONIC_FAST: ClockId = ClockId(libc::CLOCK_MONOTONIC_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_MONOTONIC_PRECISE: ClockId = ClockId(libc::CLOCK_MONOTONIC_PRECISE);
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten")
+ )
+ ))]
+ pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
+ #[cfg(any(
+ target_os = "fuchsia",
+ target_env = "uclibc",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ all(
+ not(target_env = "newlib"),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten")
+ )
+ ))]
+ pub const CLOCK_PROCESS_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
+ pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten")
+ )
+ ))]
+ pub const CLOCK_REALTIME_ALARM: ClockId = ClockId(libc::CLOCK_REALTIME_ALARM);
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten")
+ )
+ ))]
+ pub const CLOCK_REALTIME_COARSE: ClockId = ClockId(libc::CLOCK_REALTIME_COARSE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_REALTIME_PRECISE: ClockId = ClockId(libc::CLOCK_REALTIME_PRECISE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(
+ target_os = "emscripten",
+ all(target_os = "linux", target_env = "musl")
+ )
+ )
+ ))]
+ pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
+ #[cfg(any(
+ target_os = "fuchsia",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlib")),
+ any(
+ target_os = "emscripten",
+ all(target_os = "linux", target_env = "musl")
+ )
+ )
+ ))]
+ pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
+ #[cfg(any(
+ target_env = "uclibc",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ all(
+ not(target_env = "newlib"),
+ any(target_os = "linux", target_os = "android", target_os = "emscripten",),
+ ),
+ ))]
+ pub const CLOCK_THREAD_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_UPTIME_PRECISE: ClockId = ClockId(libc::CLOCK_UPTIME_PRECISE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
+}
+
+impl Into<clockid_t> for ClockId {
+ fn into(self) -> clockid_t {
+ self.as_raw()
+ }
+}
+
+impl From<clockid_t> for ClockId {
+ fn from(clk_id: clockid_t) -> Self {
+ ClockId::from_raw(clk_id)
+ }
+}
+
+impl std::fmt::Display for ClockId {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+}
+
+/// Get the resolution of the specified clock, (see
+/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
+pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
+ let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
+ let ret = unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
+ Errno::result(ret)?;
+ let res = unsafe { c_time.assume_init() };
+ Ok(TimeSpec::from(res))
+}
+
+/// Get the time of the specified clock, (see
+/// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
+pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
+ let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
+ let ret = unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
+ Errno::result(ret)?;
+ let res = unsafe { c_time.assume_init() };
+ Ok(TimeSpec::from(res))
+}
+
+/// Set the time of the specified clock, (see
+/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ all(
+ not(any(target_env = "uclibc", target_env = "newlibc")),
+ any(target_os = "redox", target_os = "hermit",),
+ ),
+)))]
+pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
+ let ret = unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
+ Errno::result(ret).map(drop)
+}
+
+/// Get the clock id of the specified process id, (see
+/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
+ let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
+ let ret = unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
+ if ret == 0 {
+ let res = unsafe { clk_id.assume_init() };
+ Ok(ClockId::from(res))
+ } else {
+ Err(Error::Sys(Errno::from_i32(ret)))
+ }
+}
diff --git a/src/unistd.rs b/src/unistd.rs
index 4a7903a3..04031e37 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -200,7 +200,7 @@ impl ForkResult {
/// ```no_run
/// use nix::unistd::{fork, ForkResult};
///
-/// match fork() {
+/// match unsafe{fork()} {
/// Ok(ForkResult::Parent { child, .. }) => {
/// println!("Continuing execution in parent process, new child has pid: {}", child);
/// }
@@ -230,9 +230,9 @@ impl ForkResult {
///
/// [async-signal-safe]: http://man7.org/linux/man-pages/man7/signal-safety.7.html
#[inline]
-pub fn fork() -> Result<ForkResult> {
+pub unsafe fn fork() -> Result<ForkResult> {
use self::ForkResult::*;
- let res = unsafe { libc::fork() };
+ let res = libc::fork();
Errno::result(res).map(|res| match res {
0 => Child,