Convert several commands to use raw system calls instead of libc

functions
This commit is contained in:
Nathan Fisher 2023-02-02 23:34:37 -05:00
parent fcc8abb67b
commit 6963ba4a4b
12 changed files with 232 additions and 86 deletions

36
Cargo.lock generated
View File

@ -121,17 +121,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hostname"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
dependencies = [
"libc",
"match_cfg",
"winapi",
]
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.4" version = "1.0.4"
@ -166,22 +155,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "match_cfg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.2.6",
"libc",
]
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.4.1" version = "6.4.1"
@ -217,6 +190,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "sc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b"
[[package]] [[package]]
name = "shitbox" name = "shitbox"
version = "0.1.0" version = "0.1.0"
@ -227,9 +206,8 @@ dependencies = [
"clap_complete_nushell", "clap_complete_nushell",
"clap_mangen", "clap_mangen",
"data-encoding", "data-encoding",
"hostname",
"libc", "libc",
"num_cpus", "sc",
"termcolor", "termcolor",
"textwrap", "textwrap",
"walkdir", "walkdir",

View File

@ -12,9 +12,8 @@ clap_complete = "4.0"
clap_complete_nushell = "0.1" clap_complete_nushell = "0.1"
clap_mangen = "0.2" clap_mangen = "0.2"
data-encoding = "2.3" data-encoding = "2.3"
hostname = { version = "0.3", features = ["set"] }
libc = "0.2" libc = "0.2"
num_cpus = "1.15" sc = "0.2"
termcolor = "1.1" termcolor = "1.1"
textwrap = { version = "0.16", default-features = false, features = ["smawk"] } textwrap = { version = "0.16", default-features = false, features = ["smawk"] }
walkdir = "2.3.2" walkdir = "2.3.2"

53
src/cmd/expand/mod.rs Normal file
View File

@ -0,0 +1,53 @@
use std::io;
use super::Cmd;
use clap::{Arg, ArgAction, Command, ValueHint};
#[derive(Debug, Default)]
pub struct Expand;
impl Cmd for Expand {
fn cli(&self) -> clap::Command {
Command::new("expand")
.about("convert tabs to spaces")
.author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION"))
.args([
Arg::new("file")
.num_args(1..)
.default_value("-")
.value_name("FILE")
.value_hint(ValueHint::FilePath),
Arg::new("initial")
.short('i')
.long("initial")
.help("do not convert tabs after non blanks")
.action(ArgAction::SetTrue),
Arg::new("tabs")
.short('t')
.long("tabs")
.default_value("8")
.help(
"Specify the tab stops. The application shall ensure that \
the argument tablist consists of either a single positive \
decimal integer or a list of tabstops. If a single number \
is given, tabs shall be set that number of column positions \
apart instead of the default 8.",
)
.value_name("tablist")
.value_delimiter(',')
.num_args(1..),
])
}
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
let Some(matches) = matches else {
return Err(io::Error::new(io::ErrorKind::Other, "no input").into());
};
Ok(())
}
fn path(&self) -> Option<crate::Path> {
Some(crate::Path::UsrBin)
}
}

View File

