Compare commits
21 Commits
fbacfecce8
...
87c9e29932
Author | SHA1 | Date | |
---|---|---|---|
87c9e29932 | |||
1037e32e6e | |||
5c70852890 | |||
8ec18ccf75 | |||
3bfdc62035 | |||
7d174c1f7c | |||
3f41110617 | |||
7a69e1d516 | |||
64e943f64a | |||
16dcd4da37 | |||
d892fa1cac | |||
3862a95151 | |||
264ae2e82c | |||
1634761593 | |||
0b40360e7e | |||
590d98d7f7 | |||
07aa9a9abc | |||
fce49bed85 | |||
62b38a6765 | |||
23521bade5 | |||
3b774cab27 |
7
Makefile
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: 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
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"
|
||||
.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
|
||||
Allows input to come from sources other than terminals (pipes).
|
||||
.\"
|
||||
.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.
|
||||
|
||||
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\(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.
|
||||
.\"
|
||||
.SH CAVEATS
|
||||
|
||||
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 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)
|
||||
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
|
||||
.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
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? */
|
||||
|
||||
#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
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.' =
|
Loading…
Reference in New Issue
Block a user