shell/platform/
unix.rs

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