diff --git a/Cargo.toml b/Cargo.toml index c82269f..3d3641f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,10 @@ path = "corebox/main.rs" name = "hashbox" path = "hashbox/main.rs" +[[bin]] +name = "utilbox" +path = "utilbox/main.rs" + [workspace.dependencies] libc = "0.2" sc = "0.2" diff --git a/corebox/commands/base32/mod.rs b/corebox/commands/base32/mod.rs index 83fee48..8936354 100644 --- a/corebox/commands/base32/mod.rs +++ b/corebox/commands/base32/mod.rs @@ -1,7 +1,7 @@ use super::Cmd; -use shitbox::args; use clap::{value_parser, Arg, ArgAction, Command}; use data_encoding::BASE32; +use shitbox::args; use std::{ error::Error, fs, diff --git a/corebox/commands/chmod/mod.rs b/corebox/commands/chmod/mod.rs index 679b6e9..2c4d799 100644 --- a/corebox/commands/chmod/mod.rs +++ b/corebox/commands/chmod/mod.rs @@ -1,7 +1,7 @@ use super::{Cmd, Feedback}; -use shitbox::args; use clap::{Arg, ArgAction, Command}; use mode::{Mode, Parser}; +use shitbox::args; use std::{ error::Error, fs::{self, File, Permissions}, diff --git a/corebox/commands/chown/chgrp.rs b/corebox/commands/chown/chgrp.rs index e219715..ba4543c 100644 --- a/corebox/commands/chown/chgrp.rs +++ b/corebox/commands/chown/chgrp.rs @@ -1,6 +1,6 @@ use super::{Feedback, Group, Recurse, Traversal}; -use shitbox::Cmd; use clap::{Arg, ArgGroup, Command, ValueHint}; +use shitbox::Cmd; use std::{ error::Error, fs::File, diff --git a/corebox/commands/chown/mod.rs b/corebox/commands/chown/mod.rs index 7113639..84c666b 100644 --- a/corebox/commands/chown/mod.rs +++ b/corebox/commands/chown/mod.rs @@ -1,6 +1,6 @@ use super::{Cmd, Feedback}; -use shitbox::args; use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint}; +use shitbox::args; use std::{ error::Error, fs::File, diff --git a/corebox/commands/head/mod.rs b/corebox/commands/head/mod.rs index ea2b272..a047438 100644 --- a/corebox/commands/head/mod.rs +++ b/corebox/commands/head/mod.rs @@ -1,6 +1,6 @@ use super::Cmd; -use shitbox::{args, Path}; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; +use shitbox::{args, Path}; use std::{ env, error::Error, diff --git a/corebox/commands/link/mod.rs b/corebox/commands/link/mod.rs index 6fa2d8e..5168407 100644 --- a/corebox/commands/link/mod.rs +++ b/corebox/commands/link/mod.rs @@ -1,6 +1,6 @@ use super::Cmd; -use shitbox::args; use clap::{Arg, Command}; +use shitbox::args; use std::{fs, io}; #[derive(Debug, Default)] diff --git a/corebox/commands/mkfifo/mod.rs b/corebox/commands/mkfifo/mod.rs index 62dc171..7a1e7ad 100644 --- a/corebox/commands/mkfifo/mod.rs +++ b/corebox/commands/mkfifo/mod.rs @@ -1,7 +1,7 @@ use super::Cmd; -use shitbox::{args, stat}; use clap::{Arg, ArgMatches, Command}; use mode::Parser; +use shitbox::{args, stat}; use std::error::Error; #[derive(Debug, Default)] diff --git a/corebox/commands/mknod/mod.rs b/corebox/commands/mknod/mod.rs index 1e5b6d1..58b4e00 100644 --- a/corebox/commands/mknod/mod.rs +++ b/corebox/commands/mknod/mod.rs @@ -1,7 +1,7 @@ use super::Cmd; -use shitbox::{args, stat}; use clap::{value_parser, Arg, ArgMatches, Command}; use mode::{get_umask, Parser}; +use shitbox::{args, stat}; use std::{convert::Infallible, error::Error, io, str::FromStr}; #[derive(Debug, Default)] diff --git a/corebox/commands/mod.rs b/corebox/commands/mod.rs index 42bbc59..64ccd54 100644 --- a/corebox/commands/mod.rs +++ b/corebox/commands/mod.rs @@ -1,7 +1,6 @@ use clap::ArgMatches; use shitbox::Cmd; -mod corebox; mod base32; mod base64; mod basename; @@ -10,7 +9,7 @@ mod cat; mod chmod; mod chown; mod chroot; -mod clear; +mod corebox; mod cp; mod cut; mod date; @@ -33,7 +32,6 @@ mod ls; mod mkfifo; mod mknod; mod mktemp; -mod mountpoint; mod mv; mod nologin; mod nproc; @@ -66,7 +64,6 @@ pub fn get(name: &str) -> Option> { "chgrp" => Some(Box::new(chown::Chgrp::default())), "chown" => Some(Box::new(chown::Chown::default())), "chroot" => Some(Box::new(chroot::Chroot::default())), - "clear" => Some(Box::new(clear::Clear::default())), "corebox" => Some(Box::new(corebox::Corebox::default())), "cut" => Some(Box::new(cut::Cut::default())), "dirname" => Some(Box::new(dirname::Dirname::default())), @@ -84,7 +81,6 @@ pub fn get(name: &str) -> Option> { "mkfifo" => Some(Box::new(mkfifo::MkFifo::default())), "mknod" => Some(Box::new(mknod::MkNod::default())), "mktemp" => Some(Box::new(mktemp::MkTemp::default())), - "mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())), "nologin" => Some(Box::new(nologin::Nologin::default())), "nproc" => Some(Box::new(nproc::Nproc::default())), "printenv" => Some(Box::new(printenv::Printenv::default())), @@ -106,7 +102,7 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&str; 44] = [ +pub static COMMANDS: [&str; 42] = [ "base32", "base64", "basename", @@ -115,7 +111,6 @@ pub static COMMANDS: [&str; 44] = [ "chmod", "chown", "chroot", - "clear", "corebox", "cut", "dirname", @@ -133,7 +128,6 @@ pub static COMMANDS: [&str; 44] = [ "mkfifo", "mknod", "mktemp", - "mountpoint", "nologin", "nproc", "printenv", diff --git a/corebox/commands/readlink/mod.rs b/corebox/commands/readlink/mod.rs index 8b80fd7..2e4f65c 100644 --- a/corebox/commands/readlink/mod.rs +++ b/corebox/commands/readlink/mod.rs @@ -1,5 +1,5 @@ -use shitbox::Cmd; use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint}; +use shitbox::Cmd; use std::{fs, path::PathBuf}; #[derive(Debug, Default)] diff --git a/corebox/commands/rev/mod.rs b/corebox/commands/rev/mod.rs index 348ae76..cad1265 100644 --- a/corebox/commands/rev/mod.rs +++ b/corebox/commands/rev/mod.rs @@ -1,6 +1,6 @@ use super::Cmd; -use shitbox::args; use clap::Command; +use shitbox::args; use std::{ fs::File, io::{self, BufRead, BufReader, Write}, diff --git a/corebox/commands/rm/mod.rs b/corebox/commands/rm/mod.rs index 7aec737..1c0215a 100644 --- a/corebox/commands/rm/mod.rs +++ b/corebox/commands/rm/mod.rs @@ -1,6 +1,6 @@ use super::Cmd; -use shitbox::{args, fs::FileType}; use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint}; +use shitbox::{args, fs::FileType}; use std::{ error::Error, fmt, diff --git a/corebox/commands/rmdir/mod.rs b/corebox/commands/rmdir/mod.rs index ee342ba..33eb32f 100644 --- a/corebox/commands/rmdir/mod.rs +++ b/corebox/commands/rmdir/mod.rs @@ -1,6 +1,6 @@ use super::Cmd; -use shitbox::args; use clap::{Arg, ArgAction, Command, ValueHint}; +use shitbox::args; use std::{error::Error, fs, path::Path}; #[derive(Debug, Default)] diff --git a/corebox/commands/unlink/mod.rs b/corebox/commands/unlink/mod.rs index 298ff27..ff9cc50 100644 --- a/corebox/commands/unlink/mod.rs +++ b/corebox/commands/unlink/mod.rs @@ -1,6 +1,6 @@ use super::Cmd; -use shitbox::args; use clap::{Arg, Command}; +use shitbox::args; #[derive(Debug, Default)] pub struct Unlink; diff --git a/mount/src/lib.rs b/mount/src/lib.rs index 04480f6..c422024 100644 --- a/mount/src/lib.rs +++ b/mount/src/lib.rs @@ -1,5 +1,5 @@ use sc::*; -use std::{io, ffi::CString}; +use std::{ffi::CString, io}; mod mntent; mod mntentries; @@ -54,3 +54,12 @@ pub fn swapon(dev: &str, flags: usize) -> io::Result<()> { 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 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } +} diff --git a/mount/src/mntent.rs b/mount/src/mntent.rs index f30e059..9785f91 100644 --- a/mount/src/mntent.rs +++ b/mount/src/mntent.rs @@ -15,6 +15,7 @@ use std::{ }; /// The information for a mount broken out into a struct +#[derive(Clone)] pub struct MntEntry { pub fsname: String, pub dir: String, @@ -29,7 +30,8 @@ impl MntEntry { pub fn mount(&self) -> io::Result<()> { let dev = self.device()?; let dev = dev.to_string_lossy(); - let (flags, opts) = self.parse_opts() + let (flags, opts) = self + .parse_opts() .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; super::mount(&dev, &self.dir, &self.fstype, flags, Some(&opts))?; Ok(()) diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index fabc97b..27814e6 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,7 +1,6 @@ use clap::ArgMatches; use std::{error::Error, fmt}; - /// Defines a command or applet, it's cli interface, and it's installation directory /// relative to the binary pub trait Cmd: fmt::Debug + Sync { diff --git a/utilbox/commands/blkid/mod.rs b/utilbox/commands/blkid/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/utilbox/commands/blkid/mod.rs @@ -0,0 +1 @@ + diff --git a/corebox/commands/clear/mod.rs b/utilbox/commands/clear/mod.rs similarity index 100% rename from corebox/commands/clear/mod.rs rename to utilbox/commands/clear/mod.rs diff --git a/utilbox/commands/mod.rs b/utilbox/commands/mod.rs new file mode 100644 index 0000000..09ed4b0 --- /dev/null +++ b/utilbox/commands/mod.rs @@ -0,0 +1,23 @@ +use shitbox::Cmd; + +mod blkid; +mod clear; +mod mount; +mod mountpoint; +mod umount; +mod utilbox; +/// +/// Parses a string into a command to run +#[must_use] +#[allow(clippy::box_default)] +pub fn get(name: &str) -> Option> { + match name { + "clear" => Some(Box::new(clear::Clear::default())), + "mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())), + "umount" => Some(Box::new(umount::Umount::default())), + "utilbox" => Some(Box::new(utilbox::Utilbox::default())), + _ => None, + } +} + +pub static COMMANDS: [&str; 4] = ["clear", "mountpoint", "umount", "utilbox"]; diff --git a/utilbox/commands/mount/mod.rs b/utilbox/commands/mount/mod.rs new file mode 100644 index 0000000..008dee4 --- /dev/null +++ b/utilbox/commands/mount/mod.rs @@ -0,0 +1,19 @@ +use clap::{Arg, ArgAction, ArgMatches, Command}; +use shitbox::Cmd; + +#[derive(Debug, Default)] +pub struct Mount; + +impl Cmd for Mount { + fn cli(&self) -> Command { + Command::new("mount") + } + + fn run(&self, matches: &ArgMatches) -> Result<(), Box> { + Ok(()) + } + + fn path(&self) -> Option { + Some(shitbox::Path::Bin) + } +} diff --git a/corebox/commands/mountpoint/mod.rs b/utilbox/commands/mountpoint/mod.rs similarity index 100% rename from corebox/commands/mountpoint/mod.rs rename to utilbox/commands/mountpoint/mod.rs diff --git a/utilbox/commands/swaplabel/mod.rs b/utilbox/commands/swaplabel/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/utilbox/commands/swapoff/mod.rs b/utilbox/commands/swapoff/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/utilbox/commands/swapon/mod.rs b/utilbox/commands/swapon/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/utilbox/commands/umount/mod.rs b/utilbox/commands/umount/mod.rs new file mode 100644 index 0000000..9378c46 --- /dev/null +++ b/utilbox/commands/umount/mod.rs @@ -0,0 +1,140 @@ +use std::io; +use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint}; +use mount::MntEntries; +use shitbox::{args, Cmd}; + +#[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")) + .args([ + Arg::new("all") + .help( + "All of the filesystems described in /proc/self/mountinfo \ + are unmounted, except the proc, devfs, devpts, sysfs, rpc_pipefs \ + and nfsd filesystems.", + ) + .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("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 flags = if matches.get_flag("force") { + libc::MNT_FORCE + } else { + 0 + }; + if matches.get_flag("all") { + let mut mntpts: Vec = MntEntries::new("/proc/mounts")? + .filter(|x| { + x.fstype != "proc" + && x.fstype != "sysfs" + && x.fstype != "devfs" + && x.fstype != "devpts" + && x.fstype != "rpc_pipefs" + && x.fstype != "nfsd" + }) + .collect(); + if let Some(types) = matches.get_many::("types") { + for t in types { + if t.starts_with("no") { + let t = t.strip_prefix("no").unwrap(); + mntpts = mntpts + .iter() + .filter(|x| x.fstype != t) + .map(|x| x.clone()) + .collect(); + } else { + mntpts = mntpts + .iter() + .filter(|x| &x.fstype == t) + .map(|x| x.clone()) + .collect(); + } + } + } + if let Some(spec) = matches.get_many::("spec") { + for s in spec { + for p in &mntpts { + if &p.dir == s { + mount::umount(s, flags as u32)?; + if matches.get_flag("verbose") { + println!("umount: {s} unmounted"); + } + } + } + } + } else { + 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.clone()); + } else if e.dir == spec { + return Ok(spec.to_string()); + } + } + Err(io::Error::new(io::ErrorKind::Other, "no such mount")) +} diff --git a/utilbox/commands/utilbox/mod.rs b/utilbox/commands/utilbox/mod.rs new file mode 100644 index 0000000..a543b08 --- /dev/null +++ b/utilbox/commands/utilbox/mod.rs @@ -0,0 +1,48 @@ +use super::COMMANDS; +use clap::Command; +use shitbox::Cmd; +use std::{error::Error, process}; + +#[derive(Debug, Default)] +pub struct Utilbox; + +impl Cmd for Utilbox { + fn cli(&self) -> clap::Command { + let subcommands: Vec = { + let mut s = vec![]; + for c in COMMANDS { + if c == "utilbox" { + continue; + } + if let Some(cmd) = crate::commands::get(c) { + s.push(cmd.cli()); + } + } + s + }; + Command::new("utilbox") + .about("The box store multitool of embedded Linux") + .version(env!("CARGO_PKG_VERSION")) + .propagate_version(true) + .arg_required_else_help(true) + .subcommand_value_name("APPLET") + .subcommand_help_heading("APPLETS") + .subcommands(&subcommands) + } + + fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box> { + if let Some((name, matches)) = matches.subcommand() { + if let Some(command) = crate::commands::get(name) { + if let Err(e) = command.run(matches) { + eprintln!("Error: {name}: {e}"); + process::exit(1); + } + } + } + Ok(()) + } + + fn path(&self) -> Option { + None + } +} diff --git a/utilbox/main.rs b/utilbox/main.rs new file mode 100644 index 0000000..3911358 --- /dev/null +++ b/utilbox/main.rs @@ -0,0 +1,18 @@ +use std::process; + +mod commands; + +fn main() { + if let Some(progname) = shitbox::progname() { + if let Some(command) = commands::get(&progname) { + let cli = command.cli(); + if let Err(e) = command.run(&cli.get_matches()) { + eprintln!("{progname}: Error: {e}"); + process::exit(1); + } + } else { + eprintln!("shitbox: Error: unknown command {progname}"); + process::exit(1); + } + } +}