Skip to main content

linuxutils_system/
ipcs.rs

1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use std::{
7    fs::{self, File},
8    io::{self, BufRead},
9    process::ExitCode,
10};
11
12/// Show information on System V IPC facilities.
13///
14/// Reads /proc/sysvipc/{shm,msg,sem} to display active IPC resources,
15/// system limits, or usage summaries.
16#[derive(Parser)]
17#[command(name = "ipcs", about = "Show information on System V IPC facilities")]
18pub struct Args {
19    /// Show shared memory segments
20    #[arg(short = 'm', long)]
21    shmems: bool,
22
23    /// Show message queues
24    #[arg(short = 'q', long)]
25    queues: bool,
26
27    /// Show semaphore arrays
28    #[arg(short = 's', long)]
29    semaphores: bool,
30
31    /// Show all three resource types
32    #[arg(short = 'a', long)]
33    all: bool,
34
35    /// Show system resource limits
36    #[arg(short = 'l', long)]
37    limits: bool,
38
39    /// Show usage summary
40    #[arg(short = 'u', long)]
41    summary: bool,
42
43    /// Show sizes in bytes
44    #[arg(short = 'b', long)]
45    bytes: bool,
46}
47
48fn read_proc_val(path: &str) -> String {
49    fs::read_to_string(path)
50        .unwrap_or_default()
51        .trim()
52        .to_string()
53}
54
55fn show_shm_limits() {
56    let shmmax = read_proc_val("/proc/sys/kernel/shmmax");
57    let shmall = read_proc_val("/proc/sys/kernel/shmall");
58    let shmmni = read_proc_val("/proc/sys/kernel/shmmni");
59
60    println!();
61    println!("------ Shared Memory Limits --------");
62    println!("max number of segments = {shmmni}");
63    println!(
64        "max seg size (kbytes) = {}",
65        shmmax.parse::<u64>().unwrap_or(0) / 1024
66    );
67    println!(
68        "max total shared memory (kbytes) = {}",
69        shmall.parse::<u64>().unwrap_or(0) * 4096 / 1024
70    );
71    println!("min seg size (bytes) = 1");
72}
73
74fn show_msg_limits() {
75    let msgmni = read_proc_val("/proc/sys/kernel/msgmni");
76    let msgmax = read_proc_val("/proc/sys/kernel/msgmax");
77    let msgmnb = read_proc_val("/proc/sys/kernel/msgmnb");
78
79    println!();
80    println!("------ Messages Limits --------");
81    println!("max queues system wide = {msgmni}");
82    println!("max size of message (bytes) = {msgmax}");
83    println!("default max size of queue (bytes) = {msgmnb}");
84}
85
86fn show_sem_limits() {
87    let sem = read_proc_val("/proc/sys/kernel/sem");
88    let parts: Vec<&str> = sem.split_whitespace().collect();
89    let semmsl = parts.first().unwrap_or(&"0");
90    let semmns = parts.get(1).unwrap_or(&"0");
91    let semopm = parts.get(2).unwrap_or(&"0");
92    let semmni = parts.get(3).unwrap_or(&"0");
93
94    println!();
95    println!("------ Semaphore Limits --------");
96    println!("max number of arrays = {semmni}");
97    println!("max semaphores per array = {semmsl}");
98    println!("max semaphores system wide = {semmns}");
99    println!("max ops per semop call = {semopm}");
100    println!("semaphore max value = 32767");
101}
102
103fn show_shm_summary() {
104    let count = count_proc_entries("/proc/sysvipc/shm");
105    println!();
106    println!("------ Shared Memory Status --------");
107    println!("segments allocated {count}");
108}
109
110fn show_msg_summary() {
111    let count = count_proc_entries("/proc/sysvipc/msg");
112    println!();
113    println!("------ Messages Status --------");
114    println!("allocated queues = {count}");
115}
116
117fn show_sem_summary() {
118    let count = count_proc_entries("/proc/sysvipc/sem");
119    println!();
120    println!("------ Semaphore Status --------");
121    println!("used arrays = {count}");
122}
123
124fn count_proc_entries(path: &str) -> usize {
125    let Ok(file) = File::open(path) else {
126        return 0;
127    };
128    io::BufReader::new(file)
129        .lines()
130        .map_while(Result::ok)
131        .skip(1)
132        .count()
133}
134
135fn show_shm_list() {
136    println!();
137    println!("------ Shared Memory Segments --------");
138    println!(
139        "{:<10} {:<10} {:<10} {:<10} {:>10} {:>6}     status",
140        "key", "shmid", "owner", "perms", "bytes", "nattch"
141    );
142
143    let Ok(file) = File::open("/proc/sysvipc/shm") else {
144        return;
145    };
146    for line in io::BufReader::new(file)
147        .lines()
148        .map_while(Result::ok)
149        .skip(1)
150    {
151        let f: Vec<&str> = line.split_whitespace().collect();
152        if f.len() >= 14 {
153            let key = format!("0x{:08x}", f[0].parse::<i64>().unwrap_or(0));
154            let uid: u32 = f[7].parse().unwrap_or(0);
155            let owner = uid_to_name(uid);
156            println!(
157                "{:<10} {:<10} {:<10} {:<10} {:>10} {:>6}     ",
158                key, f[1], owner, f[2], f[3], f[5]
159            );
160        }
161    }
162}
163
164fn show_msg_list() {
165    println!();
166    println!("------ Message Queues --------");
167    println!(
168        "{:<10} {:<10} {:<10} {:<10} {:>10} {:>8}",
169        "key", "msqid", "owner", "perms", "used-bytes", "messages"
170    );
171
172    let Ok(file) = File::open("/proc/sysvipc/msg") else {
173        return;
174    };
175    for line in io::BufReader::new(file)
176        .lines()
177        .map_while(Result::ok)
178        .skip(1)
179    {
180        let f: Vec<&str> = line.split_whitespace().collect();
181        if f.len() >= 13 {
182            let key = format!("0x{:08x}", f[0].parse::<i64>().unwrap_or(0));
183            let uid: u32 = f[7].parse().unwrap_or(0);
184            let owner = uid_to_name(uid);
185            println!(
186                "{:<10} {:<10} {:<10} {:<10} {:>10} {:>8}",
187                key, f[1], owner, f[2], f[3], f[4]
188            );
189        }
190    }
191}
192
193fn show_sem_list() {
194    println!();
195    println!("------ Semaphore Arrays --------");
196    println!(
197        "{:<10} {:<10} {:<10} {:<10} {:>5}",
198        "key", "semid", "owner", "perms", "nsems"
199    );
200
201    let Ok(file) = File::open("/proc/sysvipc/sem") else {
202        return;
203    };
204    for line in io::BufReader::new(file)
205        .lines()
206        .map_while(Result::ok)
207        .skip(1)
208    {
209        let f: Vec<&str> = line.split_whitespace().collect();
210        if f.len() >= 9 {
211            let key = format!("0x{:08x}", f[0].parse::<i64>().unwrap_or(0));
212            let uid: u32 = f[4].parse().unwrap_or(0);
213            let owner = uid_to_name(uid);
214            println!(
215                "{:<10} {:<10} {:<10} {:<10} {:>5}",
216                key, f[1], owner, f[2], f[3]
217            );
218        }
219    }
220}
221
222fn uid_to_name(uid: u32) -> String {
223    fs::read_to_string("/etc/passwd")
224        .ok()
225        .and_then(|content| {
226            content.lines().find_map(|line| {
227                let parts: Vec<&str> = line.split(':').collect();
228                if parts.len() >= 3 && parts[2].parse::<u32>().ok() == Some(uid)
229                {
230                    Some(parts[0].to_string())
231                } else {
232                    None
233                }
234            })
235        })
236        .unwrap_or_else(|| uid.to_string())
237}
238
239pub fn run(args: Args) -> ExitCode {
240    let none_specified = !args.shmems && !args.queues && !args.semaphores;
241    let show_shm = args.shmems || args.all || none_specified;
242    let show_msg = args.queues || args.all || none_specified;
243    let show_sem = args.semaphores || args.all || none_specified;
244
245    if args.limits {
246        if show_shm {
247            show_shm_limits();
248        }
249        if show_msg {
250            show_msg_limits();
251        }
252        if show_sem {
253            show_sem_limits();
254        }
255    } else if args.summary {
256        if show_shm {
257            show_shm_summary();
258        }
259        if show_msg {
260            show_msg_summary();
261        }
262        if show_sem {
263            show_sem_summary();
264        }
265    } else {
266        if show_shm {
267            show_shm_list();
268        }
269        if show_msg {
270            show_msg_list();
271        }
272        if show_sem {
273            show_sem_list();
274        }
275    }
276
277    ExitCode::SUCCESS
278}