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