1use std::fmt::Display;
2
3#[cfg(target_os = "linux")]
5fn nice(adjustment: i8) -> Result<i8, nix::errno::Errno> {
6 unsafe {
7 *libc::__errno_location() = 0;
8 let niceness = libc::nice(libc::c_int::from(adjustment));
9 let errno = *libc::__errno_location();
10 if (niceness == -1) && (errno != 0) {
11 Err(errno)
12 } else {
13 Ok(niceness)
14 }
15 }
16 .map(|niceness| i8::try_from(niceness).expect("Unexpected niceness value"))
17 .map_err(nix::errno::Errno::from_raw)
18}
19
20#[cfg(target_os = "linux")]
26pub fn renice_this_thread(adjustment: i8) -> Result<(), String> {
27 nice(adjustment)
31 .map(|_| ())
32 .map_err(|err| format!("Failed to change thread's nice value: {err}"))
33}
34
35#[cfg(not(target_os = "linux"))]
41pub fn renice_this_thread(adjustment: i8) -> Result<(), String> {
42 if adjustment == 0 {
43 Ok(())
44 } else {
45 Err(String::from(
46 "Failed to change thread's nice value: only supported on Linux",
47 ))
48 }
49}
50
51#[cfg(target_os = "linux")]
53pub fn is_renice_allowed(adjustment: i8) -> bool {
54 use caps::{CapSet, Capability};
55
56 if adjustment >= 0 {
57 true
58 } else {
59 nix::unistd::geteuid().is_root()
60 || caps::has_cap(None, CapSet::Effective, Capability::CAP_SYS_NICE)
61 .map_err(|err| warn!("Failed to get thread's capabilities: {}", err))
62 .unwrap_or(false)
63 }
64}
65
66#[cfg(not(target_os = "linux"))]
68pub fn is_renice_allowed(adjustment: i8) -> bool {
69 adjustment == 0
70}
71
72pub fn is_niceness_adjustment_valid<T>(value: T) -> Result<(), String>
73where
74 T: AsRef<str> + Display,
75{
76 let adjustment = value
77 .as_ref()
78 .parse::<i8>()
79 .map_err(|err| format!("error parsing niceness adjustment value '{value}': {err}"))?;
80 if is_renice_allowed(adjustment) {
81 Ok(())
82 } else {
83 Err(String::from(
84 "niceness adjustment supported only on Linux; negative adjustment \
85 (priority increase) requires root or CAP_SYS_NICE (see `man 7 capabilities` \
86 for details)",
87 ))
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 #[cfg(target_os = "linux")]
94 use super::*;
95
96 #[cfg(target_os = "linux")]
97 #[test]
98 fn test_nice() {
99 let niceness = nice(0).unwrap();
101
102 let result = std::thread::spawn(|| nice(1)).join().unwrap();
104 assert_eq!(result, Ok(niceness + 1));
105
106 assert_eq!(nice(0), Ok(niceness));
109
110 let inherited_niceness = std::thread::spawn(|| {
112 nice(1).unwrap();
113 std::thread::spawn(|| nice(0).unwrap()).join().unwrap()
114 })
115 .join()
116 .unwrap();
117 assert_eq!(inherited_niceness, niceness + 1);
118
119 if !is_renice_allowed(-1) {
120 let result = std::thread::spawn(|| nice(-1)).join().unwrap();
122 assert!(result.is_err());
123 }
124 }
125
126 #[test]
127 fn test_is_niceness_adjustment_valid() {
128 use super::is_niceness_adjustment_valid;
129 assert_eq!(is_niceness_adjustment_valid("0"), Ok(()));
130 assert!(is_niceness_adjustment_valid("128").is_err());
131 assert!(is_niceness_adjustment_valid("-129").is_err());
132 }
133}