Initial commit

This commit is contained in:
2024-11-23 02:54:01 +01:00
commit fb0c0f0948
19 changed files with 2861 additions and 0 deletions

16
src/canary.rs Normal file
View File

@@ -0,0 +1,16 @@
/// The canary thread needs to ensure that it's not disturbed by signals,
/// in order to get an accurate reading of scheduler starvation. This function
/// will disable all signals for the current thread.
fn disable_signals() {}
// The canary thread polls in a loop and measures the time between each time it
// gets rescheduled. If the time between two reschedules is too long, the canary
// thread will report this to the main thread so it can start reducing the realtime
// privileges of the supervised processes.
//
// Or is it the watchdog thread that does this?
fn canary_thread() {}
fn watchdog_thread() {}
// CancellationToken

145
src/cli.rs Normal file
View File

@@ -0,0 +1,145 @@
use clap::Parser;
use crate::nix::{SchedPolicy, SchedType};
#[derive(Parser, Debug)]
pub struct Args {
/// Log to STDERR in addition to journald
#[arg(long)]
pub stderr: bool,
/// Run daemon as user
#[arg(long, default_value = "rtkit", value_name = "USER")]
pub user_name: String,
/// Choose scheduling policy
#[arg(long, default_value_t = SchedulingPolicy::Rr)]
pub scheduling_policy: SchedulingPolicy,
/// Realtime priority for the daemon
#[arg(long, default_value = "0", value_name = "[0..99]")]
pub our_realtime_priority: u32,
/// Nice level for the daemon
#[arg(long, default_value = "0", value_name = "[-20..19]")]
pub our_nice_level: i32,
/// Max realtime priority for clients
#[arg(long, default_value = "99", value_name = "[0..99]")]
pub max_realtime_priority: u32,
/// Min nice level for clients
#[arg(long, default_value = "-20", value_name = "[-20..19]")]
pub min_nice_level: i32,
/// Require clients to have set RLIMIT_RTTIME not greater than this
#[arg(long, value_name = "USEC")]
pub rttime_usec_max: Option<u64>,
/// How many users this daemon will serve at max at the same time
#[arg(long)]
pub users_max: Option<u32>,
/// How many processes this daemon will serve at max per user at the same time
#[arg(long)]
pub processes_per_user_max: Option<u32>,
/// How many threads this daemon will serve at max per user at the same time
#[arg(long)]
pub threads_per_user_max: Option<u32>,
/// Enforce requests limits in this time
#[arg(long, value_name = "SEC")]
pub actions_burst_sec: Option<u32>,
/// Allow this many requests per burst
#[arg(long)]
pub actions_per_burst_max: Option<u32>,
/// Canary cheep interval
#[arg(long, default_value = "5000", value_name = "MILLISEC")]
pub canary_cheep_msec: u32,
/// Watchdog action delay
#[arg(long, default_value = "10000", value_name = "MILLISEC")]
pub canary_watchdog_msec: u32,
/// When the canary dies demote unknown processes too?
#[arg(long)]
pub canary_demote_unknown: bool,
/// When the canary dies demote root processes too?
#[arg(long)]
pub canary_demote_root: bool,
/// How long to refuse further requests after the canary died
#[arg(long, default_value = "300", value_name = "SEC")]
pub canary_refuse_sec: u32,
/// Don't run a canary-based RT watchdog
#[arg(long)]
pub no_canary: bool,
/// Don't drop privileges
#[arg(long)]
pub no_drop_privileges: bool,
/// Don't chroot
#[arg(long)]
pub no_chroot: bool,
/// Don't limit daemon's resources
#[arg(long)]
pub no_limit_resources: bool,
}
impl Args {
pub fn proc_dir(&self) -> &str {
if self.no_chroot {
"/proc"
} else {
"/"
}
}
pub fn scheduling_policy(&self) -> SchedPolicy {
let sched_type: SchedType = self.scheduling_policy.into();
SchedPolicy::new(sched_type)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SchedulingPolicy {
Fifo,
Rr,
}
impl std::str::FromStr for SchedulingPolicy {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"FIFO" | "fifo" => Ok(Self::Fifo),
"RR" | "rr" => Ok(Self::Rr),
_ => Err(format!("unknown scheduling policy: {}", s)),
}
}
}
impl std::fmt::Display for SchedulingPolicy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fifo => write!(f, "FIFO"),
Self::Rr => write!(f, "RR"),
}
}
}
impl Into<SchedType> for SchedulingPolicy {
fn into(self) -> SchedType {
match self {
Self::Fifo => SchedType::SCHED_FIFO,
Self::Rr => SchedType::SCHED_RR,
}
}
}

