From 4e5fc680e9b0befc2bb96e7cf96dbdc08efa19c6 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 17 Apr 2024 16:37:54 -0600 Subject: [PATCH 01/21] Import peek(1) from trinity/src --- Makefile | 8 ++- docs/peek.1 | 116 ++++++++++++++++++++++++++++++++++++++ src/peek.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 docs/peek.1 create mode 100644 src/peek.c diff --git a/Makefile b/Makefile index 68efd20..c68f941 100644 --- a/Makefile +++ b/Makefile @@ -102,11 +102,15 @@ mm: build/bin/mm build/bin/mm: src/mm.c build $(CC) $(CFLAGS) -o $@ src/mm.c - .PHONY: npc npc: build/bin/npc build/bin/npc: src/npc.c build - $(CC) $(CFLAGAS) -o $@ src/npc.c + $(CC) $(CFLAGS) -o $@ src/npc.c + +.PHONY: peek +peek: build/bin/peek +build/bin/peek: src/peek.c build + $(CC) $(CFLAGS) -o $@ src/peek.c .PHONY: rpn rpn: build/bin/rpn diff --git a/docs/peek.1 b/docs/peek.1 new file mode 100644 index 0000000..21c5069 --- /dev/null +++ b/docs/peek.1 @@ -0,0 +1,116 @@ +.\" Copyright (c) 2023-2024 DTB +.\" +.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, +.\" visit . + +.TH PEEK 1 + +.SH NAME + +peek \(en read from standard input, furtively + +.SH SYNOPSIS + +peek +.RB ( -1enot ) +.RB ( -p +.RB [ program +.RB [ arguments... ]]) + +.SH DESCRIPTION + +Peek reads input from standard input with terminal echo disabled, which may be +useful to prevent secrets being spied upon by adversaries watching a user's +screen. + +.SH OPTIONS + +.B -1 +.RS +Limits input to a single line (stopping when the newline character is read). +.RE + +.B -e +.RS +Configures peek to output to standard error. +.RE + +.B -n +.RS +Prints ("iNcludes") the terminating character in outputs. This is nonsensical +if not combined with the +.B -1 +option. +.RE + +.B -o +.RS +Configures peek to output to standard output. +.RE + +.B -p +.RS +Configures peek to pipe output to an executed child program, e.g. a password +hashing utility. This is provided as a convenience and to avoid potential +insecurities resulting from programmer error in scripts. +.RE + +.B -t +.RS +Makes peek exit if not run within a terminal. +.RE + +.SH DIAGNOSTICS + +Peek prints an error message and exits with the appropriate status from +sysexits.h(3) if it encounters an unrecoverable error. + +Peek tries to handle the signal SIGINT (^C) to ensure the user's terminal is +still usable after premature termination; if the signal can't be handled, it +prints an error message and continues. If peek is interrupted, it exits +unsuccessfully, without an error message. + +.SH BUGS + +Accepting secrets in shell scripts is not adviseable in any context. + +If used in a safety-critical application it must be ensured that the +environment with which peek is used is not compromised. + +On systems that support it, the ioctl(2) command TIOCSTI can be used to insert +characters into the standard input going to peek. This doesn't allow snooping +but can be used for general mischief. + +Peek will happily run without outputs, slurping input and doing nothing with +it. + +.SH EXAMPLES + +This is a command line for POSIX sh(1), using POSIX env(1) to discard the +environment except for PATH, and htpassword(1) from Apache's utilities to hash +the input with the bcrypt algorithm, printing the resulting hash: + +.RS +.R $ env -i PATH="$PATH" peek -1tp htpasswd -nBi _ | cut -d : -f 2 +.RE + +This is a POSIX sh(1) command line that lets a user blindly write into a text +file, only able to see written lines. Some writers have the habit of +prematurely revising their work and use tools like this to prevent it: + +.RS +.R $ echo Input ^D to quit. && peek -eot >writing.txt +.RE + +.SH AUTHOR + +Written by DTB . + +.SH COPYRIGHT + +Copyright (c) 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later +. + +.SH SEE ALSO + +env(1), ioctl(2), ioctl_tty(2), read(1), sh(1) diff --git a/src/peek.c b/src/peek.c new file mode 100644 index 0000000..27c9793 --- /dev/null +++ b/src/peek.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2023–2024 DTB + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +#include /* sigaction(2), signal(2), struct sigaction, SIGINT */ +#include /* fclose(3), fdopen(3), fprintf(3), getc(3), perror(3), + * putc(3), stderr, stdin, stdout, EOF, NULL */ +#include /* exit(3), size_t, EXIT_FAILURE */ +#include /* strerror(3) */ +#if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE +# include +#endif +#include /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ +#include /* dup(2), execvp(3), fork(2), getopt(3), isatty(3), + * pipe(2), STDIN_FILENO */ + +static int oserr(char *s){ perror(s); return EX_OSERR; } + +static char *program_name = "peek"; + +static int usage(char *s){ + fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", + s == NULL ? program_name : s); + return EX_USAGE; +} + +/* Restores terminal echo; otherwise when a user ^Cs the terminal would + * continue to not display typed text. If sig isn't zero, this will terminate + * the program. */ +void restore_echo(int sig){ + static struct termios t; + + tcgetattr(STDIN_FILENO, &t); + t.c_lflag |= ECHO; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + + if(sig != 0) + exit(EXIT_FAILURE); + else + return; +} + +int main(int argc, char *argv[]){ + int eof; + char include_eof; + FILE *outputs[] = { + NULL /* stdout */, + NULL /* stderr */, + NULL /* -p */ + }; + int p[2] = {0, 0}; + + if(argc < 1) + usage(argv[0]); + + eof = EOF; + include_eof = 0; + { /* options parsing */ + int c; + + while((c = getopt(argc, argv, "1enopt")) != -1) + switch(c){ + case '1': eof = '\n'; break; + case 'n': include_eof = 1; break; + case 'o': outputs[0] = stdout; break; + case 'e': outputs[1] = stderr; break; + case 'p': + if(pipe(p) != 0 || (outputs[2] = fdopen(p[1], "ab")) == NULL) + return oserr(argv[0]); + break; + case 't': + if(isatty(STDIN_FILENO) != 1){ + fprintf(stderr, "%s: Must be run in a terminal" + " (option -t specified)\n", argv[0]); + return EX_USAGE; + } + default: return usage(argv[0]); + } + } + + /* If -p is used there must be additional arguments. getopt(3) wouldn't + * work for this because optarg would have to be one string to give to + * system(3) or an equivalent and it would be a mess of parsing and + * security issues. Any intended usage works with this slightly funkier + * argument parsing, unintended usages work as happy coincidence. */ + if((argc > optind) == (outputs[2] == 0)) + return usage(argv[0]); + + if(outputs[2] != 0) + switch(fork()){ + case 0: + if(close(p[1]) == 0 && dup2(p[0], STDIN_FILENO) == STDIN_FILENO) + execvp(argv[optind], &argv[optind]); + case -1: + return oserr(argv[0]); + default: + if(close(p[0]) != 0) + return oserr(argv[0]); + } + + { /* install signal handler */ + /* There isn't a difference in functionality between the signal(2) and + * sigaction(2) methods. sigaction(2) is vastly preferred for + * portability but some configurations can only use signal(2). */ + /* Errors aren't terminating because the worst that happens is some + * terminal phooeyness if things go awry. */ +#if defined _POSIX_C_SOURCE + struct sigaction act = { 0 }; + + act.sa_handler = restore_echo; + if(sigaction(SIGINT, &act, NULL) != 0) + perror(argv[0]); +#else + if(signal(SIGINT, restore_echo) == SIG_ERR) + perror(argv[0]); +#endif + } + + { /* banish terminal echo */ + struct termios t; + + tcgetattr(STDIN_FILENO, &t); + t.c_lflag ^= ECHO; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + } + + { /* actual input loop */ + int c; + size_t i; + + do{ if((c = getc(stdin)) != eof || include_eof) + for(i = 0; i < (sizeof outputs)/(sizeof *outputs); ++i) + if(outputs[i] != NULL && putc(c, outputs[i]) == EOF){ + perror(argv[0]); + if(outputs[i] != stdout && outputs[i] != stderr) + fclose(outputs[i]); + outputs[i] = NULL; + } + }while(c != eof); + } + + restore_echo(0); + + return EX_OK; +} -- 2.46.1 From 46b607384a8a96c1128d95abebd842afccff34bc Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Apr 2024 09:08:08 -0600 Subject: [PATCH 02/21] peek(1): remove -p --- src/peek.c | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/src/peek.c b/src/peek.c index 27c9793..277401c 100644 --- a/src/peek.c +++ b/src/peek.c @@ -25,8 +25,7 @@ # include #endif #include /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ -#include /* dup(2), execvp(3), fork(2), getopt(3), isatty(3), - * pipe(2), STDIN_FILENO */ +#include /* getopt(3), isatty(3), STDIN_FILENO */ static int oserr(char *s){ perror(s); return EX_OSERR; } @@ -59,29 +58,23 @@ int main(int argc, char *argv[]){ char include_eof; FILE *outputs[] = { NULL /* stdout */, - NULL /* stderr */, - NULL /* -p */ + NULL /* stderr */ }; - int p[2] = {0, 0}; if(argc < 1) - usage(argv[0]); + return usage(argv[0]); eof = EOF; include_eof = 0; { /* options parsing */ int c; - while((c = getopt(argc, argv, "1enopt")) != -1) + while((c = getopt(argc, argv, "1enot")) != -1) switch(c){ case '1': eof = '\n'; break; case 'n': include_eof = 1; break; case 'o': outputs[0] = stdout; break; case 'e': outputs[1] = stderr; break; - case 'p': - if(pipe(p) != 0 || (outputs[2] = fdopen(p[1], "ab")) == NULL) - return oserr(argv[0]); - break; case 't': if(isatty(STDIN_FILENO) != 1){ fprintf(stderr, "%s: Must be run in a terminal" @@ -90,28 +83,11 @@ int main(int argc, char *argv[]){ } default: return usage(argv[0]); } + + if(argc > optind) + return usage(argv[0]); } - /* If -p is used there must be additional arguments. getopt(3) wouldn't - * work for this because optarg would have to be one string to give to - * system(3) or an equivalent and it would be a mess of parsing and - * security issues. Any intended usage works with this slightly funkier - * argument parsing, unintended usages work as happy coincidence. */ - if((argc > optind) == (outputs[2] == 0)) - return usage(argv[0]); - - if(outputs[2] != 0) - switch(fork()){ - case 0: - if(close(p[1]) == 0 && dup2(p[0], STDIN_FILENO) == STDIN_FILENO) - execvp(argv[optind], &argv[optind]); - case -1: - return oserr(argv[0]); - default: - if(close(p[0]) != 0) - return oserr(argv[0]); - } - { /* install signal handler */ /* There isn't a difference in functionality between the signal(2) and * sigaction(2) methods. sigaction(2) is vastly preferred for @@ -146,8 +122,6 @@ int main(int argc, char *argv[]){ for(i = 0; i < (sizeof outputs)/(sizeof *outputs); ++i) if(outputs[i] != NULL && putc(c, outputs[i]) == EOF){ perror(argv[0]); - if(outputs[i] != stdout && outputs[i] != stderr) - fclose(outputs[i]); outputs[i] = NULL; } }while(c != eof); -- 2.46.1 From c7739612a701c57bb7fd3c639824e87870391701 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Apr 2024 10:03:02 -0600 Subject: [PATCH 03/21] peek(1): strip down to bare essentials, rewrite man page to match --- docs/peek.1 | 82 ++++++++++++++++++----------------------------------- src/peek.c | 72 +++++++++++++++++++--------------------------- 2 files changed, 57 insertions(+), 97 deletions(-) diff --git a/docs/peek.1 b/docs/peek.1 index 21c5069..e73a702 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -12,52 +12,17 @@ peek \(en read from standard input, furtively .SH SYNOPSIS peek -.RB ( -1enot ) -.RB ( -p -.RB [ program -.RB [ arguments... ]]) +.RB ( -i ) .SH DESCRIPTION -Peek reads input from standard input with terminal echo disabled, which may be -useful to prevent secrets being spied upon by adversaries watching a user's -screen. +Peek reads input from standard input with terminal echo disabled. .SH OPTIONS -.B -1 +.B -i .RS -Limits input to a single line (stopping when the newline character is read). -.RE - -.B -e -.RS -Configures peek to output to standard error. -.RE - -.B -n -.RS -Prints ("iNcludes") the terminating character in outputs. This is nonsensical -if not combined with the -.B -1 -option. -.RE - -.B -o -.RS -Configures peek to output to standard output. -.RE - -.B -p -.RS -Configures peek to pipe output to an executed child program, e.g. a password -hashing utility. This is provided as a convenience and to avoid potential -insecurities resulting from programmer error in scripts. -.RE - -.B -t -.RS -Makes peek exit if not run within a terminal. +Allows input to come from sources other than terminals (i.e. a pipe). .RE .SH DIAGNOSTICS @@ -70,36 +35,45 @@ still usable after premature termination; if the signal can't be handled, it prints an error message and continues. If peek is interrupted, it exits unsuccessfully, without an error message. +.SH RATIONALE + +This tool was originally written to accept passwords in shell scripts, as an +extremely simple alternative to the GNU Privacy Guard project's pinentry(1). +Accepting input without showing what is being typed is useful when keying in +secrets in public settings or places with installed surveillance cameras. + .SH BUGS -Accepting secrets in shell scripts is not adviseable in any context. +This does nothing to prevent others seeing the keyboard being used to input +secrets or mask the sound of typing. Audio or video recordings of typing can be +used to determine what was input without needing to see the characters appear +on the screen. -If used in a safety-critical application it must be ensured that the -environment with which peek is used is not compromised. +Accepting secrets in shell scripts is probably not adviseable. On systems that support it, the ioctl(2) command TIOCSTI can be used to insert characters into the standard input going to peek. This doesn't allow snooping but can be used for general mischief. -Peek will happily run without outputs, slurping input and doing nothing with -it. - .SH EXAMPLES -This is a command line for POSIX sh(1), using POSIX env(1) to discard the -environment except for PATH, and htpassword(1) from Apache's utilities to hash -the input with the bcrypt algorithm, printing the resulting hash: +This is an sh(1p) command line that hashes a given password. It uses head(1p) +to only accept one line of input, xargs(1p) and printf(1p) to strip the +trailing newline, htpasswd(1) from Apache's utilities to hash the input with +the bcrypt algorithm, and cut(1p) to print only the resulting hash: .RS -.R $ env -i PATH="$PATH" peek -1tp htpasswd -nBi _ | cut -d : -f 2 +.R $ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2 .RE -This is a POSIX sh(1) command line that lets a user blindly write into a text -file, only able to see written lines. Some writers have the habit of -prematurely revising their work and use tools like this to prevent it: +This is an sh(1p) command line that lets a user blindly write into a text file, +only able to see written lines. Some writers have the habit of prematurely +revising their work and use tools like this to prevent it. It uses mm(1) to +pipe the output of peek to both the standard error and the regular file +writing.txt. .RS -.R $ echo Input ^D to quit. && peek -eot >writing.txt +.R $ echo Input ^D to quit. && peek | mm -eo - >writing.txt .RE .SH AUTHOR @@ -113,4 +87,4 @@ Copyright (c) 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later .SH SEE ALSO -env(1), ioctl(2), ioctl_tty(2), read(1), sh(1) +ioctl(2), ioctl_tty(2), read(1), sh(1) diff --git a/src/peek.c b/src/peek.c index 277401c..67d0a42 100644 --- a/src/peek.c +++ b/src/peek.c @@ -17,75 +17,66 @@ */ #include /* sigaction(2), signal(2), struct sigaction, SIGINT */ -#include /* fclose(3), fdopen(3), fprintf(3), getc(3), perror(3), - * putc(3), stderr, stdin, stdout, EOF, NULL */ -#include /* exit(3), size_t, EXIT_FAILURE */ -#include /* strerror(3) */ +#include /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin, + * stdout, EOF, NULL */ +#include /* exit(3), EXIT_FAILURE */ #if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE # include #endif #include /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ #include /* getopt(3), isatty(3), STDIN_FILENO */ -static int oserr(char *s){ perror(s); return EX_OSERR; } - static char *program_name = "peek"; -static int usage(char *s){ - fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", - s == NULL ? program_name : s); - return EX_USAGE; -} - /* Restores terminal echo; otherwise when a user ^Cs the terminal would * continue to not display typed text. If sig isn't zero, this will terminate * the program. */ -void restore_echo(int sig){ +static void restore_echo(int sig){ static struct termios t; tcgetattr(STDIN_FILENO, &t); t.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + /* If, for whatever ungodly reason, exit(3) returns, the user will notice + * their typed characters on the screen. */ if(sig != 0) exit(EXIT_FAILURE); - else - return; + + return; +} + +static int oserr(char *s){ perror(s); return EX_OSERR; } +static int ioerr(char *s){ perror(s); restore_echo(0); return EX_IOERR; } +static int usage(char *s){ + fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", s); + return EX_USAGE; } int main(int argc, char *argv[]){ - int eof; - char include_eof; - FILE *outputs[] = { - NULL /* stdout */, - NULL /* stderr */ - }; if(argc < 1) - return usage(argv[0]); + return usage(program_name); - eof = EOF; - include_eof = 0; { /* options parsing */ + char allow_nonterminals; int c; - while((c = getopt(argc, argv, "1enot")) != -1) + allow_nonterminals = 0; + while((c = getopt(argc, argv, "i")) != -1) switch(c){ - case '1': eof = '\n'; break; - case 'n': include_eof = 1; break; - case 'o': outputs[0] = stdout; break; - case 'e': outputs[1] = stderr; break; - case 't': - if(isatty(STDIN_FILENO) != 1){ - fprintf(stderr, "%s: Must be run in a terminal" - " (option -t specified)\n", argv[0]); - return EX_USAGE; - } + case 'i': allow_nonterminals = 1; break; default: return usage(argv[0]); } if(argc > optind) return usage(argv[0]); + + if(!allow_nonterminals && isatty(STDIN_FILENO) != 1){ + fprintf(stderr, "%s: Must be run in a terminal" + " (option -i skips this check)\n", argv[0]); + return EX_USAGE; + } } { /* install signal handler */ @@ -116,15 +107,10 @@ int main(int argc, char *argv[]){ { /* actual input loop */ int c; - size_t i; - do{ if((c = getc(stdin)) != eof || include_eof) - for(i = 0; i < (sizeof outputs)/(sizeof *outputs); ++i) - if(outputs[i] != NULL && putc(c, outputs[i]) == EOF){ - perror(argv[0]); - outputs[i] = NULL; - } - }while(c != eof); + while((c = getc(stdin)) != EOF) + if(putc(c, stdout) == EOF) + return ioerr(argv[0]); } restore_echo(0); -- 2.46.1 From c295f531cb6caeb2cdf22448ee75abe4a40adad1 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Apr 2024 10:04:26 -0600 Subject: [PATCH 04/21] peek(1): correct grammar --- docs/peek.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/peek.1 b/docs/peek.1 index e73a702..1c482a2 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -7,7 +7,7 @@ .SH NAME -peek \(en read from standard input, furtively +peek \(en read from the standard input, furtively .SH SYNOPSIS @@ -16,7 +16,7 @@ peek .SH DESCRIPTION -Peek reads input from standard input with terminal echo disabled. +Peek reads input from the standard input with terminal echo disabled. .SH OPTIONS -- 2.46.1 From 6d3f084d9c74ab4b52796cd1911a94b80696b835 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Apr 2024 10:14:52 -0600 Subject: [PATCH 05/21] peek(1): remove unused variables, clean up some typos --- src/peek.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/peek.c b/src/peek.c index 67d0a42..a769e90 100644 --- a/src/peek.c +++ b/src/peek.c @@ -20,7 +20,7 @@ #include /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin, * stdout, EOF, NULL */ #include /* exit(3), EXIT_FAILURE */ -#if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE +#if !defined EX_IOERR || !defined EX_OK || !defined EX_USAGE # include #endif #include /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ @@ -46,7 +46,6 @@ static void restore_echo(int sig){ return; } -static int oserr(char *s){ perror(s); return EX_OSERR; } static int ioerr(char *s){ perror(s); restore_echo(0); return EX_IOERR; } static int usage(char *s){ fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", s); @@ -58,7 +57,7 @@ int main(int argc, char *argv[]){ if(argc < 1) return usage(program_name); - { /* options parsing */ + { /* option parsing */ char allow_nonterminals; int c; @@ -108,8 +107,8 @@ int main(int argc, char *argv[]){ { /* actual input loop */ int c; - while((c = getc(stdin)) != EOF) - if(putc(c, stdout) == EOF) + while((c = fgetc(stdin)) != EOF) + if(fputc(c, stdout) == EOF) return ioerr(argv[0]); } -- 2.46.1 From 3db83fd79c087e7eff97aa6af9b716c681c06ac0 Mon Sep 17 00:00:00 2001 From: emma Date: Mon, 17 Jun 2024 22:28:41 -0600 Subject: [PATCH 06/21] peek.1: updates to be more consistent with current documentation --- docs/peek.1 | 124 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/docs/peek.1 b/docs/peek.1 index 1c482a2..c9cb6ed 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -1,90 +1,110 @@ .\" Copyright (c) 2023-2024 DTB +.\" Copyright (c) 2024 Emma Tebibyte .\" .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" visit . - -.TH PEEK 1 - +.\" +.TH PEEK 1 2024-06-09 "Bonsai Core Utilites 0.13.9" .SH NAME - peek \(en read from the standard input, furtively - +.\" .SH SYNOPSIS peek .RB ( -i ) - +.\" .SH DESCRIPTION -Peek reads input from the standard input with terminal echo disabled. - +Read input from the standard input with terminal echo disabled. +.\" .SH OPTIONS -.B -i -.RS -Allows input to come from sources other than terminals (i.e. a pipe). -.RE - +.IP \fB-i\fP +Allows input to come from sources other than terminals (i.e., a pipe). +.\" .SH DIAGNOSTICS -Peek prints an error message and exits with the appropriate status from -sysexits.h(3) if it encounters an unrecoverable error. - -Peek tries to handle the signal SIGINT (^C) to ensure the user's terminal is -still usable after premature termination; if the signal can't be handled, it -prints an error message and continues. If peek is interrupted, it exits -unsuccessfully, without an error message. +In the event of an error, a debug message will be printed and the program will +exit with the appropriate +.BR sysexits.h (3) +error code. +In order to ensure the user\(cqs terminal is still usable after premature +termination, the program attempts to handle the SIGINT signal; if it cannot, +an error message is printed and execution continues. If the program is +interrupted, it exits unsuccessfully without an error message. +.\" .SH RATIONALE -This tool was originally written to accept passwords in shell scripts, as an -extremely simple alternative to the GNU Privacy Guard project's pinentry(1). +This tool was originally written to accept passwords in shell scripts as an +extremely simple alternative to the GNU Privacy Guard project\(cqs +.BR pinentry (1) +utility. + Accepting input without showing what is being typed is useful when keying in -secrets in public settings or places with installed surveillance cameras. +secrets in public settings or in places where surveillance cameras are +installed. +.\" +.SH CAVEATS -.SH BUGS +This program does nothing to prevent others from seeing the key presses input to +a keyboard. It also does not protect against the sound of typing being analyzed +to determine what was inputted without needing to see screen or keyboard. -This does nothing to prevent others seeing the keyboard being used to input -secrets or mask the sound of typing. Audio or video recordings of typing can be -used to determine what was input without needing to see the characters appear -on the screen. - -Accepting secrets in shell scripts is probably not adviseable. - -On systems that support it, the ioctl(2) command TIOCSTI can be used to insert -characters into the standard input going to peek. This doesn't allow snooping -but can be used for general mischief. +Accepting secrets in shell scripts is probably not advisable. +On systems that support it, the +.BR ioctl (2) +command TIOCSTI can be used to insert characters into the standard input. This +doesn't allow snooping but can be used for general mischief. +.\" .SH EXAMPLES -This is an sh(1p) command line that hashes a given password. It uses head(1p) -to only accept one line of input, xargs(1p) and printf(1p) to strip the -trailing newline, htpasswd(1) from Apache's utilities to hash the input with -the bcrypt algorithm, and cut(1p) to print only the resulting hash: +This is an +.BR sh (1p) +command line that hashes a given password. It uses +.BR head (1p) +to only accept one line of input, +.BR xargs (1p) +and +.BR printf (1p) +to strip the trailing newline, +.BR htpasswd (1) +from Apache\(cqs utilities to hash the input with the bcrypt algorithm, and +.BR cut (1p) +to print only the resulting hash: .RS -.R $ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2 +$ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2 .RE -This is an sh(1p) command line that lets a user blindly write into a text file, -only able to see written lines. Some writers have the habit of prematurely -revising their work and use tools like this to prevent it. It uses mm(1) to -pipe the output of peek to both the standard error and the regular file -writing.txt. +This is an +.BR sh (1p) +command line that allows a user to write blindly into a text file but displaying +only written lines. Some writers have the habit of prematurely revising their +work and use tools with functionality similar to this to prevent it. +It uses +.BR mm (1) +to pipe the output of the program to both the standard error and the regular +file writing.txt: .RS -.R $ echo Input ^D to quit. && peek | mm -eo - >writing.txt +$ echo Input ^D to quit. && peek | mm -eo - >writing.txt .RE - +.\" .SH AUTHOR -Written by DTB . - +Written by DTB +.MT trinity@trinity.moe +.ME . +.\" .SH COPYRIGHT -Copyright (c) 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later +Copyright \(co 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later . - +.\" .SH SEE ALSO - -ioctl(2), ioctl_tty(2), read(1), sh(1) +.BR ioctl (2), +.BR ioctl_tty (2), +.BR read (1), +.BR sh (1) -- 2.46.1 From ff993eebdce06595908bd102aa48df2d617d036d Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 09:46:32 -0600 Subject: [PATCH 07/21] Makefile: all: add peek --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5c8b5e7..72ccd0e 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \ CFLAGS += -I$(SYSEXITS) .PHONY: all -all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true +all: dj false fop hru intcmp mm npc peek rpn scrut str strcmp swab true # keep build/include until bindgen(1) has stdin support # https://github.com/rust-lang/rust-bindgen/issues/2703 -- 2.46.1 From 70ee886b835d047f3d70452a68d2c83c732475a1 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 09:55:32 -0600 Subject: [PATCH 08/21] peek.1: fix wording on -i --- docs/peek.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/peek.1 b/docs/peek.1 index c9cb6ed..30c00eb 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -20,7 +20,7 @@ Read input from the standard input with terminal echo disabled. .SH OPTIONS .IP \fB-i\fP -Allows input to come from sources other than terminals (i.e., a pipe). +Allows input to come from sources other than terminals (pipes). .\" .SH DIAGNOSTICS -- 2.46.1 From e807a49612b0ce4464aec336309e642973124985 Mon Sep 17 00:00:00 2001 From: DTB Date: Fri, 28 Jun 2024 08:36:47 -0600 Subject: [PATCH 09/21] peek.1: fix version string --- docs/peek.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/peek.1 b/docs/peek.1 index 30c00eb..398e9fd 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -4,7 +4,7 @@ .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" visit . .\" -.TH PEEK 1 2024-06-09 "Bonsai Core Utilites 0.13.9" +.TH PEEK 1 2024-06-26 "Harakit X.X.X" .SH NAME peek \(en read from the standard input, furtively .\" -- 2.46.1 From c7a4cd1d19f033021fbe908b52f15fd8d77083df Mon Sep 17 00:00:00 2001 From: DTB Date: Fri, 28 Jun 2024 08:37:49 -0600 Subject: [PATCH 10/21] peek.1: s/inputted/input/ --- docs/peek.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/peek.1 b/docs/peek.1 index 398e9fd..1eaf07d 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -4,7 +4,7 @@ .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" visit . .\" -.TH PEEK 1 2024-06-26 "Harakit X.X.X" +.TH PEEK 1 2024-06-28 "Harakit X.X.X" .SH NAME peek \(en read from the standard input, furtively .\" @@ -49,7 +49,7 @@ installed. This program does nothing to prevent others from seeing the key presses input to a keyboard. It also does not protect against the sound of typing being analyzed -to determine what was inputted without needing to see screen or keyboard. +to determine what was input without needing to see screen or keyboard. Accepting secrets in shell scripts is probably not advisable. -- 2.46.1 From a927c39c0604c24daaddbc67e3c4c6ffdc925e2e Mon Sep 17 00:00:00 2001 From: DTB Date: Fri, 26 Jul 2024 07:42:05 -0600 Subject: [PATCH 11/21] peek(1): update style --- src/peek.c | 91 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/src/peek.c b/src/peek.c index a769e90..cde3ac4 100644 --- a/src/peek.c +++ b/src/peek.c @@ -20,60 +20,67 @@ #include /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin, * stdout, EOF, NULL */ #include /* exit(3), EXIT_FAILURE */ -#if !defined EX_IOERR || !defined EX_OK || !defined EX_USAGE -# include -#endif +#include /* EX_IOERR, EX_OK, EX_USAGE */ #include /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ #include /* getopt(3), isatty(3), STDIN_FILENO */ -static char *program_name = "peek"; +char *program_name = "peek"; /* Restores terminal echo; otherwise when a user ^Cs the terminal would * continue to not display typed text. If sig isn't zero, this will terminate * the program. */ -static void restore_echo(int sig){ +static void +restore_echo(int sig) { static struct termios t; - tcgetattr(STDIN_FILENO, &t); - t.c_lflag |= ECHO; - tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + /* Failure isn't reported because this is the termination routine anyway; + * errors will be obvious. */ + if (tcgetattr(STDIN_FILENO, &t) == 0) { + t.c_lflag |= ECHO; + (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + } - /* If, for whatever ungodly reason, exit(3) returns, the user will notice - * their typed characters on the screen. */ - if(sig != 0) - exit(EXIT_FAILURE); + if (sig != 0) { exit(EXIT_FAILURE); } /* Terminated by signal. */ return; } -static int ioerr(char *s){ perror(s); restore_echo(0); return EX_IOERR; } -static int usage(char *s){ - fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", s); +static int +ioerr(char *argv0) { + perror(argv0); + restore_echo(0); + return EX_IOERR; +} + +static int +usage(char *argv0) { + (void)fprintf(stderr, "Usage: %s [-i]\n", argv0); return EX_USAGE; } int main(int argc, char *argv[]){ - - if(argc < 1) - return usage(program_name); - - { /* option parsing */ + if (argc > 0) { /* option parsing */ char allow_nonterminals; int c; + program_name = argv[0]; + allow_nonterminals = 0; - while((c = getopt(argc, argv, "i")) != -1) - switch(c){ - case 'i': allow_nonterminals = 1; break; - default: return usage(argv[0]); + while ((c = getopt(argc, argv, "i")) != -1) { + switch (c) { + case 'i': allow_nonterminals = 1; break; + default: return usage(argv[0]); } + } - if(argc > optind) - return usage(argv[0]); + if (argc > optind) { return usage(argv[0]); } - if(!allow_nonterminals && isatty(STDIN_FILENO) != 1){ - fprintf(stderr, "%s: Must be run in a terminal" - " (option -i skips this check)\n", argv[0]); + if (!allow_nonterminals && isatty(STDIN_FILENO) != 1) { + (void)fprintf( + stderr, + "%s: Must be run in a terminal (option -i skips this check)\n", + argv[0] + ); return EX_USAGE; } } @@ -88,28 +95,30 @@ int main(int argc, char *argv[]){ struct sigaction act = { 0 }; act.sa_handler = restore_echo; - if(sigaction(SIGINT, &act, NULL) != 0) - perror(argv[0]); + if(sigaction(SIGINT, &act, NULL) != 0) { perror(program_name); } #else - if(signal(SIGINT, restore_echo) == SIG_ERR) - perror(argv[0]); + if(signal(SIGINT, restore_echo) == SIG_ERR) { perror(program_name); } #endif } - - { /* banish terminal echo */ + + /* Banish terminal echo; this terminates when it fails, because this is the + * whole point of the program. */ + { struct termios t; - tcgetattr(STDIN_FILENO, &t); + if (tcgetattr(STDIN_FILENO, &t) != 0) { return ioerror(program_name); } t.c_lflag ^= ECHO; - tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) != 0) { + return ioerror(program_name); + } } - { /* actual input loop */ + { /* Input loop */ int c; - while((c = fgetc(stdin)) != EOF) - if(fputc(c, stdout) == EOF) - return ioerr(argv[0]); + while ((c = fgetc(stdin)) != EOF) { + if(fputc(c, stdout) == EOF) { return ioerr(program_name); } + } } restore_echo(0); -- 2.46.1 From 3f23e437e8b13114f7433ca03776e00c4773579d Mon Sep 17 00:00:00 2001 From: DTB Date: Fri, 26 Jul 2024 07:58:34 -0600 Subject: [PATCH 12/21] peek(1): more style fixes --- src/peek.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/peek.c b/src/peek.c index cde3ac4..965f2f6 100644 --- a/src/peek.c +++ b/src/peek.c @@ -85,7 +85,7 @@ int main(int argc, char *argv[]){ } } - { /* install signal handler */ + { /* Install signal handler */ /* There isn't a difference in functionality between the signal(2) and * sigaction(2) methods. sigaction(2) is vastly preferred for * portability but some configurations can only use signal(2). */ @@ -95,13 +95,13 @@ int main(int argc, char *argv[]){ struct sigaction act = { 0 }; act.sa_handler = restore_echo; - if(sigaction(SIGINT, &act, NULL) != 0) { perror(program_name); } + if (sigaction(SIGINT, &act, NULL) != 0) { perror(program_name); } #else - if(signal(SIGINT, restore_echo) == SIG_ERR) { perror(program_name); } + if (signal(SIGINT, restore_echo) == SIG_ERR) { perror(program_name); } #endif } - /* Banish terminal echo; this terminates when it fails, because this is the + /* Banish terminal echo; this terminates when it fails, because it's the * whole point of the program. */ { struct termios t; @@ -117,7 +117,7 @@ int main(int argc, char *argv[]){ int c; while ((c = fgetc(stdin)) != EOF) { - if(fputc(c, stdout) == EOF) { return ioerr(program_name); } + if (fputc(c, stdout) == EOF) { return ioerr(program_name); } } } -- 2.46.1 From b68be7ff621fc0153d6271d4c8d5acfe87d53a82 Mon Sep 17 00:00:00 2001 From: DTB Date: Sat, 27 Jul 2024 18:51:09 -0600 Subject: [PATCH 13/21] peek(1): fix ioerr typos --- src/peek.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/peek.c b/src/peek.c index 965f2f6..f9fb6f8 100644 --- a/src/peek.c +++ b/src/peek.c @@ -55,6 +55,7 @@ ioerr(char *argv0) { static int usage(char *argv0) { (void)fprintf(stderr, "Usage: %s [-i]\n", argv0); + return EX_USAGE; } @@ -106,10 +107,10 @@ int main(int argc, char *argv[]){ { struct termios t; - if (tcgetattr(STDIN_FILENO, &t) != 0) { return ioerror(program_name); } + if (tcgetattr(STDIN_FILENO, &t) != 0) { return ioerr(program_name); } t.c_lflag ^= ECHO; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) != 0) { - return ioerror(program_name); + return ioerr(program_name); } } -- 2.46.1 From 15c4f0cb8a665733d5dcd434b72fe4d22656a379 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 14 Aug 2024 11:06:31 -0600 Subject: [PATCH 14/21] peek.1: correct references to use the 1p section, add reference to stty(1p) --- docs/peek.1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/peek.1 b/docs/peek.1 index 1eaf07d..bb32f3b 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -4,7 +4,7 @@ .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" visit . .\" -.TH PEEK 1 2024-06-28 "Harakit X.X.X" +.TH PEEK 1 2024-08-14 "Harakit X.X.X" .SH NAME peek \(en read from the standard input, furtively .\" @@ -106,5 +106,6 @@ Copyright \(co 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later .SH SEE ALSO .BR ioctl (2), .BR ioctl_tty (2), -.BR read (1), -.BR sh (1) +.BR read (1p), +.BR sh (1p), +.BR stty (1p) -- 2.46.1 From 4dc19003397260029b93f0693e751fadd8889971 Mon Sep 17 00:00:00 2001 From: DTB Date: Tue, 27 Aug 2024 21:16:12 -0600 Subject: [PATCH 15/21] peek(1): pledge(2) and unveil(2) on OpenBSD --- src/peek.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/peek.c b/src/peek.c index f9fb6f8..95fcf80 100644 --- a/src/peek.c +++ b/src/peek.c @@ -22,7 +22,8 @@ #include /* exit(3), EXIT_FAILURE */ #include /* EX_IOERR, EX_OK, EX_USAGE */ #include /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ -#include /* getopt(3), isatty(3), STDIN_FILENO */ +#include /* getopt(3), isatty(3), pledge(2), unveil(2), + * STDIN_FILENO */ char *program_name = "peek"; @@ -60,6 +61,11 @@ usage(char *argv0) { } int main(int argc, char *argv[]){ +#ifdef __OpenBSD__ + pledge("stdio tty unveil", NULL); + unveil(NULL, NULL); +#endif + if (argc > 0) { /* option parsing */ char allow_nonterminals; int c; -- 2.46.1 From 21ee79a929881ee016ef423dd44474984e856053 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 28 Aug 2024 18:17:58 -0600 Subject: [PATCH 16/21] peek(1): warn on pledge(2) or unveil(2) errors --- src/peek.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/peek.c b/src/peek.c index 95fcf80..bde3811 100644 --- a/src/peek.c +++ b/src/peek.c @@ -62,8 +62,11 @@ usage(char *argv0) { int main(int argc, char *argv[]){ #ifdef __OpenBSD__ - pledge("stdio tty unveil", NULL); - unveil(NULL, NULL); + if (pledge("stdio tty unveil", "") != 0 || unveil(NULL, NULL) != 0) { + /* This isn't fatal; these return values could be cast to void just as + * easily. */ + (void)perror(argv[0] == NULL ? argv[0] : program_name); + } #endif if (argc > 0) { /* option parsing */ -- 2.46.1 From 8374b348342ae92312bbf40b389a139ae8056cda Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 28 Aug 2024 18:31:25 -0600 Subject: [PATCH 17/21] peek(1): better error on piping --- src/peek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/peek.c b/src/peek.c index bde3811..54f010e 100644 --- a/src/peek.c +++ b/src/peek.c @@ -88,7 +88,8 @@ int main(int argc, char *argv[]){ if (!allow_nonterminals && isatty(STDIN_FILENO) != 1) { (void)fprintf( stderr, - "%s: Must be run in a terminal (option -i skips this check)\n", + "%s: Must be run in a terminal (specify -i to skip this" + " check)\n", argv[0] ); return EX_USAGE; -- 2.46.1 From 47ecf87e167d8d99de4c95e5dcd74f307a25a0d9 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 28 Aug 2024 18:34:33 -0600 Subject: [PATCH 18/21] peek(1): use stdbool.h --- src/peek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/peek.c b/src/peek.c index 54f010e..c84a2f8 100644 --- a/src/peek.c +++ b/src/peek.c @@ -17,6 +17,7 @@ */ #include /* sigaction(2), signal(2), struct sigaction, SIGINT */ +#include /* bool */ #include /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin, * stdout, EOF, NULL */ #include /* exit(3), EXIT_FAILURE */ @@ -70,7 +71,7 @@ int main(int argc, char *argv[]){ #endif if (argc > 0) { /* option parsing */ - char allow_nonterminals; + bool allow_nonterminals; int c; program_name = argv[0]; -- 2.46.1 From ea8a8249dfc0ea1fb5c7b02bb66aa80b4e41b561 Mon Sep 17 00:00:00 2001 From: DTB Date: Fri, 30 Aug 2024 21:27:42 -0600 Subject: [PATCH 19/21] peek.1: replace parens with square brackets --- docs/peek.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/peek.1 b/docs/peek.1 index bb32f3b..9ec336c 100644 --- a/docs/peek.1 +++ b/docs/peek.1 @@ -11,7 +11,7 @@ peek \(en read from the standard input, furtively .SH SYNOPSIS peek -.RB ( -i ) +.RB [ -i ] .\" .SH DESCRIPTION -- 2.46.1 From e1145931a5427552702791220395801d8d09c7ea Mon Sep 17 00:00:00 2001 From: DTB Date: Sat, 31 Aug 2024 22:19:13 -0600 Subject: [PATCH 20/21] peek(1): fix erroring on stdin/stdout redirection --- src/peek.c | 74 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/peek.c b/src/peek.c index c84a2f8..fb77189 100644 --- a/src/peek.c +++ b/src/peek.c @@ -62,6 +62,9 @@ usage(char *argv0) { } int main(int argc, char *argv[]){ + bool is_term; /* Is stdin a terminal? */ + bool must_be_term = 1; /* Must it be? */ + #ifdef __OpenBSD__ if (pledge("stdio tty unveil", "") != 0 || unveil(NULL, NULL) != 0) { /* This isn't fatal; these return values could be cast to void just as @@ -70,58 +73,63 @@ int main(int argc, char *argv[]){ } #endif + is_term = isatty(STDIN_FILENO); + if (argc > 0) { /* option parsing */ - bool allow_nonterminals; int c; program_name = argv[0]; - allow_nonterminals = 0; while ((c = getopt(argc, argv, "i")) != -1) { switch (c) { - case 'i': allow_nonterminals = 1; break; + case 'i': must_be_term = 0; break; default: return usage(argv[0]); } } if (argc > optind) { return usage(argv[0]); } - - if (!allow_nonterminals && isatty(STDIN_FILENO) != 1) { - (void)fprintf( - stderr, - "%s: Must be run in a terminal (specify -i to skip this" - " check)\n", - argv[0] - ); - return EX_USAGE; - } } - { /* Install signal handler */ - /* There isn't a difference in functionality between the signal(2) and - * sigaction(2) methods. sigaction(2) is vastly preferred for - * portability but some configurations can only use signal(2). */ - /* Errors aren't terminating because the worst that happens is some - * terminal phooeyness if things go awry. */ + if (!is_term && must_be_term) { + (void)fprintf( + stderr, + "%s: Must be run in a terminal (specify -i to skip this check)\n", + argv[0] + ); + return EX_USAGE; + } + + if (is_term) { + { /* Install signal handler */ + /* There isn't a difference in functionality between the signal(2) + * and sigaction(2) methods. sigaction(2) is vastly preferred for + * portability but some older systems only have signal(2). */ + /* Errors aren't terminating because the worst that happens is some + * terminal phooeyness if things go awry. */ #if defined _POSIX_C_SOURCE - struct sigaction act = { 0 }; + struct sigaction act = { 0 }; - act.sa_handler = restore_echo; - if (sigaction(SIGINT, &act, NULL) != 0) { perror(program_name); } + act.sa_handler = restore_echo; + if (sigaction(SIGINT, &act, NULL) != 0) { perror(program_name); } #else - if (signal(SIGINT, restore_echo) == SIG_ERR) { perror(program_name); } + if (signal(SIGINT, restore_echo) == SIG_ERR) { + perror(program_name); + } #endif - } + } - /* Banish terminal echo; this terminates when it fails, because it's the - * whole point of the program. */ - { - struct termios t; + { /* Banish terminal echo */ + /* This terminates when it fails because it's the whole point of + * the program. */ + struct termios t; - if (tcgetattr(STDIN_FILENO, &t) != 0) { return ioerr(program_name); } - t.c_lflag ^= ECHO; - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) != 0) { - return ioerr(program_name); + if (tcgetattr(STDIN_FILENO, &t) != 0) { + return ioerr(program_name); + } + t.c_lflag ^= ECHO; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) != 0) { + return ioerr(program_name); + } } } @@ -133,7 +141,7 @@ int main(int argc, char *argv[]){ } } - restore_echo(0); + if (is_term) { restore_echo(0); } return EX_OK; } -- 2.46.1 From f7a8158347623fd41caa63e0619055ed4543e2cf Mon Sep 17 00:00:00 2001 From: DTB Date: Sat, 31 Aug 2024 22:22:06 -0600 Subject: [PATCH 21/21] tests: bonsai/peek.mk --- tests/bonsai/peek.mk | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100755 tests/bonsai/peek.mk diff --git a/tests/bonsai/peek.mk b/tests/bonsai/peek.mk new file mode 100755 index 0000000..20e2e24 --- /dev/null +++ b/tests/bonsai/peek.mk @@ -0,0 +1,24 @@ +# Copyright (c) 2024 DTB +# SPDX-License-Identifier: FSFAP +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice and this +# notice are preserved. This file is offered as-is, without any warranty. + +# Testing peek is hard as it requires visual confirmation that text isn't being +# echoed. These tests don't go that far but are a start, and have already +# caught a bug in -i behavior. + +.PHONY: peek_tests +peek_tests: peek_help peek_stdio + +.PHONY: peek_help +peek_help: $(BIN)/peek + ! $(BIN)/peek -h + +.PHONY: peek_stdio +# Test peek -i +peek_stdio: $(BIN)/peek + printf 'Test.\n' \ + | $(BIN)/peek -i \ + | xargs test 'Test.' = -- 2.46.1