第二章节书中代码有如下内容
这些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;}
}