Skip to content

Commit e0b0bd7

Browse files
committed
feat: inspect-platform
1 parent bb055d8 commit e0b0bd7

File tree

4 files changed

+426
-1
lines changed

4 files changed

+426
-1
lines changed

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ exclude = [
2222
"/Cargo.lock",
2323
]
2424

25+
[[bin]]
26+
name = "inspect-platform"
27+
path = "inspect-platform/main.rs"
28+
test = false
29+
2530
[[example]]
2631
name = "unnamed_pipe_sync"
2732
path = "examples/unnamed_pipe/sync/main.rs"
@@ -129,9 +134,9 @@ rust_2018_idioms = { level = "deny", priority = -1 }
129134
dead_code = "allow"
130135

131136
[lints.clippy]
132-
exit = "forbid"
133137
ptr_as_ptr = "forbid"
134138

139+
exit = "deny"
135140
get_unwrap = "deny"
136141
as_conversions = "deny"
137142

inspect-platform/libc_wrappers.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use {
2+
super::*,
3+
libc::mode_t,
4+
std::{
5+
ffi::CStr,
6+
io,
7+
mem::MaybeUninit,
8+
os::unix::{ffi::OsStrExt as _, net::UnixListener, prelude::*},
9+
path::Path,
10+
},
11+
};
12+
13+
trait RetExt: Sized {
14+
fn is_ok(&self) -> bool;
15+
fn val_or_errno<T>(self, f: impl FnOnce(Self) -> T) -> io::Result<T> {
16+
if self.is_ok() {
17+
Ok(f(self))
18+
} else {
19+
Err(io::Error::last_os_error())
20+
}
21+
}
22+
fn ok_or_errno(self) -> io::Result<()> { self.val_or_errno(drop) }
23+
}
24+
impl RetExt for c_int {
25+
fn is_ok(&self) -> bool { *self >= 0 }
26+
}
27+
28+
pub fn stat(path: &CStr) -> io::Result<libc::stat> {
29+
let mut out = MaybeUninit::uninit();
30+
unsafe { libc::stat(path.as_ptr(), out.as_mut_ptr()) }
31+
.val_or_errno(|_| unsafe { out.assume_init() })
32+
}
33+
pub fn fstat(fd: BorrowedFd<'_>) -> io::Result<libc::stat> {
34+
let mut out = MaybeUninit::uninit();
35+
unsafe { libc::fstat(fd.as_raw_fd(), out.as_mut_ptr()) }
36+
.val_or_errno(|_| unsafe { out.assume_init() })
37+
}
38+
pub fn fchmod(fd: BorrowedFd<'_>, mode: mode_t) -> io::Result<()> {
39+
unsafe { libc::fchmod(fd.as_raw_fd(), mode) }.ok_or_errno()
40+
}
41+
42+
#[allow(clippy::as_conversions)]
43+
fn sockaddr_un_init(
44+
sau: &mut MaybeUninit<libc::sockaddr_un>,
45+
path: &Path,
46+
) -> io::Result<libc::socklen_t> {
47+
const SUN_PATH_LEN: usize = {
48+
let sau = unsafe { std::mem::zeroed::<libc::sockaddr_un>() };
49+
std::mem::size_of_val(&sau.sun_path)
50+
};
51+
52+
std::os::unix::net::SocketAddr::from_pathname(path)?;
53+
54+
let sauptr = sau.as_mut_ptr();
55+
let path_bytes = path.as_os_str().as_bytes();
56+
let pathlen = path_bytes.len();
57+
assert!(pathlen <= SUN_PATH_LEN);
58+
unsafe {
59+
(&raw mut (*sauptr).sun_family).write(libc::AF_UNIX as _);
60+
let pathbase = (&raw mut (*sauptr).sun_path).cast::<c_char>();
61+
std::ptr::copy_nonoverlapping(path_bytes.as_ptr().cast(), pathbase, path_bytes.len());
62+
if path_bytes.len() != SUN_PATH_LEN {
63+
pathbase.add(pathlen).write(0);
64+
}
65+
}
66+
let addrlen = std::mem::offset_of!(libc::sockaddr_un, sun_path) + pathlen;
67+
Ok(addrlen as _)
68+
}
69+
70+
pub fn bind_with_hook(
71+
path: &Path,
72+
f: impl FnOnce(BorrowedFd<'_>) -> io::Result<()>,
73+
) -> io::Result<UnixListener> {
74+
let mut sau = MaybeUninit::<libc::sockaddr_un>::uninit();
75+
let addrlen = sockaddr_un_init(&mut sau, path)?;
76+
77+
let fd = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_STREAM, 0) }
78+
.val_or_errno(|fd| unsafe { OwnedFd::from_raw_fd(fd) })?;
79+
f(fd.as_fd())?;
80+
unsafe { libc::bind(fd.as_raw_fd(), sau.as_ptr().cast(), addrlen) }.ok_or_errno()?;
81+
Ok(fd.into())
82+
}
83+
84+
pub fn umask(mask: mode_t) -> UmaskGuard { UmaskGuard(unsafe { libc::umask(mask) }) }
85+
#[derive(Debug)]
86+
#[repr(transparent)]
87+
pub struct UmaskGuard(mode_t);
88+
impl Drop for UmaskGuard {
89+
fn drop(&mut self) { std::mem::forget(umask(self.0)) }
90+
}

inspect-platform/main.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![allow(clippy::exit, clippy::incompatible_msrv)]
2+
use std::ffi::{c_char, c_int, c_long, c_longlong, c_short};
3+
4+
macro_rules! bitwidths {
5+
($($nam:ident),+ $(,)?) => {[$((stringify!($nam), $nam::BITS)),+]};
6+
}
7+
#[allow(unused_macros)]
8+
macro_rules! sizes {
9+
($($nam:ident),+ $(,)?) => {[$((stringify!($nam), ::std::mem::size_of::<$nam>())),+]};
10+
}
11+
12+
#[cfg(unix)]
13+
mod libc_wrappers;
14+
#[cfg(unix)]
15+
mod unix;
16+
17+
fn maxlen<T>(a: &[(&str, T)]) -> usize { a.iter().map(|&(nm, _)| nm.len()).max().unwrap_or(0) }
18+
fn print_bitwidths(bw: &[(&str, u32)]) {
19+
let width = maxlen(bw);
20+
bw.iter().for_each(|&(nm, bw)| println!("{nm:width$} : {bw:>2} bits"));
21+
}
22+
#[allow(dead_code)]
23+
fn print_sizes(sz: &[(&str, usize)]) {
24+
let width = maxlen(sz);
25+
sz.iter().for_each(|&(nm, sz)| println!("{nm:width$} : {sz:>3} bytes"));
26+
}
27+
fn print_signedness(nm: &str, signed: bool) {
28+
println!("{nm} is {}signed", if signed { "" } else { "un" })
29+
}
30+
31+
fn print_common_intro() {
32+
print_bitwidths(&bitwidths!(usize, c_char, c_short, c_int, c_long, c_longlong));
33+
print_signedness("c_char", c_char::MIN != 0);
34+
}
35+
36+
fn main() {
37+
print_common_intro();
38+
#[cfg(unix)]
39+
unix::main();
40+
#[cfg(not(unix))]
41+
println!("Not a Unix system, no further information will be gathered.");
42+
}

0 commit comments

Comments
 (0)