mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +00:00
commit
548b58a09e
11 changed files with 320 additions and 358 deletions
|
@ -7,11 +7,10 @@ authors = []
|
||||||
name = "uu_pinky"
|
name = "uu_pinky"
|
||||||
path = "pinky.rs"
|
path = "pinky.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies.uucore]
|
||||||
getopts = "*"
|
path = "../uucore"
|
||||||
time = "*"
|
default-features = false
|
||||||
libc = "^0.2"
|
features = ["utmpx", "c_types"]
|
||||||
uucore = { path="../uucore" }
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "pinky"
|
name = "pinky"
|
||||||
|
|
|
@ -13,13 +13,9 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
use uucore::c_types::getpwnam;
|
use uucore::c_types::getpwnam;
|
||||||
use uucore::utmpx;
|
use uucore::utmpx::{self, time, Utmpx};
|
||||||
|
use uucore::coreopts;
|
||||||
extern crate getopts;
|
use uucore::libc::{uid_t, gid_t, c_char, S_IWGRP};
|
||||||
extern crate libc;
|
|
||||||
use libc::{uid_t, gid_t, c_char, S_IWGRP};
|
|
||||||
|
|
||||||
extern crate time;
|
|
||||||
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
@ -29,21 +25,16 @@ use std::fs::File;
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::ffi::{CStr, CString, OsStr};
|
use std::ffi::{CStr, CString};
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod utmp;
|
|
||||||
|
|
||||||
static NAME: &'static str = "pinky";
|
static NAME: &'static str = "pinky";
|
||||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
|
||||||
|
|
||||||
const BUFSIZE: usize = 1024;
|
const BUFSIZE: usize = 1024;
|
||||||
|
|
||||||
pub fn uumain(args: Vec<String>) -> i32 {
|
pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
let mut opts = getopts::Options::new();
|
let mut opts = coreopts::CoreOptions::new(NAME);
|
||||||
opts.optflag("l",
|
opts.optflag("l",
|
||||||
"l",
|
"l",
|
||||||
"produce long format output for the specified USERs");
|
"produce long format output for the specified USERs");
|
||||||
|
@ -64,16 +55,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
opts.optflag("", "help", "display this help and exit");
|
opts.optflag("", "help", "display this help and exit");
|
||||||
opts.optflag("", "version", "output version information and exit");
|
opts.optflag("", "version", "output version information and exit");
|
||||||
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
opts.help(format!(
|
||||||
Ok(m) => m,
|
"Usage: {} [OPTION]... [USER]...
|
||||||
Err(f) => {
|
|
||||||
disp_err!("{}", f);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if matches.opt_present("help") {
|
|
||||||
println!("Usage: {} [OPTION]... [USER]...
|
|
||||||
|
|
||||||
-l produce long format output for the specified USERs
|
-l produce long format output for the specified USERs
|
||||||
-b omit the user's home directory and shell in long format
|
-b omit the user's home directory and shell in long format
|
||||||
|
@ -91,14 +74,9 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
A lightweight 'finger' program; print user information.
|
A lightweight 'finger' program; print user information.
|
||||||
The utmp file will be {}",
|
The utmp file will be {}",
|
||||||
NAME,
|
NAME,
|
||||||
utmpx::DEFAULT_FILE);
|
utmpx::DEFAULT_FILE));
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.opt_present("version") {
|
let matches = opts.parse(args);
|
||||||
println!("{} {}", NAME, VERSION);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If true, display the hours:minutes since each user has touched
|
// If true, display the hours:minutes since each user has touched
|
||||||
// the keyboard, or blank if within the last minute, or days followed
|
// the keyboard, or blank if within the last minute, or days followed
|
||||||
|
@ -244,16 +222,6 @@ impl Capitalize for str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait UtmpUtils {
|
|
||||||
fn is_user_process(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UtmpUtils for utmpx::c_utmp {
|
|
||||||
fn is_user_process(&self) -> bool {
|
|
||||||
self.ut_user[0] != 0 && self.ut_type == utmpx::USER_PROCESS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn idle_string(when: i64) -> String {
|
fn idle_string(when: i64) -> String {
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static NOW: time::Tm = time::now()
|
static NOW: time::Tm = time::now()
|
||||||
|
@ -276,59 +244,14 @@ fn idle_string(when: i64) -> String {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn time_string(ut: &utmpx::c_utmp) -> String {
|
fn time_string(ut: &Utmpx) -> String {
|
||||||
let tm = time::at(time::Timespec::new(ut.ut_tv.tv_sec as i64, ut.ut_tv.tv_usec as i32));
|
time::strftime("%Y-%m-%d %H:%M", &ut.login_time()).unwrap()
|
||||||
time::strftime("%Y-%m-%d %H:%M", &tm).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
const AI_CANONNAME: libc::c_int = 0x2;
|
|
||||||
|
|
||||||
fn canon_host(host: &str) -> Option<String> {
|
|
||||||
let hints = libc::addrinfo {
|
|
||||||
ai_flags: AI_CANONNAME,
|
|
||||||
ai_family: 0,
|
|
||||||
ai_socktype: 0,
|
|
||||||
ai_protocol: 0,
|
|
||||||
ai_addrlen: 0,
|
|
||||||
ai_addr: ptr::null_mut(),
|
|
||||||
ai_canonname: ptr::null_mut(),
|
|
||||||
ai_next: ptr::null_mut(),
|
|
||||||
};
|
|
||||||
let c_host = CString::new(host).unwrap();
|
|
||||||
let mut res = ptr::null_mut();
|
|
||||||
let status = unsafe {
|
|
||||||
libc::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints as *const _, &mut res as *mut _)
|
|
||||||
};
|
|
||||||
if status == 0 {
|
|
||||||
let info: libc::addrinfo = unsafe {
|
|
||||||
ptr::read(res as *const _)
|
|
||||||
};
|
|
||||||
// http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
|
|
||||||
// says Darwin 7.9.0 getaddrinfo returns 0 but sets
|
|
||||||
// res->ai_canonname to NULL.
|
|
||||||
let ret = if info.ai_canonname.is_null() {
|
|
||||||
Some(String::from(host))
|
|
||||||
} else {
|
|
||||||
Some(unsafe {
|
|
||||||
CString::from_raw(info.ai_canonname).into_string().unwrap()
|
|
||||||
})
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
libc::freeaddrinfo(res);
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pinky {
|
impl Pinky {
|
||||||
fn print_entry(&self, ut: &utmpx::c_utmp) {
|
fn print_entry(&self, ut: &Utmpx) {
|
||||||
let mut pts_path = PathBuf::from("/dev");
|
let mut pts_path = PathBuf::from("/dev");
|
||||||
let line: &Path = OsStr::from_bytes(unsafe {
|
pts_path.push(ut.tty_device().as_ref());
|
||||||
CStr::from_ptr(ut.ut_line.as_ref().as_ptr()).to_bytes()
|
|
||||||
}).as_ref();
|
|
||||||
pts_path.push(line);
|
|
||||||
|
|
||||||
let mesg;
|
let mesg;
|
||||||
let last_change;
|
let last_change;
|
||||||
|
@ -347,11 +270,10 @@ impl Pinky {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ut_user = String::from_chars(ut.ut_user.as_ref().as_ptr());
|
print!("{1:<8.0$}", utmpx::UT_NAMESIZE, ut.user());
|
||||||
print!("{1:<8.0$}", utmpx::UT_NAMESIZE, ut_user);
|
|
||||||
|
|
||||||
if self.include_fullname {
|
if self.include_fullname {
|
||||||
if let Some(pw) = getpw(&ut_user) {
|
if let Some(pw) = getpw(ut.user().as_ref()) {
|
||||||
let mut gecos = pw.pw_gecos;
|
let mut gecos = pw.pw_gecos;
|
||||||
if let Some(n) = gecos.find(',') {
|
if let Some(n) = gecos.find(',') {
|
||||||
gecos.truncate(n + 1);
|
gecos.truncate(n + 1);
|
||||||
|
@ -363,7 +285,7 @@ impl Pinky {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print!(" {}{:<8.*}", mesg, utmpx::UT_LINESIZE, String::from_chars(ut.ut_line.as_ref().as_ptr()));
|
print!(" {}{:<8.*}", mesg, utmpx::UT_LINESIZE, ut.tty_device());
|
||||||
|
|
||||||
if self.include_idle {
|
if self.include_idle {
|
||||||
if last_change != 0 {
|
if last_change != 0 {
|
||||||
|
@ -375,11 +297,11 @@ impl Pinky {
|
||||||
|
|
||||||
print!(" {}", time_string(&ut));
|
print!(" {}", time_string(&ut));
|
||||||
|
|
||||||
if self.include_where && ut.ut_host[0] != 0 {
|
if self.include_where && !ut.host().is_empty() {
|
||||||
let ut_host = String::from_chars(ut.ut_host.as_ref().as_ptr());
|
let ut_host = ut.host().into_owned();
|
||||||
let mut res = ut_host.split(':');
|
let mut res = ut_host.split(':');
|
||||||
let host = match res.next() {
|
let host = match res.next() {
|
||||||
Some(h) => canon_host(&h).unwrap_or(ut_host.clone()),
|
Some(_) => ut.canon_host().unwrap_or(ut_host.clone()),
|
||||||
None => ut_host.clone(),
|
None => ut_host.clone(),
|
||||||
};
|
};
|
||||||
match res.next() {
|
match res.next() {
|
||||||
|
@ -411,15 +333,12 @@ impl Pinky {
|
||||||
if self.include_heading {
|
if self.include_heading {
|
||||||
self.print_heading();
|
self.print_heading();
|
||||||
}
|
}
|
||||||
for ut in utmp::read_utmps() {
|
for ut in Utmpx::iter_all_records() {
|
||||||
if ut.is_user_process() {
|
if ut.is_user_process() {
|
||||||
if self.names.is_empty() {
|
if self.names.is_empty() {
|
||||||
self.print_entry(&ut)
|
self.print_entry(&ut)
|
||||||
} else {
|
} else {
|
||||||
let ut_user = unsafe {
|
if self.names.iter().any(|n| n.as_str() == ut.user()) {
|
||||||
CStr::from_ptr(ut.ut_user.as_ref().as_ptr()).to_bytes()
|
|
||||||
};
|
|
||||||
if self.names.iter().any(|n| n.as_bytes() == ut_user) {
|
|
||||||
self.print_entry(&ut);
|
self.print_entry(&ut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
// This file is part of the uutils coreutils package.
|
|
||||||
//
|
|
||||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
|
||||||
//
|
|
||||||
// For the full copyright and license information, please view the LICENSE
|
|
||||||
// file that was distributed with this source code.
|
|
||||||
//
|
|
||||||
extern crate uucore;
|
|
||||||
use uucore::utmpx;
|
|
||||||
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
pub struct UtmpIter;
|
|
||||||
|
|
||||||
impl UtmpIter {
|
|
||||||
fn new() -> Self {
|
|
||||||
unsafe {
|
|
||||||
utmpx::setutxent();
|
|
||||||
}
|
|
||||||
UtmpIter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for UtmpIter {
|
|
||||||
type Item = utmpx::c_utmp;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
unsafe {
|
|
||||||
let line = utmpx::getutxent();
|
|
||||||
|
|
||||||
if line.is_null() {
|
|
||||||
utmpx::endutxent();
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(ptr::read(line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_utmps() -> UtmpIter {
|
|
||||||
UtmpIter::new()
|
|
||||||
}
|
|
|
@ -9,9 +9,11 @@ path = "uptime.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
getopts = "*"
|
getopts = "*"
|
||||||
libc = { git = "https://github.com/rust-lang/libc.git" }
|
|
||||||
time = "*"
|
[dependencies.uucore]
|
||||||
uucore = { path="../uucore" }
|
path = "../uucore"
|
||||||
|
default-features = false
|
||||||
|
features = ["utmpx"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "uptime"
|
name = "uptime"
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
*
|
*
|
||||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* (c) Jian Zeng <anonymousknight86@gmail.com>
|
||||||
*
|
*
|
||||||
* For the full copyright and license information, please view the LICENSE
|
* For the full copyright and license information, please view the LICENSE
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
|
@ -12,20 +13,18 @@
|
||||||
/* last synced with: cat (GNU coreutils) 8.13 */
|
/* last synced with: cat (GNU coreutils) 8.13 */
|
||||||
|
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
extern crate libc;
|
|
||||||
extern crate time as rtime;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
// import crate time from utmpx
|
||||||
|
use uucore::utmpx::*;
|
||||||
|
use uucore::libc::{time_t, c_double};
|
||||||
|
pub use uucore::libc;
|
||||||
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use libc::{time_t, c_double};
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
use std::ptr::null;
|
|
||||||
use uucore::utmpx::*;
|
|
||||||
|
|
||||||
static NAME: &'static str = "uptime";
|
static NAME: &'static str = "uptime";
|
||||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
@ -92,38 +91,21 @@ fn print_loadavg() {
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn process_utmpx() -> (Option<time_t>, usize) {
|
fn process_utmpx() -> (Option<time_t>, usize) {
|
||||||
unsafe {
|
|
||||||
utmpxname(CString::new(DEFAULT_FILE).unwrap().as_ptr());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut nusers = 0;
|
let mut nusers = 0;
|
||||||
let mut boot_time = None;
|
let mut boot_time = None;
|
||||||
|
|
||||||
unsafe {
|
for line in Utmpx::iter_all_records() {
|
||||||
setutxent();
|
match line.record_type() {
|
||||||
|
USER_PROCESS => nusers += 1,
|
||||||
loop {
|
BOOT_TIME => {
|
||||||
let line = getutxent();
|
let t = line.login_time().to_timespec();
|
||||||
|
if t.sec > 0 {
|
||||||
if line == null() {
|
boot_time = Some(t.sec as time_t);
|
||||||
break;
|
}
|
||||||
}
|
},
|
||||||
|
_ => continue
|
||||||
match (*line).ut_type {
|
|
||||||
USER_PROCESS => nusers += 1,
|
|
||||||
BOOT_TIME => {
|
|
||||||
let t = (*line).ut_tv;
|
|
||||||
if t.tv_sec > 0 {
|
|
||||||
boot_time = Some(t.tv_sec as time_t);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endutxent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(boot_time, nusers)
|
(boot_time, nusers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +123,7 @@ fn print_nusers(nusers: usize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_time() {
|
fn print_time() {
|
||||||
let local_time = rtime::now();
|
let local_time = time::now();
|
||||||
|
|
||||||
print!(" {:02}:{:02}:{:02} ", local_time.tm_hour,
|
print!(" {:02}:{:02}:{:02} ", local_time.tm_hour,
|
||||||
local_time.tm_min, local_time.tm_sec);
|
local_time.tm_min, local_time.tm_sec);
|
||||||
|
@ -160,9 +142,9 @@ fn get_uptime(boot_time: Option<time_t>) -> i64 {
|
||||||
} else {
|
} else {
|
||||||
match boot_time {
|
match boot_time {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
let now = rtime::get_time().sec;
|
let now = time::get_time().sec;
|
||||||
let time = t as i64;
|
let boottime = t as i64;
|
||||||
((now - time) * 100)
|
((now - boottime) * 100)
|
||||||
},
|
},
|
||||||
_ => -1,
|
_ => -1,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,11 @@ path = "users.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
getopts = "*"
|
getopts = "*"
|
||||||
libc = "*"
|
|
||||||
uucore = { path="../uucore" }
|
[dependencies.uucore]
|
||||||
|
default-features = false
|
||||||
|
features = ["utmpx"]
|
||||||
|
path = "../uucore"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "users"
|
name = "users"
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
*
|
*
|
||||||
* (c) KokaKiwi <kokakiwi@kokakiwi.net>
|
* (c) KokaKiwi <kokakiwi@kokakiwi.net>
|
||||||
|
* (c) Jian Zeng <anonymousknight86@gmail.com>
|
||||||
*
|
*
|
||||||
* For the full copyright and license information, please view the LICENSE
|
* For the full copyright and license information, please view the LICENSE
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
|
@ -15,16 +16,12 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
extern crate libc;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
use uucore::utmpx::*;
|
||||||
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use std::ffi::{CStr, CString};
|
|
||||||
use std::mem;
|
|
||||||
use std::ptr;
|
|
||||||
use uucore::utmpx::*;
|
|
||||||
|
|
||||||
static NAME: &'static str = "users";
|
static NAME: &'static str = "users";
|
||||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
@ -67,30 +64,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(filename: &str) {
|
fn exec(filename: &str) {
|
||||||
unsafe {
|
let mut users = Utmpx::iter_all_records()
|
||||||
utmpxname(CString::new(filename).unwrap().as_ptr());
|
.read_from(filename)
|
||||||
}
|
.filter(|ut| ut.is_user_process())
|
||||||
|
.map(|ut| ut.user().into_owned())
|
||||||
let mut users = vec!();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
unsafe {
|
|
||||||
setutxent();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let line = getutxent();
|
|
||||||
|
|
||||||
if line == ptr::null() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*line).ut_type == USER_PROCESS {
|
|
||||||
let user = String::from_utf8_lossy(CStr::from_ptr(mem::transmute(&(*line).ut_user)).to_bytes()).to_string();
|
|
||||||
users.push(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
endutxent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if !users.is_empty() {
|
if !users.is_empty() {
|
||||||
users.sort();
|
users.sort();
|
||||||
|
|
|
@ -4,10 +4,22 @@ version = "0.0.1"
|
||||||
authors = []
|
authors = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "*"
|
libc = { git = "https://github.com/rust-lang/libc.git" }
|
||||||
winapi = "*"
|
|
||||||
getopts = "*"
|
getopts = "*"
|
||||||
data-encoding = "^1.1"
|
time = { version = "*", optional = true }
|
||||||
|
data-encoding = { version = "^1.1", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
fs = []
|
||||||
|
utf8 = []
|
||||||
|
encoding = ["data-encoding"]
|
||||||
|
parse_time = []
|
||||||
|
utmpx = ["time"]
|
||||||
|
c_types = []
|
||||||
|
process = []
|
||||||
|
signals = []
|
||||||
|
wide = []
|
||||||
|
default = ["fs", "utf8", "encoding", "parse_time", "utmpx", "c_types", "process", "signals", "wide"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
|
@ -1,18 +1,27 @@
|
||||||
extern crate libc;
|
pub extern crate libc;
|
||||||
#[cfg(windows)] extern crate winapi;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
pub mod fs;
|
|
||||||
pub mod parse_time;
|
|
||||||
pub mod utf8;
|
|
||||||
pub mod encoding;
|
|
||||||
pub mod coreopts;
|
pub mod coreopts;
|
||||||
|
|
||||||
#[cfg(unix)] pub mod c_types;
|
#[cfg(feature = "fs")]
|
||||||
#[cfg(unix)] pub mod process;
|
pub mod fs;
|
||||||
#[cfg(unix)] pub mod signals;
|
#[cfg(feature = "utf8")]
|
||||||
#[cfg(unix)] pub mod utmpx;
|
pub mod utf8;
|
||||||
|
#[cfg(feature = "encoding")]
|
||||||
|
pub mod encoding;
|
||||||
|
#[cfg(feature = "parse_time")]
|
||||||
|
pub mod parse_time;
|
||||||
|
|
||||||
#[cfg(windows)] pub mod wide;
|
#[cfg(all(unix, feature = "utmpx"))]
|
||||||
|
pub mod utmpx;
|
||||||
|
#[cfg(all(unix, feature = "c_types"))]
|
||||||
|
pub mod c_types;
|
||||||
|
#[cfg(all(unix, feature = "process"))]
|
||||||
|
pub mod process;
|
||||||
|
#[cfg(all(unix, feature = "signals"))]
|
||||||
|
pub mod signals;
|
||||||
|
|
||||||
|
#[cfg(all(windows, feature = "wide"))]
|
||||||
|
pub mod wide;
|
||||||
|
|
|
@ -322,15 +322,21 @@ macro_rules! msg_arg_invalid_value { ($expects:expr, $received:expr) => (
|
||||||
msg_invalid_input!(format!("expects its argument to be {}, but was provided {}", $expects, $received)) ); }
|
msg_invalid_input!(format!("expects its argument to be {}, but was provided {}", $expects, $received)) ); }
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! msg_args_invalid_value { ($expects:expr, $received:expr) => (
|
macro_rules! msg_args_invalid_value {
|
||||||
msg_invalid_input!(format!("expects its arguments to be {}, but was provided {}", $expects, $received)) ); }
|
($expects:expr, $received:expr) => (
|
||||||
|
msg_invalid_input!(format!("expects its arguments to be {}, but was provided {}", $expects, $received))
|
||||||
|
);
|
||||||
|
($msg:expr) => (
|
||||||
|
msg_invalid_input!($msg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! msg_args_nonexistent_file { ($received:expr) => (
|
macro_rules! msg_args_nonexistent_file { ($received:expr) => (
|
||||||
msg_args_invalid_value!("paths to files", snippet_no_file_at_path!($received)));}
|
msg_args_invalid_value!("paths to files", snippet_no_file_at_path!($received)));}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! msg_wrong_number_of_arguments { ($received:expr) => (
|
macro_rules! msg_wrong_number_of_arguments { () => (
|
||||||
msg_args_invalid_value!("wrong number of arguments") ); }
|
msg_args_invalid_value!("wrong number of arguments") ); }
|
||||||
|
|
||||||
// -- message templates : invalid input : input combinations
|
// -- message templates : invalid input : input combinations
|
||||||
|
|
|
@ -1,125 +1,87 @@
|
||||||
#![allow(dead_code, non_camel_case_types)]
|
//! Aim to provide platform-independent methods to obtain login records
|
||||||
|
//!
|
||||||
|
//! **ONLY** support linux, macos and freebsd for the time being
|
||||||
|
|
||||||
extern crate libc;
|
use super::libc;
|
||||||
|
pub extern crate time;
|
||||||
|
use self::time::{Tm, Timespec};
|
||||||
|
|
||||||
pub use self::utmpx::{UT_NAMESIZE, UT_LINESIZE, DEFAULT_FILE, USER_PROCESS, BOOT_TIME};
|
use ::std::io::Result as IOResult;
|
||||||
pub use self::utmpx::c_utmp;
|
use ::std::io::Error as IOError;
|
||||||
|
use ::std::ptr;
|
||||||
extern "C" {
|
use ::std::borrow::Cow;
|
||||||
pub fn getutxent() -> *const c_utmp;
|
use ::std::ffi::CStr;
|
||||||
pub fn getutxid(ut: *const c_utmp) -> *const c_utmp;
|
use ::std::ffi::CString;
|
||||||
pub fn getutxline(ut: *const c_utmp) -> *const c_utmp;
|
|
||||||
pub fn pututxline(ut: *const c_utmp) -> *const c_utmp;
|
|
||||||
pub fn setutxent();
|
|
||||||
pub fn endutxent();
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
|
||||||
pub fn utmpxname(file: *const libc::c_char) -> libc::c_int;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
pub use self::ut::*;
|
||||||
|
use libc::utmpx;
|
||||||
|
// pub use libc::getutxid;
|
||||||
|
// pub use libc::getutxline;
|
||||||
|
// pub use libc::pututxline;
|
||||||
|
pub use libc::getutxent;
|
||||||
|
pub use libc::setutxent;
|
||||||
|
pub use libc::endutxent;
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||||
|
pub use libc::utmpxname;
|
||||||
#[cfg(target_os = "freebsd")]
|
#[cfg(target_os = "freebsd")]
|
||||||
pub unsafe extern fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
|
pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
macro_rules! bytes2cow {
|
||||||
mod utmpx {
|
($name:expr) => (
|
||||||
use super::libc;
|
unsafe {
|
||||||
|
CStr::from_ptr($name.as_ref().as_ptr()).to_string_lossy()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod ut {
|
||||||
pub static DEFAULT_FILE: &'static str = "/var/run/utmp";
|
pub static DEFAULT_FILE: &'static str = "/var/run/utmp";
|
||||||
|
|
||||||
pub const UT_LINESIZE: usize = 32;
|
pub use libc::__UT_LINESIZE as UT_LINESIZE;
|
||||||
pub const UT_NAMESIZE: usize = 32;
|
pub use libc::__UT_NAMESIZE as UT_NAMESIZE;
|
||||||
|
pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE;
|
||||||
pub const UT_IDSIZE: usize = 4;
|
pub const UT_IDSIZE: usize = 4;
|
||||||
pub const UT_HOSTSIZE: usize = 256;
|
|
||||||
|
|
||||||
pub const EMPTY: libc::c_short = 0;
|
pub use libc::EMPTY;
|
||||||
pub const RUN_LVL: libc::c_short = 1;
|
pub use libc::RUN_LVL;
|
||||||
pub const BOOT_TIME: libc::c_short = 2;
|
pub use libc::BOOT_TIME;
|
||||||
pub const NEW_TIME: libc::c_short = 3;
|
pub use libc::NEW_TIME;
|
||||||
pub const OLD_TIME: libc::c_short = 4;
|
pub use libc::OLD_TIME;
|
||||||
pub const INIT_PROCESS: libc::c_short = 5;
|
pub use libc::INIT_PROCESS;
|
||||||
pub const LOGIN_PROCESS: libc::c_short = 6;
|
pub use libc::LOGIN_PROCESS;
|
||||||
pub const USER_PROCESS: libc::c_short = 7;
|
pub use libc::USER_PROCESS;
|
||||||
pub const DEAD_PROCESS: libc::c_short = 8;
|
pub use libc::DEAD_PROCESS;
|
||||||
pub const ACCOUNTING: libc::c_short = 9;
|
pub use libc::ACCOUNTING;
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct __exit_status {
|
|
||||||
pub e_termination: libc::c_short,
|
|
||||||
pub e_exit: libc::c_short,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct __timeval {
|
|
||||||
pub tv_sec: libc::int32_t,
|
|
||||||
pub tv_usec: libc::int32_t,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct c_utmp {
|
|
||||||
pub ut_type: libc::c_short,
|
|
||||||
pub ut_pid: libc::pid_t,
|
|
||||||
pub ut_line: [libc::c_char; UT_LINESIZE],
|
|
||||||
pub ut_id: [libc::c_char; UT_IDSIZE],
|
|
||||||
|
|
||||||
pub ut_user: [libc::c_char; UT_NAMESIZE],
|
|
||||||
pub ut_host: [libc::c_char; UT_HOSTSIZE],
|
|
||||||
pub ut_exit: __exit_status,
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
pub ut_session: libc::c_long,
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
pub ut_tv: libc::timeval,
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
pub ut_session: libc::int32_t,
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
pub ut_tv: __timeval,
|
|
||||||
|
|
||||||
pub ut_addr_v6: [libc::int32_t; 4],
|
|
||||||
__glibc_reserved: [libc::c_char; 20],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod utmpx {
|
mod ut {
|
||||||
use super::libc;
|
|
||||||
|
|
||||||
pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";
|
pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";
|
||||||
|
|
||||||
pub const UT_LINESIZE: usize = 32;
|
pub use libc::_UTX_LINESIZE as UT_LINESIZE;
|
||||||
pub const UT_NAMESIZE: usize = 256;
|
pub use libc::_UTX_USERSIZE as UT_NAMESIZE;
|
||||||
pub const UT_IDSIZE: usize = 4;
|
pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE;
|
||||||
pub const UT_HOSTSIZE: usize = 256;
|
pub use libc::_UTX_IDSIZE as UT_IDSIZE;
|
||||||
|
|
||||||
pub const EMPTY: libc::c_short = 0;
|
pub use libc::EMPTY;
|
||||||
pub const RUN_LVL: libc::c_short = 1;
|
pub use libc::RUN_LVL;
|
||||||
pub const BOOT_TIME: libc::c_short = 2;
|
pub use libc::BOOT_TIME;
|
||||||
pub const OLD_TIME: libc::c_short = 3;
|
pub use libc::NEW_TIME;
|
||||||
pub const NEW_TIME: libc::c_short = 4;
|
pub use libc::OLD_TIME;
|
||||||
pub const INIT_PROCESS: libc::c_short = 5;
|
pub use libc::INIT_PROCESS;
|
||||||
pub const LOGIN_PROCESS: libc::c_short = 6;
|
pub use libc::LOGIN_PROCESS;
|
||||||
pub const USER_PROCESS: libc::c_short = 7;
|
pub use libc::USER_PROCESS;
|
||||||
pub const DEAD_PROCESS: libc::c_short = 8;
|
pub use libc::DEAD_PROCESS;
|
||||||
pub const ACCOUNTING: libc::c_short = 9;
|
pub use libc::ACCOUNTING;
|
||||||
|
pub use libc::SIGNATURE;
|
||||||
#[repr(C)]
|
pub use libc::SHUTDOWN_TIME;
|
||||||
pub struct c_utmp {
|
|
||||||
pub ut_user: [libc::c_char; UT_NAMESIZE],
|
|
||||||
pub ut_id: [libc::c_char; UT_IDSIZE],
|
|
||||||
pub ut_line: [libc::c_char; UT_LINESIZE],
|
|
||||||
pub ut_pid: libc::pid_t,
|
|
||||||
pub ut_type: libc::c_short,
|
|
||||||
pub ut_tv: libc::timeval,
|
|
||||||
pub ut_host: [libc::c_char; UT_HOSTSIZE],
|
|
||||||
pub __unused: [libc::uint32_t; 16],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "freebsd")]
|
#[cfg(target_os = "freebsd")]
|
||||||
mod utmpx {
|
mod ut {
|
||||||
use super::libc;
|
use super::libc;
|
||||||
|
|
||||||
pub static DEFAULT_FILE: &'static str = "";
|
pub static DEFAULT_FILE: &'static str = "";
|
||||||
|
@ -129,25 +91,158 @@ mod utmpx {
|
||||||
pub const UT_IDSIZE: usize = 8;
|
pub const UT_IDSIZE: usize = 8;
|
||||||
pub const UT_HOSTSIZE: usize = 128;
|
pub const UT_HOSTSIZE: usize = 128;
|
||||||
|
|
||||||
pub const EMPTY: libc::c_short = 0;
|
pub use libc::EMPTY;
|
||||||
pub const BOOT_TIME: libc::c_short = 1;
|
pub use libc::BOOT_TIME;
|
||||||
pub const OLD_TIME: libc::c_short = 2;
|
pub use libc::OLD_TIME;
|
||||||
pub const NEW_TIME: libc::c_short = 3;
|
pub use libc::NEW_TIME;
|
||||||
pub const USER_PROCESS: libc::c_short = 4;
|
pub use libc::USER_PROCESS;
|
||||||
pub const INIT_PROCESS: libc::c_short = 5;
|
pub use libc::INIT_PROCESS;
|
||||||
pub const LOGIN_PROCESS: libc::c_short = 6;
|
pub use libc::LOGIN_PROCESS;
|
||||||
pub const DEAD_PROCESS: libc::c_short = 7;
|
pub use libc::DEAD_PROCESS;
|
||||||
pub const SHUTDOWN_TIME: libc::c_short = 8;
|
pub use libc::SHUTDOWN_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
/// Login records
|
||||||
pub struct c_utmp {
|
///
|
||||||
pub ut_type: libc::c_short,
|
/// Examples:
|
||||||
pub ut_tv: libc::timeval,
|
/// ---------
|
||||||
pub ut_id: [libc::c_char; UT_IDSIZE],
|
///
|
||||||
pub ut_pid: libc::pid_t,
|
/// ```
|
||||||
pub ut_user: [libc::c_char; UT_NAMESIZE],
|
/// for ut in Utmpx::iter_all_records() {
|
||||||
pub ut_line: [libc::c_char; UT_LINESIZE],
|
/// if ut.is_user_process() {
|
||||||
pub ut_host: [libc::c_char; UT_HOSTSIZE],
|
/// println!("{}: {}", ut.host(), ut.user())
|
||||||
pub ut_spare: [libc::c_char; 64],
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Specifying the path to login record:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// for ut in Utmpx::iter_all_records().read_from("/some/where/else") {
|
||||||
|
/// if ut.is_user_process() {
|
||||||
|
/// println!("{}: {}", ut.host(), ut.user())
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct Utmpx {
|
||||||
|
inner: utmpx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Utmpx {
|
||||||
|
/// A.K.A. ut.ut_type
|
||||||
|
pub fn record_type(&self) -> i16 {
|
||||||
|
self.inner.ut_type as i16
|
||||||
|
}
|
||||||
|
/// A.K.A. ut.ut_pid
|
||||||
|
pub fn pid(&self) -> i32 {
|
||||||
|
self.inner.ut_pid as i32
|
||||||
|
}
|
||||||
|
/// A.K.A. ut.ut_id
|
||||||
|
pub fn terminal_suffix(&self) -> Cow<str> {
|
||||||
|
bytes2cow!(self.inner.ut_id)
|
||||||
|
}
|
||||||
|
/// A.K.A. ut.ut_user
|
||||||
|
pub fn user(&self) -> Cow<str> {
|
||||||
|
bytes2cow!(self.inner.ut_user)
|
||||||
|
}
|
||||||
|
/// A.K.A. ut.ut_host
|
||||||
|
pub fn host(&self) -> Cow<str> {
|
||||||
|
bytes2cow!(self.inner.ut_host)
|
||||||
|
}
|
||||||
|
/// A.K.A. ut.ut_line
|
||||||
|
pub fn tty_device(&self) -> Cow<str> {
|
||||||
|
bytes2cow!(self.inner.ut_line)
|
||||||
|
}
|
||||||
|
/// A.K.A. ut.ut_tv
|
||||||
|
pub fn login_time(&self) -> Tm {
|
||||||
|
time::at(Timespec::new(self.inner.ut_tv.tv_sec as i64,
|
||||||
|
self.inner.ut_tv.tv_usec as i32))
|
||||||
|
}
|
||||||
|
/// Consumes the `Utmpx`, returning the underlying C struct utmpx
|
||||||
|
pub fn into_inner(self) -> utmpx {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
pub fn is_user_process(&self) -> bool {
|
||||||
|
!self.user().is_empty() && self.record_type() == USER_PROCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Canonicalize host name using DNS
|
||||||
|
pub fn canon_host(&self) -> IOResult<String> {
|
||||||
|
const AI_CANONNAME: libc::c_int = 0x2;
|
||||||
|
let host = self.host();
|
||||||
|
let hints = libc::addrinfo {
|
||||||
|
ai_flags: AI_CANONNAME,
|
||||||
|
ai_family: 0,
|
||||||
|
ai_socktype: 0,
|
||||||
|
ai_protocol: 0,
|
||||||
|
ai_addrlen: 0,
|
||||||
|
ai_addr: ptr::null_mut(),
|
||||||
|
ai_canonname: ptr::null_mut(),
|
||||||
|
ai_next: ptr::null_mut(),
|
||||||
|
};
|
||||||
|
let c_host = CString::new(host.as_ref()).unwrap();
|
||||||
|
let mut res = ptr::null_mut();
|
||||||
|
let status = unsafe {
|
||||||
|
libc::getaddrinfo(c_host.as_ptr(),
|
||||||
|
ptr::null(),
|
||||||
|
&hints as *const _,
|
||||||
|
&mut res as *mut _)
|
||||||
|
};
|
||||||
|
if status == 0 {
|
||||||
|
let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
|
||||||
|
// http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
|
||||||
|
// says Darwin 7.9.0 getaddrinfo returns 0 but sets
|
||||||
|
// res->ai_canonname to NULL.
|
||||||
|
let ret = if info.ai_canonname.is_null() {
|
||||||
|
Ok(String::from(host.as_ref()))
|
||||||
|
} else {
|
||||||
|
Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
libc::freeaddrinfo(res);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
Err(IOError::last_os_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn iter_all_records() -> UtmpxIter {
|
||||||
|
UtmpxIter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator of login records
|
||||||
|
pub struct UtmpxIter;
|
||||||
|
|
||||||
|
impl UtmpxIter {
|
||||||
|
/// Sets the name of the utmpx-format file for the other utmpx functions to access.
|
||||||
|
///
|
||||||
|
/// If not set, default record file will be used(file path depends on the target OS)
|
||||||
|
pub fn read_from(self, f: &str) -> Self {
|
||||||
|
let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) };
|
||||||
|
if res != 0 {
|
||||||
|
println!("Warning: {}", IOError::last_os_error());
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
setutxent();
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for UtmpxIter {
|
||||||
|
type Item = Utmpx;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
unsafe {
|
||||||
|
let res = getutxent();
|
||||||
|
if !res.is_null() {
|
||||||
|
Some(Utmpx {
|
||||||
|
inner: ptr::read(res as *const _)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
endutxent();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue