第二章节书中代码有如下内容
这些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;
}
}
标签:章节,std,use,u8,ut,代码,utmp,let,RUST
From: https://www.cnblogs.com/lizhuotong/p/18249383