135
src/core.rs Normal file
View File

@@ -0,0 +1,135 @@
use nix::unistd::{Pid, Uid};
use anyhow::Result;
use crate::{
cli::Args, nix::{sched_get_priority_max, sched_get_priority_min}, self_drop_realtime, self_set_realtime
};
struct User {
uid: Uid,
timestamp: u64,
n_actions: u32,
processes: Vec<Process>,
}
struct Process {
pid: Pid,
starttime: u64,
threads: Vec<Thread>,
}
struct Thread {
tid: Pid,
starttime: u64,
}
struct State {
// TODO:
}
impl State {
fn make_thread_realtime_with_pid(
&mut self,
args: &Args,
user: User,
pid: Pid,
tid: Pid,
priority: u32,
) {
if sched_get_priority_min(args.scheduling_policy())
.is_ok_and(|min| priority < u32::try_from(min).unwrap())
|| sched_get_priority_max(args.scheduling_policy())
.is_ok_and(|max| priority > u32::try_from(max).unwrap())
{
todo!("bail EINVAL")
}
// We always want to be able to get a higher RT priority than the client
if priority >= args.our_realtime_priority
|| priority > args.max_realtime_priority
{
todo!("bail EPERM")
}
// Make sure users don't flood us with requests
if !verify_burst(&user) {
todo!("bail EBUSY")
}
// TODO: implement
self_drop_realtime(args.our_nice_level);
}
fn make_thread_high_priority_with_pid(&mut self) {
unimplemented!()
}
fn reset_known(&mut self) {
unimplemented!()
}
fn reset_all(&mut self) {
unimplemented!()
}
fn exit(&mut self) {
unimplemented!()
}
}
// Polkit
// Unix process lookup
fn read_start_time(pid: Pid, tid: Pid) -> u64 {
unimplemented!("Read /proc and get the start time of the process/thread")
}
fn verify_burst(user: &User) -> bool {
unimplemented!("Check if the user has issued more than ACTIONS_PER_BURST_MAX in this time")
}
// TODO: make into impls
// fn find_user(users: &[User], uid: Uid) -> Option<&User> {
// unimplemented!("Find the user with the given UID")
// }
// fn find_process(user: &User, pid: Pid) -> Option<&Process> {
// unimplemented!("Find the process with the given PID")
// }
// fn find_thread(process: &Process, tid: Pid) -> Option<&Thread> {
// unimplemented!("Find the thread with the given TID")
// }
// fn thread_gc(process: &mut Process) {
// unimplemented!("Garbage collect threads")
// }
// fn process_gc(user: &mut User) {
// unimplemented!("Garbage collect processes")
// }
// fn user_gc(users: &mut Vec<User>) {
// unimplemented!("Garbage collect users")
// }
/* This checks if a thread still matters to us, i.e. if its
* PID still refers to the same thread and if it is still high
* priority or real time */
fn thread_relevant(pid: Pid, tid: Pid) -> bool {
unimplemented!("Check if the thread is still relevant")
}
/* Verifies that RLIMIT_RTTIME is set for the specified process */
fn verify_process_rttime(process: &Process) -> Result<()> {
unimplemented!()
}
// Ensure that the user owns the process
fn verify_process_user(process: &Process, user: &User) -> Result<()> {
unimplemented!()
}

10
src/dbus.rs Normal file
View File

@@ -0,0 +1,10 @@
use anyhow::Result;
const RTKIT_DBUS_SERVICE_NAME: &str = "org.freedesktop.RealtimeKit1";
const RTKIT_DBUS_OBJECT_PATH: &str = "/org/freedesktop/RealtimeKit1";
// pub fn setup_dbus() -> Result<> {
// }
// pub fn dbus_handler()

47
src/main.rs Normal file
View File

