1use std::collections::HashSet;
2use std::convert::Infallible;
3use std::ffi::{CString, OsStr, OsString, c_char};
4use std::fmt::{Display, Formatter};
5use std::fs::File;
6use std::io::{self, Error, ErrorKind, Read, Write};
7use std::os::fd::{AsFd, BorrowedFd, IntoRawFd, RawFd};
8use std::os::unix::ffi::OsStrExt;
9use std::os::unix::io::FromRawFd;
10use std::ptr;
11use std::str::FromStr;
12
13use crate::command_data::{Arg, CommandWithArgs, Run};
14use crate::jobs::{Job, JobStatus, Jobs};
15pub use crate::platform::unix::umask::mode_t;
16use crate::platform::{FromFileDesc, Platform, RLimit, RLimitVals};
17use crate::run::run_job;
18use crate::signals::test_clear_sigint;
19use nix::libc;
20use nix::sys::signal::{self, SigHandler, Signal, kill};
21use nix::sys::termios;
22use nix::sys::wait::{self, WaitPidFlag, WaitStatus};
23use nix::unistd::{self, Uid};
24
25mod umask;
26
27#[cfg(not(any(target_env = "gnu", target_env = "uclibc")))]
29pub type RlimitResource = nix::libc::c_int;
30#[cfg(any(target_env = "gnu", target_env = "uclibc"))]
31pub type RlimitResource = nix::libc::__rlimit_resource_t;
32
33pub struct Sys {}
34impl Platform for Sys {
35 type Pid = UnixPid;
36 type FileDesc = UnixFileDesc;
37 type TermSettings = UnixTermSettings;
38
39 fn get_term_settings(terminal: UnixFileDesc) -> Result<UnixTermSettings, io::Error> {
41 Ok(UnixTermSettings(termios::tcgetattr(terminal)?))
42 }
43
44 fn restore_terminal(
46 term_settings: &UnixTermSettings,
47 shell_pid: UnixPid,
48 ) -> Result<(), io::Error> {
49 let stdin = UnixFileDesc(0);
50 termios::tcsetattr(stdin, termios::SetArg::TCSANOW, &term_settings.0)?;
51 unistd::tcsetpgrp(stdin, unistd::Pid::from_raw(shell_pid.0))?;
53 Ok(())
54 }
55
56 fn terminal_foreground(terminal: UnixFileDesc) {
59 let mut shell_pgid = unistd::getpgrp();
61 while unistd::tcgetpgrp(terminal) != Ok(shell_pgid) {
62 if let Err(err) = signal::kill(shell_pgid, Signal::SIGTTIN) {
63 eprintln!("Error sending sigttin: {}.", err);
64 }
65 shell_pgid = unistd::getpgrp();
66 }
67 }
68
69 fn set_self_pgroup() -> Result<(), io::Error> {
72 let pgid = unistd::getpid();
74 if let Err(err) = unistd::setpgid(pgid, pgid) {
75 match err {
76 nix::errno::Errno::EPERM => { }
77 _ => return Err(err.into()),
78 }
79 }
80 Ok(())
81 }
82
83 fn grab_terminal(terminal: UnixFileDesc) -> Result<(), io::Error> {
86 let pgid = unistd::getpid();
88 Ok(unistd::tcsetpgrp(terminal, pgid)?)
89 }
90
91 fn anon_pipe() -> Result<(UnixFileDesc, UnixFileDesc), io::Error> {
93 let mut fds = [0; 2];
95
96 cfg_if::cfg_if! {
100 if #[cfg(any(
101 target_os = "dragonfly",
102 target_os = "freebsd",
103 target_os = "linux",
104 target_os = "netbsd",
105 target_os = "openbsd",
106 target_os = "redox"
107 ))] {
108 cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
109 Ok((UnixFileDesc(fds[0]), UnixFileDesc(fds[1])))
110 } else {
111 unsafe {
112 cvt(libc::pipe(fds.as_mut_ptr()))?;
113 cvt(libc::fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
114 cvt(libc::fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
115 }
116 Ok((UnixFileDesc(fds[0]), UnixFileDesc(fds[1])))
117 }
118 }
119 }
120
121 fn close_fd(fd: UnixFileDesc) -> Result<(), io::Error> {
123 unsafe {
124 cvt(libc::close(fd.0))?;
125 }
126 Ok(())
127 }
128
129 fn fork_run(run: &Run, job: &mut Job, jobs: &mut Jobs) -> Result<(), io::Error> {
130 let result = unsafe { cvt(libc::fork())? };
131 let pid = unsafe {
132 match result {
133 0 => {
134 setup_group_term(UnixPid(unistd::getpid().into()), job);
135
136 let redir_fds = run.get_internal_fds();
137 close_extra_fds(&redir_fds);
138 jobs.set_interactive(false);
139 jobs.set_no_tty();
140 match run_job(run, jobs, false) {
141 Ok(status) => libc::_exit(status),
142 Err(e) => {
143 eprintln!("Error running subshell: {e}");
144 libc::_exit(1);
145 }
146 }
147 }
148 n => n,
149 }
150 };
151 setup_group_term(UnixPid(pid), job);
152 let redir_fds = run.get_internal_fds();
154 for fd in redir_fds {
155 if fd > STDERR_FILENO {
156 let _ = Self::close_fd(fd);
157 }
158 }
159 job.add_process(UnixPid(pid), format!("{run}"));
160 Ok(())
161 }
162
163 fn fork_exec(
164 command: &CommandWithArgs,
165 job: &mut Job,
166 jobs: &mut Jobs,
167 ) -> Result<(), io::Error> {
168 let program = if let Some(program) = command.command(jobs) {
169 program?
170 } else {
171 return Err(io::Error::other("no program to execute"));
172 };
173 let args = command.args_iter();
174 let (UnixFileDesc(input), UnixFileDesc(output)) = Sys::anon_pipe()?;
175 let result = unsafe { cvt(libc::fork())? };
176
177 let pid = unsafe {
178 match result {
179 0 => {
180 libc::close(input);
181 jobs.set_no_tty();
182 jobs.set_interactive(false);
183 match command.process_redirects(jobs) {
184 Ok(mut fds) => {
185 fds.insert(UnixFileDesc(output));
186 close_extra_fds(&fds);
187 }
188 Err(err) => send_error_to_parent(output, err), }
190 setup_group_term(UnixPid(unistd::getpid().into()), job);
191
192 let err = exec(&program, args, jobs);
193 send_error_to_parent(output, err.unwrap_err());
196 0
197 }
198 n => n,
199 }
200 };
201 setup_group_term(UnixPid(pid), job);
202 unsafe {
203 libc::close(output);
204 let redir_fds = command.get_internal_fds();
207 for fd in redir_fds {
208 if fd > STDERR_FILENO {
209 let _ = Self::close_fd(fd);
210 }
211 }
212 }
213 let mut bytes = [0; 8];
214
215 let mut input = unsafe { File::from_raw_fd(input) };
217 loop {
219 match input.read(&mut bytes) {
220 Ok(0) => {
221 job.add_process(UnixPid(pid), program.to_string_lossy());
222 return Ok(());
223 }
224 Ok(8) => {
225 let (errno, footer) = bytes.split_at(4);
226 assert_eq!(
227 CLOEXEC_MSG_FOOTER, footer,
228 "Validation on the CLOEXEC pipe failed: {:?}",
229 bytes
230 );
231 let errno = i32::from_be_bytes(errno.try_into().unwrap());
232 return Err(io::Error::from_raw_os_error(errno));
234 }
235 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
236 Err(e) => {
237 panic!("the CLOEXEC pipe failed: {:?}", e)
238 }
239 Ok(_n) => {
240 panic!("short read on the CLOEXEC pipe")
242 }
243 }
244 }
245 }
246
247 fn try_wait_pid(pid: UnixPid, job: &mut Job) -> (bool, Option<i32>) {
248 let mut opts = WaitPidFlag::WUNTRACED;
249 opts.insert(WaitPidFlag::WCONTINUED);
250 opts.insert(WaitPidFlag::WNOHANG);
251 match wait::waitpid(unistd::Pid::from_raw(pid.0), Some(opts)) {
252 Err(nix::errno::Errno::ECHILD) => {
253 (true, None)
255 }
256 Err(err) => {
257 eprintln!("Error waiting for pid {pid}, {err}");
258 job.process_error(pid);
259 (true, None)
260 }
261 Ok(WaitStatus::Exited(_, status)) => {
262 job.process_done(pid, status);
263 (true, Some(status))
264 }
265 Ok(WaitStatus::Stopped(..)) => {
266 job.mark_stopped();
267 (true, None)
268 }
269 Ok(WaitStatus::Signaled(pid, signal, _core_dumped)) => {
270 job.process_signaled(UnixPid(pid.into()), signal as i32);
271 (true, None)
272 }
273 Ok(WaitStatus::Continued(_)) => (false, None),
274 #[cfg(any(target_os = "linux", target_os = "android"))]
275 Ok(WaitStatus::PtraceEvent(_pid, _signal, _)) => (false, None),
276 #[cfg(any(target_os = "linux", target_os = "android"))]
277 Ok(WaitStatus::PtraceSyscall(_pid)) => (false, None),
278 Ok(WaitStatus::StillAlive) => (false, None),
279 }
280 }
281
282 fn wait_job(job: &mut Job) -> Option<i32> {
283 let mut result: Option<i32> = None;
284 let mut int_cnt = 0;
285 let pgid = job.pgid();
286 let mut i = 0;
287 while let Some(pid) = job.pids().get(i) {
288 i += 1;
289 let pid = pid.pid();
290 loop {
291 if test_clear_sigint() {
292 if int_cnt == 0 {
293 if let Err(err) = kill(unistd::Pid::from_raw(-pgid.0), Signal::SIGINT) {
294 eprintln!("ERROR sending SIGINT to child process group {pgid}, {err}");
295 }
296 } else if int_cnt == 1 {
297 if let Err(err) = kill(unistd::Pid::from_raw(-pgid.0), Signal::SIGTERM) {
298 eprintln!("ERROR sending SIGTERM to child process group {pgid}, {err}");
299 }
300 } else if let Err(err) = kill(unistd::Pid::from_raw(-pgid.0), Signal::SIGKILL) {
301 eprintln!("ERROR sending SIGKILL to child process group {pgid}, {err}");
302 }
303 int_cnt += 1;
304 }
305 let (stop, status) = Self::try_wait_pid(pid, job);
306 if stop {
307 result = status;
308 break;
309 }
310 std::thread::sleep(std::time::Duration::from_millis(100));
311 }
312 }
313 result
314 }
315
316 fn foreground_job(
318 job: &mut Job,
319 term_settings: &Option<UnixTermSettings>,
320 ) -> Result<(), io::Error> {
321 let pgid = job.pgid();
322 if let JobStatus::Stopped = job.status() {
323 let ppgid = unistd::Pid::from_raw(-pgid.0);
324 signal::kill(ppgid, Signal::SIGCONT)?;
325 let ppgid = unistd::Pid::from_raw(pgid.0);
326 unistd::tcsetpgrp(UnixFileDesc(libc::STDIN_FILENO), ppgid)?;
327 job.mark_running();
328 Self::wait_job(job);
329 if let Some(term_settings) = term_settings {
330 Self::restore_terminal(term_settings, job.shell_pid())?;
331 }
332 } else {
333 let ppgid = unistd::Pid::from_raw(pgid.0);
334 unistd::tcsetpgrp(UnixFileDesc(libc::STDIN_FILENO), ppgid)?;
336 Self::wait_job(job);
337 if let Some(term_settings) = term_settings {
338 Self::restore_terminal(term_settings, job.shell_pid())?;
339 }
340 }
341 Ok(())
342 }
343
344 fn background_job(job: &mut Job) -> Result<(), io::Error> {
346 let pgid = job.pgid();
347 if let JobStatus::Stopped = job.status() {
348 let ppgid = unistd::Pid::from_raw(-pgid.0);
349 signal::kill(ppgid, Signal::SIGCONT)?;
350 job.mark_running();
351 }
352 Ok(())
353 }
354
355 fn dup2_fd(src_fd: UnixFileDesc, dst_fd: UnixFileDesc) -> Result<UnixFileDesc, io::Error> {
357 Ok(UnixFileDesc(unsafe {
358 cvt(libc::dup2(src_fd.0, dst_fd.0))?
359 }))
360 }
361
362 fn getpid() -> UnixPid {
364 UnixPid(unistd::getpid().into())
365 }
366
367 fn gethostname() -> Option<OsString> {
369 nix::unistd::gethostname().ok()
370 }
371
372 fn current_uid() -> u32 {
374 Uid::current().into()
375 }
376
377 fn effective_uid() -> u32 {
379 Uid::effective().into()
380 }
381
382 fn is_tty(terminal: UnixFileDesc) -> bool {
383 unistd::isatty(terminal.0).unwrap_or(false)
384 }
385
386 fn set_rlimit(rlimit: RLimit, values: RLimitVals) -> Result<(), io::Error> {
387 let val = libc::rlimit {
388 rlim_cur: values.current,
389 rlim_max: values.max,
390 };
391 unsafe {
392 cvt(libc::setrlimit(
393 rlimit_to_c(rlimit)?,
394 &val as *const libc::rlimit,
395 ))?;
396 }
397 Ok(())
398 }
399
400 fn get_rlimit(rlimit: RLimit) -> Result<RLimitVals, io::Error> {
401 let mut val = libc::rlimit {
402 rlim_cur: 0,
403 rlim_max: 0,
404 };
405 unsafe {
406 cvt(libc::getrlimit(
407 rlimit_to_c(rlimit)?,
408 &mut val as *mut libc::rlimit,
409 ))?;
410 }
411 Ok(RLimitVals {
412 current: val.rlim_cur,
413 max: val.rlim_max,
414 })
415 }
416
417 fn merge_and_set_umask(current_umask: mode_t, mask_string: &str) -> Result<mode_t, Error> {
418 umask::merge_and_set_umask(current_umask, mask_string)
419 }
420
421 fn get_and_clear_umask() -> mode_t {
422 umask::get_and_clear_umask()
423 }
424
425 fn set_umask(umask: mode_t) -> Result<(), Error> {
426 umask::set_umask(umask)
427 }
428}
429
430#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
432pub struct UnixPid(i32);
433
434impl Display for UnixPid {
435 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
436 write!(f, "{}", self.0)
437 }
438}
439
440impl TryFrom<UnixPid> for i32 {
441 type Error = Infallible;
442
443 fn try_from(value: UnixPid) -> Result<Self, Self::Error> {
444 Ok(value.0)
445 }
446}
447
448#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
450pub struct UnixFileDesc(RawFd);
451
452impl FromStr for UnixFileDesc {
453 type Err = io::Error;
454
455 fn from_str(fd_str: &str) -> Result<Self, Self::Err> {
456 match fd_str.parse::<i32>() {
457 Ok(fd) => Ok(Self(fd)),
458 Err(err) => Err(io::Error::other(err)),
459 }
460 }
461}
462
463impl Display for UnixFileDesc {
464 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
465 write!(f, "{}", self.0)
466 }
467}
468impl From<File> for UnixFileDesc {
469 fn from(file: File) -> Self {
470 Self(file.into_raw_fd())
471 }
472}
473impl FromFileDesc for File {
474 unsafe fn from_file_desc(fd: UnixFileDesc) -> Self {
475 unsafe { File::from_raw_fd(fd.0) }
476 }
477}
478
479impl AsFd for UnixFileDesc {
480 fn as_fd(&self) -> BorrowedFd<'_> {
481 unsafe { BorrowedFd::borrow_raw(self.0) }
482 }
483}
484
485#[derive(Clone, Debug, Eq, PartialEq)]
487pub struct UnixTermSettings(termios::Termios);
488
489pub const STDIN_FILENO: UnixFileDesc = UnixFileDesc(0);
490pub const STDOUT_FILENO: UnixFileDesc = UnixFileDesc(1);
491pub const STDERR_FILENO: UnixFileDesc = UnixFileDesc(2);
492
493trait IsMinusOne {
494 fn is_minus_one(&self) -> bool;
495}
496
497macro_rules! impl_is_minus_one {
498 ($($t:ident)*) => ($(impl IsMinusOne for $t {
499 fn is_minus_one(&self) -> bool {
500 *self == -1
501 }
502 })*)
503}
504
505impl_is_minus_one! { i8 i16 i32 i64 isize }
506
507fn cvt<T: IsMinusOne>(t: T) -> Result<T, io::Error> {
508 if t.is_minus_one() {
509 Err(io::Error::last_os_error())
510 } else {
511 Ok(t)
512 }
513}
514
515pub type OsSignal = i32;
517
518fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
519 CString::new(s.as_bytes()).unwrap_or_else(|_e| {
520 *saw_nul = true;
521 CString::new("<string-with-nul>").unwrap()
522 })
523}
524
525fn arg_into_args<S: AsRef<OsStr>>(
526 arg: S,
527 argv: &mut Vec<*const c_char>,
528 args_t: &mut Vec<CString>,
529 saw_nul: &mut bool,
530) {
531 let arg = os2c(arg.as_ref(), saw_nul);
534 argv[args_t.len()] = arg.as_ptr();
535 argv.push(ptr::null());
536 args_t.push(arg);
539}
540
541fn exec<'arg, I, P>(program: P, args: I, jobs: &mut Jobs) -> Result<(), io::Error>
542where
543 I: IntoIterator<Item = &'arg Arg>,
544 P: AsRef<OsStr>,
545{
546 let mut saw_nul = false;
547 let program = os2c(program.as_ref(), &mut saw_nul);
548 let mut argv = vec![program.as_ptr(), ptr::null()];
549 let mut args_t = vec![program.clone()];
550 for arg in args {
551 arg_into_args(arg.resolve_arg(jobs)?, &mut argv, &mut args_t, &mut saw_nul);
552 }
553
554 unsafe {
555 signal::signal(Signal::SIGINT, SigHandler::SigDfl).unwrap();
558 signal::signal(Signal::SIGHUP, SigHandler::SigDfl).unwrap();
559 signal::signal(Signal::SIGTERM, SigHandler::SigDfl).unwrap();
560 signal::signal(Signal::SIGQUIT, SigHandler::SigDfl).unwrap();
561 signal::signal(Signal::SIGTSTP, SigHandler::SigDfl).unwrap();
562 signal::signal(Signal::SIGTTIN, SigHandler::SigDfl).unwrap();
563 signal::signal(Signal::SIGTTOU, SigHandler::SigDfl).unwrap();
564 signal::signal(Signal::SIGCHLD, SigHandler::SigDfl).unwrap();
565 libc::execvp(program.as_ptr(), argv.as_ptr());
566 }
567 Err(io::Error::last_os_error())
568}
569
570const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
571
572unsafe fn send_error_to_parent(output: i32, err: io::Error) {
577 let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
578 let errno = errno.to_be_bytes();
579 let bytes = [
580 errno[0],
581 errno[1],
582 errno[2],
583 errno[3],
584 CLOEXEC_MSG_FOOTER[0],
585 CLOEXEC_MSG_FOOTER[1],
586 CLOEXEC_MSG_FOOTER[2],
587 CLOEXEC_MSG_FOOTER[3],
588 ];
589 if let Err(e) = unsafe { File::from_raw_fd(output) }.write_all(&bytes) {
593 eprintln!("Error on child reporting error (err) to parent: {e}");
594 }
595 unsafe {
596 libc::_exit(1);
597 }
598}
599
600unsafe fn close_extra_fds(opened: &HashSet<UnixFileDesc>) {
601 let fd_max = unsafe { libc::sysconf(libc::_SC_OPEN_MAX) } as i32;
602 for fd in 3..fd_max {
603 if !opened.contains(&UnixFileDesc(fd)) {
604 unsafe {
605 libc::close(fd);
606 }
607 }
608 }
609}
610
611fn setup_group_term(pid: UnixPid, job: &Job) {
614 let pid = unistd::Pid::from_raw(pid.0);
615 let pgid = if job.is_empty() {
616 pid
617 } else {
618 unistd::Pid::from_raw(job.pgid().0)
619 };
620 if job.interactive() {
621 if let Err(_err) = unistd::setpgid(pid, pgid) {
622 }
624 if let Err(_err) = unistd::tcsetpgrp(UnixFileDesc(libc::STDIN_FILENO), pgid) {
626 }
628 } else {
629 if let Err(_err) = unistd::setpgid(pid, unistd::Pid::from_raw(job.shell_pid().0)) {
631 }
633 }
634}
635
636fn rlimit_to_c(rlimit: RLimit) -> Result<RlimitResource, io::Error> {
637 match rlimit {
638 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
639 RLimit::SocketBufferSize => Ok(libc::RLIMIT_SBSIZE),
640 #[cfg(not(any(target_os = "freebsd", target_os = "dragonfly")))]
641 RLimit::SocketBufferSize => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
642 RLimit::CoreSize => Ok(libc::RLIMIT_CORE),
643 RLimit::DataSize => Ok(libc::RLIMIT_DATA),
644 #[cfg(any(target_os = "android", target_os = "linux"))]
645 RLimit::Nice => Ok(libc::RLIMIT_NICE),
646 #[cfg(not(any(target_os = "android", target_os = "linux")))]
647 RLimit::Nice => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
648 RLimit::FileSize => Ok(libc::RLIMIT_FSIZE),
649 #[cfg(any(target_os = "android", target_os = "linux"))]
650 RLimit::SigPending => Ok(libc::RLIMIT_SIGPENDING),
651 #[cfg(not(any(target_os = "android", target_os = "linux")))]
652 RLimit::SigPending => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
653 #[cfg(target_os = "freebsd")]
654 RLimit::KQueues => Ok(libc::RLIMIT_KQUEUES),
655 #[cfg(not(target_os = "freebsd"))]
656 RLimit::KQueues => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
657 #[cfg(any(
658 target_os = "android",
659 target_os = "freebsd",
660 target_os = "openbsd",
661 target_os = "linux",
662 target_os = "netbsd"
663 ))]
664 RLimit::MemLock => Ok(libc::RLIMIT_MEMLOCK),
665 #[cfg(not(any(
666 target_os = "android",
667 target_os = "freebsd",
668 target_os = "openbsd",
669 target_os = "linux",
670 target_os = "netbsd"
671 )))]
672 RLimit::MemLock => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
673 #[cfg(any(
674 target_os = "android",
675 target_os = "freebsd",
676 target_os = "openbsd",
677 target_os = "linux",
678 target_os = "netbsd"
679 ))]
680 RLimit::RSS => Ok(libc::RLIMIT_RSS),
681 #[cfg(not(any(
682 target_os = "android",
683 target_os = "freebsd",
684 target_os = "openbsd",
685 target_os = "linux",
686 target_os = "netbsd"
687 )))]
688 RLimit::RSS => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
689 RLimit::MaxFiles => Ok(libc::RLIMIT_NOFILE),
690 #[cfg(any(target_os = "android", target_os = "linux"))]
692 RLimit::MessageQueueByte => Ok(libc::RLIMIT_MSGQUEUE),
693 #[cfg(not(any(target_os = "android", target_os = "linux")))]
694 RLimit::MessageQueueByte => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
695 #[cfg(any(target_os = "android", target_os = "linux"))]
696 RLimit::RealTimePriority => Ok(libc::RLIMIT_RTPRIO),
697 #[cfg(not(any(target_os = "android", target_os = "linux")))]
698 RLimit::RealTimePriority => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
699 RLimit::StackSize => Ok(libc::RLIMIT_STACK),
700 RLimit::CpuTime => Ok(libc::RLIMIT_CPU),
701 #[cfg(any(
702 target_os = "android",
703 target_os = "freebsd",
704 target_os = "openbsd",
705 target_os = "linux",
706 target_os = "netbsd"
707 ))]
708 RLimit::MaxProcs => Ok(libc::RLIMIT_NPROC),
709 #[cfg(not(any(
710 target_os = "android",
711 target_os = "freebsd",
712 target_os = "openbsd",
713 target_os = "linux",
714 target_os = "netbsd"
715 )))]
716 RLimit::MaxProcs => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
717 #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
718 RLimit::MaxMemory => Ok(libc::RLIMIT_AS),
719 #[cfg(target_os = "freebsd")]
720 RLimit::MaxMemory => Ok(libc::RLIMIT_VMEM),
721 #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
722 RLimit::MaxMemory => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
723 #[cfg(any(target_os = "android", target_os = "linux"))]
724 RLimit::MaxFileLocks => Ok(libc::RLIMIT_LOCKS),
725 #[cfg(not(any(target_os = "android", target_os = "linux")))]
726 RLimit::MaxFileLocks => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
727 #[cfg(target_os = "freebsd")]
728 RLimit::MaxPtty => Ok(libc::RLIMIT_NPTS),
729 #[cfg(not(target_os = "freebsd"))]
730 RLimit::MaxPtty => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
731 #[cfg(target_os = "linux")]
732 RLimit::MaxRealTime => Ok(libc::RLIMIT_RTTIME),
733 #[cfg(not(any(target_os = "linux")))]
734 RLimit::MaxRealTime => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
735 #[cfg(target_os = "linux")]
736 RLimit::MaxThreads => Ok(libc::RLIMIT_NPROC),
737 #[cfg(not(any(target_os = "linux")))]
738 RLimit::MaxThreads => Err(Error::new(ErrorKind::Unsupported, "not on platform")),
739 }
740}