Skip to main content

linuxutils_system/
renice.rs

1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use rustix::process::{
7    Pid, Uid, getpriority_pgrp, getpriority_process, getpriority_user,
8    setpriority_pgrp, setpriority_process, setpriority_user,
9};
10use std::{ffi::CString, process::ExitCode};
11
12#[derive(Parser)]
13#[command(
14    name = "renice",
15    about = "Alter priority of running processes",
16    override_usage = "renice [-n|--priority|--relative] priority [-p|--pid] pid...\n       \
17                      renice [-n|--priority|--relative] priority -g|--pgrp pgid...\n       \
18                      renice [-n|--priority|--relative] priority -u|--user user..."
19)]
20pub struct Args {
21    /// Specify the scheduling priority (absolute by default)
22    #[arg(short = 'n', long = "priority", allow_hyphen_values = true)]
23    priority_flag: Option<i32>,
24
25    /// Use relative priority adjustment instead of absolute
26    #[arg(long = "relative")]
27    relative: bool,
28
29    /// Interpret targets as process IDs (default)
30    #[arg(short = 'p', long = "pid")]
31    pid: bool,
32
33    /// Interpret targets as process group IDs
34    #[arg(short = 'g', long = "pgrp")]
35    pgrp: bool,
36
37    /// Interpret targets as usernames or UIDs
38    #[arg(short = 'u', long = "user")]
39    user: bool,
40
41    /// Priority value and targets
42    #[arg(required = true, allow_hyphen_values = true)]
43    args: Vec<String>,
44}
45
46fn resolve_uid(name: &str) -> Option<u32> {
47    let c_name = CString::new(name).ok()?;
48    let pw = unsafe { libc::getpwnam(c_name.as_ptr()) };
49    if pw.is_null() {
50        None
51    } else {
52        Some(unsafe { (*pw).pw_uid })
53    }
54}
55
56fn renice_pid(
57    pid_val: u32,
58    priority: i32,
59    relative: bool,
60) -> Result<(i32, i32), String> {
61    let pid = Pid::from_raw(pid_val as i32)
62        .ok_or_else(|| "invalid pid 0".to_string())?;
63    let old = getpriority_process(Some(pid)).map_err(|e| format!("{e}"))?;
64    let new_prio = if relative { old + priority } else { priority };
65    setpriority_process(Some(pid), new_prio).map_err(|e| format!("{e}"))?;
66    let actual = getpriority_process(Some(pid)).map_err(|e| format!("{e}"))?;
67    Ok((old, actual))
68}
69
70fn renice_pgrp(
71    pgid_val: u32,
72    priority: i32,
73    relative: bool,
74) -> Result<(i32, i32), String> {
75    let pgid = if pgid_val == 0 {
76        None
77    } else {
78        Some(
79            Pid::from_raw(pgid_val as i32)
80                .ok_or_else(|| "invalid pgid".to_string())?,
81        )
82    };
83    let old = getpriority_pgrp(pgid).map_err(|e| format!("{e}"))?;
84    let new_prio = if relative { old + priority } else { priority };
85    setpriority_pgrp(pgid, new_prio).map_err(|e| format!("{e}"))?;
86    let actual = getpriority_pgrp(pgid).map_err(|e| format!("{e}"))?;
87    Ok((old, actual))
88}
89
90fn renice_user(
91    uid_val: u32,
92    priority: i32,
93    relative: bool,
94) -> Result<(i32, i32), String> {
95    let uid = Uid::from_raw(uid_val);
96    let old = getpriority_user(uid).map_err(|e| format!("{e}"))?;
97    let new_prio = if relative { old + priority } else { priority };
98    setpriority_user(uid, new_prio).map_err(|e| format!("{e}"))?;
99    let actual = getpriority_user(uid).map_err(|e| format!("{e}"))?;
100    Ok((old, actual))
101}
102
103pub fn run(args: Args) -> ExitCode {
104    let (priority, targets) = if let Some(p) = args.priority_flag {
105        (p, args.args.as_slice())
106    } else {
107        let Some(first) = args.args.first() else {
108            eprintln!("renice: missing priority");
109            return ExitCode::FAILURE;
110        };
111        let p: i32 = match first.parse() {
112            Ok(v) => v,
113            Err(_) => {
114                eprintln!("renice: invalid priority '{first}'");
115                return ExitCode::FAILURE;
116            }
117        };
118        if args.args.len() < 2 {
119            eprintln!("renice: missing target");
120            return ExitCode::FAILURE;
121        }
122        (p, &args.args[1..])
123    };
124
125    let mut failed = false;
126
127    for target in targets {
128        if args.user {
129            let uid = match target.parse::<u32>() {
130                Ok(uid) => uid,
131                Err(_) => match resolve_uid(target) {
132                    Some(uid) => uid,
133                    None => {
134                        eprintln!("renice: unknown user {target}");
135                        failed = true;
136                        continue;
137                    }
138                },
139            };
140            match renice_user(uid, priority, args.relative) {
141                Ok((old, new)) => println!(
142                    "{uid} (user ID) old priority {old}, new priority {new}"
143                ),
144                Err(e) => {
145                    eprintln!(
146                        "renice: failed to set priority for user {target}: {e}"
147                    );
148                    failed = true;
149                }
150            }
151        } else if args.pgrp {
152            let pgid: u32 = match target.parse() {
153                Ok(v) => v,
154                Err(_) => {
155                    eprintln!("renice: invalid process group ID '{target}'");
156                    failed = true;
157                    continue;
158                }
159            };
160            match renice_pgrp(pgid, priority, args.relative) {
161                Ok((old, new)) => {
162                    println!(
163                        "{pgid} (process group ID) old priority {old}, new priority {new}"
164                    )
165                }
166                Err(e) => {
167                    eprintln!(
168                        "renice: failed to set priority for pgid {pgid}: {e}"
169                    );
170                    failed = true;
171                }
172            }
173        } else {
174            let pid: u32 = match target.parse() {
175                Ok(v) => v,
176                Err(_) => {
177                    eprintln!("renice: invalid process ID '{target}'");
178                    failed = true;
179                    continue;
180                }
181            };
182            match renice_pid(pid, priority, args.relative) {
183                Ok((old, new)) => {
184                    println!(
185                        "{pid} (process ID) old priority {old}, new priority {new}"
186                    )
187                }
188                Err(e) => {
189                    eprintln!(
190                        "renice: failed to set priority for pid {pid}: {e}"
191                    );
192                    failed = true;
193                }
194            }
195        }
196    }
197
198    if failed {
199        ExitCode::FAILURE
200    } else {
201        ExitCode::SUCCESS
202    }
203}