Rust系统日志处理库utmp-classic的使用,高效解析和管理Linux/Unix用户登录记录

Rust系统日志处理库utmp-classic的使用,高效解析和管理Linux/Unix用户登录记录

utmp-classic介绍

utmp-classic是一个用于读取utmp文件的Rust库。需要注意的是,所有类Unix系统(包括所有GNU/Linux发行版、MacOS和除OpenBSD外的所有BSD系统)都使用较新的utmpx文件格式,即使它们仍然称之为utmp。这个库仅适用于原版Unix的utmp文件,据我所知目前只有OpenBSD在使用这种格式。

如果你需要用在OpenBSD以外的系统上,你可能需要寻找一个utmpx库,尽管大多数这样的库都自称是utmp库。

示例运行

根目录中包含了一个示例utmp文件,你可以通过以下命令运行示例:

cargo run --package utmp-classic --example dump-utmp utmp

历史背景

这个库基于upsuper开发的utmp-rs库,修改后适用于OpenBSD仍在使用的经典AT&T Unix v1风格的utmp文件。

完整示例代码

下面是一个完整的示例代码,展示了如何使用utmp-classic库:

use std::fs::File;
use std::io::Read;
use utmp_classic::UtmpEntry;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 打开utmp文件
    let mut file = File::open("/var/run/utmp")?;
    let mut buffer = Vec::new();
    
    // 读取文件内容
    file.read_to_end(&mut buffer)?;
    
    // 解析utmp记录
    let entries = UtmpEntry::parse_utmp(&buffer)?;
    
    // 打印每个登录记录
    for entry in entries {
        println!("User: {:?}", entry.user());
        println!("Terminal: {:?}", entry.tty_device());
        println!("Host: {:?}", entry.host());
        println!("Login Time: {:?}", entry.login_time());
        println!("PID: {}", entry.pid());
        println!("Type: {:?}", entry.entry_type());
        println!("----------------------------------");
    }
    
    Ok(())
}

代码说明

  1. 首先打开并读取utmp文件内容到缓冲区
  2. 使用UtmpEntry::parse_utmp方法解析缓冲区内容
  3. 遍历所有登录记录,打印每个记录的信息:
    • 用户名
    • 终端设备
    • 主机名
    • 登录时间
    • 进程ID
    • 记录类型

安装

要在你的项目中使用utmp-classic,可以运行以下Cargo命令:

cargo add utmp-classic

或者在你的Cargo.toml中添加:

utmp-classic = "0.1.6"

这个库特别适合需要在OpenBSD系统上处理用户登录记录的场景,提供了高效的解析能力和简单的API接口。


1 回复

Rust系统日志处理库utmp-classic使用指南

简介

utmp-classic 是一个用于解析和管理 Linux/Unix 用户登录记录的 Rust 库。它专门处理 /var/run/utmp/var/log/wtmp 文件格式,这些文件记录了系统用户的登录、注销和其他状态变化信息。

主要功能

  • 解析 utmp/wtmp 二进制文件格式
  • 提供结构化的用户登录记录
  • 支持多种记录类型(用户进程、登录进程、死进程等)
  • 跨平台支持(Linux/Unix系统)

安装方法

在 Cargo.toml 中添加依赖:

[dependencies]
utmp-classic = "0.1"

基本使用方法

1. 读取 utmp/wtmp 文件

use std::fs::File;
use utmp_classic::{UtmpEntry, UtmpParser};

fn main() -> std::io::Result<()> {
    let file = File::open("/var/run/utmp")?;
    let parser = UtmpParser::new(file);
    
    for entry in parser {
        if let Ok(entry) = entry {
            println!("{:?}", entry);
        }
    }
    
    Ok(())
}

2. 解析单个记录

use utmp_classic::UtmpEntry;

fn print_entry(entry: &UtmpEntry) {
    println!("User: {}", entry.user());
    println!("Terminal: {}", entry.line());
    println!("Host: {}", entry.host());
    println!("Login time: {:?}", entry.time());
    println!("Process ID: {}", entry.pid());
    println!("Type: {:?}", entry.entry_type());
    println!();
}

3. 过滤特定类型的记录

use utmp_classic::{UtmpEntry, UtmpEntryType};

fn filter_user_processes(entries: Vec<UtmpEntry>) -> Vec<UtmpEntry> {
    entries.into_iter()
        .filter(|e| e.entry_type() == UtmpEntryType::UserProcess)
        .collect()
}

4. 获取当前登录用户