@@ -0,0 +1,47 @@
mod canary;
mod cli;
mod core;
mod dbus;
mod privileges;
// mod sched;
mod nix;
use cli::Args;
use clap::Parser;
use privileges::*;
use anyhow::{bail, Result, Context};
fn main() -> Result<()> {
let args = Args::parse();
if !self_is_root() {
bail!("rtkit must be run as root");
}
// TODO: initialize logging
self_raise_timer_slack(&args).context("failed to raise our own timer slack")?;
self_drop_realtime(args.our_nice_level).context("failed to drop our own realtime scheduler")?;
// setup_dbus();
self_drop_privileges(&args).context("failed to drop our own privileges")?;
if !args.no_limit_resources {
set_resource_limits(&args).context("failed to set resource limits")?;
}
// let canary_handle = start_canary();
// let watchdog_handle = start_watchdog();
self_reduce_umask();
// start_dbus();
Ok(())
}

10
src/nix.rs Normal file
View File

@@ -0,0 +1,10 @@
mod macros;
mod priority;
mod sched;
pub use sched::{
sched_get_priority_max, sched_get_priority_min, sched_getscheduler, sched_setscheduler,
SchedFlags, SchedParam, SchedPolicy, SchedType,
};
pub use priority::{getpriority, setpriority, PriorityKind};

322
src/nix/macros.rs Normal file
View File

