1 //! libc syscalls supporting `rustix::process`.
2
3 use super::super::c;
4 #[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
5 use super::super::conv::borrowed_fd;
6 #[cfg(not(target_os = "wasi"))]
7 use super::super::conv::ret_pid_t;
8 use super::super::conv::{c_str, ret, ret_c_int, ret_discarded_char_ptr};
9 #[cfg(any(target_os = "android", target_os = "linux"))]
10 use super::super::conv::{syscall_ret, syscall_ret_u32};
11 #[cfg(any(
12 target_os = "android",
13 target_os = "dragonfly",
14 target_os = "fuchsia",
15 target_os = "linux",
16 ))]
17 use super::types::RawCpuSet;
18 #[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
19 use crate::fd::BorrowedFd;
20 use crate::ffi::CStr;
21 use crate::io;
22 use core::mem::MaybeUninit;
23 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
24 use {
25 super::super::conv::ret_infallible,
26 super::super::offset::{libc_getrlimit, libc_rlimit, libc_setrlimit, LIBC_RLIM_INFINITY},
27 crate::process::{Resource, Rlimit},
28 core::convert::TryInto,
29 };
30 #[cfg(any(target_os = "android", target_os = "linux"))]
31 use {
32 super::super::offset::libc_prlimit,
33 crate::process::{Cpuid, MembarrierCommand, MembarrierQuery},
34 };
35 #[cfg(not(target_os = "wasi"))]
36 use {
37 super::types::RawUname,
38 crate::process::{Gid, Pid, RawNonZeroPid, RawPid, Signal, Uid, WaitOptions, WaitStatus},
39 };
40
41 #[cfg(not(target_os = "wasi"))]
chdir(path: &CStr) -> io::Result<()>42 pub(crate) fn chdir(path: &CStr) -> io::Result<()> {
43 unsafe { ret(c::chdir(c_str(path))) }
44 }
45
46 #[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
fchdir(dirfd: BorrowedFd<'_>) -> io::Result<()>47 pub(crate) fn fchdir(dirfd: BorrowedFd<'_>) -> io::Result<()> {
48 unsafe { ret(c::fchdir(borrowed_fd(dirfd))) }
49 }
50
51 #[cfg(not(target_os = "wasi"))]
getcwd(buf: &mut [u8]) -> io::Result<()>52 pub(crate) fn getcwd(buf: &mut [u8]) -> io::Result<()> {
53 unsafe { ret_discarded_char_ptr(c::getcwd(buf.as_mut_ptr().cast(), buf.len())) }
54 }
55
56 #[cfg(any(target_os = "android", target_os = "linux"))]
membarrier_query() -> MembarrierQuery57 pub(crate) fn membarrier_query() -> MembarrierQuery {
58 // GLIBC does not have a wrapper for `membarrier`; [the documentation]
59 // says to use `syscall`.
60 //
61 // [the documentation]: https://man7.org/linux/man-pages/man2/membarrier.2.html#NOTES
62 const MEMBARRIER_CMD_QUERY: u32 = 0;
63 unsafe {
64 match syscall_ret_u32(c::syscall(c::SYS_membarrier, MEMBARRIER_CMD_QUERY, 0)) {
65 Ok(query) => MembarrierQuery::from_bits_unchecked(query),
66 Err(_) => MembarrierQuery::empty(),
67 }
68 }
69 }
70
71 #[cfg(any(target_os = "android", target_os = "linux"))]
membarrier(cmd: MembarrierCommand) -> io::Result<()>72 pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> {
73 unsafe { syscall_ret(c::syscall(c::SYS_membarrier, cmd as u32, 0)) }
74 }
75
76 #[cfg(any(target_os = "android", target_os = "linux"))]
membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()>77 pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> {
78 const MEMBARRIER_CMD_FLAG_CPU: u32 = 1;
79 unsafe {
80 syscall_ret(c::syscall(
81 c::SYS_membarrier,
82 cmd as u32,
83 MEMBARRIER_CMD_FLAG_CPU,
84 cpu.as_raw(),
85 ))
86 }
87 }
88
89 #[cfg(not(target_os = "wasi"))]
90 #[inline]
91 #[must_use]
getuid() -> Uid92 pub(crate) fn getuid() -> Uid {
93 unsafe {
94 let uid = c::getuid();
95 Uid::from_raw(uid)
96 }
97 }
98
99 #[cfg(not(target_os = "wasi"))]
100 #[inline]
101 #[must_use]
geteuid() -> Uid102 pub(crate) fn geteuid() -> Uid {
103 unsafe {
104 let uid = c::geteuid();
105 Uid::from_raw(uid)
106 }
107 }
108
109 #[cfg(not(target_os = "wasi"))]
110 #[inline]
111 #[must_use]
getgid() -> Gid112 pub(crate) fn getgid() -> Gid {
113 unsafe {
114 let gid = c::getgid();
115 Gid::from_raw(gid)
116 }
117 }
118
119 #[cfg(not(target_os = "wasi"))]
120 #[inline]
121 #[must_use]
getegid() -> Gid122 pub(crate) fn getegid() -> Gid {
123 unsafe {
124 let gid = c::getegid();
125 Gid::from_raw(gid)
126 }
127 }
128
129 #[cfg(not(target_os = "wasi"))]
130 #[inline]
131 #[must_use]
getpid() -> Pid132 pub(crate) fn getpid() -> Pid {
133 unsafe {
134 let pid = c::getpid();
135 debug_assert_ne!(pid, 0);
136 Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid))
137 }
138 }
139
140 #[cfg(not(target_os = "wasi"))]
141 #[inline]
142 #[must_use]
getppid() -> Option<Pid>143 pub(crate) fn getppid() -> Option<Pid> {
144 unsafe {
145 let pid: i32 = c::getppid();
146 Pid::from_raw(pid)
147 }
148 }
149
150 #[cfg(not(target_os = "wasi"))]
151 #[inline]
getpgid(pid: Option<Pid>) -> io::Result<Pid>152 pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> {
153 unsafe {
154 let pgid = ret_pid_t(c::getpgid(Pid::as_raw(pid) as _))?;
155 debug_assert_ne!(pgid, 0);
156 Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid)))
157 }
158 }
159
160 #[cfg(not(target_os = "wasi"))]
161 #[inline]
162 #[must_use]
getpgrp() -> Pid163 pub(crate) fn getpgrp() -> Pid {
164 unsafe {
165 let pgid = c::getpgrp();
166 debug_assert_ne!(pgid, 0);
167 Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid))
168 }
169 }
170
171 #[cfg(any(
172 target_os = "android",
173 target_os = "dragonfly",
174 target_os = "fuchsia",
175 target_os = "linux",
176 ))]
177 #[inline]
sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()>178 pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> {
179 unsafe {
180 ret(c::sched_getaffinity(
181 Pid::as_raw(pid) as _,
182 core::mem::size_of::<RawCpuSet>(),
183 cpuset,
184 ))
185 }
186 }
187
188 #[cfg(any(
189 target_os = "android",
190 target_os = "dragonfly",
191 target_os = "fuchsia",
192 target_os = "linux",
193 ))]
194 #[inline]
sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()>195 pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> {
196 unsafe {
197 ret(c::sched_setaffinity(
198 Pid::as_raw(pid) as _,
199 core::mem::size_of::<RawCpuSet>(),
200 cpuset,
201 ))
202 }
203 }
204
205 #[inline]
sched_yield()206 pub(crate) fn sched_yield() {
207 unsafe {
208 let _ = c::sched_yield();
209 }
210 }
211
212 #[cfg(not(target_os = "wasi"))]
213 #[inline]
uname() -> RawUname214 pub(crate) fn uname() -> RawUname {
215 let mut uname = MaybeUninit::<RawUname>::uninit();
216 unsafe {
217 ret(c::uname(uname.as_mut_ptr())).unwrap();
218 uname.assume_init()
219 }
220 }
221
222 #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
223 #[inline]
nice(inc: i32) -> io::Result<i32>224 pub(crate) fn nice(inc: i32) -> io::Result<i32> {
225 libc_errno::set_errno(libc_errno::Errno(0));
226 let r = unsafe { c::nice(inc) };
227 if libc_errno::errno().0 != 0 {
228 ret_c_int(r)
229 } else {
230 Ok(r)
231 }
232 }
233
234 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
235 #[inline]
getpriority_user(uid: Uid) -> io::Result<i32>236 pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> {
237 libc_errno::set_errno(libc_errno::Errno(0));
238 let r = unsafe { c::getpriority(c::PRIO_USER, uid.as_raw() as _) };
239 if libc_errno::errno().0 != 0 {
240 ret_c_int(r)
241 } else {
242 Ok(r)
243 }
244 }
245
246 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
247 #[inline]
getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32>248 pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> {
249 libc_errno::set_errno(libc_errno::Errno(0));
250 let r = unsafe { c::getpriority(c::PRIO_PGRP, Pid::as_raw(pgid) as _) };
251 if libc_errno::errno().0 != 0 {
252 ret_c_int(r)
253 } else {
254 Ok(r)
255 }
256 }
257
258 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
259 #[inline]
getpriority_process(pid: Option<Pid>) -> io::Result<i32>260 pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> {
261 libc_errno::set_errno(libc_errno::Errno(0));
262 let r = unsafe { c::getpriority(c::PRIO_PROCESS, Pid::as_raw(pid) as _) };
263 if libc_errno::errno().0 != 0 {
264 ret_c_int(r)
265 } else {
266 Ok(r)
267 }
268 }
269
270 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
271 #[inline]
setpriority_user(uid: Uid, priority: i32) -> io::Result<()>272 pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> {
273 unsafe { ret(c::setpriority(c::PRIO_USER, uid.as_raw() as _, priority)) }
274 }
275
276 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
277 #[inline]
setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()>278 pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> {
279 unsafe {
280 ret(c::setpriority(
281 c::PRIO_PGRP,
282 Pid::as_raw(pgid) as _,
283 priority,
284 ))
285 }
286 }
287
288 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
289 #[inline]
setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()>290 pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> {
291 unsafe {
292 ret(c::setpriority(
293 c::PRIO_PROCESS,
294 Pid::as_raw(pid) as _,
295 priority,
296 ))
297 }
298 }
299
300 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
301 #[inline]
getrlimit(limit: Resource) -> Rlimit302 pub(crate) fn getrlimit(limit: Resource) -> Rlimit {
303 let mut result = MaybeUninit::<libc_rlimit>::uninit();
304 unsafe {
305 ret_infallible(libc_getrlimit(limit as _, result.as_mut_ptr()));
306 rlimit_from_libc(result.assume_init())
307 }
308 }
309
310 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
311 #[inline]
setrlimit(limit: Resource, new: Rlimit) -> io::Result<()>312 pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> {
313 let lim = rlimit_to_libc(new)?;
314 unsafe { ret(libc_setrlimit(limit as _, &lim)) }
315 }
316
317 #[cfg(any(target_os = "android", target_os = "linux"))]
318 #[inline]
prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit>319 pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> {
320 let lim = rlimit_to_libc(new)?;
321 let mut result = MaybeUninit::<libc_rlimit>::uninit();
322 unsafe {
323 ret(libc_prlimit(
324 Pid::as_raw(pid),
325 limit as _,
326 &lim,
327 result.as_mut_ptr(),
328 ))?;
329 Ok(rlimit_from_libc(result.assume_init()))
330 }
331 }
332
333 /// Convert a Rust [`Rlimit`] to a C `libc_rlimit`.
334 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
rlimit_from_libc(lim: libc_rlimit) -> Rlimit335 fn rlimit_from_libc(lim: libc_rlimit) -> Rlimit {
336 let current = if lim.rlim_cur == LIBC_RLIM_INFINITY {
337 None
338 } else {
339 Some(lim.rlim_cur.try_into().unwrap())
340 };
341 let maximum = if lim.rlim_max == LIBC_RLIM_INFINITY {
342 None
343 } else {
344 Some(lim.rlim_max.try_into().unwrap())
345 };
346 Rlimit { current, maximum }
347 }
348
349 /// Convert a C `libc_rlimit` to a Rust `Rlimit`.
350 #[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
rlimit_to_libc(lim: Rlimit) -> io::Result<libc_rlimit>351 fn rlimit_to_libc(lim: Rlimit) -> io::Result<libc_rlimit> {
352 let Rlimit { current, maximum } = lim;
353 let rlim_cur = match current {
354 Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
355 None => LIBC_RLIM_INFINITY as _,
356 };
357 let rlim_max = match maximum {
358 Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
359 None => LIBC_RLIM_INFINITY as _,
360 };
361 Ok(libc_rlimit { rlim_cur, rlim_max })
362 }
363
364 #[cfg(not(target_os = "wasi"))]
365 #[inline]
wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>>366 pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
367 _waitpid(!0, waitopts)
368 }
369
370 #[cfg(not(target_os = "wasi"))]
371 #[inline]
waitpid( pid: Option<Pid>, waitopts: WaitOptions, ) -> io::Result<Option<(Pid, WaitStatus)>>372 pub(crate) fn waitpid(
373 pid: Option<Pid>,
374 waitopts: WaitOptions,
375 ) -> io::Result<Option<(Pid, WaitStatus)>> {
376 _waitpid(Pid::as_raw(pid), waitopts)
377 }
378
379 #[cfg(not(target_os = "wasi"))]
380 #[inline]
_waitpid( pid: RawPid, waitopts: WaitOptions, ) -> io::Result<Option<(Pid, WaitStatus)>>381 pub(crate) fn _waitpid(
382 pid: RawPid,
383 waitopts: WaitOptions,
384 ) -> io::Result<Option<(Pid, WaitStatus)>> {
385 unsafe {
386 let mut status: c::c_int = 0;
387 let pid = ret_c_int(c::waitpid(pid as _, &mut status, waitopts.bits() as _))?;
388 Ok(RawNonZeroPid::new(pid).map(|non_zero| {
389 (
390 Pid::from_raw_nonzero(non_zero),
391 WaitStatus::new(status as _),
392 )
393 }))
394 }
395 }
396
397 #[inline]
exit_group(code: c::c_int) -> !398 pub(crate) fn exit_group(code: c::c_int) -> ! {
399 // `_exit` and `_Exit` are the same; it's just a matter of which ones
400 // the libc bindings expose.
401 #[cfg(any(target_os = "wasi", target_os = "solid"))]
402 unsafe {
403 c::_Exit(code)
404 }
405 #[cfg(unix)]
406 unsafe {
407 c::_exit(code)
408 }
409 }
410
411 #[cfg(not(target_os = "wasi"))]
412 #[inline]
setsid() -> io::Result<Pid>413 pub(crate) fn setsid() -> io::Result<Pid> {
414 unsafe {
415 let pid = ret_c_int(c::setsid())?;
416 debug_assert_ne!(pid, 0);
417 Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid)))
418 }
419 }
420
421 #[cfg(not(target_os = "wasi"))]
422 #[inline]
kill_process(pid: Pid, sig: Signal) -> io::Result<()>423 pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> {
424 unsafe { ret(c::kill(pid.as_raw_nonzero().get(), sig as i32)) }
425 }
426
427 #[cfg(not(target_os = "wasi"))]
428 #[inline]
kill_process_group(pid: Pid, sig: Signal) -> io::Result<()>429 pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> {
430 unsafe {
431 ret(c::kill(
432 pid.as_raw_nonzero().get().wrapping_neg(),
433 sig as i32,
434 ))
435 }
436 }
437
438 #[cfg(not(target_os = "wasi"))]
439 #[inline]
kill_current_process_group(sig: Signal) -> io::Result<()>440 pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> {
441 unsafe { ret(c::kill(0, sig as i32)) }
442 }
443
444 #[cfg(any(target_os = "android", target_os = "linux"))]
445 #[inline]
prctl( option: c::c_int, arg2: *mut c::c_void, arg3: *mut c::c_void, arg4: *mut c::c_void, arg5: *mut c::c_void, ) -> io::Result<c::c_int>446 pub(crate) unsafe fn prctl(
447 option: c::c_int,
448 arg2: *mut c::c_void,
449 arg3: *mut c::c_void,
450 arg4: *mut c::c_void,
451 arg5: *mut c::c_void,
452 ) -> io::Result<c::c_int> {
453 ret_c_int(c::prctl(option, arg2, arg3, arg4, arg5))
454 }
455
456 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
457 #[inline]
procctl( idtype: c::idtype_t, id: c::id_t, option: c::c_int, data: *mut c::c_void, ) -> io::Result<()>458 pub(crate) unsafe fn procctl(
459 idtype: c::idtype_t,
460 id: c::id_t,
461 option: c::c_int,
462 data: *mut c::c_void,
463 ) -> io::Result<()> {
464 ret(c::procctl(idtype, id, option, data))
465 }
466