Skip to main content

linux_api/
sched.rs

1use linux_syscall::{Result as LinuxSyscallResult, Result64};
2use num_enum::{IntoPrimitive, TryFromPrimitive};
3
4use crate::errno::Errno;
5use crate::ldt::linux_user_desc;
6use crate::posix_types::{Pid, kernel_pid_t};
7use crate::signal::Signal;
8use crate::{bindings, const_conversions};
9
10#[allow(non_camel_case_types)]
11#[repr(i32)]
12#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
13pub enum Sched {
14    SCHED_NORMAL = const_conversions::i32_from_u32(bindings::LINUX_SCHED_NORMAL),
15    SCHED_FIFO = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FIFO),
16    SCHED_RR = const_conversions::i32_from_u32(bindings::LINUX_SCHED_RR),
17    SCHED_BATCH = const_conversions::i32_from_u32(bindings::LINUX_SCHED_BATCH),
18    SCHED_IDLE = const_conversions::i32_from_u32(bindings::LINUX_SCHED_IDLE),
19    SCHED_DEADLINE = const_conversions::i32_from_u32(bindings::LINUX_SCHED_DEADLINE),
20    SCHED_EXT = const_conversions::i32_from_u32(bindings::LINUX_SCHED_EXT),
21}
22
23pub const SCHED_RESET_ON_FORK: i32 =
24    const_conversions::i32_from_u32(bindings::LINUX_SCHED_RESET_ON_FORK);
25
26bitflags::bitflags! {
27    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
28    pub struct SchedFlags: i32 {
29        const SCHED_FLAG_RESET_ON_FORK = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FLAG_RESET_ON_FORK);
30        const SCHED_FLAG_RECLAIM = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FLAG_RECLAIM);
31        const SCHED_FLAG_DL_OVERRUN = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FLAG_DL_OVERRUN);
32        const SCHED_FLAG_KEEP_POLICY = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FLAG_KEEP_POLICY);
33        const SCHED_FLAG_KEEP_PARAMS = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FLAG_KEEP_PARAMS);
34        const SCHED_FLAG_UTIL_CLAMP_MIN = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FLAG_UTIL_CLAMP_MIN);
35        const SCHED_FLAG_UTIL_CLAMP_MAX = const_conversions::i32_from_u32(bindings::LINUX_SCHED_FLAG_UTIL_CLAMP_MAX);
36    }
37}
38
39/// This is the ABI-stable kernel version of `struct sched_param`, as for
40/// syscall `sched_getparam`.
41#[allow(non_camel_case_types)]
42pub type sched_attr = bindings::linux_sched_attr;
43
44bitflags::bitflags! {
45    /// The flags passed to the `clone` and `clone3` syscalls.
46    /// While `clone` is documented as taking an i32 parameter for flags,
47    /// in `clone3` its a u64. Promote to u64 throughout.
48    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
49    pub struct CloneFlags: u64 {
50        const CLONE_CLEAR_SIGHAND = bindings::LINUX_CLONE_CLEAR_SIGHAND;
51        const CLONE_INTO_CGROUP = bindings::LINUX_CLONE_INTO_CGROUP;
52        const CLONE_NEWTIME = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWTIME);
53        const CLONE_VM = const_conversions::u64_from_u32(bindings::LINUX_CLONE_VM);
54        const CLONE_FS = const_conversions::u64_from_u32(bindings::LINUX_CLONE_FS);
55        const CLONE_FILES = const_conversions::u64_from_u32(bindings::LINUX_CLONE_FILES);
56        const CLONE_SIGHAND = const_conversions::u64_from_u32(bindings::LINUX_CLONE_SIGHAND);
57        const CLONE_PIDFD = const_conversions::u64_from_u32(bindings::LINUX_CLONE_PIDFD);
58        const CLONE_PTRACE = const_conversions::u64_from_u32(bindings::LINUX_CLONE_PTRACE);
59        const CLONE_VFORK = const_conversions::u64_from_u32(bindings::LINUX_CLONE_VFORK);
60        const CLONE_PARENT = const_conversions::u64_from_u32(bindings::LINUX_CLONE_PARENT);
61        const CLONE_THREAD = const_conversions::u64_from_u32(bindings::LINUX_CLONE_THREAD);
62        const CLONE_NEWNS = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWNS);
63        const CLONE_SYSVSEM = const_conversions::u64_from_u32(bindings::LINUX_CLONE_SYSVSEM);
64        const CLONE_SETTLS = const_conversions::u64_from_u32(bindings::LINUX_CLONE_SETTLS);
65        const CLONE_PARENT_SETTID = const_conversions::u64_from_u32(bindings::LINUX_CLONE_PARENT_SETTID);
66        const CLONE_CHILD_CLEARTID = const_conversions::u64_from_u32(bindings::LINUX_CLONE_CHILD_CLEARTID);
67        const CLONE_DETACHED = const_conversions::u64_from_u32(bindings::LINUX_CLONE_DETACHED);
68        const CLONE_UNTRACED = const_conversions::u64_from_u32(bindings::LINUX_CLONE_UNTRACED);
69        const CLONE_CHILD_SETTID = const_conversions::u64_from_u32(bindings::LINUX_CLONE_CHILD_SETTID);
70        const CLONE_NEWCGROUP = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWCGROUP);
71        const CLONE_NEWUTS = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWUTS);
72        const CLONE_NEWIPC = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWIPC);
73        const CLONE_NEWUSER = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWUSER);
74        const CLONE_NEWPID = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWPID);
75        const CLONE_NEWNET = const_conversions::u64_from_u32(bindings::LINUX_CLONE_NEWNET);
76        const CLONE_IO = const_conversions::u64_from_u32(bindings::LINUX_CLONE_IO);
77    }
78}
79
80pub use bindings::linux_clone_args;
81#[allow(non_camel_case_types)]
82pub type clone_args = linux_clone_args;
83
84#[allow(clippy::derivable_impls)]
85impl Default for clone_args {
86    fn default() -> Self {
87        Self {
88            flags: Default::default(),
89            pidfd: Default::default(),
90            child_tid: Default::default(),
91            parent_tid: Default::default(),
92            exit_signal: Default::default(),
93            stack: Default::default(),
94            stack_size: Default::default(),
95            tls: Default::default(),
96            set_tid: Default::default(),
97            set_tid_size: Default::default(),
98            cgroup: Default::default(),
99        }
100    }
101}
102
103impl clone_args {
104    #[inline]
105    pub fn with_flags(mut self, flags: CloneFlags) -> Self {
106        self.flags = flags.bits();
107        self
108    }
109
110    #[inline]
111    pub fn with_exit_signal(mut self, exit_signal: Option<Signal>) -> Self {
112        self.exit_signal = u64::try_from(Signal::as_raw(exit_signal)).unwrap();
113        self
114    }
115}
116
117unsafe impl shadow_pod::Pod for clone_args {}
118unsafe impl shadow_pod::Pod for sched_attr {}
119
120/// The "dumpable" state, as manipulated via the prctl operations `PR_SET_DUMPABLE` and
121/// `PR_GET_DUMPABLE`.
122#[derive(Copy, Clone, PartialEq, Eq)]
123pub struct SuidDump(i32);
124
125impl SuidDump {
126    pub const SUID_DUMP_DISABLE: Self = Self(const_conversions::i32_from_u32(
127        bindings::LINUX_SUID_DUMP_DISABLE,
128    ));
129    pub const SUID_DUMP_USER: Self = Self(const_conversions::i32_from_u32(
130        bindings::LINUX_SUID_DUMP_USER,
131    ));
132    pub const SUID_DUMP_ROOT: Self = Self(const_conversions::i32_from_u32(
133        bindings::LINUX_SUID_DUMP_ROOT,
134    ));
135    // NOTE: add new entries to `to_str` below
136
137    pub const fn new(val: i32) -> Self {
138        Self(val)
139    }
140
141    pub const fn val(&self) -> i32 {
142        self.0
143    }
144
145    pub const fn to_str(&self) -> Option<&'static str> {
146        match *self {
147            Self::SUID_DUMP_DISABLE => Some("SUID_DUMP_DISABLE"),
148            Self::SUID_DUMP_USER => Some("SUID_DUMP_USER"),
149            Self::SUID_DUMP_ROOT => Some("SUID_DUMP_ROOT"),
150            _ => None,
151        }
152    }
153}
154
155impl core::fmt::Display for SuidDump {
156    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
157        match self.to_str() {
158            Some(s) => formatter.write_str(s),
159            None => write!(formatter, "(unknown dumpable option {})", self.0),
160        }
161    }
162}
163
164impl core::fmt::Debug for SuidDump {
165    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
166        match self.to_str() {
167            Some(s) => write!(formatter, "SuidDump::{s}"),
168            None => write!(formatter, "SuidDump::<{}>", self.0),
169        }
170    }
171}
172
173pub fn sched_yield() -> Result<(), Errno> {
174    unsafe { linux_syscall::syscall!(linux_syscall::SYS_sched_yield) }
175        .check()
176        .map_err(Errno::from)
177}
178
179/// # Safety
180///
181/// Too many requirements to list here. See `clone(2)`.
182pub unsafe fn clone_raw(
183    flags: core::ffi::c_ulong,
184    stack: core::ffi::c_ulong,
185    parent_tid: *mut core::ffi::c_int,
186    child_tid: *mut core::ffi::c_int,
187    tls: core::ffi::c_ulong,
188) -> Result<core::ffi::c_long, Errno> {
189    unsafe {
190        linux_syscall::syscall!(
191            linux_syscall::SYS_clone,
192            flags,
193            stack,
194            parent_tid,
195            child_tid,
196            tls
197        )
198    }
199    .try_i64()
200    .map_err(Errno::from)
201}
202
203/// # Safety
204///
205/// Too many requirements to list here. See `clone(2)`.
206pub unsafe fn clone3_raw(args: *const clone_args, size: usize) -> Result<core::ffi::c_long, Errno> {
207    unsafe { linux_syscall::syscall!(linux_syscall::SYS_clone3, args, size,) }
208        .try_i64()
209        .map_err(Errno::from)
210}
211
212pub enum CloneResult {
213    CallerIsChild,
214    // Caller is the parent; child has the given pid
215    CallerIsParent(Pid),
216}
217
218/// # Safety
219///
220/// Too many requirements to list here. See `clone(2)`.
221pub unsafe fn clone(
222    flags: CloneFlags,
223    exit_signal: Option<Signal>,
224    stack: *mut core::ffi::c_void,
225    parent_tid: *mut kernel_pid_t,
226    child_tid: *mut kernel_pid_t,
227    tls: *mut linux_user_desc,
228) -> Result<CloneResult, Errno> {
229    unsafe {
230        clone_raw(
231            flags.bits() | u64::try_from(Signal::as_raw(exit_signal)).unwrap(),
232            stack as core::ffi::c_ulong,
233            parent_tid,
234            child_tid,
235            tls as core::ffi::c_ulong,
236        )
237    }
238    .map(|res| match res.cmp(&0) {
239        core::cmp::Ordering::Equal => CloneResult::CallerIsChild,
240        core::cmp::Ordering::Greater => {
241            CloneResult::CallerIsParent(Pid::from_raw(res.try_into().unwrap()).unwrap())
242        }
243        core::cmp::Ordering::Less => unreachable!(),
244    })
245}
246
247/// # Safety
248///
249/// Too many requirements to list here. See `clone(2)`.
250pub unsafe fn clone3(args: &clone_args) -> Result<CloneResult, Errno> {
251    unsafe { clone3_raw(args, core::mem::size_of::<clone_args>()) }.map(|res| match res.cmp(&0) {
252        core::cmp::Ordering::Equal => CloneResult::CallerIsChild,
253        core::cmp::Ordering::Greater => {
254            CloneResult::CallerIsParent(Pid::from_raw(res.try_into().unwrap()).unwrap())
255        }
256        core::cmp::Ordering::Less => unreachable!(),
257    })
258}
259
260/// See `fork(2)`.
261///
262/// # Safety
263///
264/// *Mostly* safe, since most memory will be copy-on-write in the child process.
265/// Non-private mutable mappings *are* shared in the child, though, which may
266/// break soundness. (Such mappings aren't common in practice)
267///
268/// Additionally some OS resources are shared with the parent, and others are
269/// dropped, which may /// break assumptions by code that uses or wraps such
270/// resources. See `fork(2)` for a full list, but some notable examples include:
271///
272/// * The child shares file descriptors (and underlying file descriptions) with the parent.
273/// * The child is the only thread in the new process.
274pub unsafe fn fork_raw() -> Result<core::ffi::c_long, Errno> {
275    unsafe { linux_syscall::syscall!(linux_syscall::SYS_fork) }
276        .try_i64()
277        .map_err(Errno::from)
278}
279
280/// # Safety
281///
282/// See `fork_raw`
283pub unsafe fn fork() -> Result<CloneResult, Errno> {
284    unsafe { fork_raw() }.map(|res| match res.cmp(&0) {
285        core::cmp::Ordering::Equal => CloneResult::CallerIsChild,
286        core::cmp::Ordering::Greater => {
287            CloneResult::CallerIsParent(Pid::from_raw(res.try_into().unwrap()).unwrap())
288        }
289        core::cmp::Ordering::Less => unreachable!(),
290    })
291}