compositor: set scheduler to SCHED_RR before dropping CAP_SYS_NICE
This commit is contained in:
parent
af6e868a78
commit
7a623006e2
8 changed files with 202 additions and 12 deletions
|
|
@ -234,7 +234,9 @@ impl ValueEnum for &'static Format {
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let cli = Jay::parse();
|
let cli = Jay::parse();
|
||||||
drop_all_pr_caps();
|
if not_matches!(cli.command, Cmd::Run(_)) {
|
||||||
|
drop_all_pr_caps();
|
||||||
|
}
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Cmd::Run(a) => start_compositor(cli.global, a),
|
Cmd::Run(a) => start_compositor(cli.global, a),
|
||||||
Cmd::GenerateCompletion(g) => generate::main(g),
|
Cmd::GenerateCompletion(g) => generate::main(g),
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ use {
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
output_schedule::OutputSchedule,
|
output_schedule::OutputSchedule,
|
||||||
portal::{self, PortalStartup},
|
portal::{self, PortalStartup},
|
||||||
|
pr_caps::pr_caps,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
sighand::{self, SighandError},
|
sighand::{self, SighandError},
|
||||||
state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState},
|
state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState},
|
||||||
|
|
@ -51,9 +52,17 @@ use {
|
||||||
},
|
},
|
||||||
user_session::import_environment,
|
user_session::import_environment,
|
||||||
utils::{
|
utils::{
|
||||||
clone3::ensure_reaper, clonecell::CloneCell, errorfmt::ErrorFmt, fdcloser::FdCloser,
|
clone3::ensure_reaper,
|
||||||
numcell::NumCell, oserror::OsError, queue::AsyncQueue, refcounted::RefCounted,
|
clonecell::CloneCell,
|
||||||
run_toplevel::RunToplevel, tri::Try,
|
errorfmt::ErrorFmt,
|
||||||
|
fdcloser::FdCloser,
|
||||||
|
nice::{did_elevate_scheduler, elevate_scheduler},
|
||||||
|
numcell::NumCell,
|
||||||
|
oserror::OsError,
|
||||||
|
queue::AsyncQueue,
|
||||||
|
refcounted::RefCounted,
|
||||||
|
run_toplevel::RunToplevel,
|
||||||
|
tri::Try,
|
||||||
},
|
},
|
||||||
version::VERSION,
|
version::VERSION,
|
||||||
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
||||||
|
|
@ -72,6 +81,11 @@ pub const MAX_EXTENTS: i32 = (1 << 22) - 1;
|
||||||
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||||
sighand::reset_all();
|
sighand::reset_all();
|
||||||
let reaper_pid = ensure_reaper();
|
let reaper_pid = ensure_reaper();
|
||||||
|
let caps = pr_caps().into_comp();
|
||||||
|
if caps.has_nice() {
|
||||||
|
elevate_scheduler();
|
||||||
|
}
|
||||||
|
drop(caps);
|
||||||
let forker = create_forker(reaper_pid);
|
let forker = create_forker(reaper_pid);
|
||||||
let portal = portal::run_from_compositor(global.log_level.into());
|
let portal = portal::run_from_compositor(global.log_level.into());
|
||||||
enable_profiler();
|
enable_profiler();
|
||||||
|
|
@ -146,6 +160,9 @@ fn start_compositor2(
|
||||||
) -> Result<(), CompositorError> {
|
) -> Result<(), CompositorError> {
|
||||||
log::info!("pid = {}", uapi::getpid());
|
log::info!("pid = {}", uapi::getpid());
|
||||||
log::info!("version = {VERSION}");
|
log::info!("version = {VERSION}");
|
||||||
|
if did_elevate_scheduler() {
|
||||||
|
log::info!("Running with elevated scheduler: SCHED_RR");
|
||||||
|
}
|
||||||
init_fd_limit();
|
init_fd_limit();
|
||||||
leaks::init();
|
leaks::init();
|
||||||
clientmem::init()?;
|
clientmem::init()?;
|
||||||
|
|
@ -680,7 +697,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
state.dummy_output.set(Some(dummy_output));
|
state.dummy_output.set(Some(dummy_output));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_dir() -> Option<String> {
|
pub fn config_dir() -> Option<String> {
|
||||||
if let Ok(xdg) = env::var("XDG_CONFIG_HOME") {
|
if let Ok(xdg) = env::var("XDG_CONFIG_HOME") {
|
||||||
Some(format!("{}/jay", xdg))
|
Some(format!("{}/jay", xdg))
|
||||||
} else if let Ok(home) = env::var("HOME") {
|
} else if let Ok(home) = env::var("HOME") {
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,13 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
tree::ToplevelData,
|
tree::ToplevelData,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, numcell::NumCell, ptr_ext::PtrExt,
|
clonecell::CloneCell,
|
||||||
toplevel_identifier::ToplevelIdentifier, unlink_on_drop::UnlinkOnDrop, xrd::xrd,
|
nice::{JAY_NO_REALTIME, dont_allow_config_so},
|
||||||
|
numcell::NumCell,
|
||||||
|
ptr_ext::PtrExt,
|
||||||
|
toplevel_identifier::ToplevelIdentifier,
|
||||||
|
unlink_on_drop::UnlinkOnDrop,
|
||||||
|
xrd::xrd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
bincode::Options,
|
bincode::Options,
|
||||||
|
|
@ -26,7 +31,7 @@ use {
|
||||||
window::{self, TileState},
|
window::{self, TileState},
|
||||||
},
|
},
|
||||||
libloading::Library,
|
libloading::Library,
|
||||||
std::{cell::Cell, io, mem, ptr, rc::Rc},
|
std::{cell::Cell, io, mem, path::Path, ptr, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -42,6 +47,8 @@ pub enum ConfigError {
|
||||||
CopyConfigFile(#[source] io::Error),
|
CopyConfigFile(#[source] io::Error),
|
||||||
#[error("XDG_RUNTIME_DIR is not set")]
|
#[error("XDG_RUNTIME_DIR is not set")]
|
||||||
XrdNotSet,
|
XrdNotSet,
|
||||||
|
#[error("Custom config.so is not permitted")]
|
||||||
|
NotPermitted,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConfigProxy {
|
pub struct ConfigProxy {
|
||||||
|
|
@ -280,11 +287,24 @@ impl ConfigProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_config_dir(state: &Rc<State>) -> Result<Self, ConfigError> {
|
pub fn from_config_dir(state: &Rc<State>) -> Result<Self, ConfigError> {
|
||||||
|
if dont_allow_config_so() {
|
||||||
|
if have_config_so(state.config_dir.as_deref()) {
|
||||||
|
log::warn!("Not loading config.so because");
|
||||||
|
log::warn!(" 1. Jay was started with CAP_SYS_NICE");
|
||||||
|
log::warn!(" 2. Jay was not started with {}=1", JAY_NO_REALTIME);
|
||||||
|
log::warn!(" 3. The scheduler was elevated to SCHED_RR");
|
||||||
|
log::warn!(
|
||||||
|
" 4. Jay was not compiled with {}=1",
|
||||||
|
jay_allow_realtime_config_so!(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Err(ConfigError::NotPermitted);
|
||||||
|
}
|
||||||
let dir = match state.config_dir.as_deref() {
|
let dir = match state.config_dir.as_deref() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
_ => return Err(ConfigError::ConfigDirNotSet),
|
_ => return Err(ConfigError::ConfigDirNotSet),
|
||||||
};
|
};
|
||||||
let file = format!("{}/config.so", dir);
|
let file = format!("{}/{CONFIG_SO}", dir);
|
||||||
unsafe { Self::from_file(&file, state) }
|
unsafe { Self::from_file(&file, state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,3 +375,15 @@ pub struct InvokedShortcut {
|
||||||
pub effective_mods: Modifiers,
|
pub effective_mods: Modifiers,
|
||||||
pub sym: KeySym,
|
pub sym: KeySym,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CONFIG_SO: &str = "config.so";
|
||||||
|
|
||||||
|
pub fn have_config_so(config_dir: Option<&str>) -> bool {
|
||||||
|
let Some(dir) = config_dir else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let mut dir = dir.to_owned();
|
||||||
|
dir.push_str("/");
|
||||||
|
dir.push_str(CONFIG_SO);
|
||||||
|
Path::new(&dir).exists()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -760,3 +760,15 @@ macro_rules! client_wire_scale_to_logical {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! not_matches {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
!matches!($($tt)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! jay_allow_realtime_config_so {
|
||||||
|
() => {
|
||||||
|
"JAY_ALLOW_REALTIME_CONFIG_SO"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,52 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
pr_caps::sys::{
|
pr_caps::sys::{
|
||||||
_LINUX_CAPABILITY_U32S_3, _LINUX_CAPABILITY_VERSION_3, cap_user_data_t,
|
_LINUX_CAPABILITY_U32S_3, _LINUX_CAPABILITY_VERSION_3, CAP_SYS_NICE, cap_user_data_t,
|
||||||
cap_user_header_t,
|
cap_user_header_t,
|
||||||
},
|
},
|
||||||
utils::{errorfmt::ErrorFmt, oserror::OsError},
|
utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt, oserror::OsError},
|
||||||
},
|
},
|
||||||
uapi::{
|
uapi::{
|
||||||
c::{SYS_capset, syscall},
|
c::{SYS_capget, SYS_capset, syscall},
|
||||||
map_err,
|
map_err,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub struct PrCaps {
|
||||||
|
effective: u64,
|
||||||
|
permitted: u64,
|
||||||
|
inheritable: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PrCompCaps {
|
||||||
|
caps: PrCaps,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pr_caps() -> PrCaps {
|
||||||
|
let mut hdr = cap_user_header_t {
|
||||||
|
version: _LINUX_CAPABILITY_VERSION_3,
|
||||||
|
pid: 0,
|
||||||
|
};
|
||||||
|
let mut caps = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3];
|
||||||
|
let ret = unsafe { syscall(SYS_capget, &mut hdr, &mut caps) };
|
||||||
|
if let Err(e) = map_err!(ret) {
|
||||||
|
eprintln!(
|
||||||
|
"Could not get process capabilities: {}",
|
||||||
|
ErrorFmt(OsError(e.0))
|
||||||
|
);
|
||||||
|
return PrCaps {
|
||||||
|
effective: 0,
|
||||||
|
permitted: 0,
|
||||||
|
inheritable: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
PrCaps {
|
||||||
|
effective: caps[0].effective as u64 | ((caps[1].effective as u64) << 32),
|
||||||
|
permitted: caps[0].permitted as u64 | ((caps[1].permitted as u64) << 32),
|
||||||
|
inheritable: caps[0].inheritable as u64 | ((caps[1].inheritable as u64) << 32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn drop_all_pr_caps() {
|
pub fn drop_all_pr_caps() {
|
||||||
let mut hdr = cap_user_header_t {
|
let mut hdr = cap_user_header_t {
|
||||||
version: _LINUX_CAPABILITY_VERSION_3,
|
version: _LINUX_CAPABILITY_VERSION_3,
|
||||||
|
|
@ -27,6 +62,55 @@ pub fn drop_all_pr_caps() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PrCaps {
|
||||||
|
pub fn into_comp(mut self) -> PrCompCaps {
|
||||||
|
let mut caps = 0;
|
||||||
|
macro_rules! add_cap {
|
||||||
|
($name:ident) => {
|
||||||
|
if self.permitted.contains(1 << $name) {
|
||||||
|
caps |= 1 << $name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
add_cap!(CAP_SYS_NICE);
|
||||||
|
let mut hdr = cap_user_header_t {
|
||||||
|
version: _LINUX_CAPABILITY_VERSION_3,
|
||||||
|
pid: 0,
|
||||||
|
};
|
||||||
|
let caps_hi = (caps >> 32) as u32;
|
||||||
|
let caps_lo = caps as u32;
|
||||||
|
let mut data = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3];
|
||||||
|
data[0].effective = caps_lo;
|
||||||
|
data[1].effective = caps_hi;
|
||||||
|
data[0].permitted = caps_lo;
|
||||||
|
data[1].permitted = caps_hi;
|
||||||
|
let ret = unsafe { syscall(SYS_capset, &mut hdr, &data) };
|
||||||
|
if let Err(e) = map_err!(ret) {
|
||||||
|
eprintln!(
|
||||||
|
"Could not get set compositor capabilities: {}",
|
||||||
|
ErrorFmt(OsError(e.0))
|
||||||
|
);
|
||||||
|
return PrCompCaps { caps: self };
|
||||||
|
}
|
||||||
|
self.effective = caps;
|
||||||
|
self.permitted = caps;
|
||||||
|
self.inheritable = 0;
|
||||||
|
PrCompCaps { caps: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrCompCaps {
|
||||||
|
pub fn has_nice(&self) -> bool {
|
||||||
|
self.caps.effective.contains(1 << CAP_SYS_NICE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PrCaps {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
drop_all_pr_caps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod sys {
|
mod sys {
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ pub mod line_logger;
|
||||||
pub mod linkedlist;
|
pub mod linkedlist;
|
||||||
pub mod log_on_drop;
|
pub mod log_on_drop;
|
||||||
pub mod mmap;
|
pub mod mmap;
|
||||||
|
pub mod nice;
|
||||||
pub mod nonblock;
|
pub mod nonblock;
|
||||||
pub mod num_cpus;
|
pub mod num_cpus;
|
||||||
pub mod numcell;
|
pub mod numcell;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
forker::ForkerError,
|
forker::ForkerError,
|
||||||
|
pr_caps::drop_all_pr_caps,
|
||||||
utils::{errorfmt::ErrorFmt, on_drop::OnDrop, process_name::set_process_name},
|
utils::{errorfmt::ErrorFmt, on_drop::OnDrop, process_name::set_process_name},
|
||||||
},
|
},
|
||||||
std::{env, mem::MaybeUninit, process, slice, str::FromStr},
|
std::{env, mem::MaybeUninit, process, slice, str::FromStr},
|
||||||
|
|
@ -161,6 +162,7 @@ pub fn ensure_reaper() -> c::pid_t {
|
||||||
set_deathsig();
|
set_deathsig();
|
||||||
return reaper_pid;
|
return reaper_pid;
|
||||||
};
|
};
|
||||||
|
drop_all_pr_caps();
|
||||||
set_process_name("jay reaper");
|
set_process_name("jay reaper");
|
||||||
while let Ok((pid, status)) = uapi::wait() {
|
while let Ok((pid, status)) = uapi::wait() {
|
||||||
if pid == main_process_id {
|
if pid == main_process_id {
|
||||||
|
|
|
||||||
40
src/utils/nice.rs
Normal file
40
src/utils/nice.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
use {
|
||||||
|
crate::{compositor::config_dir, config::have_config_so},
|
||||||
|
c::sched_setscheduler,
|
||||||
|
std::{
|
||||||
|
env, mem,
|
||||||
|
sync::atomic::{AtomicBool, Ordering::Relaxed},
|
||||||
|
},
|
||||||
|
uapi::c::{self, SCHED_RESET_ON_FORK, SCHED_RR, sched_param},
|
||||||
|
};
|
||||||
|
|
||||||
|
static DID_ELEVATE_SCHEDULER: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
pub const JAY_NO_REALTIME: &str = "JAY_NO_REALTIME";
|
||||||
|
|
||||||
|
pub fn elevate_scheduler() {
|
||||||
|
if env::var(JAY_NO_REALTIME).as_deref().unwrap_or_default() == "1" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if have_config_so(config_dir().as_deref()) && dont_allow_realtime_config_so() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut param = unsafe { mem::zeroed::<sched_param>() };
|
||||||
|
param.sched_priority = 1;
|
||||||
|
let res = unsafe { sched_setscheduler(0, SCHED_RR | SCHED_RESET_ON_FORK, ¶m) };
|
||||||
|
if res == 0 {
|
||||||
|
DID_ELEVATE_SCHEDULER.store(true, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn did_elevate_scheduler() -> bool {
|
||||||
|
DID_ELEVATE_SCHEDULER.load(Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dont_allow_realtime_config_so() -> bool {
|
||||||
|
option_env!(jay_allow_realtime_config_so!()).unwrap_or_default() != "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dont_allow_config_so() -> bool {
|
||||||
|
did_elevate_scheduler() && dont_allow_realtime_config_so()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue