use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint}; use mount::MntEntries; use shitbox::{args, Cmd}; use std::io; #[derive(Debug)] 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")) .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.", ) .args([ Arg::new("all") .help( "All of the file systems described in /proc/mounts are \ unmounted. The proc filesystem is not unmounted.", ) .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), 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), 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> { let mut flags = if matches.get_flag("force") { libc::MNT_FORCE } else { 0 }; if matches.get_flag("lazy") { flags |= libc::MNT_DETACH; } if matches.get_flag("all") { 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::("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; } } } if let Some(spec) = matches.get_many::("spec") { let mut r2 = false; for s in spec { if &x.dir == s || &x.fsname == s { r2 = true; } } 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); } } } else if let Some(spec) = matches.get_many::("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 { Some(shitbox::Path::Bin) } } fn get_mntpt(spec: &str) -> io::Result { let entries = MntEntries::new("/proc/mounts")?; for e in entries { if e.fsname == spec { return Ok(e.dir); } else if e.dir == spec { return Ok(spec.to_string()); } } Err(io::Error::new(io::ErrorKind::Other, "no such mount")) }