Add swapoff
applet
This commit is contained in:
parent
565e2c52d4
commit
ebebf2b4e5
@ -60,6 +60,7 @@ code between applets, making for an overall smaller binary.
|
||||
- sha512sum
|
||||
- sleep
|
||||
- swaplabel
|
||||
- swapoff
|
||||
- sync
|
||||
- true
|
||||
- umount
|
||||
@ -80,6 +81,11 @@ for the project include:
|
||||
The code is not intended to be portable across different architectures, only
|
||||
Linux is supported.
|
||||
|
||||
Full compatability with GNU coreutils is not intended. Compliance with POSIX is
|
||||
the main goal, with extensions provided by GNU or BSD where they make sense, and
|
||||
certain tweaks to improve consistency of behavior where they do not interfere
|
||||
with POSIX compliance.
|
||||
|
||||
## Installation
|
||||
Building is done using the official Rust toolchain. It is recommended that you
|
||||
install your toolchain using Rustup rather than distro packages, as old compiler
|
||||
|
@ -35,8 +35,8 @@ impl Cmd for Sha224sum {
|
||||
}
|
||||
}
|
||||
if erred > 0 {
|
||||
println!("sha224sum: WARNING: {erred} computed checksum did NOT match");
|
||||
process::exit(1);
|
||||
let msg = format!("WARNING: {erred} computed checksums did NOT match");
|
||||
return Err(io::Error::new(io::ErrorKind::Other, msg).into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -12,7 +12,7 @@ fn main() {
|
||||
process::exit(1);
|
||||
}
|
||||
} else {
|
||||
eprintln!("shitbox: Error: unknown command {progname}");
|
||||
eprintln!("hashbox: Error: unknown command {progname}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,15 @@ pub fn swapon(dev: &str, flags: usize) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swapoff(path: &str) -> io::Result<()> {
|
||||
let ret = unsafe { syscall!(SWAPOFF, CString::new(path)?.as_ptr()) };
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn umount(special: &str, flags: u32) -> io::Result<()> {
|
||||
let ret = unsafe { syscall!(UMOUNT2, CString::new(special)?.as_ptr(), flags) };
|
||||
if ret == 0 {
|
||||
|
@ -8,8 +8,7 @@ use blkid::{
|
||||
use std::{
|
||||
fmt::{self, Write},
|
||||
fs::File,
|
||||
io,
|
||||
os::unix::fs::MetadataExt,
|
||||
io::{self, BufRead, BufReader},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
@ -48,6 +47,30 @@ impl MntEntry {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_swapped(&self) -> bool {
|
||||
if self.fstype != "swap" {
|
||||
return false;
|
||||
}
|
||||
let dev = self.device().map(|x| x.to_str().map(|x| x.to_string()));
|
||||
let Ok(Some(dev)) = dev else { return false };
|
||||
let Ok(fd) = File::open("/proc/swaps") else {
|
||||
return false;
|
||||
};
|
||||
let reader = BufReader::new(fd);
|
||||
for line in reader.lines() {
|
||||
let Ok(line) = line else {
|
||||
return false;
|
||||
};
|
||||
let swap = line.split_whitespace().next();
|
||||
if let Some(swap) = swap {
|
||||
if swap == &dev {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn parse_opts(&self) -> Result<(u32, String), fmt::Error> {
|
||||
let mut res = (0, String::new());
|
||||
let splitopts = self.opts.split(',');
|
||||
@ -93,21 +116,7 @@ impl MntEntry {
|
||||
s if s.starts_with("LABEL=") => dev_from_label(s),
|
||||
s if s.starts_with("PARTUUID=") => dev_from_partuuid(s),
|
||||
s if s.starts_with("PARTLABEL=") => dev_from_partlabel(s),
|
||||
s => {
|
||||
let mut p = PathBuf::from(s);
|
||||
if p.exists() {
|
||||
p = p.canonicalize()?;
|
||||
let fd = File::open(&p)?;
|
||||
let meta = fd.metadata()?;
|
||||
if meta.rdev() != 0 {
|
||||
Ok(p)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "not a block device"))
|
||||
}
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "no such file"))
|
||||
}
|
||||
}
|
||||
s => PathBuf::from(s).canonicalize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,22 +171,17 @@ fn dev_from_uuid(s: &str) -> io::Result<PathBuf> {
|
||||
"this should never happen",
|
||||
))?;
|
||||
let cache = Cache::new().map_err(|x| io::Error::new(io::ErrorKind::Other, x))?;
|
||||
cache
|
||||
.probe_all()
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
let devs = cache.devs();
|
||||
for dev in devs {
|
||||
let tags = dev.tags();
|
||||
for tag in tags {
|
||||
match tag.typ() {
|
||||
TagType::Superblock(SuperblockTag::Uuid) => {
|
||||
if uuid.to_lowercase().as_str() == tag.value() {
|
||||
let devname = dev.name();
|
||||
let devfile = devname.canonicalize()?;
|
||||
let fd = File::open(&devfile)?;
|
||||
let meta = fd.metadata()?;
|
||||
if meta.rdev() != 0 {
|
||||
return Ok(devfile);
|
||||
} else {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not a block device"));
|
||||
}
|
||||
if uuid.to_lowercase().as_str().trim() == tag.value().trim() {
|
||||
return dev.name().canonicalize();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -200,15 +204,7 @@ fn dev_from_label(s: &str) -> io::Result<PathBuf> {
|
||||
match tag.typ() {
|
||||
TagType::Superblock(SuperblockTag::Label) => {
|
||||
if label == tag.value() {
|
||||
let devname = dev.name();
|
||||
let devfile = devname.canonicalize()?;
|
||||
let fd = File::open(&devfile)?;
|
||||
let meta = fd.metadata()?;
|
||||
if meta.rdev() != 0 {
|
||||
return Ok(devfile);
|
||||
} else {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not a block device"));
|
||||
}
|
||||
return dev.name().canonicalize();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -231,15 +227,7 @@ fn dev_from_partuuid(s: &str) -> io::Result<PathBuf> {
|
||||
match tag.typ() {
|
||||
TagType::Partition(PartitionTag::PartEntryUuid) => {
|
||||
if partlabel == tag.value() {
|
||||
let devname = dev.name();
|
||||
let devfile = devname.canonicalize()?;
|
||||
let fd = File::open(&devfile)?;
|
||||
let meta = fd.metadata()?;
|
||||
if meta.rdev() != 0 {
|
||||
return Ok(devfile);
|
||||
} else {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not a block device"));
|
||||
}
|
||||
return dev.name().canonicalize();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -262,15 +250,7 @@ fn dev_from_partlabel(s: &str) -> io::Result<PathBuf> {
|
||||
match tag.typ() {
|
||||
TagType::Partition(PartitionTag::PartEntryName) => {
|
||||
if partlabel == tag.value() {
|
||||
let devname = dev.name();
|
||||
let devfile = devname.canonicalize()?;
|
||||
let fd = File::open(&devfile)?;
|
||||
let meta = fd.metadata()?;
|
||||
if meta.rdev() != 0 {
|
||||
return Ok(devfile);
|
||||
} else {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not a block device"));
|
||||
}
|
||||
return dev.name().canonicalize();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
@ -18,10 +18,18 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
||||
"clear" => Some(Box::new(clear::Clear::default())),
|
||||
"mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())),
|
||||
"swaplabel" => Some(Box::new(swaplabel::Swaplabel::default())),
|
||||
"swapoff" => Some(Box::new(swapoff::Swapoff::default())),
|
||||
"umount" => Some(Box::new(umount::Umount::default())),
|
||||
"utilbox" => Some(Box::new(utilbox::Utilbox::default())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub static COMMANDS: [&str; 5] = ["clear", "mountpoint", "swaplabel", "umount", "utilbox"];
|
||||
pub static COMMANDS: [&str; 6] = [
|
||||
"clear",
|
||||
"mountpoint",
|
||||
"swaplabel",
|
||||
"swapoff",
|
||||
"umount",
|
||||
"utilbox",
|
||||
];
|
||||
|
@ -1 +1,77 @@
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use mount::MntEntries;
|
||||
use shitbox::{args, Cmd};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufRead, BufReader},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Swapoff;
|
||||
|
||||
impl Cmd for Swapoff {
|
||||
fn cli(&self) -> clap::Command {
|
||||
Command::new("swapoff")
|
||||
.about("disable devices and files for paging and swapping")
|
||||
.author("Nathan Fisher")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.args([
|
||||
Arg::new("all")
|
||||
.help("Disable swapping on all known swap devices and files as found in /etc/fstab.")
|
||||
.short('a')
|
||||
.long("all")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::verbose(),
|
||||
Arg::new("device")
|
||||
.num_args(1..)
|
||||
.conflicts_with("all")
|
||||
.required_unless_present("all"),
|
||||
])
|
||||
}
|
||||
|
||||
fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if matches.get_flag("all") {
|
||||
if let Ok(entries) = MntEntries::new("/etc/fstab") {
|
||||
for e in entries.filter(|x| &x.fstype == "swap") {
|
||||
if e.is_swapped() {
|
||||
let dev = e
|
||||
.device()?
|
||||
.to_str()
|
||||
.ok_or(io::Error::new(io::ErrorKind::Other, "utf8 error"))?
|
||||
.to_string();
|
||||
mount::swapoff(&dev)?;
|
||||
if matches.get_flag("verbose") {
|
||||
println!("swapoff {dev}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(devices) = matches.get_many::<String>("device") {
|
||||
for d in devices {
|
||||
let fd = File::open("/proc/swaps")?;
|
||||
let reader = BufReader::new(fd);
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
let swap = line.split_whitespace().next().ok_or(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Error reading /proc/swaps",
|
||||
))?;
|
||||
if swap == d {
|
||||
mount::swapoff(d)?;
|
||||
if matches.get_flag("verbose") {
|
||||
println!("swapoff {d}");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let msg = format!("{d} is not swapped");
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, msg)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<shitbox::Path> {
|
||||
Some(shitbox::Path::Sbin)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user