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#[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 fn get_term_settings(terminal: UnixFileDesc) -> Result<UnixTermSettings, io::Error> {
40 Ok(UnixTermSettings(termios::tcgetattr(terminal)?))
41 }
42
43 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 unistd::tcsetpgrp(stdin, unistd::Pid::from_raw(shell_pid.0))?;
52 Ok(())
53 }
54
55 fn terminal_foreground(terminal: UnixFileDesc) {
58 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 fn set_self_pgroup() -> Result<(), io::Error> {
71 let pgid = unistd::getpid();
73 if let Err(err) = unistd::setpgid(pgid, pgid) {
74 match err {
75 nix::errno::Errno::EPERM => { }
76 _ => return Err(err.into()),
77 }
78 }
79 Ok(())
80 }
81
82 fn grab_terminal(terminal: UnixFileDesc) -> Result<(), io::Error> {
85 let pgid = unistd::getpid();
87 Ok(unistd::tcsetpgrp(terminal, pgid)?)
88 }
89
90 fn anon_pipe() -> Result<(UnixFileDesc, UnixFileDesc), io::Error> {
92 let mut fds = [0; 2];
94
95 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 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 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), }
189 setup_group_term(UnixPid(unistd::getpid().into()), job);
190
191 let err = exec(&program, args, jobs);
192 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 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 let mut input = unsafe { File::from_raw_fd(input) };
216 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 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 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 (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 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 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 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 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 fn getpid() -> UnixPid {
363 UnixPid(unistd::getpid().into())
364 }
365
366 fn gethostname() -> Option<OsString> {
368 nix::unistd::gethostname().ok()
369 }
370
371 fn current_uid() -> u32 {
373 Uid::current().into()
374 }
375
376 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#[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#[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#[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
512pub 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 let arg = os2c(arg.as_ref(), saw_nul);
531 argv[args_t.len()] = arg.as_ptr();
532 argv.push(ptr::null());
533 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 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
569unsafe 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 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
608fn 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 }
621 if let Err(_err) = unistd::tcsetpgrp(UnixFileDesc(libc::STDIN_FILENO), pgid) {
623 }
625 } else {
626 if let Err(_err) = unistd::setpgid(pid, unistd::Pid::from_raw(job.shell_pid().0)) {
628 }
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 #[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}