第二章节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
}

posted @ 2024-06-15 15:53  LLLZTTT  阅读(20)  评论(0编辑  收藏  举报
$