peek(1) #93
7
Makefile
@ -42,7 +42,7 @@ BIN = build/bin
|
||||
default: all test
|
||||
|
||||
.PHONY: all
|
||||
all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true
|
||||
all: docs 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
|
||||
@ -137,6 +137,11 @@ npc: build/bin/npc
|
||||
build/bin/npc: src/npc.c build
|
||||
$(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
|
||||
build/bin/rpn: src/rpn.rs build rustlibs
|
||||
|
111
docs/peek.1
Normal file
@ -0,0 +1,111 @@
|
||||
.\" Copyright (c) 2023-2024 DTB <trinity@trinity.moe>
|
||||
.\" Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
|
||||
.\"
|
||||
.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license,
|
||||
.\" visit <http://creativecommons.org/licenses/by-sa/4.0/>.
|
||||
.\"
|
||||
.TH PEEK 1 2024-08-14 "Harakit X.X.X"
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
Outdated
Review
> .TH PEEK 1 [whenever you make the change] "Harakit X.X.X"
|
||||
.SH NAME
|
||||
peek \(en read from the standard input, furtively
|
||||
.\"
|
||||
.SH SYNOPSIS
|
||||
|
||||
peek
|
||||
.RB [ -i ]
|
||||
.\"
|
||||
.SH DESCRIPTION
|
||||
|
||||
Read input from the standard input with terminal echo disabled.
|
||||
.\"
|
||||
.SH OPTIONS
|
||||
|
||||
.IP \fB-i\fP
|
||||
emma marked this conversation as resolved
Outdated
emma
commented
Read input from the standard input with terminal echo disabled.
Should this be placed in a RATIONALE section? > Peek reads input from standard input with terminal echo disabled,
Read input from the standard input with terminal echo disabled.
> which may be useful to prevent secrets being spied upon by adversaries watching a user's
screen.
Should this be placed in a RATIONALE section?
|
||||
Allows input to come from sources other than terminals (pipes).
|
||||
trinity marked this conversation as resolved
Outdated
silt
commented
This appears to imply that there exists a multitude of other possible sources, but it's not made clear what those are or how to make use of them. Does it support reading input from… files? TCP sockets? IRC messages?? This appears to imply that there exists a multitude of other possible sources, but it's not made clear what those are or how to make use of them. Does it support reading input from… files? TCP sockets? IRC messages??
trinity
commented
The "i.e." stands for "id est" and can be read as "in other words". Perhaps I should use the English. I mean to say that the source other than a terminal, in this case, is a pipe. The "i.e." stands for "id est" and can be read as "in other words". Perhaps I should use the English. I mean to say that the source other than a terminal, in this case, is a pipe.
trinity
commented
Changed to (pipes) which I think is more clear. Changed to (pipes) which I think is more clear.
|
||||
.\"
|
||||
.SH DIAGNOSTICS
|
||||
|
||||
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.
|
||||
emma marked this conversation as resolved
Outdated
emma
commented
For parity with the man pages in the > In the event of an error, a debug message will be printed and the program will
exit with the appropriate sysexits.h(3) error code.
For parity with the man pages in the `docs` branch. For example, see [`npc.1`](https://git.tebibyte.media/bonsai/coreutils/src/commit/b7f52902b6bde54876b5fd8efafa433e2cf0df9a/docs/npc.1#L44).
|
||||
|
||||
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,
|
||||
emma marked this conversation as resolved
Outdated
emma
commented
If it encounters a SIGINT signal (^C), the program will attempt to handle it 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 the program is interrupted, it exits unsuccessfully and without printing an error message. If it encounters a SIGINT signal (^C), the program will attempt to handle it 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 the program is interrupted, it exits unsuccessfully and without printing an error message.
|
||||
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\(cqs
|
||||
.BR pinentry (1)
|
||||
utility.
|
||||
|
||||
Accepting input without showing what is being typed is useful when keying in
|
||||
secrets in public settings or in places where surveillance cameras are
|
||||
installed.
|
||||
.\"
|
||||
emma marked this conversation as resolved
Outdated
emma
commented
This being in the BUGS section implies that this can be fixed by the program. Therefore, this belongs in a CAVEATS section because it is definitionally a caveat. This being in the BUGS section implies that this can be fixed by the program. Therefore, this belongs in a CAVEATS section because it is definitionally a caveat.
|
||||
.SH CAVEATS
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
“[...] the standard output” “[...] the standard output”
|
||||
|
||||
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
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
What is the benefit to this over using a shell pipe? What is the benefit to this over using a shell pipe?
|
||||
to determine what was input without needing to see screen or keyboard.
|
||||
|
||||
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
|
||||
.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)
|
||||
emma marked this conversation as resolved
Outdated
emma
commented
> It uses mm(1) to pipe the hidden input to both the standard error and the regular file writing.txt.
|
||||
from Apache\(cqs utilities to hash the input with the bcrypt algorithm, and
|
||||
.BR cut (1p)
|
||||
to print only the resulting hash:
|
||||
|
||||
.RS
|
||||
$ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2
|
||||
.RE
|
||||
|
||||
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
|
||||
$ echo Input ^D to quit. && peek | mm -eo - >writing.txt
|
||||
.RE
|
||||
.\"
|
||||
.SH AUTHOR
|
||||
|
||||
Written by DTB
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
Either:
or:
Either:
> This is a POSIX shell [...]
or:
> This is an sh(1p) [...]
|
||||
.MT trinity@trinity.moe
|
||||
.ME .
|
||||
.\"
|
||||
.SH COPYRIGHT
|
||||
|
||||
Copyright \(co 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
|
||||
<https://gnu.org/licenses/gpl.html>.
|
||||
.\"
|
||||
.SH SEE ALSO
|
||||
.BR ioctl (2),
|
||||
.BR ioctl_tty (2),
|
||||
.BR read (1p),
|
||||
.BR sh (1p),
|
||||
.BR stty (1p)
|
147
src/peek.c
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2023–2024 DTB <trinity@trinity.moe>
|
||||
* 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 <signal.h> /* sigaction(2), signal(2), struct sigaction, SIGINT */
|
||||
#include <stdbool.h> /* bool */
|
||||
#include <stdio.h> /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin,
|
||||
* stdout, EOF, NULL */
|
||||
#include <stdlib.h> /* exit(3), EXIT_FAILURE */
|
||||
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_USAGE */
|
||||
#include <termios.h> /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */
|
||||
#include <unistd.h> /* getopt(3), isatty(3), pledge(2), unveil(2),
|
||||
* STDIN_FILENO */
|
||||
|
||||
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 struct termios 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 (sig != 0) { exit(EXIT_FAILURE); } /* Terminated by signal. */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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[]){
|
||||
bool is_term; /* Is stdin a terminal? */
|
||||
bool must_be_term = 1; /* Must it be? */
|
||||
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
Remember to handle the possible errors here! Remember to handle the possible errors here!
|
||||
#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
|
||||
* easily. */
|
||||
(void)perror(argv[0] == NULL ? argv[0] : program_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
is_term = isatty(STDIN_FILENO);
|
||||
|
||||
if (argc > 0) { /* option parsing */
|
||||
int c;
|
||||
|
||||
program_name = argv[0];
|
||||
|
||||
while ((c = getopt(argc, argv, "i")) != -1) {
|
||||
switch (c) {
|
||||
case 'i': must_be_term = 0; break;
|
||||
default: return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > optind) { return usage(argv[0]); }
|
||||
}
|
||||
|
||||
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 };
|
||||
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
{ /* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ /* Input loop */
|
||||
int c;
|
||||
|
||||
while ((c = fgetc(stdin)) != EOF) {
|
||||
if (fputc(c, stdout) == EOF) { return ioerr(program_name); }
|
||||
}
|
||||
}
|
||||
|
||||
if (is_term) { restore_echo(0); }
|
||||
|
||||
return EX_OK;
|
||||
}
|
24
tests/bonsai/peek.mk
Executable file
@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2024 DTB <trinity@trinity.moe>
|
||||
# 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.' =
|