Skip to content

Commit a5948ce

Browse files
authored
Merge pull request #3155 from jtracey/gecos-off-by-one
pinky: fix off-by-one in GECOS parsing
2 parents 7e05833 + 687dcae commit a5948ce

File tree

5 files changed

+60
-27
lines changed

5 files changed

+60
-27
lines changed

.github/workflows/CICD.yml

+30-11
Original file line numberDiff line numberDiff line change
@@ -651,17 +651,6 @@ jobs:
651651
*-pc-windows-msvc) STRIP="" ;;
652652
esac;
653653
outputs STRIP
654-
- name: Install/setup prerequisites
655-
shell: bash
656-
run: |
657-
## Install/setup prerequisites
658-
case '${{ matrix.job.target }}' in
659-
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
660-
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
661-
esac
662-
case '${{ matrix.job.os }}' in
663-
macos-latest) brew install coreutils ;; # needed for testing
664-
esac
665654
- name: Create all needed build/work directories
666655
shell: bash
667656
run: |
@@ -680,6 +669,21 @@ jobs:
680669
case '${{ matrix.job.os }}' in
681670
macos-latest) brew install coreutils ;; # needed for testing
682671
esac
672+
case '${{ matrix.job.os }}' in
673+
ubuntu-*)
674+
# pinky is a tool to show logged-in users from utmp, and gecos fields from /etc/passwd.
675+
# In GitHub Action *nix VMs, no accounts log in, even the "runner" account that runs the commands. The account also has empty gecos fields.
676+
# To work around this for pinky tests, we create a fake login entry for the GH runner account...
677+
FAKE_UTMP='[7] [999999] [tty2] [runner] [tty2] [] [0.0.0.0] [2022-02-22T22:22:22,222222+00:00]'
678+
# ... by dumping the login records, adding our fake line, then reverse dumping ...
679+
(utmpdump /var/run/utmp ; echo $FAKE_UTMP) | sudo utmpdump -r -o /var/run/utmp
680+
# ... and add a full name to each account with a gecos field but no full name.
681+
sudo sed -i 's/:,/:runner name,/' /etc/passwd
682+
# We also create a couple optional files pinky looks for
683+
touch /home/runner/.project
684+
echo "foo" > /home/runner/.plan
685+
;;
686+
esac
683687
- name: rust toolchain ~ install
684688
uses: actions-rs/toolchain@v1
685689
with:
@@ -949,6 +953,21 @@ jobs:
949953
case '${{ matrix.job.os }}' in
950954
macos-latest) brew install coreutils ;; # needed for testing
951955
esac
956+
case '${{ matrix.job.os }}' in
957+
ubuntu-latest)
958+
# pinky is a tool to show logged-in users from utmp, and gecos fields from /etc/passwd.
959+
# In GitHub Action *nix VMs, no accounts log in, even the "runner" account that runs the commands. The account also has empty gecos fields.
960+
# To work around this for pinky tests, we create a fake login entry for the GH runner account...
961+
FAKE_UTMP='[7] [999999] [tty2] [runner] [tty2] [] [0.0.0.0] [2022-02-22T22:22:22,222222+00:00]'
962+
# ... by dumping the login records, adding our fake line, then reverse dumping ...
963+
(utmpdump /var/run/utmp ; echo $FAKE_UTMP) | sudo utmpdump -r -o /var/run/utmp
964+
# ... and add a full name to each account with a gecos field but no full name.
965+
sudo sed -i 's/:,/:runner name,/' /etc/passwd
966+
# We also create a couple optional files pinky looks for
967+
touch /home/runner/.project
968+
echo "foo" > /home/runner/.plan
969+
;;
970+
esac
952971
- name: rust toolchain ~ install
953972
uses: actions-rs/toolchain@v1
954973
with:

src/uu/pinky/src/pinky.rs

+13-15
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ use clap::{crate_version, App, AppSettings, Arg};
2222
use std::path::PathBuf;
2323
use uucore::{format_usage, InvalidEncodingHandling};
2424

25-
const BUFSIZE: usize = 1024;
26-
2725
static ABOUT: &str = "pinky - lightweight finger";
2826
const USAGE: &str = "{} [OPTION]... [USER]...";
2927

@@ -239,6 +237,14 @@ fn time_string(ut: &Utmpx) -> String {
239237
time::strftime("%b %e %H:%M", &ut.login_time()).unwrap() // LC_ALL=C
240238
}
241239