@@ -0,0 +1,322 @@
/// Borrowed from https://github.com/nix-rust/nix/blob/master/src/macros.rs
/// The `libc_bitflags!` macro helps with a common use case of defining a public bitflags type
/// with values from the libc crate. It is used the same way as the `bitflags!` macro, except
/// that only the name of the flag value has to be given.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```ignore
/// libc_bitflags!{
/// pub struct ProtFlags: libc::c_int {
/// PROT_NONE;
/// PROT_READ;
/// /// PROT_WRITE enables write protect
/// PROT_WRITE;
/// PROT_EXEC;
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN;
/// #[cfg(linux_android)]
/// PROT_GROWSUP;
/// }
/// }
/// ```
///
/// Example with casting, due to a mistake in libc. In this example, the
/// various flags have different types, so we cast the broken ones to the right
/// type.
///
/// ```ignore
/// libc_bitflags!{
/// pub struct SaFlags: libc::c_ulong {
/// SA_NOCLDSTOP as libc::c_ulong;
/// SA_NOCLDWAIT;
/// SA_NODEFER as libc::c_ulong;
/// SA_ONSTACK;
/// SA_RESETHAND as libc::c_ulong;
/// SA_RESTART as libc::c_ulong;
/// SA_SIGINFO;
/// }
/// }
/// ```
macro_rules! libc_bitflags {
(
$(#[$outer:meta])*
pub struct $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
$Flag:ident $(as $cast:ty)*;
)+
}
) => {
::bitflags::bitflags! {
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
$(#[$outer])*
pub struct $BitFlags: $T {
$(
$(#[$inner $($args)*])*
const $Flag = libc::$Flag $(as $cast)*;
)+
}
}
};
}
/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using
/// values from the `libc` crate. This macro supports both `pub` and private `enum`s.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```ignore
/// libc_enum!{
/// pub enum ProtFlags {
/// PROT_NONE,
/// PROT_READ,
/// PROT_WRITE,
/// PROT_EXEC,
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN,
/// #[cfg(linux_android)]
/// PROT_GROWSUP,
/// }
/// }
/// ```
// Some targets don't use all rules.
#[allow(unused_macro_rules)]
macro_rules! libc_enum {
// Exit rule.
(@make_enum
name: $BitFlags:ident,
{
$v:vis
attrs: [$($attrs:tt)*],
entries: [$($entries:tt)*],
}
) => {
$($attrs)*
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
$v enum $BitFlags {
$($entries)*
}
};
// Exit rule including TryFrom
(@make_enum
name: $BitFlags:ident,
{
$v:vis
attrs: [$($attrs:tt)*],
entries: [$($entries:tt)*],
from_type: $repr:path,
try_froms: [$($try_froms:tt)*]
}
) => {
$($attrs)*
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
$v enum $BitFlags {
$($entries)*
}
impl ::std::convert::TryFrom<$repr> for $BitFlags {
type Error = nix::Error;
#[allow(unused_doc_comments)]
#[allow(deprecated)]
#[allow(unused_attributes)]
fn try_from(x: $repr) -> nix::Result<Self> {
match x {
$($try_froms)*
_ => Err(nix::Error::EINVAL)
}
}
}
};
// Done accumulating.
(@accumulate_entries
name: $BitFlags:ident,
{
$v:vis
attrs: $attrs:tt,
},
$entries:tt,
$try_froms:tt;
) => {
libc_enum! {
@make_enum
name: $BitFlags,
{
$v
attrs: $attrs,
entries: $entries,
}
}
};
// Done accumulating and want TryFrom
(@accumulate_entries
name: $BitFlags:ident,
{
$v:vis
attrs: $attrs:tt,
from_type: $repr:path,
},
$entries:tt,
$try_froms:tt;
) => {
libc_enum! {
@make_enum
name: $BitFlags,
{
$v
attrs: $attrs,
entries: $entries,
from_type: $repr,
try_froms: $try_froms
}
}
};
// Munch an attr.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
#[$attr:meta] $($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
#[$attr]
],
[
$($try_froms)*
#[$attr]
];
$($tail)*
}
};
// Munch last ident if not followed by a comma.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
$entry:ident
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry,
],
[
$($try_froms)*
libc::$entry => Ok($BitFlags::$entry),
];
}
};
// Munch an ident; covers terminating comma case.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
$entry:ident,
$($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry,
],
[
$($try_froms)*
libc::$entry => Ok($BitFlags::$entry),
];
$($tail)*
}
};
// Munch an ident and cast it to the given type; covers terminating comma.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
$entry:ident as $ty:ty,
$($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry as $ty,
],
[
$($try_froms)*
libc::$entry as $ty => Ok($BitFlags::$entry),
];
$($tail)*
}
};
// Entry rule.
(
$(#[$attr:meta])*
$v:vis enum $BitFlags:ident {
$($vals:tt)*
}
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
{
$v
attrs: [$(#[$attr])*],
},
[],
[];
$($vals)*
}
};
// Entry rule including TryFrom
(
$(#[$attr:meta])*
$v:vis enum $BitFlags:ident {
$($vals:tt)*
}
impl TryFrom<$repr:path>
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
{
$v
attrs: [$(#[$attr])*],
from_type: $repr,
},
[],
[];
$($vals)*
}
};
}
pub(crate) use libc_bitflags;
pub(crate) use libc_enum;

86
src/nix/priority.rs Normal file
View File

@@ -0,0 +1,86 @@
use nix::{
errno::Errno,
libc::{self, __priority_which_t, id_t},
unistd::{Gid, Pid, Uid},
Result,
};
use super::macros::*;
libc_enum! {
#[repr(u32)]
#[non_exhaustive]
enum WhichPriority {
PRIO_PROCESS,
PRIO_PGRP,
PRIO_USER,
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum PriorityKind {
Process(Option<Pid>),
Group(Option<Gid>),
User(Option<Uid>),
}
impl From<Pid> for PriorityKind {
fn from(pid: Pid) -> Self {
PriorityKind::Process(Some(pid))
}
}
impl From<Gid> for PriorityKind {
fn from(gid: Gid) -> Self {
PriorityKind::Group(Some(gid))
}
}
impl From<Uid> for PriorityKind {
fn from(uid: Uid) -> Self {
PriorityKind::User(Some(uid))
}
}
impl Into<WhichPriority> for PriorityKind {
fn into(self) -> WhichPriority {
match self {
PriorityKind::Process(_) => WhichPriority::PRIO_PROCESS,
PriorityKind::Group(_) => WhichPriority::PRIO_PGRP,
PriorityKind::User(_) => WhichPriority::PRIO_USER,
}
}
}
impl Into<u32> for PriorityKind {
fn into(self) -> u32 {
match self {
// TODO: Deal with this unwrap
PriorityKind::Process(pid) => pid
.map(|p| u32::try_from(i32::from(p)).unwrap())
.unwrap_or(0),
PriorityKind::Group(gid) => gid.map(|g| g.into()).unwrap_or(0),
PriorityKind::User(uid) => uid.map(|u| u.into()).unwrap_or(0),
}
}
}
fn into_priority_args(priority: PriorityKind) -> (__priority_which_t, id_t) {
let _which = WhichPriority::from(priority.into());
let which = __priority_which_t::from(_which as u32);
let who: id_t = priority.into();
(which, who)
}
pub fn getpriority(priority: PriorityKind) -> Result<i32> {
let (which, who) = into_priority_args(priority);
let res = unsafe { libc::getpriority(which, who) };
Errno::result(res)
}
pub fn setpriority(priority: PriorityKind, prio: i32) -> Result<i32> {
let (which, who) = into_priority_args(priority);
let result = unsafe { libc::setpriority(which, who, prio) };
Errno::result(result)
}

183
src/nix/sched.rs Normal file
View File

@@ -0,0 +1,183 @@
/// Kindly borrowed from https://github.com/nix-rust/nix/pull/1435
use nix::{
errno::Errno,
libc::{self, c_int},
unistd::Pid,
Result,
};
use super::macros::*;
libc_enum! {
#[repr(i32)]
#[non_exhaustive]
pub enum SchedType {
#[cfg(target_os = "android")]
SCHED_NORMAL,
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd"))]
SCHED_OTHER,
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd"))]
SCHED_FIFO,
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd"))]
SCHED_RR,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
SCHED_BATCH,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
SCHED_IDLE,
#[cfg(target_os = "android")]
SCHED_DEADLINE,
#[cfg(target_os = "illumos")]
SCHED_SYS,
#[cfg(target_os = "illumos")]
SCHED_IA,
#[cfg(target_os = "illumos")]
SCHED_FSS,
#[cfg(target_os = "illumos")]
SCHED_FX,
}
impl TryFrom<i32>
}
libc_bitflags! {
pub struct SchedFlags: c_int {
#[cfg(target_os = "linux")]
SCHED_RESET_ON_FORK;
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SchedPolicy {
pub sched_type: SchedType,
#[cfg(target_os = "linux")]
pub sched_flags: SchedFlags,
}
impl SchedPolicy {
pub fn new(sched_type: SchedType) -> Self {
SchedPolicy::_with_flags(sched_type, SchedFlags::empty())
}
pub fn with_flags(sched_type: SchedType, sched_flags: SchedFlags) -> Self {
SchedPolicy::_with_flags(sched_type, sched_flags)
}
#[cfg(target_os = "linux")]
pub const fn bits(&self) -> i32 {
self.sched_type as i32 | self.sched_flags.bits()
}
#[cfg(not(target_os = "linux"))]
pub const fn bits(&self) -> i32 {
self.sched_type as i32
}
#[cfg(target_os = "linux")]
pub fn from_bits(bits: i32) -> Option<Self> {
let type_bits = bits & !SchedFlags::all().bits();
let sched_type = SchedType::try_from(type_bits).ok()?;
let flag_bits = bits & SchedFlags::all().bits();
let sched_flags = SchedFlags::from_bits(flag_bits)?;
Some(SchedPolicy::with_flags(sched_type, sched_flags))
}
#[cfg(not(target_os = "linux"))]
pub fn from_bits(bits: i32) -> Option<Self> {
let type_bits = bits & !SchedFlags::all().bits();
let sched_type = SchedType::try_from(type_bits).ok()?;
Some(SchedPolicy::new(sched_type))
}
#[cfg(target_os = "linux")]
#[inline]
fn _with_flags(sched_type: SchedType, sched_flags: SchedFlags) -> Self {
SchedPolicy {
sched_type,
sched_flags,
}
}
#[cfg(not(target_os = "linux"))]
#[inline]
fn _with_flags(sched_type: SchedType, _sched_flags: SchedFlags) -> Self {
SchedPolicy { sched_type }
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SchedParam(libc::sched_param);
impl SchedParam {
pub fn new(priority: i32) -> Self {
let sched_param = unsafe {
// Illumos's sched_param has a private padding field.
let mut param = std::mem::MaybeUninit::<libc::sched_param>::zeroed();
let p = param.as_mut_ptr();
(*p).sched_priority = priority;
param.assume_init()
};
SchedParam(sched_param)
}
pub const fn priority(&self) -> i32 {
self.0.sched_priority
}
}
impl Default for SchedParam {
fn default() -> Self {
SchedParam::new(0)
}
}
/// Get minimum priority value for policy
///
/// See also [`sched_get_priority_min(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_min.html)
pub fn sched_get_priority_min(policy: SchedPolicy) -> Result<i32> {
let res = unsafe { libc::sched_get_priority_min(policy.bits()) };
Errno::result(res)
}
/// Get maximum priority value for policy
///
/// See also [`sched_get_priority_max(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_max.html)
pub fn sched_get_priority_max(policy: SchedPolicy) -> Result<i32> {
let res = unsafe { libc::sched_get_priority_max(policy.bits()) };
Errno::result(res)
}
/// Set thread's scheduling policy and parameters
///
/// `pid` is the thread ID to update.
/// If `pid` is None or zero, then the policy and parameters for the calling thread are set.
///
/// See also [`sched_setscheduler(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_setscheduler.html)
pub fn sched_setscheduler(
pid: Option<Pid>,
policy: SchedPolicy,
sched_param: SchedParam,
) -> Result<()> {
let res = unsafe {
libc::sched_setscheduler(
pid.unwrap_or_else(|| Pid::from_raw(0)).into(),
policy.bits(),
&sched_param.0,
)
};
Errno::result(res).map(drop)
}
/// Get thread's scheduling policy and parameters
///
/// `pid` is the thread ID to check.
/// If `pid` is None or zero, then the policy and parameters for the calling thread are retrieved.
///
/// See also [`sched_getscheduler(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_getscheduler.html)
pub fn sched_getscheduler(pid: Option<Pid>) -> Result<SchedPolicy> {
let res = unsafe { libc::sched_getscheduler(pid.unwrap_or_else(|| Pid::from_raw(0)).into()) };
Errno::result(res).and(SchedPolicy::from_bits(res).ok_or(Errno::EINVAL))
}

90
src/privileges.rs Normal file
View File

@@ -0,0 +1,90 @@
use std::{env::set_current_dir, os::unix::fs::chroot};
use anyhow::Result;
use nix::{
sys::{
prctl,
resource::{getrlimit, setrlimit, Resource},
stat::{umask, Mode},
},
unistd::{getuid, User},
};
use crate::{cli::Args, nix::*};
pub fn self_is_root() -> bool {
getuid().is_root()
}
pub fn self_raise_timer_slack(args: &Args) -> Result<()> {
let slack_ns = u64::from((args.canary_watchdog_msec - args.canary_cheep_msec) / 2) * 1_000_000;
prctl::set_timerslack(slack_ns)?;
Ok(())
}
pub fn self_drop_privileges(args: &Args) -> Result<()> {
let user = if !args.no_drop_privileges {
Some(User::from_name(&args.user_name)?)
} else {
None
};
if !args.no_chroot {
chroot("/proc")?;
set_current_dir("/")?;
}
if let Some(user) = user { }
Ok(())
}
pub fn set_resource_limits(args: &Args) -> Result<()> {
let limits: [(Resource, nix::libc::rlim_t); 8] = [
(Resource::RLIMIT_FSIZE, 0),
(Resource::RLIMIT_MEMLOCK, 0),
(Resource::RLIMIT_MSGQUEUE, 0),
(Resource::RLIMIT_NICE, 20),
(Resource::RLIMIT_NOFILE, 50),
(Resource::RLIMIT_NPROC, 3),
(Resource::RLIMIT_RTPRIO, 0),
(Resource::RLIMIT_RTTIME, args.rttime_usec_max.unwrap_or(0)),
];
for (resource, value) in limits.into_iter() {
let (_, rlim_max) = getrlimit(resource)?;
if rlim_max < value {
continue;
}
setrlimit(resource, value, value)?;
}
Ok(())
}
pub fn self_set_realtime(args: &Args, priority: i32) -> Result<()> {
let policy = SchedPolicy::with_flags(
args.scheduling_policy.into(),
SchedFlags::SCHED_RESET_ON_FORK,
);
let param = SchedParam::new(priority);
sched_setscheduler(None, policy, param)?;
Ok(())
}
pub fn self_drop_realtime(nice_level: i32) -> Result<()> {
let param = SchedParam::default();
let policy = SchedPolicy::new(SchedType::SCHED_OTHER);
sched_setscheduler(None, policy, param)?;
setpriority(PriorityKind::Process(None), nice_level)?;
Ok(())
}
pub fn self_reduce_umask() {
umask(Mode::from_bits_truncate(0o777));
}

1
src/util.rs Normal file
View File

@@ -0,0 +1 @@