shell/
platform.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
mod unix;

use crate::command_data::{CommandWithArgs, Run};
use crate::jobs::{Job, Jobs};
pub use crate::platform::unix::*;
use std::ffi::OsString;
use std::io;
use std::io::ErrorKind;

/// Abstraction for a "platform" (for instance Unix or Windows).
pub trait Platform {
    type Pid;
    type FileDesc;
    type TermSettings;

    /// If terminal is a terminal then get it's term settings.
    fn get_term_settings(terminal: FileDesc) -> Result<TermSettings, io::Error>;
    /// Restore terminal settings and put the shell back into the foreground.
    fn restore_terminal(term_settings: &TermSettings, shell_pid: Pid) -> Result<(), io::Error>;
    /// Put terminal in the foreground, loop until this succeeds.
    /// Used during shell startup.
    fn terminal_foreground(terminal: FileDesc);
    /// Puts the running process into its own process group.
    /// Do this during shell initialization.
    fn set_self_pgroup() -> Result<(), io::Error>;
    /// Grab control of terminal.
    /// Used for shell startup.
    fn grab_terminal(terminal: FileDesc) -> Result<(), io::Error>;
    /// Return the input and output file descriptors for an anonymous pipe.
    fn anon_pipe() -> Result<(FileDesc, FileDesc), io::Error>;
    /// Close a raw file descriptor.
    fn close_fd(fd: FileDesc) -> Result<(), io::Error>;

    fn fork_run(run: &Run, job: &mut Job, jobs: &mut Jobs) -> Result<(), io::Error>;
    fn fork_exec(
        command: &CommandWithArgs,
        job: &mut Job,
        jobs: &mut Jobs,
    ) -> Result<(), io::Error>;
    fn try_wait_pid(pid: Pid, job: &mut Job) -> (bool, Option<i32>);
    fn wait_job(job: &mut Job) -> Option<i32>;
    /// Move the job for job_num to te foreground.
    fn foreground_job(job: &mut Job, term_settings: &Option<TermSettings>)
        -> Result<(), io::Error>;
    /// Move the job for job_num to te background and running (start a stopped job in the background).
    fn background_job(job: &mut Job) -> Result<(), io::Error>;
    /// Duplicate a raw file descriptor to another file descriptor.
    fn dup2_fd(src_fd: FileDesc, dst_fd: FileDesc) -> Result<FileDesc, io::Error>;
    /// Get the current PID.
    fn getpid() -> Pid;
    /// Get the current machines hostname if available.
    fn gethostname() -> Option<OsString>;
    /// Get current UID of the process.
    fn current_uid() -> u32;
    /// Get effective UID of the process.
    fn effective_uid() -> u32;
    fn is_tty(terminal: FileDesc) -> bool;

    fn set_rlimit(rlimit: RLimit, values: RLimitVals) -> Result<(), io::Error>;
    fn get_rlimit(rlimit: RLimit) -> Result<RLimitVals, io::Error>;

    // umask operations, these can be NoOps if platform does not have umasks.
    /// If mask_string is a mode string then merge it with umask and set the current umask.
    /// If mask_string is an int then treat it as a umask and set the current umask (no merge)).
    fn merge_and_set_umask(current_umask: mode_t, mask_string: &str) -> Result<mode_t, io::Error>;
    /// Clears the current umask and returns the previous umask.
    fn get_and_clear_umask() -> mode_t;
    /// Set current umask to umask.
    fn set_umask(umask: mode_t) -> Result<(), io::Error>;
    /// Convert mode to the octal string umask format.
    fn to_octal_string(mode: mode_t) -> Result<String, io::Error> {
        let mut octal = format!("{:o}", mode);
        if octal.len() < 4 {
            while octal.len() < 4 {
                octal = "0".to_owned() + &octal;
            }
            Ok(octal)
        } else {
            let msg = format!("encountered invalid umask {octal}.");
            Err(io::Error::new(ErrorKind::Other, msg))
        }
    }
}

pub type Pid = <Sys as Platform>::Pid;
pub type FileDesc = <Sys as Platform>::FileDesc;
pub type TermSettings = <Sys as Platform>::TermSettings;

