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
|
- sha512sum
|
||||||
- sleep
|
- sleep
|
||||||
- swaplabel
|
- swaplabel
|
||||||
|
- swapoff
|
||||||
- sync
|
- sync
|
||||||
- true
|
- true
|
||||||
- umount
|
- umount
|
||||||
@ -80,6 +81,11 @@ for the project include:
|
|||||||
The code is not intended to be portable across different architectures, only
|
The code is not intended to be portable across different architectures, only
|
||||||
Linux is supported.
|
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
|
## Installation
|
||||||
Building is done using the official Rust toolchain. It is recommended that you
|
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
|
install your toolchain using Rustup rather than distro packages, as old compiler
|
||||||
|
@ -35,8 +35,8 @@ impl Cmd for Sha224sum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if erred > 0 {
|
if erred > 0 {
|
||||||
println!("sha224sum: WARNING: {erred} computed checksum did NOT match");
|
let msg = format!("WARNING: {erred} computed checksums did NOT match");
|
||||||
process::exit(1);
|
return Err(io::Error::new(io::ErrorKind::Other, msg).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -12,7 +12,7 @@ fn main() {
|
|||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("shitbox: Error: unknown command {progname}");
|
eprintln!("hashbox: Error: unknown command {progname}");
|
||||||
process::exit(1);
|
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<()> {
|
pub fn umount(special: &str, flags: u32) -> io::Result<()> {
|
||||||
let ret = unsafe { syscall!(UMOUNT2, CString::new(special)?.as_ptr(), flags) };
|
let ret = unsafe { syscall!(UMOUNT2, CString::new(special)?.as_ptr(), flags) };
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
|
@ -8,8 +8,7 @@ use blkid::{
|
|||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Write},
|
fmt::{self, Write},
|
||||||
fs::File,
|
fs::File,
|
||||||
io,
|
io::{self, BufRead, BufReader},
|
||||||
os::unix::fs::MetadataExt,
|
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
@ -48,6 +47,30 @@ impl MntEntry {
|
|||||||
false
|
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> {
|
pub fn parse_opts(&self) -> Result<(u32, String), fmt::Error> {
|
||||||
let mut res = (0, String::new());
|
let mut res = (0, String::new());
|
||||||
let splitopts = self.opts.split(',');
|
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("LABEL=") => dev_from_label(s),
|
||||||
s if s.starts_with("PARTUUID=") => dev_from_partuuid(s),
|
s if s.starts_with("PARTUUID=") => dev_from_partuuid(s),
|
||||||
s if s.starts_with("PARTLABEL=") => dev_from_partlabel(s),
|
s if s.starts_with("PARTLABEL=") => dev_from_partlabel(s),
|
||||||
s => {
|
s => PathBuf::from(s).canonicalize(),
|
||||||
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"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,22 +171,17 @@ fn dev_from_uuid(s: &str) -> io::Result<PathBuf> {
|
|||||||
"this should never happen",
|
"this should never happen",
|
||||||
))?;
|
))?;
|
||||||
let cache = Cache::new().map_err(|x| io::Error::new(io::ErrorKind::Other, x))?;
|
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();
|
let devs = cache.devs();
|
||||||
for dev in devs {
|
for dev in devs {
|
||||||
let tags = dev.tags();
|
let tags = dev.tags();
|
||||||
for tag in tags {
|
for tag in tags {
|
||||||
match tag.typ() {
|
match tag.typ() {
|
||||||
TagType::Superblock(SuperblockTag::Uuid) => {
|
TagType::Superblock(SuperblockTag::Uuid) => {
|
||||||
if uuid.to_lowercase().as_str() == tag.value() {
|
if uuid.to_lowercase().as_str().trim() == tag.value().trim() {
|
||||||
let devname = dev.name();
|
return dev.name().canonicalize();
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -200,15 +204,7 @@ fn dev_from_label(s: &str) -> io::Result<PathBuf> {
|
|||||||
match tag.typ() {
|
match tag.typ() {
|
||||||
TagType::Superblock(SuperblockTag::Label) => {
|
TagType::Superblock(SuperblockTag::Label) => {
|
||||||
if label == tag.value() {
|
if label == tag.value() {
|
||||||
let devname = dev.name();
|
return dev.name().canonicalize();
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -231,15 +227,7 @@ fn dev_from_partuuid(s: &str) -> io::Result<PathBuf> {
|
|||||||
match tag.typ() {
|
match tag.typ() {
|
||||||
TagType::Partition(PartitionTag::PartEntryUuid) => {
|
TagType::Partition(PartitionTag::PartEntryUuid) => {
|
||||||
if partlabel == tag.value() {
|
if partlabel == tag.value() {
|
||||||
let devname = dev.name();
|
return dev.name().canonicalize();
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -262,15 +250,7 @@ fn dev_from_partlabel(s: &str) -> io::Result<PathBuf> {
|
|||||||
match tag.typ() {
|
match tag.typ() {
|
||||||
TagType::Partition(PartitionTag::PartEntryName) => {
|
TagType::Partition(PartitionTag::PartEntryName) => {
|
||||||
if partlabel == tag.value() {
|
if partlabel == tag.value() {
|
||||||
let devname = dev.name();
|
return dev.name().canonicalize();
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -18,10 +18,18 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
|||||||
"clear" => Some(Box::new(clear::Clear::default())),
|
"clear" => Some(Box::new(clear::Clear::default())),
|
||||||
"mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())),
|
"mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())),
|
||||||
"swaplabel" => Some(Box::new(swaplabel::Swaplabel::default())),
|
"swaplabel" => Some(Box::new(swaplabel::Swaplabel::default())),
|
||||||
|
"swapoff" => Some(Box::new(swapoff::Swapoff::default())),
|
||||||
"umount" => Some(Box::new(umount::Umount::default())),
|
"umount" => Some(Box::new(umount::Umount::default())),
|
||||||
"utilbox" => Some(Box::new(utilbox::Utilbox::default())),
|
"utilbox" => Some(Box::new(utilbox::Utilbox::default())),
|
||||||
_ => None,
|
_ => 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