commit da3eabaf8f3b95eb0545a7564e0b1141cadc3f51 Author: Arne Keller Date: Fri Mar 26 11:24:43 2021 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0bcd358 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,69 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ascii_table" +version = "3.0.2" +source = "git+https://gitlab.com/arnekeller/ascii-table.git?rev=7fffb5d93b8c63283fc1359ee3c6dbfcf12ed90b#7fffb5d93b8c63283fc1359ee3c6dbfcf12ed90b" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "df" +version = "0.1.0" +dependencies = [ + "ascii_table", + "libc", + "procinfo", +] + +[[package]] +name = "libc" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" + +[[package]] +name = "nom" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff" + +[[package]] +name = "procinfo" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab1427f3d2635891f842892dda177883dca0639e05fe66796a62c9d2f23b49c" +dependencies = [ + "byteorder", + "libc", + "nom", + "rustc_version", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a073de5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "df" +version = "0.1.0" +authors = ["Arne Keller "] +license = "GPL-3.0-or-later" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ascii_table = { git = "https://gitlab.com/arnekeller/ascii-table.git", rev = "7fffb5d93b8c63283fc1359ee3c6dbfcf12ed90b" } +libc = "0.2.91" +procinfo = "0.4.2" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5c45697 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,91 @@ +use libc::{c_char, statvfs64, strerror_r}; +use procinfo::pid::mountinfo_self; +use ascii_table::{Align, AsciiTable, Column}; + +use std::{array::IntoIter, ffi::CString, fs, mem::MaybeUninit}; +use std::os::unix::ffi::OsStrExt; + +const IGNORED_FS: &'static [&'static str] = &["proc", "sysfs", "securityfs", "bpf", "cgroup", "cgroup2", "fusectl", "efivarfs", "debugfs"]; +const IGNORED_FS2: &'static [(&'static str, &'static str)] = &[];//("fuse", "portal")]; + +fn main() { + let mut rows = Vec::new(); + let mut devices = Vec::new(); + for mount in mountinfo_self().unwrap() { + if IGNORED_FS.contains(&&*mount.fs_type.0) { + continue; + } + if let Some(second) = mount.fs_type.1.as_ref() { + if IGNORED_FS2.contains(&(&mount.fs_type.0, second)) { + continue; + } + } + // skip multiple FS on the same partition + let device = (mount.major, mount.minor); + if devices.contains(&device) { + continue; + } + devices.push(device); + let mut data = MaybeUninit::::uninit(); + let path = CString::new(mount.mount_point.as_os_str().as_bytes()).unwrap(); + let mut res = unsafe { statvfs64(path.as_ptr(), data.as_mut_ptr()) }; + if res != 0 { + if res < 0 { + res = unsafe { *libc::__errno_location() }; + } + let mut error_buffer = vec![0 as c_char; 256]; + let error: String = unsafe { + let result = strerror_r(res, error_buffer.as_mut_ptr(), error_buffer.len()); + if result != 0 { + eprintln!("fail {}", result); + res.to_string().into() + } else { + error_buffer.into_iter().take_while(|&x| x != 0).map(|x| x as u8 as char).collect() + } + }; + eprintln!("df: {}: {}", mount.mount_point.display(), error); + continue; + } + let data = unsafe { data.assume_init() }; + if data.f_blocks == 0 { + continue; // metadata is useless + } + + let typ = match mount.mount_src.as_deref() { + Some(path) if path.starts_with('/') => { + fs::canonicalize(path).unwrap().display().to_string() + }, + _ => mount.fs_type.0 + }; + rows.push((typ, data.f_blocks, data.f_bfree, mount.mount_point)); + } + let mut ascii_table = AsciiTable::default(); + ascii_table.draw_lines = false; + + for (i, &(header, align)) in [ + ("Filesystem", Align::Left), + ("Blocks", Align::Right), + ("Used", Align::Right), + ("Free", Align::Right), + ("Use%", Align::Right), + ("Mounted on", Align::Left) + ].iter().enumerate() { + let mut column = Column::default(); + column.header = header.to_owned(); + column.align = align; + ascii_table.columns.insert(i, column); + } + + ascii_table.print(rows.into_iter().map( + |(typ, blocks, free, path)| IntoIter::new( + [ + typ, + blocks.to_string(), + (blocks-free).to_string(), + free.to_string(), + format!("{:.0}%", ((blocks-free) as f64 * 100.0 / blocks as f64).ceil()), + path.display().to_string() + ] + ) + )); +}