第二章节C代码RUST实现
第二章节书中代码有如下内容
这些C语言代码大致实现了一个简单版的 who 命令。这个命令的功能是读取系统的 utmp 文件,并显示当前登录的用户信息。utmp 文件包含关于用户登录会话的信息,包括用户名、登录终端、登录时间等。以下是对上述所有代码实现功能的总结:
- cp1:实现复制文件内容到另一指定文件的功能
- who0.c:基本版本,读取并显示 utmp 文件中的所有记录。
- who1.c:增加了对空记录的抑制功能,只显示有用户登录的记录。
- who2.c:增加了时间格式化显示功能,使时间信息更易读。
- who3.c:使用了缓冲读取,提高了读取效率,增加了对 utmplib 的支持。
实现过程
在linux虚拟机中实现
所有代码工程文件如下
代码约束 Cargo.toml文件
[package]
name = "who2"
version = "0.1.0"
edition = "2018"
[dependencies]
nix = "0.23.2"
chrono = "0.4"
cp1
use std::ffi::CString;
use std::process;
use libc::{open, read, write, close, O_RDONLY, O_CREAT, O_WRONLY, S_IRUSR, S_IWUSR, S_IRGRP, S_IROTH};
const BUFFERSIZE: usize = 4096;
const COPYMODE: u32 = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
fn oops(s1: &str, s2: &str) {
eprintln!("Error: {} {}", s1, s2);
process::exit(1);
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() != 3 {
eprintln!("usage: {} source destination", args[0]);
process::exit(1);
}
let src = &args[1];
let dest = &args[2];
let src_cstr = CString::new(src.as_str()).expect("CString::new failed");
let dest_cstr = CString::new(dest.as_str()).expect("CString::new failed");
let in_fd = unsafe { open(src_cstr.as_ptr(), O_RDONLY) };
if in_fd == -1 {
oops("Cannot open", src);
utmplib
use std::ffi::CString;
use std::os::unix::io::RawFd;
use std::ptr;
use libc::{open, read, close, O_RDONLY};
use std::mem::size_of;
const NRECS: usize = 16;
const UTSIZE: usize = size_of::<libc::utmpx>();
static mut UTMPBUF: [u8; NRECS * UTSIZE] = [0; NRECS * UTSIZE];
static mut NUM_RECS: usize = 0;
static mut CUR_REC: usize = 0;
static mut FD_UTMP: RawFd = -1;
fn oops(s1: &str, s2: &str) {
eprintln!("Error: {} {}", s1, s2);
std::process::exit(1);
}
fn utmp_open(filename: &str) -> RawFd {
let c_filename = CString::new(filename).expect("CString::new failed");
unsafe {
FD_UTMP = open(c_filename.as_ptr(), O_RDONLY);
CUR_REC = 0;
NUM_RECS = 0;
if FD_UTMP == -1 {
oops("Cannot open", filename);
}
}
unsafe { FD_UTMP }
}
fn utmp_next() -> Option<libc::utmpx> {
unsafe {
if FD_UTMP == -1 {
return None;
}
if CUR_REC == NUM_RECS && utmp_reload() == 0 {
return None;
}
let recp = ptr::read(UTMPBUF.as_ptr().add(CUR_REC * UTSIZE) as *const libc::utmpx);
CUR_REC += 1;
Some(recp)
}
}
fn utmp_reload() -> usize {
unsafe {
let amt_read = read(FD_UTMP, UTMPBUF.as_mut_ptr() as *mut libc::c_void, NRECS * UTSIZE);
if amt_read < 0 {
oops("Read error from", "utmp file");
}
NUM_RECS = amt_read as usize / UTSIZE;
CUR_REC = 0;
NUM_RECS
}
}
fn utmp_close() {
unsafe {
if FD_UTMP != -1 {
close(FD_UTMP);
FD_UTMP = -1;
}
}
}
fn main() {
let filename = "/var/log/wtmp"; // 替换为实际的utmp文件路径
utmp_open(filename);
while let Some(record) = utmp_next() {
let user = unsafe { std::ffi::CStr::from_ptr(record.ut_user.as_ptr()) };
println!("User: {}", user.to_str().unwrap());
}
utmp_close();
}
who0
use std::fs::File;
use std::io::{self, Read};
use std::mem::size_of;
use std::ptr;
// 定义与 C 语言中的 struct utmp 等价的结构体
#[repr(C)]
struct Utmp {
ut_type: i16,
ut_pid: i32,
ut_line: [u8; 32],
ut_id: [u8; 4],
ut_user: [u8; 32],
ut_host: [u8; 256],
ut_exit: ExitStatus,
ut_session: i32,
ut_tv: TimeVal,
ut_addr_v6: [i32; 4],
unused: [u8; 20],
}
#[repr(C)]
struct ExitStatus {
e_termination: i16,
e_exit: i16,
}
#[repr(C)]
struct TimeVal {
tv_sec: i32,
tv_usec: i32,
}
fn main() -> io::Result<()> {
let mut file = File::open("/var/run/utmp")?;
let reclen = size_of::<Utmp>();
let mut buffer = vec![0u8; reclen];
while file.read_exact(&mut buffer).is_ok() {
let utmp: Utmp = unsafe { ptr::read(buffer.as_ptr() as *const _) };
show_info(&utmp);
}
Ok(())
}
fn show_info(utmp: &Utmp) {
let user = String::from_utf8_lossy(&utmp.ut_user).trim_end_matches(char::from(0)).to_string();
let line = String::from_utf8_lossy(&utmp.ut_line).trim_end_matches(char::from(0)).to_string();
let host = String::from_utf8_lossy(&utmp.ut_host).trim_end_matches(char::from(0)).to_string();
println!("User: {}, Line: {}, Host: {}", user, line, host);
}
who1
use std::fs::File;
use std::io::{self, Read};
use std::mem::size_of;
use std::ptr;
use std::time::{UNIX_EPOCH, Duration};
// 定义与 C 语言中的 struct utmp 等价的结构体
#[repr(C)]
struct Utmp {
ut_type: i16,
ut_pid: i32,
ut_line: [u8; 32],
ut_id: [u8; 4],
ut_user: [u8; 32],
ut_host: [u8; 256],
ut_exit: ExitStatus,
ut_session: i32,
ut_tv: TimeVal,
ut_addr_v6: [i32; 4],
unused: [u8; 20],
}
#[repr(C)]
struct ExitStatus {
e_termination: i16,
e_exit: i16,
}
#[repr(C)]
struct TimeVal {
tv_sec: i32,
tv_usec: i32,
}
fn main() -> io::Result<()> {
let mut file = File::open("/var/run/utmp")?;
let reclen = size_of::<Utmp>();
let mut buffer = vec![0u8; reclen];
while file.read_exact(&mut buffer).is_ok() {
let utmp: Utmp = unsafe { ptr::read(buffer.as_ptr() as *const _) };
show_info(&utmp);
}
Ok(())
}
fn show_info(utmp: &Utmp) {
let user = String::from_utf8_lossy(&utmp.ut_user).trim_end_matches(char::from(0)).to_string();
let line = String::from_utf8_lossy(&utmp.ut_line).trim_end_matches(char::from(0)).to_string();
let host = String::from_utf8_lossy(&utmp.ut_host).trim_end_matches(char::from(0)).to_string();
let login_time = UNIX_EPOCH + Duration::new(utmp.ut_tv.tv_sec as u64, 0);
println!("{:<8} {:<8} {:>10} ({})", user, line, login_time.duration_since(UNIX_EPOCH).unwrap().as_secs(), host);
}
who1bot
use std::fs::File;
use std::io::{self, Read};
use std::mem::size_of;
use std::ptr;
use std::time::{UNIX_EPOCH, Duration};
// 定义与 C 语言中的 struct utmp 等价的结构体
#[repr(C)]
struct Utmp {
ut_type: i16,
ut_pid: i32,
ut_line: [u8; 32],
ut_id: [u8; 4],
ut_user: [u8; 32],
ut_host: [u8; 256],
ut_exit: ExitStatus,
ut_session: i32,
ut_tv: TimeVal,
ut_addr_v6: [i32; 4],
unused: [u8; 20],
}
#[repr(C)]
struct ExitStatus {
e_termination: i16,
e_exit: i16,
}
#[repr(C)]
struct TimeVal {
tv_sec: i32,
tv_usec: i32,
}
fn main() -> io::Result<()> {
let mut file = File::open("/var/run/utmp")?;
let reclen = size_of::<Utmp>();
let mut buffer = vec![0u8; reclen];
while file.read_exact(&mut buffer).is_ok() {
let utmp: Utmp = unsafe { ptr::read(buffer.as_ptr() as *const _) };
show_info(&utmp);
}
Ok(())
}
fn show_info(utmp: &Utmp) {
let user = String::from_utf8_lossy(&utmp.ut_user).trim_end_matches(char::from(0)).to_string();
let line = String::from_utf8_lossy(&utmp.ut_line).trim_end_matches(char::from(0)).to_string();
let host = String::from_utf8_lossy(&utmp.ut_host).trim_end_matches(char::from(0)).to_string();
let login_time = UNIX_EPOCH + Duration::new(utmp.ut_tv.tv_sec as u64, 0);
println!(
"{:<8} {:<8} {:>10} ({})",
user,
line,
login_time.duration_since(UNIX_EPOCH).unwrap().as_secs(),
host
);
}
who1top
use std::io::{self, Read};
use std::mem::size_of;
use std::ptr;
use std::time::{UNIX_EPOCH, Duration};
use nix::fcntl::{open, OFlag};
use nix::unistd::{close, read};
use nix::sys::stat::Mode;
// 定义与 C 语言中的 struct utmp 等价的结构体
#[repr(C)]
struct Utmp {
ut_type: i16,
ut_pid: i32,
ut_line: [u8; 32],
ut_id: [u8; 4],
ut_user: [u8; 32],
ut_host: [u8; 256],
ut_exit: ExitStatus,
ut_session: i32,
ut_tv: TimeVal,
ut_addr_v6: [i32; 4],
unused: [u8; 20],
}
#[repr(C)]
struct ExitStatus {
e_termination: i16,
e_exit: i16,
}
#[repr(C)]
struct TimeVal {
tv_sec: i32,
tv_usec: i32,
}
fn main() -> io::Result<()> {
let utmp_path = "/var/run/utmp";
let utmpfd = open(utmp_path, OFlag::O_RDONLY, Mode::empty())
.expect("Failed to open utmp file");
let reclen = size_of::<Utmp>();
let mut buffer = vec![0u8; reclen];
while read(utmpfd, &mut buffer).expect("Failed to read from utmp file") == reclen {
let utmp: Utmp = unsafe { ptr::read(buffer.as_ptr() as *const _) };
show_info(&utmp);
}
close(utmpfd).expect("Failed to close utmp file");
Ok(())
}
fn show_info(utmp: &Utmp) {
let user = String::from_utf8_lossy(&utmp.ut_user).trim_end_matches(char::from(0)).to_string();
let line = String::from_utf8_lossy(&utmp.ut_line).trim_end_matches(char::from(0)).to_string();
let host = String::from_utf8_lossy(&utmp.ut_host).trim_end_matches(char::from(0)).to_string();
let login_time = UNIX_EPOCH + Duration::new(utmp.ut_tv.tv_sec as u64, 0);
println!(
"{:<8} {:<8} {:>10} ({})",
user,
line,
login_time.duration_since(UNIX_EPOCH).unwrap().as_secs(),
host
);
}
who2
use std::io;
use std::mem::size_of;
use std::ptr;
use std::time::{UNIX_EPOCH, Duration, SystemTime};
use nix::fcntl::{open, OFlag};
use nix::unistd::{close, read};
use nix::sys::stat::Mode;
use chrono::prelude::*;
// 定义与 C 语言中的 struct utmp 等价的结构体
#[repr(C)]
struct Utmp {
ut_type: i16,
ut_pid: i32,
ut_line: [u8; 32],
ut_id: [u8; 4],
ut_user: [u8; 32],
ut_host: [u8; 256],
ut_exit: ExitStatus,
ut_session: i32,
ut_tv: TimeVal,
ut_addr_v6: [i32; 4],
unused: [u8; 20],
}
#[repr(C)]
struct ExitStatus {
e_termination: i16,
e_exit: i16,
}
#[repr(C)]
struct TimeVal {
tv_sec: i32,
tv_usec: i32,
}
const USER_PROCESS: i16 = 7;
fn main() -> io::Result<()> {
let utmp_path = "/var/run/utmp";
let utmpfd = open(utmp_path, OFlag::O_RDONLY, Mode::empty())
.expect("Failed to open utmp file");
let reclen = size_of::<Utmp>();
let mut buffer = vec![0u8; reclen];
while read(utmpfd, &mut buffer).expect("Failed to read from utmp file") == reclen {
let utmp: Utmp = unsafe { ptr::read(buffer.as_ptr() as *const _) };
show_info(&utmp);
}
close(utmpfd).expect("Failed to close utmp file");
Ok(())
}
fn show_info(utmp: &Utmp) {
if utmp.ut_type != USER_PROCESS {
return;
}
let user = String::from_utf8_lossy(&utmp.ut_user).trim_end_matches(char::from(0)).to_string();
let line = String::from_utf8_lossy(&utmp.ut_line).trim_end_matches(char::from(0)).to_string();
let login_time = UNIX_EPOCH + Duration::new(utmp.ut_tv.tv_sec as u64, 0);
print!("{:<8} {:<8} ", user, line);
showtime(login_time);
// 注释掉未使用的 host 变量
/*
#[cfg(feature = "showhost")]
if !host.is_empty() {
print!(" ({})", host);
}
*/
println!();
}
fn showtime(system_time: SystemTime) {
let datetime: DateTime<Local> = system_time.into();
print!("{}", datetime.format("%b %e %H:%M"));
}
who3
use std::io;
use std::mem::size_of;
use std::ptr;
use std::time::{UNIX_EPOCH, Duration, SystemTime};
use nix::fcntl::{open, OFlag};
use nix::unistd::{close};
use nix::sys::stat::Mode;
use chrono::prelude::*;
// 定义与 C 语言中的 struct utmp 等价的结构体
#[repr(C)]
#[derive(Clone, Copy)]
struct Utmp {
ut_type: i16,
ut_pid: i32,
ut_line: [u8; 32],
ut_id: [u8; 4],
ut_user: [u8; 32],
ut_host: [u8; 256],
ut_exit: ExitStatus,
ut_session: i32,
ut_tv: TimeVal,
ut_addr_v6: [i32; 4],
unused: [u8; 20],
}
#[repr(C)]
#[derive(Clone, Copy)]
struct ExitStatus {
e_termination: i16,
e_exit: i16,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct TimeVal {
tv_sec: i32,
tv_usec: i32,
}
const USER_PROCESS: i16 = 7;
const UTMP_FILE: &str = "/var/run/utmp";
fn main() -> io::Result<()> {
if utmp_open(UTMP_FILE).is_err() {
eprintln!("Failed to open utmp file");
std::process::exit(1);
}
while let Some(utmp) = utmp_next() {
show_info(&utmp);
}
utmp_close();
Ok(())
}
fn show_info(utmp: &Utmp) {
if utmp.ut_type != USER_PROCESS {
return;
}
let user = String::from_utf8_lossy(&utmp.ut_user).trim_end_matches(char::from(0)).to_string();
let line = String::from_utf8_lossy(&utmp.ut_line).trim_end_matches(char::from(0)).to_string();
let host = String::from_utf8_lossy(&utmp.ut_host).trim_end_matches(char::from(0)).to_string();
let login_time = UNIX_EPOCH + Duration::new(utmp.ut_tv.tv_sec as u64, 0);
print!("{:<8} {:<8} ", user, line);
showtime(login_time);
#[cfg(feature = "showhost")]
if !host.is_empty() {
print!(" ({})", host);
}
println!();
}
fn showtime(system_time: SystemTime) {
let datetime: DateTime<Local> = system_time.into();
print!("{}", datetime.format("%b %e %H:%M"));
}
static mut UTMP_BUFFER: Option<Vec<Utmp>> = None;
static mut INDEX: usize = 0;
fn utmp_open(file: &str) -> Result<(), io::Error> {
let utmpfd = open(file, OFlag::O_RDONLY, Mode::empty())?;
let reclen = size_of::<Utmp>();
let mut buffer = Vec::new();
let mut temp_buffer = vec![0u8; reclen];
while let Ok(n) = nix::unistd::read(utmpfd, &mut temp_buffer) {
if n == 0 { break; }
if n == reclen {
let utmp: Utmp = unsafe { ptr::read(temp_buffer.as_ptr() as *const _) };
buffer.push(utmp);
}
}
unsafe {
UTMP_BUFFER = Some(buffer);
INDEX = 0;
}
close(utmpfd)?;
Ok(())
}
fn utmp_next() -> Option<Utmp> {
unsafe {
if let Some(ref buffer) = UTMP_BUFFER {
if INDEX < buffer.len() {
let record = buffer[INDEX].clone();
INDEX += 1;
return Some(record);
}
}
}
None
}
fn utmp_close() {
unsafe {
UTMP_BUFFER = None;
INDEX = 0;
}
}
who3top
use std::io;
use std::mem::size_of;
use std::ptr;
use std::time::{UNIX_EPOCH, Duration, SystemTime};
use nix::fcntl::{open, OFlag};
use nix::unistd::{close};
use nix::sys::stat::Mode;
use chrono::prelude::*;
// 定义与 C 语言中的 struct utmp 等价的结构体
#[repr(C)]
#[derive(Clone, Copy)]
struct Utmp {
ut_type: i16,
ut_pid: i32,
ut_line: [u8; 32],
ut_id: [u8; 4],
ut_user: [u8; 32],
ut_host: [u8; 256],
ut_exit: ExitStatus,
ut_session: i32,
ut_tv: TimeVal,
ut_addr_v6: [i32; 4],
unused: [u8; 20],
}
#[repr(C)]
#[derive(Clone, Copy)]
struct ExitStatus {
e_termination: i16,
e_exit: i16,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct TimeVal {
tv_sec: i32,
tv_usec: i32,
}
const USER_PROCESS: i16 = 7;
const UTMP_FILE: &str = "/var/run/utmp";
fn main() -> io::Result<()> {
if utmp_open(UTMP_FILE).is_err() {
eprintln!("Failed to open utmp file");
std::process::exit(1);
}
while let Some(utmp) = utmp_next() {
show_info(&utmp);
}
utmp_close();
Ok(())
}
fn show_info(utmp: &Utmp) {
if utmp.ut_type != USER_PROCESS {
return;
}
let user = String::from_utf8_lossy(&utmp.ut_user).trim_end_matches(char::from(0)).to_string();
let line = String::from_utf8_lossy(&utmp.ut_line).trim_end_matches(char::from(0)).to_string();
let host = String::from_utf8_lossy(&utmp.ut_host).trim_end_matches(char::from(0)).to_string();
let login_time = UNIX_EPOCH + Duration::new(utmp.ut_tv.tv_sec as u64, 0);
print!("{:<8} {:<8} ", user, line);
showtime(login_time);
#[cfg(feature = "showhost")]
if !host.is_empty() {
print!(" ({})", host);
}
println!();
}
fn showtime(system_time: SystemTime) {
let datetime: DateTime<Local> = system_time.into();
print!("{}", datetime.format("%b %e %H:%M"));
}
static mut UTMP_BUFFER: Option<Vec<Utmp>> = None;
static mut INDEX: usize = 0;
fn utmp_open(file: &str) -> Result<(), io::Error> {
let utmpfd = open(file, OFlag::O_RDONLY, Mode::empty())?;
let reclen = size_of::<Utmp>();
let mut buffer = Vec::new();
let mut temp_buffer = vec![0u8; reclen];
while let Ok(n) = nix::unistd::read(utmpfd, &mut temp_buffer) {
if n == 0 { break; }
if n == reclen {
let utmp: Utmp = unsafe { ptr::read(temp_buffer.as_ptr() as *const _) };
buffer.push(utmp);
}
}
unsafe {
UTMP_BUFFER = Some(buffer);
INDEX = 0;
}
close(utmpfd)?;
Ok(())
}
fn utmp_next() -> Option<Utmp> {
unsafe {
if let Some(ref buffer) = UTMP_BUFFER {
if INDEX < buffer.len() {
let record = buffer[INDEX].clone();
INDEX += 1;
return Some(record);
}
}
}
None
}
fn utmp_close() {
unsafe {
UTMP_BUFFER = None;
INDEX = 0;
}
}
第三章节
fileinfo
use std::ffi::CString;
use std::env;
use libc::{stat, S_IFMT, S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK};
use std::mem;
fn show_stat_info(fname: &str, buf: &libc::stat) {
println!(" mode: {:o}", buf.st_mode & 0o777);
println!(" links: {}", buf.st_nlink);
println!(" user: {}", buf.st_uid);
println!(" group: {}", buf.st_gid);
println!(" size: {}", buf.st_size);
println!("modtime: {}", buf.st_mtime);
println!(" name: {}", fname);
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
let c_fname = CString::new(args[1].as_str()).expect("CString::new failed");
let mut info: libc::stat = unsafe { mem::zeroed() };
if unsafe { stat(c_fname.as_ptr(), &mut info) } == 0 {
show_stat_info(&args[1], &info);
} else {
eprintln!("Failed to stat {}", args[1]);
}
} else {
eprintln!("Usage: {} <filename>", args[0]);
}
}
filesize
use std::ffi::CString;
use libc::stat;
use std::mem;
fn main() {
let file_path = "/etc/passwd";
let c_file_path = CString::new(file_path).expect("CString::new failed");
let mut info: libc::stat = unsafe { mem::zeroed() };
if unsafe { stat(c_file_path.as_ptr(), &mut info) } == -1 {
eprintln!("Failed to stat {}", file_path);
} else {
println!("The size of {} is {}", file_path, info.st_size);
}
}
ls1
use std::ffi::CString;
use std::env;
use libc::{opendir, readdir, closedir};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() == 1 {
do_ls(".");
} else {
for arg in &args[1..] {
println!("{}:", arg);
do_ls(arg);
}
}
}
fn do_ls(dirname: &str) {
let c_dirname = CString::new(dirname).expect("CString::new failed");
unsafe {
let dir_ptr = opendir(c_dirname.as_ptr());
if dir_ptr.is_null() {
eprintln!("ls1: cannot open {}", dirname);
} else {
loop {
let direntp = readdir(dir_ptr);
if direntp.is_null() {
break;
}
let name = std::ffi::CStr::from_ptr((*direntp).d_name.as_ptr());
println!("{}", name.to_string_lossy());
}
closedir(dir_ptr);
}
}
}
ls2
use std::env;
use std::ffi::{CString, CStr};
use std::mem;
use std::time::{UNIX_EPOCH, Duration};
use libc::{opendir, readdir, closedir, stat};
use libc::{S_IFMT, S_IFDIR, S_IFCHR, S_IFBLK, S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH};
use libc::{getpwuid, getgrgid, uid_t, gid_t};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() == 1 {
do_ls(".");
} else {
for arg in &args[1..] {
println!("{}:", arg);
do_ls(arg);
}
}
}
fn do_ls(dirname: &str) {
let c_dirname = CString::new(dirname).expect("CString::new failed");
unsafe {
let dir_ptr = opendir(c_dirname.as_ptr());
if dir_ptr.is_null() {
eprintln!("ls2: cannot open {}", dirname);
} else {
loop {
let direntp = readdir(dir_ptr);
if direntp.is_null() {
break;
}
let name = CStr::from_ptr((*direntp).d_name.as_ptr()).to_string_lossy().into_owned();
dostat(&name);
}
closedir(dir_ptr);
}
}
}
fn dostat(filename: &str) {
let c_filename = CString::new(filename).expect("CString::new failed");
let mut info: stat = unsafe { mem::zeroed() };
if unsafe { stat(c_filename.as_ptr(), &mut info) } == -1 {
eprintln!("Cannot stat {}", filename);
} else {
show_file_info(filename, &info);
}
}
fn show_file_info(filename: &str, info_p: &stat) {
let modestr = mode_to_letters(info_p.st_mode);
let uname = uid_to_name(info_p.st_uid);
let gname = gid_to_name(info_p.st_gid);
let size = info_p.st_size;
let mtime = format_time(info_p.st_mtime);
println!(
"{} {:4} {:8} {:8} {:8} {:.12} {}",
modestr,
info_p.st_nlink,
uname,
gname,
size,
mtime,
filename
);
}
fn mode_to_letters(mode: libc::mode_t) -> String {
let mut str = String::from("----------");
if mode & S_IFMT == S_IFDIR { str.replace_range(0..1, "d"); }
if mode & S_IFMT == S_IFCHR { str.replace_range(0..1, "c"); }
if mode & S_IFMT == S_IFBLK { str.replace_range(0..1, "b"); }
if mode & S_IRUSR != 0 { str.replace_range(1..2, "r"); }
if mode & S_IWUSR != 0 { str.replace_range(2..3, "w"); }
if mode & S_IXUSR != 0 { str.replace_range(3..4, "x"); }
if mode & S_IRGRP != 0 { str.replace_range(4..5, "r"); }
if mode & S_IWGRP != 0 { str.replace_range(5..6, "w"); }
if mode & S_IXGRP != 0 { str.replace_range(6..7, "x"); }
if mode & S_IROTH != 0 { str.replace_range(7..8, "r"); }
if mode & S_IWOTH != 0 { str.replace_range(8..9, "w"); }
if mode & S_IXOTH != 0 { str.replace_range(9..10, "x"); }
str
}
fn uid_to_name(uid: uid_t) -> String {
unsafe {
let pw_ptr = getpwuid(uid);
if pw_ptr.is_null() {
return uid.to_string();
}
CStr::from_ptr((*pw_ptr).pw_name).to_string_lossy().into_owned()
}
}
fn gid_to_name(gid: gid_t) -> String {
unsafe {
let grp_ptr = getgrgid(gid);
if grp_ptr.is_null() {
return gid.to_string();
}
CStr::from_ptr((*grp_ptr).gr_name).to_string_lossy().into_owned()
}
}
fn format_time(time: i64) -> String {
let d = UNIX_EPOCH + Duration::new(time as u64, 0);
let datetime = std::time::SystemTime::from(d);
let datetime: chrono::DateTime<chrono::Local> = datetime.into();
datetime.format("%b %e %H:%M").to_string()
}
第四章节
spwd
use std::ffi::{CString, CStr};
use std::mem;
use libc::{opendir, readdir, closedir, stat, chdir, stat as stat_t};
use libc::ino_t;
fn main() {
printpathto(get_inode("."));
println!();
}
fn printpathto(this_inode: ino_t) {
let mut my_inode: ino_t;
let mut its_name = vec![0 as libc::c_char; libc::PATH_MAX as usize];
unsafe {
if get_inode("..") != this_inode {
chdir(CString::new("..").unwrap().as_ptr());
inum_to_name(this_inode, its_name.as_mut_ptr(), libc::PATH_MAX as i32);
my_inode = get_inode(".");
printpathto(my_inode);
print!("/{}", CStr::from_ptr(its_name.as_ptr()).to_str().unwrap());
}
}
}
fn inum_to_name(inode_to_find: ino_t, namebuf: *mut libc::c_char, buflen: i32) {
unsafe {
let dir_ptr = opendir(CString::new(".").unwrap().as_ptr());
if dir_ptr.is_null() {
perror(".");
std::process::exit(1);
}
loop {
let direntp = readdir(dir_ptr);
if direntp.is_null() {
break;
}
if (*direntp).d_ino == inode_to_find {
std::ptr::copy_nonoverlapping((*direntp).d_name.as_ptr(), namebuf, buflen as usize);
*namebuf.add(buflen as usize - 1) = 0; // null terminate
closedir(dir_ptr);
return;
}
}
closedir(dir_ptr);
eprintln!("error looking for inum {}", inode_to_find);
std::process::exit(1);
}
}
fn get_inode(fname: &str) -> ino_t {
let mut info: stat_t = unsafe { mem::zeroed() };
if unsafe { stat(CString::new(fname).unwrap().as_ptr(), &mut info) } == -1 {
eprintln!("Cannot stat {}", fname);
std::process::exit(1);
}
info.st_ino
}
fn perror(s: &str) {
let c_s = CString::new(s).unwrap();
unsafe {
libc::perror(c_s.as_ptr());
}
}
第五章节
echostate
use std::mem;
use libc::{tcgetattr, termios, ECHO};
use std::io::{self, Write};
fn main() {
let mut info: termios = unsafe { mem::zeroed() };
let rv = unsafe { tcgetattr(0, &mut info) };
if rv == -1 {
eprintln!("tcgetattr");
std::process::exit(1);
}
if info.c_lflag & ECHO != 0 {
println!("echo is on, since its bit is 1");
} else {
println!("echo is OFF, since its bit is 0");
}
}
listchars
use std::io::{self, Read};
fn main() {
let mut n = 0;
let stdin = io::stdin();
let mut handle = stdin.lock();
let mut buffer = [0; 1];
while handle.read(&mut buffer).unwrap() > 0 {
let c = buffer[0] as char;
if c == 'Q' {
break;
}
println!("char {:3} is {} code {}", n, c, buffer[0]);
n += 1;
}
}
setecho
use std::env;
use std::process::exit;
use libc::{tcgetattr, tcsetattr, termios, TCSANOW, ECHO};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: setecho [y|n]");
exit(1);
}
let mut info: termios = unsafe { std::mem::zeroed() };
if unsafe { tcgetattr(0, &mut info) } == -1 {
eprintln!("tcgetattr failed");
exit(1);
}
if args[1] == "y" {
info.c_lflag |= ECHO;
} else if args[1] == "n" {
info.c_lflag &= !ECHO;
} else {
eprintln!("Usage: setecho [y|n]");
exit(1);
}
if unsafe { tcsetattr(0, TCSANOW, &info) } == -1 {
eprintln!("tcsetattr failed");
exit(1);
}
}
showtty
use libc::{tcgetattr, termios, cfgetospeed, B300, B600, B1200, B1800, B2400, B4800, B9600, VERASE, VKILL};
use std::io::{self};
struct FlagInfo {
fl_value: libc::c_int,
fl_name: &'static str,
}
const INPUT_FLAGS: [FlagInfo; 10] = [
FlagInfo { fl_value: libc::IGNBRK as libc::c_int, fl_name: "Ignore break condition" },
FlagInfo { fl_value: libc::BRKINT as libc::c_int, fl_name: "Signal interrupt on break" },
FlagInfo { fl_value: libc::IGNPAR as libc::c_int, fl_name: "Ignore chars with parity errors" },
FlagInfo { fl_value: libc::PARMRK as libc::c_int, fl_name: "Mark parity errors" },
FlagInfo { fl_value: libc::INPCK as libc::c_int, fl_name: "Enable input parity check" },
FlagInfo { fl_value: libc::ISTRIP as libc::c_int, fl_name: "Strip character" },
FlagInfo { fl_value: libc::INLCR as libc::c_int, fl_name: "Map NL to CR on input" },
FlagInfo { fl_value: libc::IGNCR as libc::c_int, fl_name: "Ignore CR" },
FlagInfo { fl_value: libc::ICRNL as libc::c_int, fl_name: "Map CR to NL on input" },
FlagInfo { fl_value: libc::IXON as libc::c_int, fl_name: "Enable start/stop output control" },
];
const LOCAL_FLAGS: [FlagInfo; 5] = [
FlagInfo { fl_value: libc::ISIG as libc::c_int, fl_name: "Enable signals" },
FlagInfo { fl_value: libc::ICANON as libc::c_int, fl_name: "Canonical input (erase and kill)" },
FlagInfo { fl_value: libc::ECHO as libc::c_int, fl_name: "Enable echo" },
FlagInfo { fl_value: libc::ECHOE as libc::c_int, fl_name: "Echo ERASE as BS-SPACE-BS" },
FlagInfo { fl_value: libc::ECHOK as libc::c_int, fl_name: "Echo KILL by starting new line" },
];
fn main() {
let mut ttyinfo: termios = unsafe { std::mem::zeroed() };
if unsafe { tcgetattr(0, &mut ttyinfo) } == -1 {
eprintln!("cannot get params about stdin");
std::process::exit(1);
}
showbaud(unsafe { cfgetospeed(&ttyinfo) });
println!(
"The erase character is ascii {}, Ctrl-{}",
ttyinfo.c_cc[VERASE as usize],
(ttyinfo.c_cc[VERASE as usize] - 1 + b'A') as char
);
println!(
"The line kill character is ascii {}, Ctrl-{}",
ttyinfo.c_cc[VKILL as usize],
(ttyinfo.c_cc[VKILL as usize] - 1 + b'A') as char
);
show_some_flags(&ttyinfo);
}
fn showbaud(thespeed: libc::speed_t) {
print!("the baud rate is ");
match thespeed {
B300 => println!("300"),
B600 => println!("600"),
B1200 => println!("1200"),
B1800 => println!("1800"),
B2400 => println!("2400"),
B4800 => println!("4800"),
B9600 => println!("9600"),
_ => println!("Fast"),
}
}
fn show_some_flags(ttyp: &termios) {
show_flagset(ttyp.c_iflag, &INPUT_FLAGS);
show_flagset(ttyp.c_lflag, &LOCAL_FLAGS);
}
fn show_flagset(thevalue: libc::tcflag_t, thebitnames: &[FlagInfo]) {
for flag in thebitnames {
print!(" {} is ", flag.fl_name);
if thevalue & flag.fl_value as libc::tcflag_t != 0 {
println!("ON");
} else {
println!("OFF");
}
}
}
write0
use std::env;
use std::fs::OpenOptions;
use std::io::{self, BufRead};
use std::process::exit;
use std::os::unix::io::AsRawFd;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("usage: write0 ttyname");
exit(1);
}
let ttyname = &args[1];
let file = match OpenOptions::new().write(true).open(ttyname) {
Ok(file) => file,
Err(err) => {
eprintln!("Error opening {}: {}", ttyname, err);
exit(1);
}
};
let fd = file.as_raw_fd();
let stdin = io::stdin();
let handle = stdin.lock();
for line in handle.lines() {
match line {
Ok(buffer) => {
if unsafe { libc::write(fd, buffer.as_ptr() as *const _, buffer.len()) } == -1 {
eprintln!("Error writing to {}", ttyname);
break;
}
}
Err(err) => {
eprintln!("Error reading from stdin: {}", err);
break;
}
}
}
if let Err(err) = file.sync_all() {
eprintln!("Error flushing to {}: {}", ttyname, err);
}
}
write1
use std::env;
use std::ffi::{CStr, CString};
use std::io::{self, BufRead};
use libc::{open, close, read, O_RDONLY, O_WRONLY};
use libc::utmpx;
const UTMP_FILE: &str = "/var/run/utmp";
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("usage: write1 logname");
std::process::exit(1);
}
let logname = &args[1];
let tty_for_user = match get_tty(logname) {
Some(tty) => tty,
None => {
eprintln!("User not found or not logged in");
std::process::exit(1);
}
};
let path = format!("/dev/{}", tty_for_user);
let fd = unsafe { libc::open(CString::new(path.clone()).unwrap().as_ptr(), O_WRONLY) };
if fd == -1 {
eprintln!("Error opening {}: {}", path, io::Error::last_os_error());
std::process::exit(1);
}
let stdin = io::stdin();
let handle = stdin.lock();
for line in handle.lines() {
match line {
Ok(buffer) => {
if unsafe { libc::write(fd, buffer.as_ptr() as *const _, buffer.len()) } == -1 {
eprintln!("Error writing to {}", path);
break;
}
}
Err(err) => {
eprintln!("Error reading from stdin: {}", err);
break;
}
}
}
unsafe { libc::close(fd) };
}
fn get_tty(logname: &str) -> Option<String> {
let utmp_file = CString::new(UTMP_FILE).unwrap();
let fd = unsafe { open(utmp_file.as_ptr(), O_RDONLY) };
if fd == -1 {
return None;
}
let mut utrec: utmpx = unsafe { std::mem::zeroed() };
while unsafe { read(fd, &mut utrec as *mut utmpx as *mut _, std::mem::size_of::<utmpx>()) } == std::mem::size_of::<utmpx>() as isize {
let name = unsafe { CStr::from_ptr(utrec.ut_user.as_ptr()) }.to_str().unwrap_or("").to_string();
if name == logname {
let tty = unsafe { CStr::from_ptr(utrec.ut_line.as_ptr()) }.to_str().unwrap_or("").to_string();
unsafe { close(fd) };
return Some(tty);
}
}
unsafe { close(fd) };
None
}