use utmp_classic::{UtmpEntry, UtmpEntryType};

fn get_logged_in_users() -> Vec<UtmpEntry> {
    let file = File::open("/var/run/utmp").unwrap();
    UtmpParser::new(file)
        .filter_map(Result::ok)
        .filter(|e| e.entry_type() == UtmpEntryType::UserProcess)
        .collect()
}

高级用法

1. 监控登录/注销事件

use std::{fs::File, thread, time::Duration};
use utmp_classic::{UtmpParser, UtmpEntryType};

fn monitor_logins() {
    let mut last_size = 0;
    
    loop {
        if let Ok(file) = File::open("/var/log/wtmp") {
            let current_size = file.metadata().unwrap().len();
            
            if current_size > last_size {
                let parser = UtmpParser::new(file);
                for entry in parser.skip(last_size as usize / std::mem::size_of::<UtmpEntry>()) {
                    if let Ok(entry) = entry {
                        match entry.entry_type() {
                            UtmpEntryType::UserProcess => println!("Login: {}", entry.user()),
                            UtmpEntryType::DeadProcess => println!("Logout: {}", entry.user()),
                            _ => {}
                        }
                    }
                }
                last_size = current_size;
            }
        }
        
        thread::sleep(Duration::from_secs(5));
    }
}

2. 生成登录统计报告

use std::collections::HashMap;
use utmp_classic::{UtmpParser, UtmpEntryType};

fn generate_login_stats() -> HashMap<String, usize> {
    let file = File::open("/var/log/wtmp").unwrap();
    let mut stats = HashMap::new();
    
    for entry in UtmpParser::new(file).filter_map(Result::ok) {
        if entry.entry_type() == UtmpEntryType::UserProcess {
            *stats.entry(entry.user().to_string()).or_insert(0) += 1;
        }
    }
    
    stats
}

注意事项

  1. 读取 utmp/wtmp 文件通常需要 root 权限
  2. 不同系统的 utmp/wtmp 文件位置可能有所不同
  3. 文件格式在不同 Unix 变种中可能有细微差异
  4. 处理大文件时注意内存使用

错误处理

建议对可能出现的错误进行适当处理:

use utmp_classic::{UtmpParser, ParseError};

fn safe_parse() -> Result<(), ParseError> {
    let file = File::open("/var/run/utmp").map_err(|e| ParseError::Io(e))?;
    let parser = UtmpParser::new(file);
    
    for entry in parser {
        let entry = entry?;
        // 处理记录
    }
    
    Ok(())
}

完整示例代码

下面是一个综合使用 utmp-classic 库的完整示例,展示如何读取登录记录并生成统计报告:

use std::{collections::HashMap, fs::File, io};
use utmp_classic::{UtmpEntry, UtmpEntryType, UtmpParser};

fn main() -> io::Result<()> {
    // 读取当前登录用户
    println!("当前登录用户:");
    let logged_in_users = get_logged_in_users()?;
    for user in &logged_in_users {
        println!("- {} 从 {} 登录,终端: {}", 
            user.user(), 
            user.host(),
            user.line()
        );
    }

    // 生成登录统计报告
    println!("\n登录统计:");
    let stats = generate_login_stats()?;
    for (user, count) in stats {
        println!("- {}: {} 次登录", user, count);
    }

    Ok(())
}

/// 获取当前登录用户
fn get_logged_in_users() -> io::Result<Vec<UtmpEntry>> {
    let file = File::open("/var/run/utmp")?;
    Ok(UtmpParser::new(file)
        .filter_map(Result::ok)
        .filter(|e| e.entry_type() == UtmpEntryType::UserProcess)
        .collect())
}

/// 生成登录统计报告
fn generate_login_stats() -> io::Result<HashMap<String, usize>> {
    let file = File::open("/var/log/wtmp")?;
    let mut stats = HashMap::new();
    
    for entry in UtmpParser::new(file).filter_map(Result::ok) {
        if entry.entry_type() == UtmpEntryType::UserProcess {
            *stats.entry(entry.user().to_string()).or_insert(0) += 1;
        }
    }
    
    Ok(stats)
}

这个完整示例展示了:

  1. 读取当前登录用户信息
  2. 显示每个登录用户的详细信息
  3. 统计历史登录次数并生成报告
  4. 包含了基本的错误处理

utmp-classic 库为 Rust 开发者提供了高效解析系统登录记录的能力,适合用于安全监控、用户行为分析和系统管理工具开发。

回到顶部