2023-02-06 18:57:28 -05:00
|
|
|
use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint};
|
|
|
|
use mount::MntEntries;
|
|
|
|
use shitbox::{args, Cmd};
|
2023-02-07 00:13:42 -05:00
|
|
|
use std::io;
|
2023-02-06 18:57:28 -05:00
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub struct Umount;
|
|
|
|
|
|
|
|
impl Cmd for Umount {
|
|
|
|
fn cli(&self) -> Command {
|
|
|
|
Command::new("umount")
|
|
|
|
.about("unmount filesystems")
|
|
|
|
.author("Nathan Fisher")
|
|
|
|
.version(env!("CARGO_PKG_VERSION"))
|
2023-02-07 00:13:42 -05:00
|
|
|
.before_long_help(
|
|
|
|
"umount detaches the target filesystem(s). A file system is specified \
|
|
|
|
by giving the directory where it has been mounted. Giving the special \
|
|
|
|
device on which the file system lives may also work, but is obsolete.",
|
|
|
|
)
|
2023-02-06 18:57:28 -05:00
|
|
|
.args([
|
|
|
|
Arg::new("all")
|
|
|
|
.help(
|
2023-02-07 00:13:42 -05:00
|
|
|
"All of the file systems described in /proc/mounts are \
|
|
|
|
unmounted. The proc filesystem is not unmounted.",
|
2023-02-06 18:57:28 -05:00
|
|
|
)
|
|
|
|
.short('a')
|
|
|
|
.long("all")
|
|
|
|
.action(ArgAction::SetTrue),
|
|
|
|
Arg::new("force")
|
|
|
|
.help("Force an unmount (in case of an unreachable NFS system).")
|
|
|
|
.short('f')
|
|
|
|
.long("force")
|
|
|
|
.action(ArgAction::SetTrue),
|
2023-02-07 00:13:42 -05:00
|
|
|
Arg::new("lazy")
|
|
|
|
.help(
|
|
|
|
"Lazy unmount. Detach the filesystem from the fs hierarchy \
|
|
|
|
now, and cleanup all references to the filesystem as soon \
|
|
|
|
as it is not busy anymore.",
|
|
|
|
)
|
|
|
|
.short('l')
|
|
|
|
.long("lazy")
|
|
|
|
.action(ArgAction::SetTrue),
|
|
|
|
Arg::new("nomtab")
|
|
|
|
.help(
|
|
|
|
"Unmount without writing in /etc/mtab. This is the default action. \
|
|
|
|
This flag exists only for historical compatability.",
|
|
|
|
)
|
|
|
|
.short('n')
|
|
|
|
.action(ArgAction::SetTrue),
|
2023-02-06 18:57:28 -05:00
|
|
|
Arg::new("types")
|
|
|
|
.help(
|
|
|
|
"Indicate that the actions should only be taken on filesystems \
|
|
|
|
of the specified type.",
|
|
|
|
)
|
|
|
|
.long_help(
|
|
|
|
"Indicate that the actions should only be taken on filesystems \
|
|
|
|
of the specified type. More than one type may be specified in a \
|
|
|
|
comma-separated list. The list of filesystem types can be prefixed \
|
|
|
|
with no to indicate that no action should be taken for all of the \
|
|
|
|
mentioned types.",
|
|
|
|
)
|
|
|
|
.short('t')
|
|
|
|
.long("types")
|
|
|
|
.value_delimiter(',')
|
|
|
|
.value_name("type")
|
|
|
|
.requires("all")
|
|
|
|
.num_args(1..),
|
|
|
|
args::verbose(),
|
|
|
|
Arg::new("spec")
|
|
|
|
.value_name("directory|device")
|
|
|
|
.value_hint(ValueHint::AnyPath)
|
|
|
|
.num_args(0..)
|
|
|
|
.required_unless_present("all"),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run(&self, matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
|
2023-02-07 00:13:42 -05:00
|
|
|
let mut flags = if matches.get_flag("force") {
|
2023-02-06 18:57:28 -05:00
|
|
|
libc::MNT_FORCE
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
2023-02-07 00:13:42 -05:00
|
|
|
if matches.get_flag("lazy") {
|
|
|
|
flags |= libc::MNT_DETACH;
|
|
|
|
}
|
2023-02-06 18:57:28 -05:00
|
|
|
if matches.get_flag("all") {
|
2023-02-07 00:13:42 -05:00
|
|
|
let mntpts = MntEntries::new("/proc/mounts")?.filter(|x| {
|
|
|
|
let mut ret = x.fstype != "proc"
|
|
|
|
&& x.fstype != "sysfs"
|
|
|
|
&& x.fstype != "devfs"
|
|
|
|
&& x.fstype != "devpts"
|
|
|
|
&& x.fstype != "rpc_pipefs"
|
|
|
|
&& x.fstype != "nfsd"
|
|
|
|
&& x.fstype != "swap"
|
|
|
|
&& x.fsname != "/";
|
|
|
|
if let Some(types) = matches.get_many::<String>("types") {
|
|
|
|
for t in types {
|
|
|
|
if t.starts_with("no") {
|
|
|
|
let t = t.strip_prefix("no").unwrap();
|
|
|
|
if x.fstype == t {
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if &x.fstype != t {
|
|
|
|
ret = false;
|
2023-02-06 18:57:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-07 00:13:42 -05:00
|
|
|
if let Some(spec) = matches.get_many::<String>("spec") {
|
|
|
|
let mut r2 = false;
|
|
|
|
for s in spec {
|
|
|
|
if &x.dir == s || &x.fsname == s {
|
|
|
|
r2 = true;
|
|
|
|
}
|
2023-02-06 18:57:28 -05:00
|
|
|
}
|
2023-02-07 00:13:42 -05:00
|
|
|
if !r2 {
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
});
|
|
|
|
for p in mntpts {
|
|
|
|
mount::umount(&p.dir, flags as u32)?;
|
|
|
|
if matches.get_flag("verbose") {
|
|
|
|
println!("umount: {} unmounted", &p.dir);
|
2023-02-06 18:57:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if let Some(spec) = matches.get_many::<String>("spec") {
|
|
|
|
for s in spec {
|
|
|
|
let mntpt = get_mntpt(s)?;
|
|
|
|
mount::umount(&mntpt, flags as u32)?;
|
|
|
|
if matches.get_flag("verbose") {
|
|
|
|
println!("umount: {mntpt} unmounted");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn path(&self) -> Option<shitbox::Path> {
|
|
|
|
Some(shitbox::Path::Bin)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_mntpt(spec: &str) -> io::Result<String> {
|
|
|
|
let entries = MntEntries::new("/proc/mounts")?;
|
|
|
|
for e in entries {
|
|
|
|
if e.fsname == spec {
|
|
|
|
return Ok(e.dir.clone());
|
|
|
|
} else if e.dir == spec {
|
|
|
|
return Ok(spec.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(io::Error::new(io::ErrorKind::Other, "no such mount"))
|
|
|
|
}
|