/// Trait to turn a UnixFileDesc into another object (like File).
pub trait FromFileDesc {
    /// Constructs a new instance of Self from the given UnixFileDesc.
    /// # Safety
    /// The fd passed in must be a valid and open file descriptor.
    unsafe fn from_file_desc(fd: FileDesc) -> Self;
}

/// Holder for setting or getting rlimits (get/setrlimit).
#[derive(Copy, Clone, Debug)]
pub struct RLimitVals {
    pub current: u64,
    pub max: u64,
}

/// Abstraction over the various rlimits.
pub enum RLimit {
    ///-b: The maximum socket buffer size. RLIMIT_SBSIZE (freebsd, dragonfly)
    SocketBufferSize,
    /// -c; The maximum size of core files created. RLIMIT_CORE
    /// The maximum size core file that this process can create. If the process terminates and would
    /// dump a core file larger than this, then no core file is created. So setting this limit to zero
    /// prevents core files from ever being created.
    CoreSize,
    /// -d: The maximum size of a process’s data segment. RLIMIT_DATA
    /// The maximum size of data memory for the process. If the process tries to allocate data memory
    /// beyond this amount, the allocation function fails.
    DataSize,
    /// -e: The maximum scheduling priority ("nice"). RLIMIT_NICE
    /// Specifies a ceiling to which the process's nice value can be raised using setpriority(2) or
    /// nice(2). The actual ceiling for the nice value is calculated as 20 - rlim_cur. (This
    /// strangeness occurs because negative numbers cannot be specified as resource limit values,
    /// since they typically have special meanings. For example, RLIM_INFINITY typically is the
    /// same as -1.)
    Nice,
    /// -f: The maximum size of files written by the shell and its children. RLIMIT_FSIZE
    /// The maximum size of file the process can create. Trying to write a larger file causes a signal: SIGXFSZ.
    FileSize,
    /// -i: The maximum number of pending signals.RLIMIT_SIGPENDING
    /// Specifies the limit on the number of signals that may be queued for the real user ID of the
    /// calling process. Both standard and real-time signals are counted for the purpose of checking
    /// this limit. However, the limit is only enforced for sigqueue(3); it is always possible to use
    /// kill(2) to queue one instance of any of the signals that are not already queued to the process.
    SigPending,
    /// -k: The maximum number of kqueues that may be allocated. RLIMIT_KQUEUES (freebsd)
    KQueues,
    /// -l: The maximum size that may be locked into memory. RLIMIT_MEMLOCK,
    /// The maximum size (in bytes) which a process may lock into memory
    /// using the mlock(2) system call.
    MemLock,
    /// -m: The maximum resident set size (many systems do not honor this limit). RLIMIT_RSS
    /// When there is memory pressure and swap is available, prioritize
    /// eviction of a process' resident pages beyond this amount (in bytes).
    RSS,
    /// -n: The maximum number of open file descriptors (most systems do not allow this value to be set). RLIMIT_NOFILE
    MaxFiles,
    // -p: The pipe buffer size.
    //PipeBufferSize,
    /// -q: The maximum number of bytes in POSIX message queues. RLIMIT_MSGQUEUE
    /// A limit on the number of bytes that can be allocated for POSIX
    /// message queues  for  the  real  user  ID  of  the  calling process.
    MessageQueueByte,
    /// -r: The maximum real-time scheduling priority. RLIMIT_RTPRIO
    RealTimePriority,
    /// -s: The maximum stack size. RLIMIT_STACK
    StackSize,
    /// -t: The maximum amount of cpu time in seconds. RLIMIT_CPU
    CpuTime,
    /// -u: The maximum number of processes available to a single user. RLIMIT_NPROC
    MaxProcs,
    /// -v: The maximum amount of virtual memory available to the shell, and, on some systems, to its children. RLIMIT_AS
    MaxMemory,
    /// -x: The maximum number of file locks. RLIMIT_LOCKS
    MaxFileLocks,
    /// -P: The maximum number of pseudoterminals. RLIMIT_NPTS
    MaxPtty,
    /// -R: The maximum time a real-time process can run before blocking, in microseconds. RLIMIT_RTTIME
    MaxRealTime,
    /// -T: The maximum number of threads. RLIMIT_NPROC (Same as MaxProcs- Linux)
    MaxThreads,
}