uu_users/
users.rs

1// This file is part of the uutils coreutils package.
2//
3// For the full copyright and license information, please view the LICENSE
4// file that was distributed with this source code.
5
6// spell-checker:ignore (paths) wtmp
7
8use std::ffi::OsString;
9use std::path::Path;
10
11use clap::builder::ValueParser;
12use clap::{Arg, Command};
13use uucore::error::UResult;
14use uucore::format_usage;
15use uucore::translate;
16
17#[cfg(target_os = "openbsd")]
18use utmp_classic::{UtmpEntry, parse_from_path};
19#[cfg(not(target_os = "openbsd"))]
20use uucore::utmpx::{self, Utmpx};
21
22#[cfg(target_os = "openbsd")]
23const OPENBSD_UTMP_FILE: &str = "/var/run/utmp";
24
25static ARG_FILE: &str = "file";
26
27fn get_long_usage() -> String {
28    #[cfg(not(target_os = "openbsd"))]
29    let default_path: &str = utmpx::DEFAULT_FILE;
30    #[cfg(target_os = "openbsd")]
31    let default_path: &str = OPENBSD_UTMP_FILE;
32
33    translate!("users-long-usage", "default_path" => default_path)
34}
35
36#[uucore::main]
37pub fn uumain(args: impl uucore::Args) -> UResult<()> {
38    let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
39
40    let maybe_file: Option<&Path> = matches.get_one::<OsString>(ARG_FILE).map(AsRef::as_ref);
41
42    let mut users: Vec<String>;
43
44    // OpenBSD uses the Unix version 1 UTMP, all other Unixes use the newer UTMPX
45    #[cfg(target_os = "openbsd")]
46    {
47        let filename = maybe_file.unwrap_or(Path::new(OPENBSD_UTMP_FILE));
48        let entries = parse_from_path(filename).unwrap_or_default();
49        users = Vec::new();
50        for entry in entries {
51            if let UtmpEntry::UTMP {
52                line: _,
53                user,
54                host: _,
55                time: _,
56            } = entry
57            {
58                if !user.is_empty() {
59                    users.push(user);
60                }
61            }
62        }
63    };
64    #[cfg(not(target_os = "openbsd"))]
65    {
66        let filename = maybe_file.unwrap_or(utmpx::DEFAULT_FILE.as_ref());
67
68        users = Utmpx::iter_all_records_from(filename)
69            .filter(|ut| ut.is_user_process())
70            .map(|ut| ut.user())
71            .collect::<Vec<_>>();
72    };
73
74    if !users.is_empty() {
75        users.sort();
76        println!("{}", users.join(" "));
77    }
78
79    Ok(())
80}
81
82pub fn uu_app() -> Command {
83    #[cfg(not(target_env = "musl"))]
84    let about = translate!("users-about");
85    #[cfg(target_env = "musl")]
86    let about = translate!("users-about") + &translate!("users-about-musl-warning");
87
88    Command::new(uucore::util_name())
89        .version(uucore::crate_version!())
90        .help_template(uucore::localized_help_template(uucore::util_name()))
91        .about(about)
92        .override_usage(format_usage(&translate!("users-usage")))
93        .infer_long_args(true)
94        .after_help(get_long_usage())
95        .arg(
96            Arg::new(ARG_FILE)
97                .num_args(1)
98                .value_hint(clap::ValueHint::FilePath)
99                .value_parser(ValueParser::os_string()),
100        )
101}