Initial ptrace compatibility for Redox OS
This commit is contained in:
Generated
+7
-1
@@ -155,6 +155,11 @@ name = "rand_core"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
source = "git+https://gitlab.redox-os.org/redox-os/syscall#9e9f47d2a570c55dd96cd80c83bc818d63cab8af"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
@@ -174,7 +179,7 @@ dependencies = [
|
||||
"posix-regex 0.1.0",
|
||||
"ralloc 1.0.0",
|
||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall)",
|
||||
"sc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -279,6 +284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
||||
"checksum redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall)" = "<none>"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ optional = true
|
||||
sc = "0.2.2"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox_syscall = "0.1.52"
|
||||
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall" }
|
||||
spin = "0.4.10"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#ifndef _BITS_SYS_WAIT_H
|
||||
#define _BITS_SYS_WAIT_H
|
||||
|
||||
#define WEXITSTATUS(s) (((s) & 0xff00) >> 8)
|
||||
#define WTERMSIG(s) ((s) & 0x7f)
|
||||
#define WEXITSTATUS(s) (((s) >> 8) & 0xff)
|
||||
#define WTERMSIG(s) (((s) & 0x7f) != 0)
|
||||
#define WSTOPSIG(s) WEXITSTATUS(s)
|
||||
#define WCOREDUMP(s) ((s) & 0x80)
|
||||
#define WIFEXITED(s) (!WTERMSIG(s))
|
||||
#define WIFSTOPPED(s) ((short)((((s)&0xffff)*0x10001)>>8) > 0x7f00)
|
||||
#define WIFSIGNALED(s) (((s)&0xffff)-1U < 0xffu)
|
||||
#define WCOREDUMP(s) (((s) & 0x80) != 0)
|
||||
#define WIFEXITED(s) (((s) & 0x7f) == 0)
|
||||
#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
|
||||
#define WIFSIGNALED(s) (((((s) & 0x7f) + 1U) & 0x7f) >= 2) // Ends with 1111111 or 10000000
|
||||
#define WIFCONTINUED(s) ((s) == 0xffff)
|
||||
|
||||
#endif /* _BITS_SYS_WAIT_H */
|
||||
|
||||
@@ -67,7 +67,7 @@ impl File {
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for File {
|
||||
impl io::Read for &File {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match Sys::read(self.fd, buf) {
|
||||
-1 => Err(io::last_os_error()),
|
||||
@@ -76,7 +76,7 @@ impl io::Read for File {
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for File {
|
||||
impl io::Write for &File {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match Sys::write(self.fd, buf) {
|
||||
-1 => Err(io::last_os_error()),
|
||||
@@ -89,7 +89,7 @@ impl io::Write for File {
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Seek for File {
|
||||
impl io::Seek for &File {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
let (offset, whence) = match pos {
|
||||
io::SeekFrom::Start(start) => (start as off_t, SEEK_SET),
|
||||
@@ -104,6 +104,28 @@ impl io::Seek for File {
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for File {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(&mut &*self).read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for File {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(&mut &*self).write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(&mut &*self).flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Seek for File {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
(&mut &*self).seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for File {
|
||||
type Target = c_int;
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ pub const PTRACE_SETFPREGS: c_int = 15;
|
||||
pub const PTRACE_ATTACH: c_int = 16;
|
||||
pub const PTRACE_DETACH: c_int = 17;
|
||||
pub const PTRACE_SYSCALL: c_int = 24;
|
||||
pub const PTRACE_SYSEMU: c_int = 31;
|
||||
pub const PTRACE_SYSEMU_SINGLESTEP: c_int = 32;
|
||||
|
||||
// Can't use "params: ..." syntax, because... guess what? Cbingen again :(
|
||||
#[no_mangle]
|
||||
|
||||
+38
-38
@@ -4,48 +4,48 @@ use platform::types::*;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct user_fpregs_struct {
|
||||
cwd: u16,
|
||||
swd: u16,
|
||||
ftw: u16,
|
||||
fop: u16,
|
||||
rip: u64,
|
||||
rdp: u64,
|
||||
mxcsr: u32,
|
||||
mxcr_mask: u32,
|
||||
st_space: [u32; 32],
|
||||
xmm_space: [u32; 64],
|
||||
padding: [u32; 24],
|
||||
pub cwd: u16,
|
||||
pub swd: u16,
|
||||
pub ftw: u16,
|
||||
pub fop: u16,
|
||||
pub rip: u64,
|
||||
pub rdp: u64,
|
||||
pub mxcsr: u32,
|
||||
pub mxcr_mask: u32,
|
||||
pub st_space: [u32; 32],
|
||||
pub xmm_space: [u32; 64],
|
||||
pub padding: [u32; 24],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct user_regs_struct {
|
||||
r15: c_ulong,
|
||||
r14: c_ulong,
|
||||
r13: c_ulong,
|
||||
r12: c_ulong,
|
||||
rbp: c_ulong,
|
||||
rbx: c_ulong,
|
||||
r11: c_ulong,
|
||||
r10: c_ulong,
|
||||
r9: c_ulong,
|
||||
r8: c_ulong,
|
||||
rax: c_ulong,
|
||||
rcx: c_ulong,
|
||||
rdx: c_ulong,
|
||||
rsi: c_ulong,
|
||||
rdi: c_ulong,
|
||||
orig_rax: c_ulong,
|
||||
rip: c_ulong,
|
||||
cs: c_ulong,
|
||||
eflags: c_ulong,
|
||||
rsp: c_ulong,
|
||||
ss: c_ulong,
|
||||
fs_base: c_ulong,
|
||||
gs_base: c_ulong,
|
||||
ds: c_ulong,
|
||||
es: c_ulong,
|
||||
fs: c_ulong,
|
||||
gs: c_ulong,
|
||||
pub r15: c_ulong,
|
||||
pub r14: c_ulong,
|
||||
pub r13: c_ulong,
|
||||
pub r12: c_ulong,
|
||||
pub rbp: c_ulong,
|
||||
pub rbx: c_ulong,
|
||||
pub r11: c_ulong,
|
||||
pub r10: c_ulong,
|
||||
pub r9: c_ulong,
|
||||
pub r8: c_ulong,
|
||||
pub rax: c_ulong,
|
||||
pub rcx: c_ulong,
|
||||
pub rdx: c_ulong,
|
||||
pub rsi: c_ulong,
|
||||
pub rdi: c_ulong,
|
||||
pub orig_rax: c_ulong,
|
||||
pub rip: c_ulong,
|
||||
pub cs: c_ulong,
|
||||
pub eflags: c_ulong,
|
||||
pub rsp: c_ulong,
|
||||
pub ss: c_ulong,
|
||||
pub fs_base: c_ulong,
|
||||
pub gs_base: c_ulong,
|
||||
pub ds: c_ulong,
|
||||
pub es: c_ulong,
|
||||
pub fs: c_ulong,
|
||||
pub gs: c_ulong,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! sys/socket implementation, following http://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/socket.h.html
|
||||
|
||||
use core::result::Result as CoreResult;
|
||||
use core::{mem, ptr, slice};
|
||||
use syscall::data::Map;
|
||||
@@ -17,6 +15,7 @@ use header::sys_mman::MAP_ANON;
|
||||
use header::sys_stat::stat;
|
||||
use header::sys_statvfs::statvfs;
|
||||
use header::sys_time::{timeval, timezone};
|
||||
use header::sys_wait;
|
||||
use header::sys_utsname::{utsname, UTSLENGTH};
|
||||
use header::time::timespec;
|
||||
use header::unistd::{F_OK, R_OK, W_OK, X_OK};
|
||||
@@ -922,14 +921,34 @@ impl Pal for Sys {
|
||||
if pid == !0 {
|
||||
pid = 0;
|
||||
}
|
||||
unsafe {
|
||||
let mut temp: usize = 0;
|
||||
let res = e(syscall::waitpid(pid as usize, &mut temp, options as usize));
|
||||
if !stat_loc.is_null() {
|
||||
*stat_loc = temp as c_int;
|
||||
let mut res = None;
|
||||
let mut status = 0;
|
||||
|
||||
// First, allow ptrace to handle waitpid
|
||||
let state = ptrace::init_state();
|
||||
let sessions = state.sessions.lock();
|
||||
if let Some(session) = sessions.get(&pid) {
|
||||
if options & sys_wait::WNOHANG != sys_wait::WNOHANG {
|
||||
let _ = (&mut &session.tracer).write(&[syscall::PTRACE_WAIT]);
|
||||
res = Some(e(syscall::waitpid(pid as usize, &mut status, (options | sys_wait::WNOHANG) as usize)));
|
||||
if res == Some(0) {
|
||||
// WNOHANG, just pretend ptrace SIGSTOP:ped this
|
||||
status = (syscall::SIGSTOP << 8) | 0x7f;
|
||||
res = Some(pid as usize);
|
||||
}
|
||||
}
|
||||
res as pid_t
|
||||
}
|
||||
|
||||
// If ptrace didn't impact this waitpid, proceed as normal
|
||||
let res = res.unwrap_or_else(|| e(syscall::waitpid(pid as usize, &mut status, options as usize)));
|
||||
|
||||
// If stat_loc is non-null, set that and the return
|
||||
unsafe {
|
||||
if !stat_loc.is_null() {
|
||||
*stat_loc = status as c_int;
|
||||
}
|
||||
}
|
||||
res as pid_t
|
||||
}
|
||||
|
||||
fn write(fd: c_int, buf: &[u8]) -> ssize_t {
|
||||
|
||||
@@ -5,23 +5,152 @@
|
||||
//! compatibility. So, this module will be a hellhole.
|
||||
|
||||
use super::super::types::*;
|
||||
use super::super::PalPtrace;
|
||||
use super::{e, Sys};
|
||||
use super::super::{errno, Pal, PalPtrace, PalSignal, Sys};
|
||||
use crate::c_str::CString;
|
||||
use crate::fs::File;
|
||||
use crate::header::sys_user::user_regs_struct;
|
||||
use crate::header::{errno as errnoh, fcntl, signal, sys_ptrace};
|
||||
use crate::io::{self, prelude::*};
|
||||
use crate::sync::{Mutex, Once};
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::collections::btree_map::Entry;
|
||||
use syscall;
|
||||
|
||||
#[derive(Default)]
|
||||
struct State {}
|
||||
pub struct Session {
|
||||
pub tracer: File,
|
||||
pub mem: File,
|
||||
pub regs: File,
|
||||
pub fpregs: File
|
||||
}
|
||||
pub struct State {
|
||||
pub sessions: Mutex<BTreeMap<pid_t, Session>>
|
||||
}
|
||||
impl State {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
sessions: Mutex::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static STATE: Once<Mutex<State>> = Once::new();
|
||||
static STATE: Once<State> = Once::new();
|
||||
|
||||
fn state() -> &'static Mutex<State> {
|
||||
STATE.call_once(|| Mutex::new(State::default()))
|
||||
pub fn init_state() -> &'static State {
|
||||
STATE.call_once(|| State::new())
|
||||
}
|
||||
|
||||
fn inner_ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> io::Result<c_int> {
|
||||
let state = init_state();
|
||||
|
||||
if request == sys_ptrace::PTRACE_TRACEME {
|
||||
// let pid = Sys::getpid();
|
||||
// todo: only auto-open session on host if this happens
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
const NEW_FLAGS: c_int = fcntl::O_RDWR | fcntl::O_CLOEXEC;
|
||||
|
||||
let mut sessions = state.sessions.lock();
|
||||
let session = match sessions.entry(pid) {
|
||||
Entry::Vacant(entry) => entry.insert(Session {
|
||||
tracer: File::open(&CString::new(format!("proc:{}/trace", pid)).unwrap(), NEW_FLAGS | fcntl::O_NONBLOCK)?,
|
||||
mem: File::open(&CString::new(format!("proc:{}/mem", pid)).unwrap(), NEW_FLAGS)?,
|
||||
regs: File::open(&CString::new(format!("proc:{}/regs/int", pid)).unwrap(), NEW_FLAGS)?,
|
||||
fpregs: File::open(&CString::new(format!("proc:{}/regs/float", pid)).unwrap(), NEW_FLAGS)?,
|
||||
}),
|
||||
Entry::Occupied(entry) => entry.into_mut()
|
||||
};
|
||||
match request {
|
||||
sys_ptrace::PTRACE_CONT | sys_ptrace::PTRACE_SINGLESTEP |
|
||||
sys_ptrace::PTRACE_SYSCALL | sys_ptrace::PTRACE_SYSEMU |
|
||||
sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => {
|
||||
Sys::kill(pid, signal::SIGCONT as _);
|
||||
|
||||
(&mut &session.tracer).write(&[match request {
|
||||
sys_ptrace::PTRACE_CONT => syscall::PTRACE_CONT,
|
||||
sys_ptrace::PTRACE_SINGLESTEP => syscall::PTRACE_SINGLESTEP,
|
||||
sys_ptrace::PTRACE_SYSCALL => syscall::PTRACE_SYSCALL,
|
||||
sys_ptrace::PTRACE_SYSEMU => syscall::PTRACE_SYSEMU | syscall::PTRACE_SYSCALL,
|
||||
sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => syscall::PTRACE_SYSEMU | syscall::PTRACE_SINGLESTEP,
|
||||
_ => unreachable!("unhandled ptrace request type {}", request)
|
||||
}])?;
|
||||
Ok(0)
|
||||
},
|
||||
sys_ptrace::PTRACE_GETREGS => {
|
||||
let c_regs = unsafe { &mut *(data as *mut user_regs_struct) };
|
||||
let mut redox_regs = syscall::IntRegisters::default();
|
||||
(&mut &session.regs).read(&mut redox_regs)?;
|
||||
*c_regs = user_regs_struct {
|
||||
r15: redox_regs.r15 as _,
|
||||
r14: redox_regs.r14 as _,
|
||||
r13: redox_regs.r13 as _,
|
||||
r12: redox_regs.r12 as _,
|
||||
rbp: redox_regs.rbp as _,
|
||||
rbx: redox_regs.rbx as _,
|
||||
r11: redox_regs.r11 as _,
|
||||
r10: redox_regs.r10 as _,
|
||||
r9: redox_regs.r9 as _,
|
||||
r8: redox_regs.r8 as _,
|
||||
rax: redox_regs.rax as _,
|
||||
rcx: redox_regs.rcx as _,
|
||||
rdx: redox_regs.rdx as _,
|
||||
rsi: redox_regs.rsi as _,
|
||||
rdi: redox_regs.rdi as _,
|
||||
orig_rax: redox_regs.rax as _, // redox_regs.orig_rax as _,
|
||||
rip: redox_regs.rip as _,
|
||||
cs: redox_regs.cs as _,
|
||||
eflags: redox_regs.rflags as _,
|
||||
rsp: redox_regs.rsp as _,
|
||||
ss: redox_regs.ss as _,
|
||||
fs_base: 0, // fs_base: redox_regs.fs_base as _,
|
||||
gs_base: 0, // gs_base: redox_regs.gs_base as _,
|
||||
ds: 0, // ds: redox_regs.ds as _,
|
||||
es: 0, // es: redox_regs.es as _,
|
||||
fs: redox_regs.fs as _,
|
||||
gs: 0, // gs: redox_regs.gs as _,
|
||||
};
|
||||
Ok(0)
|
||||
},
|
||||
sys_ptrace::PTRACE_SETREGS => {
|
||||
let c_regs = unsafe { &*(data as *mut user_regs_struct) };
|
||||
let redox_regs = syscall::IntRegisters {
|
||||
r15: c_regs.r15 as _,
|
||||
r14: c_regs.r14 as _,
|
||||
r13: c_regs.r13 as _,
|
||||
r12: c_regs.r12 as _,
|
||||
rbp: c_regs.rbp as _,
|
||||
rbx: c_regs.rbx as _,
|
||||
r11: c_regs.r11 as _,
|
||||
r10: c_regs.r10 as _,
|
||||
r9: c_regs.r9 as _,
|
||||
r8: c_regs.r8 as _,
|
||||
rax: c_regs.orig_rax as _, // c_regs.rax as _,
|
||||
rcx: c_regs.rcx as _,
|
||||
rdx: c_regs.rdx as _,
|
||||
rsi: c_regs.rsi as _,
|
||||
rdi: c_regs.rdi as _,
|
||||
// orig_rax: c_regs.orig_rax as _,
|
||||
rip: c_regs.rip as _,
|
||||
cs: c_regs.cs as _,
|
||||
rflags: c_regs.eflags as _,
|
||||
rsp: c_regs.rsp as _,
|
||||
ss: c_regs.ss as _,
|
||||
// fs_base: c_regs.fs_base as _,
|
||||
// gs_base: c_regs.gs_base as _,
|
||||
// ds: c_regs.ds as _,
|
||||
// es: c_regs.es as _,
|
||||
fs: c_regs.fs as _,
|
||||
// gs: c_regs.gs as _,
|
||||
};
|
||||
(&mut &session.regs).write(&redox_regs)?;
|
||||
Ok(0)
|
||||
},
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PalPtrace for Sys {
|
||||
fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int {
|
||||
// Oh boy, this is not gonna be fun.........
|
||||
unimplemented!()
|
||||
inner_ptrace(request, pid, addr, data).unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -19,8 +19,8 @@ int main() {
|
||||
|
||||
// Alert parent: I'm ready
|
||||
result = raise(SIGSTOP);
|
||||
ERROR_IF(close, result, == -1);
|
||||
UNEXP_IF(close, result, != 0);
|
||||
ERROR_IF(raise, result, == -1);
|
||||
UNEXP_IF(raise, result, != 0);
|
||||
|
||||
puts("This is printed to STDOUT.");
|
||||
puts("Or, at least, that's what I thought.");
|
||||
@@ -28,35 +28,35 @@ int main() {
|
||||
puts("Big surprise, right!");
|
||||
} else {
|
||||
// Wait for child process to be ready
|
||||
int result = waitpid(pid, NULL, 0);
|
||||
ERROR_IF(close, result, == -1);
|
||||
int result = waitpid(pid, NULL, WUNTRACED);
|
||||
ERROR_IF(waitpid, result, == -1);
|
||||
|
||||
int status;
|
||||
while (true) {
|
||||
// Pre-syscall:
|
||||
result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
ERROR_IF(close, result, == -1);
|
||||
UNEXP_IF(close, result, != 0);
|
||||
ERROR_IF(ptrace, result, == -1);
|
||||
UNEXP_IF(ptrace, result, != 0);
|
||||
result = waitpid(pid, &status, 0);
|
||||
ERROR_IF(close, result, == -1);
|
||||
ERROR_IF(waitpid, result, == -1);
|
||||
if (WIFEXITED(status)) { break; }
|
||||
|
||||
struct user_regs_struct regs;
|
||||
result = ptrace(PTRACE_GETREGS, pid, NULL, ®s);
|
||||
ERROR_IF(close, result, == -1);
|
||||
ERROR_IF(ptrace, result, == -1);
|
||||
|
||||
if (regs.orig_rax == 1 || regs.orig_rax == 0x21000004) { // SYS_write on Redox and Linux
|
||||
regs.rdi = 2;
|
||||
result = ptrace(PTRACE_SETREGS, pid, NULL, ®s);
|
||||
ERROR_IF(close, result, == -1);
|
||||
ERROR_IF(ptrace, result, == -1);
|
||||
}
|
||||
|
||||
// Post-syscall:
|
||||
result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
ERROR_IF(close, result, == -1);
|
||||
UNEXP_IF(close, result, != 0);
|
||||
ERROR_IF(ptrace, result, == -1);
|
||||
UNEXP_IF(ptrace, result, != 0);
|
||||
result = waitpid(pid, &status, 0);
|
||||
ERROR_IF(close, result, == -1);
|
||||
ERROR_IF(waitpid, result, == -1);
|
||||
if (WIFEXITED(status)) { break; }
|
||||
}
|
||||
printf("Child exited with status %d\n", WEXITSTATUS(status));
|
||||
|
||||
Reference in New Issue
Block a user