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(())
}
代码说明
- 首先打开并读取utmp文件内容到缓冲区
- 使用
UtmpEntry::parse_utmp
方法解析缓冲区内容 - 遍历所有登录记录,打印每个记录的信息:
- 用户名
- 终端设备
- 主机名
- 登录时间
- 进程ID
- 记录类型
安装
要在你的项目中使用utmp-classic,可以运行以下Cargo命令:
cargo add utmp-classic
或者在你的Cargo.toml中添加:
utmp-classic = "0.1.6"
这个库特别适合需要在OpenBSD系统上处理用户登录记录的场景,提供了高效的解析能力和简单的API接口。
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
}
注意事项
- 读取 utmp/wtmp 文件通常需要 root 权限
- 不同系统的 utmp/wtmp 文件位置可能有所不同
- 文件格式在不同 Unix 变种中可能有细微差异
- 处理大文件时注意内存使用
错误处理
建议对可能出现的错误进行适当处理:
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)
}
这个完整示例展示了:
- 读取当前登录用户信息
- 显示每个登录用户的详细信息
- 统计历史登录次数并生成报告
- 包含了基本的错误处理
utmp-classic 库为 Rust 开发者提供了高效解析系统登录记录的能力,适合用于安全监控、用户行为分析和系统管理工具开发。