@ -1,4 +1,5 @@
use super::Cmd; use super::Cmd;
use crate::unistd;
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use std::{error::Error, io}; use std::{error::Error, io};
@ -24,14 +25,14 @@ impl Cmd for Hostname {
fn run(&self, matches: Option<&ArgMatches>) -> Result<(), Box<dyn Error>> { fn run(&self, matches: Option<&ArgMatches>) -> Result<(), Box<dyn Error>> {
let matches = matches.unwrap(); let matches = matches.unwrap();
if let Some(name) = matches.get_one::<String>("NAME") { if let Some(name) = matches.get_one::<String>("NAME") {
hostname::set(name)?; unistd::sethostname(name)?;
Ok(()) Ok(())
} else { } else {
let hostname = hostname::get()?; let hostname = unistd::gethostname()?;
if matches.get_flag("STRIP") { if matches.get_flag("STRIP") {
println!( println!(
"{}", "{}",
if let Some(s) = hostname.to_string_lossy().split('.').next() { if let Some(s) = hostname.split('.').next() {
s s
} else { } else {
return Err(io::Error::new( return Err(io::Error::new(
@ -42,7 +43,7 @@ impl Cmd for Hostname {
} }
); );
} else { } else {
println!("{}", hostname.to_string_lossy()); println!("{hostname}");
} }
Ok(()) Ok(())
} }

View File

@ -1,7 +1,7 @@
use super::Cmd; use super::Cmd;
use crate::{args, mode::Parser}; use crate::{args, mode::Parser, stat};
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use std::{error::Error, ffi::CString, io}; use std::{error::Error, io};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MkFifo; pub struct MkFifo;
@ -46,11 +46,7 @@ impl Cmd for MkFifo {
}; };
if let Some(files) = matches.get_many::<String>("file") { if let Some(files) = matches.get_many::<String>("file") {
for f in files { for f in files {
let fname = CString::new(f.as_str())?; stat::mkfifo(f.as_str(), mode)?;
let res = unsafe { libc::mkfifo(fname.as_ptr(), mode) };
if res != 0 {
return Err(io::Error::last_os_error().into());
}
if matches.get_flag("verbose") { if matches.get_flag("verbose") {
println!( println!(
"{}: created pipe '{f}' with mode {mode:o}", "{}: created pipe '{f}' with mode {mode:o}",

View File

@ -1,10 +1,10 @@
use super::Cmd; use super::Cmd;
use crate::{ use crate::{
args, args,
mode::{get_umask, Parser}, mode::{get_umask, Parser}, stat,
}; };
use clap::{value_parser, Arg, ArgMatches, Command}; use clap::{value_parser, Arg, ArgMatches, Command};
use std::{convert::Infallible, error::Error, ffi::CString, io, str::FromStr}; use std::{convert::Infallible, error::Error, io, str::FromStr};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MkNod; pub struct MkNod;
@ -69,11 +69,7 @@ impl Cmd for MkNod {
} }
Special::Pipe => 0, Special::Pipe => 0,
}; };
let fname = CString::new(file.as_str())?; stat::mknod(file.as_str(), mode, dev_t)?;
let ret = unsafe { libc::mknod(fname.as_ptr(), mode, dev_t) };
if ret != 0 {
return Err(io::Error::last_os_error().into());
}
if matches.get_flag("verbose") { if matches.get_flag("verbose") {
match special { match special {
Special::Block => { Special::Block => {

View File

@ -24,9 +24,9 @@ impl Cmd for Nproc {
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "no input"))); return Err(Box::new(io::Error::new(io::ErrorKind::Other, "no input")));
}; };
if matches.get_flag("ALL") { if matches.get_flag("ALL") {
println!("{}", num_cpus::get()); println!("{}", unsafe { get_nprocs_conf() });
} else { } else {
println!("{}", num_cpus::get_physical()); println!("{}", unsafe { get_nprocs() });
} }
Ok(()) Ok(())
} }
@ -35,3 +35,8 @@ impl Cmd for Nproc {
Some(crate::Path::UsrBin) Some(crate::Path::UsrBin)
} }
} }
extern "C" {
fn get_nprocs() -> libc::c_int;
fn get_nprocs_conf() -> libc::c_int;
}

View File

@ -1,6 +1,8 @@
use crate::unistd;
use super::Cmd; use super::Cmd;
use clap::{Arg, ArgAction, Command}; use clap::{Arg, ArgAction, Command};
use std::{error::Error, ffi::CString, io}; use std::{error::Error, io, fs::OpenOptions};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Sync; pub struct Sync;
@ -38,37 +40,19 @@ impl Cmd for Sync {
}; };
if let Some(files) = matches.get_many::<String>("FILE") { if let Some(files) = matches.get_many::<String>("FILE") {
for f in files { for f in files {
let file = CString::new(f.as_bytes())?; let mut opts = OpenOptions::new();
let opts = opts.read(true).write(true);
let fd = opts.open(f)?;
if matches.get_flag("data") { if matches.get_flag("data") {
let rc = unsafe { unistd::fdatasync(&fd)?;
let fd = libc::open(file.as_ptr(), libc::O_RDWR | libc::O_NOATIME);
libc::fdatasync(fd)
};
if rc != 0 {
return Err(Box::new(io::Error::last_os_error()));
}
} else if matches.get_flag("fs") { } else if matches.get_flag("fs") {
let rc = unsafe { unistd::syncfs(&fd)?;
let fd = libc::open(file.as_ptr(), libc::O_RDWR | libc::O_NOATIME);
libc::syncfs(fd)
};
if rc != 0 {
return Err(Box::new(io::Error::last_os_error()));
}
} else { } else {
let rc = unsafe { unistd::fsync(&fd)?;
let fd = libc::open(file.as_ptr(), libc::O_RDWR | libc::O_NOATIME);
libc::fsync(fd)
};
if rc != 0 {
return Err(Box::new(io::Error::last_os_error()));
}
} }
} }
} else { } else {
unsafe { unistd::sync();
libc::sync();
}
} }
Ok(()) Ok(())
} }

View File

@ -1,7 +1,7 @@
use super::Cmd; use super::Cmd;
use crate::args; use crate::{args, unistd};
use clap::{Arg, Command}; use clap::{Arg, Command};
use std::{ffi::CString, io, process}; use std::io;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Unlink; pub struct Unlink;
@ -27,14 +27,9 @@ impl Cmd for Unlink {
}; };
if let Some(files) = matches.get_many::<String>("file") { if let Some(files) = matches.get_many::<String>("file") {
for f in files { for f in files {
let fname = CString::new(f.as_str())?; unistd::unlink(f)?;
let ret = unsafe { libc::unlink(fname.as_ptr()) }; if matches.get_flag("verbose") {
if matches.get_flag("verbose") && ret == 0 {
println!("unlink: {f}"); println!("unlink: {f}");
} else if ret != 0 {
let err = io::Error::last_os_error();
eprintln!("unlink: cannot unlink {f}: {err}");
process::exit(ret);
} }
} }
} }

View File

@ -8,6 +8,8 @@ pub mod fs;
pub mod math; pub mod math;
pub mod mode; pub mod mode;
pub mod pw; pub mod pw;
pub mod stat;
pub mod unistd;
pub use cmd::Cmd; pub use cmd::Cmd;

22
src/stat/mod.rs Normal file
View File

@ -0,0 +1,22 @@
use sc::*;
use std::{ffi::CString, io};
#[inline(always)]
pub fn mknod(path: &str, mode: u32, dev: u64) -> io::Result<()> {
let ret = unsafe { syscall!(MKNOD, CString::new(path)?.as_ptr(), mode, dev) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[inline(always)]
pub fn mkfifo(path: &str, mode: u32) -> io::Result<()> {
let ret = unsafe { syscall!(MKNOD, CString::new(path)?.as_ptr(), mode | libc::S_IFIFO, 0) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}

115
src/unistd/mod.rs Normal file
View File

@ -0,0 +1,115 @@
use libc::utsname;
use sc::syscall;
use std::{ffi::CString, io, u8, fs::File, os::fd::AsRawFd};
#[inline(always)]
fn new_utsname() -> utsname {
utsname {
sysname: [0; 65],
nodename: [0; 65],
release: [0; 65],
version: [0; 65],
machine: [0; 65],
domainname: [0; 65],
}
}
#[inline(always)]
pub fn gethostname() -> io::Result<String> {
let mut uts = new_utsname();
unsafe {
if syscall!(UNAME, &mut uts as *mut utsname) != 0 {
return Err(io::Error::last_os_error());
}
}
let name = uts
.nodename
.iter()
.map(|x| *x as u8)
.take_while(|x| *x != 0)
.collect();
String::from_utf8(name).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
#[inline(always)]
pub fn sethostname(name: &str) -> io::Result<()> {
let ret = unsafe { syscall!(SETHOSTNAME, name.as_ptr(), name.len()) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error().into())
}
}
#[inline(always)]
pub fn link(source: &str, dest: &str) -> io::Result<()> {
let ret = unsafe {
syscall!(
LINK,
CString::new(source)?.as_ptr(),
CString::new(dest)?.as_ptr()
)
};
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[inline(always)]
pub fn unlink(name: &str) -> io::Result<()> {
let ret = unsafe { syscall!(UNLINK, CString::new(name)?.as_ptr()) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error().into())
}
}
#[inline(always)]
pub fn readlink(name: &str) -> io::Result<String> {
let mut buf = Vec::<i8>::with_capacity(1024);
let ret = unsafe { syscall!(READLINK, CString::new(name)?.as_ptr(), buf.as_mut_ptr(), 1024) };
if ret == 0 {
let path = buf.iter().map(|x| *x as u8).take_while(|x| *x != 0).collect();
String::from_utf8(path).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
} else {
Err(io::Error::last_os_error())
}
}
#[inline(always)]
pub fn fdatasync(fd: &File) -> io::Result<()> {
let ret = unsafe { syscall!(FDATASYNC, fd.as_raw_fd()) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[inline(always)]
pub fn syncfs(fd: &File) -> io::Result<()> {
let ret = unsafe { syscall!(SYNCFS, fd.as_raw_fd()) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[inline(always)]
pub fn fsync(fd: &File) -> io::Result<()> {
let ret = unsafe { syscall!(FSYNC, fd.as_raw_fd()) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[inline(always)]
pub fn sync() {
unsafe { syscall!(SYNC) };
}