linuxutils_system/
ipcrm.rs1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use std::{
7 fs::File,
8 io::{self, BufRead},
9 process::ExitCode,
10};
11
12#[derive(Parser)]
17#[command(name = "ipcrm", about = "Remove System V IPC resources")]
18pub struct Args {
19 #[arg(short = 'm', long = "shmem-id", action = clap::ArgAction::Append)]
21 shmem_id: Vec<String>,
22
23 #[arg(short = 'M', long = "shmem-key", action = clap::ArgAction::Append)]
25 shmem_key: Vec<String>,
26
27 #[arg(short = 'q', long = "queue-id", action = clap::ArgAction::Append)]
29 queue_id: Vec<String>,
30
31 #[arg(short = 'Q', long = "queue-key", action = clap::ArgAction::Append)]
33 queue_key: Vec<String>,
34
35 #[arg(short = 's', long = "semaphore-id", action = clap::ArgAction::Append)]
37 semaphore_id: Vec<String>,
38
39 #[arg(short = 'S', long = "semaphore-key", action = clap::ArgAction::Append)]
41 semaphore_key: Vec<String>,
42
43 #[arg(short = 'a', long, num_args = 0..=1, default_missing_value = "all")]
45 all: Option<String>,
46
47 #[arg(short = 'v', long)]
49 verbose: bool,
50}
51
52fn parse_id(s: &str) -> Result<i32, String> {
53 if let Some(hex) = s.strip_prefix("0x") {
54 i32::from_str_radix(hex, 16).map_err(|_| format!("invalid id: {s}"))
55 } else if s.starts_with('0') && s.len() > 1 {
56 i32::from_str_radix(s, 8).map_err(|_| format!("invalid id: {s}"))
57 } else {
58 s.parse().map_err(|_| format!("invalid id: {s}"))
59 }
60}
61
62fn rm_shm(id: i32) -> io::Result<()> {
63 if unsafe { libc::shmctl(id, libc::IPC_RMID, std::ptr::null_mut()) } < 0 {
64 Err(io::Error::last_os_error())
65 } else {
66 Ok(())
67 }
68}
69
70fn rm_msg(id: i32) -> io::Result<()> {
71 if unsafe { libc::msgctl(id, libc::IPC_RMID, std::ptr::null_mut()) } < 0 {
72 Err(io::Error::last_os_error())
73 } else {
74 Ok(())
75 }
76}
77
78fn rm_sem(id: i32) -> io::Result<()> {
79 if unsafe { libc::semctl(id, 0, libc::IPC_RMID) } < 0 {
80 Err(io::Error::last_os_error())
81 } else {
82 Ok(())
83 }
84}
85
86fn key_to_shmid(key: i32) -> io::Result<i32> {
87 let id = unsafe { libc::shmget(key, 0, 0) };
88 if id < 0 {
89 Err(io::Error::last_os_error())
90 } else {
91 Ok(id)
92 }
93}
94
95fn key_to_msgid(key: i32) -> io::Result<i32> {
96 let id = unsafe { libc::msgget(key, 0) };
97 if id < 0 {
98 Err(io::Error::last_os_error())
99 } else {
100 Ok(id)
101 }
102}
103
104fn key_to_semid(key: i32) -> io::Result<i32> {
105 let id = unsafe { libc::semget(key, 0, 0) };
106 if id < 0 {
107 Err(io::Error::last_os_error())
108 } else {
109 Ok(id)
110 }
111}
112
113fn read_ids_from_proc(path: &str) -> Vec<i32> {
114 let Ok(file) = File::open(path) else {
115 return Vec::new();
116 };
117 io::BufReader::new(file)
118 .lines()
119 .map_while(Result::ok)
120 .skip(1)
121 .filter_map(|line| {
122 line.split_whitespace().nth(1).and_then(|s| s.parse().ok())
123 })
124 .collect()
125}
126
127fn try_remove(
128 kind: &str,
129 id: i32,
130 f: fn(i32) -> io::Result<()>,
131 verbose: bool,
132) -> bool {
133 if verbose {
134 eprintln!("ipcrm: removing {kind} id {id}");
135 }
136 if let Err(e) = f(id) {
137 eprintln!("ipcrm: {kind} id {id}: {e}");
138 return true;
139 }
140 false
141}
142
143pub fn run(args: Args) -> ExitCode {
144 let mut failed = false;
145
146 if let Some(ref filter) = args.all {
147 let do_shm = filter == "all" || filter == "shm";
148 let do_msg = filter == "all" || filter == "msg";
149 let do_sem = filter == "all" || filter == "sem";
150
151 if do_shm {
152 for id in read_ids_from_proc("/proc/sysvipc/shm") {
153 failed |= try_remove("shm", id, rm_shm, args.verbose);
154 }
155 }
156 if do_msg {
157 for id in read_ids_from_proc("/proc/sysvipc/msg") {
158 failed |= try_remove("msg", id, rm_msg, args.verbose);
159 }
160 }
161 if do_sem {
162 for id in read_ids_from_proc("/proc/sysvipc/sem") {
163 failed |= try_remove("sem", id, rm_sem, args.verbose);
164 }
165 }
166 }
167
168 for s in &args.shmem_id {
169 match parse_id(s) {
170 Ok(id) => failed |= try_remove("shm", id, rm_shm, args.verbose),
171 Err(e) => {
172 eprintln!("ipcrm: {e}");
173 failed = true;
174 }
175 }
176 }
177 for s in &args.shmem_key {
178 match parse_id(s)
179 .and_then(|k| key_to_shmid(k).map_err(|e| e.to_string()))
180 {
181 Ok(id) => failed |= try_remove("shm", id, rm_shm, args.verbose),
182 Err(e) => {
183 eprintln!("ipcrm: shm key {s}: {e}");
184 failed = true;
185 }
186 }
187 }
188 for s in &args.queue_id {
189 match parse_id(s) {
190 Ok(id) => failed |= try_remove("msg", id, rm_msg, args.verbose),
191 Err(e) => {
192 eprintln!("ipcrm: {e}");
193 failed = true;
194 }
195 }
196 }
197 for s in &args.queue_key {
198 match parse_id(s)
199 .and_then(|k| key_to_msgid(k).map_err(|e| e.to_string()))
200 {
201 Ok(id) => failed |= try_remove("msg", id, rm_msg, args.verbose),
202 Err(e) => {
203 eprintln!("ipcrm: msg key {s}: {e}");
204 failed = true;
205 }
206 }
207 }
208 for s in &args.semaphore_id {
209 match parse_id(s) {
210 Ok(id) => failed |= try_remove("sem", id, rm_sem, args.verbose),
211 Err(e) => {
212 eprintln!("ipcrm: {e}");
213 failed = true;
214 }
215 }
216 }
217 for s in &args.semaphore_key {
218 match parse_id(s)
219 .and_then(|k| key_to_semid(k).map_err(|e| e.to_string()))
220 {
221 Ok(id) => failed |= try_remove("sem", id, rm_sem, args.verbose),
222 Err(e) => {
223 eprintln!("ipcrm: sem key {s}: {e}");
224 failed = true;
225 }
226 }
227 }
228
229 if failed {
230 ExitCode::FAILURE
231 } else {
232 ExitCode::SUCCESS
233 }
234}