linuxutils_system/
pipesz.rs1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use std::{
7 fs::File,
8 os::unix::io::{AsRawFd, RawFd},
9 process::ExitCode,
10};
11
12#[derive(Parser)]
17#[command(name = "pipesz", about = "Set or examine pipe and FIFO buffer sizes")]
18pub struct Args {
19 #[arg(short = 'g', long)]
21 get: bool,
22
23 #[arg(short = 's', long)]
25 set: Option<i32>,
26
27 #[arg(short = 'f', long = "file", action = clap::ArgAction::Append)]
29 file: Vec<String>,
30
31 #[arg(short = 'n', long = "fd", action = clap::ArgAction::Append)]
33 fd: Vec<RawFd>,
34
35 #[arg(short = 'i', long)]
37 stdin: bool,
38
39 #[arg(short = 'o', long)]
41 stdout: bool,
42
43 #[arg(short = 'e', long)]
45 stderr: bool,
46
47 #[arg(short = 'c', long)]
49 check: bool,
50
51 #[arg(short = 'q', long)]
53 quiet: bool,
54
55 #[arg(short = 'v', long)]
57 verbose: bool,
58
59 #[arg(trailing_var_arg = true)]
61 command: Vec<String>,
62}
63
64fn get_pipe_sz(fd: RawFd) -> Result<i32, std::io::Error> {
65 let ret = unsafe { libc::fcntl(fd, libc::F_GETPIPE_SZ) };
66 if ret < 0 {
67 Err(std::io::Error::last_os_error())
68 } else {
69 Ok(ret)
70 }
71}
72
73fn set_pipe_sz(fd: RawFd, size: i32) -> Result<i32, std::io::Error> {
74 let ret = unsafe { libc::fcntl(fd, libc::F_SETPIPE_SZ, size) };
75 if ret < 0 {
76 Err(std::io::Error::last_os_error())
77 } else {
78 Ok(ret)
79 }
80}
81
82fn get_unread(fd: RawFd) -> i32 {
83 let mut count: libc::c_int = 0;
84 if unsafe { libc::ioctl(fd, libc::FIONREAD, &mut count) } < 0 {
85 0
86 } else {
87 count
88 }
89}
90
91fn fd_name(fd: RawFd) -> String {
92 match fd {
93 0 => "stdin".to_string(),
94 1 => "stdout".to_string(),
95 2 => "stderr".to_string(),
96 n => format!("fd {n}"),
97 }
98}
99
100fn collect_fds(args: &Args) -> Vec<(String, RawFd, Option<File>)> {
101 let mut fds: Vec<(String, RawFd, Option<File>)> = Vec::new();
102
103 if args.stdin {
104 fds.push(("stdin".to_string(), 0, None));
105 }
106 if args.stdout {
107 fds.push(("stdout".to_string(), 1, None));
108 }
109 if args.stderr {
110 fds.push(("stderr".to_string(), 2, None));
111 }
112 for &n in &args.fd {
113 fds.push((fd_name(n), n, None));
114 }
115 for path in &args.file {
116 match File::options().read(true).write(true).open(path) {
117 Ok(f) => {
118 let raw = f.as_raw_fd();
119 fds.push((path.clone(), raw, Some(f)));
120 }
121 Err(e) => {
122 if !args.quiet {
123 eprintln!("pipesz: {path}: {e}");
124 }
125 }
126 }
127 }
128 fds
129}
130
131pub fn run(args: Args) -> ExitCode {
132 let mut fds = collect_fds(&args);
133
134 if args.get {
135 if fds.is_empty() {
136 fds.push(("stdin".to_string(), 0, None));
137 }
138 if args.verbose {
139 println!("name\tsize\tunread");
140 }
141 let mut failed = false;
142 for (name, fd, _file) in &fds {
143 match get_pipe_sz(*fd) {
144 Ok(sz) => {
145 let unread = get_unread(*fd);
146 println!("{name}\t{sz}\t{unread}");
147 }
148 Err(e) => {
149 eprintln!("pipesz: {name}: {e}");
150 failed = true;
151 if args.check {
152 return ExitCode::FAILURE;
153 }
154 }
155 }
156 }
157 return if failed {
158 ExitCode::FAILURE
159 } else {
160 ExitCode::SUCCESS
161 };
162 }
163
164 if let Some(size) = args.set {
165 if fds.is_empty() {
166 fds.push(("stdout".to_string(), 1, None));
167 }
168 let mut failed = false;
169 for (name, fd, _file) in &fds {
170 match set_pipe_sz(*fd, size) {
171 Ok(actual) => {
172 if args.verbose {
173 println!("{name}\t{actual}");
174 }
175 }
176 Err(e) => {
177 eprintln!("pipesz: {name}: {e}");
178 failed = true;
179 if args.check {
180 return ExitCode::FAILURE;
181 }
182 }
183 }
184 }
185
186 if !args.command.is_empty() {
187 let err = std::os::unix::process::CommandExt::exec(
188 std::process::Command::new(&args.command[0])
189 .args(&args.command[1..]),
190 );
191 eprintln!("pipesz: failed to exec '{}': {err}", args.command[0]);
192 return ExitCode::FAILURE;
193 }
194
195 return if failed {
196 ExitCode::FAILURE
197 } else {
198 ExitCode::SUCCESS
199 };
200 }
201
202 eprintln!("pipesz: one of --get or --set must be specified");
203 ExitCode::FAILURE
204}