1 use errno::Errno;
2 use libc::{self, c_int};
3 use std::ptr;
4 use sys::signal::Signal;
5 use unistd::Pid;
6 use Result;
7
8 pub type RequestType = c_int;
9
10 cfg_if! {
11 if #[cfg(any(target_os = "dragonfly",
12 target_os = "freebsd",
13 target_os = "macos",
14 target_os = "openbsd"))] {
15 #[doc(hidden)]
16 pub type AddressType = *mut ::libc::c_char;
17 } else {
18 #[doc(hidden)]
19 pub type AddressType = *mut ::libc::c_void;
20 }
21 }
22
23 libc_enum! {
24 #[repr(i32)]
25 /// Ptrace Request enum defining the action to be taken.
26 pub enum Request {
27 PT_TRACE_ME,
28 PT_READ_I,
29 PT_READ_D,
30 #[cfg(target_os = "macos")]
31 PT_READ_U,
32 PT_WRITE_I,
33 PT_WRITE_D,
34 #[cfg(target_os = "macos")]
35 PT_WRITE_U,
36 PT_CONTINUE,
37 PT_KILL,
38 #[cfg(any(any(target_os = "dragonfly",
39 target_os = "freebsd",
40 target_os = "macos"),
41 all(target_os = "openbsd", target_arch = "x86_64"),
42 all(target_os = "netbsd", any(target_arch = "x86_64",
43 target_arch = "powerpc"))))]
44 PT_STEP,
45 PT_ATTACH,
46 PT_DETACH,
47 #[cfg(target_os = "macos")]
48 PT_SIGEXC,
49 #[cfg(target_os = "macos")]
50 PT_THUPDATE,
51 #[cfg(target_os = "macos")]
52 PT_ATTACHEXC
53 }
54 }
55
ptrace_other( request: Request, pid: Pid, addr: AddressType, data: c_int, ) -> Result<c_int>56 unsafe fn ptrace_other(
57 request: Request,
58 pid: Pid,
59 addr: AddressType,
60 data: c_int,
61 ) -> Result<c_int> {
62 Errno::result(libc::ptrace(
63 request as RequestType,
64 libc::pid_t::from(pid),
65 addr,
66 data,
67 )).map(|_| 0)
68 }
69
70 /// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
71 ///
72 /// Indicates that this process is to be traced by its parent.
73 /// This is the only ptrace request to be issued by the tracee.
traceme() -> Result<()>74 pub fn traceme() -> Result<()> {
75 unsafe { ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0).map(drop) }
76 }
77
78 /// Attach to a running process, as with `ptrace(PT_ATTACH, ...)`
79 ///
80 /// Attaches to the process specified in pid, making it a tracee of the calling process.
attach(pid: Pid) -> Result<()>81 pub fn attach(pid: Pid) -> Result<()> {
82 unsafe { ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop) }
83 }
84
85 /// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
86 ///
87 /// Detaches from the process specified in pid allowing it to run freely
detach(pid: Pid) -> Result<()>88 pub fn detach(pid: Pid) -> Result<()> {
89 unsafe { ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), 0).map(drop) }
90 }
91
92 /// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
93 ///
94 /// Continues the execution of the process with PID `pid`, optionally
95 /// delivering a signal specified by `sig`.
cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>96 pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
97 let data = match sig.into() {
98 Some(s) => s as c_int,
99 None => 0,
100 };
101 unsafe {
102 // Ignore the useless return value
103 ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data).map(drop)
104 }
105 }
106
107 /// Issues a kill request as with `ptrace(PT_KILL, ...)`
108 ///
109 /// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
kill(pid: Pid) -> Result<()>110 pub fn kill(pid: Pid) -> Result<()> {
111 unsafe {
112 ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
113 }
114 }
115
116 /// Move the stopped tracee process forward by a single step as with
117 /// `ptrace(PT_STEP, ...)`
118 ///
119 /// Advances the execution of the process with PID `pid` by a single step optionally delivering a
120 /// signal specified by `sig`.
121 ///
122 /// # Example
123 /// ```rust
124 /// extern crate nix;
125 /// use nix::sys::ptrace::step;
126 /// use nix::unistd::Pid;
127 /// use nix::sys::signal::Signal;
128 /// use nix::sys::wait::*;
129 /// fn main() {
130 /// // If a process changes state to the stopped state because of a SIGUSR1
131 /// // signal, this will step the process forward and forward the user
132 /// // signal to the stopped process
133 /// match waitpid(Pid::from_raw(-1), None) {
134 /// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
135 /// let _ = step(pid, Signal::SIGUSR1);
136 /// }
137 /// _ => {},
138 /// }
139 /// }
140 /// ```
141 #[cfg(
142 any(
143 any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
144 all(target_os = "openbsd", target_arch = "x86_64"),
145 all(target_os = "netbsd",
146 any(target_arch = "x86_64", target_arch = "powerpc")
147 )
148 )
149 )]
step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>150 pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
151 let data = match sig.into() {
152 Some(s) => s as c_int,
153 None => 0,
154 };
155 unsafe { ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop) }
156 }
157
158 /// Reads a word from a processes memory at the given address
read(pid: Pid, addr: AddressType) -> Result<c_int>159 pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
160 unsafe {
161 // Traditionally there was a difference between reading data or
162 // instruction memory but not in modern systems.
163 ptrace_other(Request::PT_READ_D, pid, addr, 0)
164 }
165 }
166
167 /// Writes a word into the processes memory at the given address
write(pid: Pid, addr: AddressType, data: c_int) -> Result<()>168 pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
169 unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
170 }
171