240+
fn gecos_to_fullname(pw: &Passwd) -> String {
241+
let mut gecos = pw.user_info.clone();
242+
if let Some(n) = gecos.find(',') {
243+
gecos.truncate(n);
244+
}
245+
gecos.replace('&', &pw.name.capitalize())
246+
}
247+
242248
impl Pinky {
243249
fn print_entry(&self, ut: &Utmpx) -> std::io::Result<()> {
244250
let mut pts_path = PathBuf::from("/dev");
@@ -265,11 +271,7 @@ impl Pinky {
265271

266272
if self.include_fullname {
267273
if let Ok(pw) = Passwd::locate(ut.user().as_ref()) {
268-
let mut gecos = pw.user_info;
269-
if let Some(n) = gecos.find(',') {
270-
gecos.truncate(n + 1);
271-
}
272-
print!(" {:<19.19}", gecos.replace('&', &pw.name.capitalize()));
274+
print!(" {:<19.19}", gecos_to_fullname(&pw));
273275
} else {
274276
print!(" {:19}", " ???");
275277
}
@@ -331,7 +333,7 @@ impl Pinky {
331333
for u in &self.names {
332334
print!("Login name: {:<28}In real life: ", u);
333335
if let Ok(pw) = Passwd::locate(u.as_str()) {
334-
println!(" {}", pw.user_info.replace('&', &pw.name.capitalize()));
336+
println!(" {}", gecos_to_fullname(&pw));
335337
if self.include_home_and_shell {
336338
print!("Directory: {:<29}", pw.user_dir);
337339
println!("Shell: {}", pw.user_shell);
@@ -362,12 +364,8 @@ impl Pinky {
362364

363365
fn read_to_console<F: Read>(f: F) {
364366
let mut reader = BufReader::new(f);
365-
let mut iobuf = [0_u8; BUFSIZE];
366-
while let Ok(n) = reader.read(&mut iobuf) {
367-
if n == 0 {
368-
break;
369-
}
370-
let s = String::from_utf8_lossy(&iobuf);
371-
print!("{}", s);
367+
let mut iobuf = Vec::new();
368+
if reader.read_to_end(&mut iobuf).is_ok() {
369+
print!("{}", String::from_utf8_lossy(&iobuf));
372370
}
373371
}

tests/by-util/test_pinky.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ fn test_long_format() {
4444
#[cfg(unix)]
4545
#[test]
4646
fn test_long_format_multiple_users() {
47-
let args = ["-l", "root", "root", "root"];
47+
// multiple instances of one account we know exists,
48+
// the account of the test runner,
49+
// and an account that (probably) doesn't exist
50+
let runner = match std::env::var("USER") {
51+
Ok(user) => user,
52+
Err(_) => "".to_string(),
53+
};
54+
let args = ["-l", "root", "root", "root", &runner, "no_such_user"];
4855
let ts = TestScenario::new(util_name!());
4956
let expect = unwrap_or_return!(expected_result(&ts, &args));
5057

tests/by-util/test_users.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ fn test_users_no_arg() {
77

88
#[test]
99
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
10+
#[ignore = "issue #3219"]
1011
fn test_users_check_name() {
1112
#[cfg(target_os = "linux")]
1213
let util_name = util_name!();

tests/by-util/test_who.rs

+8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::common::util::*;
99

1010
#[cfg(unix)]
1111
#[test]
12+
#[ignore = "issue #3219"]
1213
fn test_count() {
1314
let ts = TestScenario::new(util_name!());
1415
for opt in &["-q", "--count", "--c"] {
@@ -29,6 +30,7 @@ fn test_boot() {
2930

3031
#[cfg(unix)]
3132
#[test]
33+
#[ignore = "issue #3219"]
3234
fn test_heading() {
3335
let ts = TestScenario::new(util_name!());
3436
for opt in &["-H", "--heading", "--head"] {
@@ -47,6 +49,7 @@ fn test_heading() {
4749

4850
#[cfg(unix)]
4951
#[test]
52+
#[ignore = "issue #3219"]
5053
fn test_short() {
5154
let ts = TestScenario::new(util_name!());
5255
for opt in &["-s", "--short", "--s"] {
@@ -108,6 +111,7 @@ fn test_time() {
108111

109112
#[cfg(unix)]
110113
#[test]
114+
#[ignore = "issue #3219"]
111115
fn test_mesg() {
112116
// -T, -w, --mesg
113117
// add user's message status as +, - or ?
@@ -150,6 +154,7 @@ fn test_too_many_args() {
150154

151155
#[cfg(unix)]
152156
#[test]
157+
#[ignore = "issue #3219"]
153158
fn test_users() {
154159
let ts = TestScenario::new(util_name!());
155160
for opt in &["-u", "--users", "--us"] {
@@ -175,6 +180,7 @@ fn test_users() {
175180

176181
#[cfg(unix)]
177182
#[test]
183+
#[ignore = "issue #3219"]
178184
fn test_lookup() {
179185
let opt = "--lookup";
180186
let ts = TestScenario::new(util_name!());
@@ -194,6 +200,7 @@ fn test_dead() {
194200

195201
#[cfg(unix)]
196202
#[test]
203+
#[ignore = "issue #3219"]
197204
fn test_all_separately() {
198205
if cfg!(target_os = "macos") {
199206
// TODO: fix `-u`, see: test_users
@@ -211,6 +218,7 @@ fn test_all_separately() {
211218

212219
#[cfg(unix)]
213220
#[test]
221+
#[ignore = "issue #3219"]
214222
fn test_all() {
215223
if cfg!(target_os = "macos") {
216224
// TODO: fix `-u`, see: test_users

0 commit comments

Comments
 (0)