shell/platform/
unix.rs

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// macos does not define __rlimit_resource_t...
28#[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    /// If terminal is a terminal then get it's term settings.
40    fn get_term_settings(terminal: UnixFileDesc) -> Result<UnixTermSettings, io::Error> {
41        Ok(UnixTermSettings(termios::tcgetattr(terminal)?))
42    }
43
44    /// Restore terminal settings and put the shell back into the foreground.
45    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        // XXX TODO- be more specific if the next line fails (ie only turn off tty if that is the error)?
52        unistd::tcsetpgrp(stdin, unistd::Pid::from_raw(shell_pid.0))?;
53        Ok(())
54    }
55
56    /// Put terminal in the foreground, loop until this succeeds.
57    /// Used during shell startup.
58    fn terminal_foreground(terminal: UnixFileDesc) {
59        /* Loop until we are in the foreground.  */
60        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    /// Puts the running process into its own process group.
70    /// Do this during shell initialization.
71    fn set_self_pgroup() -> Result<(), io::Error> {
72        /* Put ourselves in our own process group.  */
73        let pgid = unistd::getpid();
74        if let Err(err) = unistd::setpgid(pgid, pgid) {
75            match err {
76                nix::errno::Errno::EPERM => { /* ignore */ }
77                _ => return Err(err.into()),
78            }
79        }
80        Ok(())
81    }
82
83    /// Grab control of terminal.
84    /// Used for shell startup.
85    fn grab_terminal(terminal: UnixFileDesc) -> Result<(), io::Error> {
86        /* Grab control of the terminal.  */
87        let pgid = unistd::getpid();
88        Ok(unistd::tcsetpgrp(terminal, pgid)?)
89    }
90
91    /// Return the input and output file descriptors for an anonymous pipe.
92    fn anon_pipe() -> Result<(UnixFileDesc, UnixFileDesc), io::Error> {
93        // Adapted from sys/unix/pipe.rs in std lib.
94        let mut fds = [0; 2];
95
96        // The only known way right now to create atomically set the CLOEXEC flag is
97        // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9
98        // and musl 0.9.3, and some other targets also have it.
99        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    /// Close a raw Unix file descriptor.
122    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        // Close any internal FDs (from pipes for instance) in this process.
153        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), // This call won't return.
189                    }
190                    setup_group_term(UnixPid(unistd::getpid().into()), job);
191
192                    let err = exec(&program, args, jobs);
193                    // This call won't return.
194                    // If exec returns then it is an error so can unwrap it...
195                    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            // Close any FD for child stdio we don't care about.
205            // This means any FDs we opened for pipes etc.
206            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        // Wait for a status message or closed pipe from the child.
216        let mut input = unsafe { File::from_raw_fd(input) };
217        // loop to handle EINTR
218        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                    //assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
233                    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                    // pipe I/O up to PIPE_BUF bytes should be atomic
241                    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                // Does not exist.
254                (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    /// Move the job for job_num to te foreground.
317    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            // The job is running, so no sig cont needed...
335            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    /// Move the job for job_num to te background and running (start a stopped job in the background).
345    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    /// Duplicate a raw file descriptor to another file descriptor.
356    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    /// Get the current PID.
363    fn getpid() -> UnixPid {
364        UnixPid(unistd::getpid().into())
365    }
366
367    /// Get the current machines hostname if available.
368    fn gethostname() -> Option<OsString> {
369        nix::unistd::gethostname().ok()
370    }
371
372    /// Get current UID of the process.
373    fn current_uid() -> u32 {
374        Uid::current().into()
375    }
376
377    /// Get effective UID of the process.
378    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/// Process ID for the target platform.
431#[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/// Raw file descriptor for the target platform.
449#[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/// Saved terminal settings.
486#[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
515/// An OS signal.
516pub 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    // Overwrite the trailing NULL pointer in `argv` and then add a new null
532    // pointer.
533    let arg = os2c(arg.as_ref(), saw_nul);
534    argv[args_t.len()] = arg.as_ptr();
535    argv.push(ptr::null());
536    // Also make sure we keep track of the owned value to schedule a
537    // destructor for this memory.
538    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        // XXX TODO, do better with these unwraps.
556        // Set the handling for job control signals back to the default.
557        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
572/// Send an error code back tot he parent from a child process indicating it failed to fork.
573/// This function exits the child when done (i.e. never returns).
574/// ONLY call on error in a child process.
575/// SAFETY: this is doing "unsafe" libc/process stuff...
576unsafe 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    // pipe I/O up to PIPE_BUF bytes should be atomic, and then
590    // we want to be sure we *don't* run at_exit destructors as
591    // we're being torn down regardless
592    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
611/// Setup the process group for the current pid as well term if interactive.
612/// Call from both the parent and child proc to avoid race conditions.
613fn 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            // Ignore, do in parent and child.
623        }
624        // XXXX only if foreground
625        if let Err(_err) = unistd::tcsetpgrp(UnixFileDesc(libc::STDIN_FILENO), pgid) {
626            // Ignore, do in parent and child.
627        }
628    } else {
629        // If not interactive then put all procs into the shells process group.
630        if let Err(_err) = unistd::setpgid(pid, unistd::Pid::from_raw(job.shell_pid().0)) {
631            // Ignore, do in parent and child.
632        }
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        //RLimit::PipeBufferSize => {}
691        #[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}