123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /*++
- Copyright (c) 2015 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- line.c
- Abstract:
- This module implements line processing functions.
- Author:
- Evan Green 9-Mar-2015
- Environment:
- User Mode C Library
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "libcp.h"
- #include <assert.h>
- #include <errno.h>
- #include <paths.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <termios.h>
- #include <unistd.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define GETLINE_INITIAL_BUFFER_SIZE 64
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- void
- ClpGetpassSignalHandler (
- int Signal
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- char *ClGetpassBuffer = NULL;
- size_t ClGetpassBufferSize = 0;
- int *ClGetpassSignals = NULL;
- //
- // ------------------------------------------------------------------ Functions
- //
- LIBC_API
- char *
- getpass (
- const char *Prompt
- )
- /*++
- Routine Description:
- This routine reads outputs the given prompt, and reads in a line of input
- without echoing it. This routine attempts to use the process' controlling
- terminal, or stdin/stderr otherwise. This routine is neither thread-safe
- nor reentrant.
- Arguments:
- Prompt - Supplies a pointer to the prompt to print.
- Return Value:
- Returns a pointer to the entered input on success. If this is a password,
- the caller should be sure to clear this buffer out as soon as possible.
- NULL on failure.
- --*/
- {
- ssize_t BytesRead;
- char Character;
- int DescriptorIn;
- FILE *FileIn;
- size_t LineSize;
- struct sigaction NewAction;
- void *NewBuffer;
- size_t NewBufferSize;
- struct termios NewSettings;
- struct termios OriginalSettings;
- PSTR ResultBuffer;
- struct sigaction SaveAlarm;
- struct sigaction SaveHup;
- struct sigaction SaveInt;
- struct sigaction SavePipe;
- struct sigaction SaveQuit;
- struct sigaction SaveTerm;
- struct sigaction SaveTstop;
- struct sigaction SaveTtin;
- struct sigaction SaveTtou;
- int Signal;
- int Signals[NSIG];
- ResultBuffer = NULL;
- memset(Signals, 0, sizeof(Signals));
- ClGetpassSignals = Signals;
- FileIn = fopen(_PATH_TTY, "w+");
- if (FileIn == NULL) {
- return NULL;
- }
- DescriptorIn = fileno(FileIn);
- //
- // Turn off echoing.
- //
- if ((DescriptorIn < 0) ||
- (tcgetattr(DescriptorIn, &OriginalSettings) != 0)) {
- fclose(FileIn);
- return NULL;
- }
- memcpy(&NewSettings, &OriginalSettings, sizeof(struct termios));
- NewSettings.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
- if (tcsetattr(DescriptorIn, TCSAFLUSH, &NewSettings) != 0) {
- goto getpassEnd;
- }
- //
- // Handle all signals so that the terminal settings can be put back.
- //
- sigemptyset(&(NewAction.sa_mask));
- NewAction.sa_flags = 0;
- NewAction.sa_handler = ClpGetpassSignalHandler;
- sigaction(SIGALRM, &NewAction, &SaveAlarm);
- sigaction(SIGHUP, &NewAction, &SaveHup);
- sigaction(SIGINT, &NewAction, &SaveInt);
- sigaction(SIGPIPE, &NewAction, &SavePipe);
- sigaction(SIGQUIT, &NewAction, &SaveQuit);
- sigaction(SIGTERM, &NewAction, &SaveTerm);
- sigaction(SIGTSTP, &NewAction, &SaveTstop);
- sigaction(SIGTTIN, &NewAction, &SaveTtin);
- sigaction(SIGTTOU, &NewAction, &SaveTtou);
- //
- // Print the prompt.
- //
- fprintf(stderr, Prompt);
- fflush(stderr);
- //
- // Loop reading characters from the input.
- //
- LineSize = 0;
- BytesRead = 0;
- while (TRUE) {
- for (Signal = 0; Signal < NSIG; Signal += 1) {
- if (ClGetpassSignals[Signal] != 0) {
- break;
- }
- }
- if (Signal != NSIG) {
- break;
- }
- BytesRead = read(DescriptorIn, &Character, 1);
- if ((BytesRead < 0) && (errno == EINTR)) {
- continue;
- }
- if (BytesRead <= 0) {
- break;
- }
- //
- // Reallocate the buffer if needed.
- //
- if (LineSize + 1 >= ClGetpassBufferSize) {
- if (ClGetpassBufferSize == 0) {
- NewBufferSize = GETLINE_INITIAL_BUFFER_SIZE;
- } else {
- NewBufferSize = ClGetpassBufferSize * 2;
- }
- NewBuffer = malloc(NewBufferSize);
- //
- // Whether or not the allocation succeeded, zero out the previous
- // buffer to avoid leaking potential passwords.
- //
- if (ClGetpassBufferSize != 0) {
- if (NewBuffer != NULL) {
- memcpy(NewBuffer, ClGetpassBuffer, ClGetpassBufferSize);
- }
- SECURITY_ZERO(ClGetpassBuffer, ClGetpassBufferSize);
- free(ClGetpassBuffer);
- ClGetpassBuffer = NULL;
- ClGetpassBufferSize = 0;
- }
- if (NewBuffer == NULL) {
- LineSize = 0;
- break;
- } else {
- ClGetpassBuffer = NewBuffer;
- ClGetpassBufferSize = NewBufferSize;
- }
- }
- if ((Character == '\r') || (Character == '\n')) {
- break;
- }
- //
- // Control-C cancels early.
- //
- if (Character == 3) {
- goto getpassEnd;
- }
- //
- // Add the character to the buffer.
- //
- ClGetpassBuffer[LineSize] = Character;
- LineSize += 1;
- }
- if (BytesRead >= 0) {
- if ((BytesRead > 0) || (LineSize > 0)) {
- assert(LineSize + 1 < ClGetpassBufferSize);
- ClGetpassBuffer[LineSize] = '\0';
- LineSize += 1;
- } else {
- BytesRead = -1;
- }
- fputc('\n', stderr);
- }
- ResultBuffer = ClGetpassBuffer;
- getpassEnd:
- //
- // If the result was not successful but there's a partial buffer, zero it
- // out.
- //
- if ((ResultBuffer == NULL) && (ClGetpassBufferSize != 0)) {
- SECURITY_ZERO(ClGetpassBuffer, ClGetpassBufferSize);
- }
- //
- // Restore the original terminal settings.
- //
- tcsetattr(DescriptorIn, TCSAFLUSH, &OriginalSettings);
- fclose(FileIn);
- //
- // Restore the original signal handlers.
- //
- sigaction(SIGALRM, &SaveAlarm, NULL);
- sigaction(SIGHUP, &SaveHup, NULL);
- sigaction(SIGINT, &SaveInt, NULL);
- sigaction(SIGPIPE, &SavePipe, NULL);
- sigaction(SIGQUIT, &SaveQuit, NULL);
- sigaction(SIGTERM, &SaveTerm, NULL);
- sigaction(SIGTSTP, &SaveTstop, NULL);
- sigaction(SIGTTIN, &SaveTtin, NULL);
- sigaction(SIGTTOU, &SaveTtou, NULL);
- //
- // Replay any signals that were sent during the read.
- //
- for (Signal = 0; Signal < NSIG; Signal += 1) {
- while (ClGetpassSignals[Signal] != 0) {
- kill(getpid(), Signal);
- ClGetpassSignals[Signal] -= 1;
- }
- }
- ClGetpassSignals = NULL;
- return ResultBuffer;
- }
- LIBC_API
- ssize_t
- getline (
- char **LinePointer,
- size_t *Size,
- FILE *Stream
- )
- /*++
- Routine Description:
- This routine reads an entire line from the given stream. This routine will
- allocate or reallocate the given buffer so that the buffer is big enough.
- Arguments:
- LinePointer - Supplies a pointer that on input contains an optional pointer
- to a buffer to use to read the line. If the buffer ends up being not
- big enough, it will be reallocated. If no buffer is supplied, one will
- be allocated. On output, contains a pointer to the buffer containing
- the read line on success.
- Size - Supplies a pointer that on input contains the size in bytes of the
- supplied line pointer. On output, this value will be updated to contain
- the size of the buffer returned in the line buffer parameter.
- Stream - Supplies the stream to read the line from.
- Return Value:
- On success, returns the number of characters read, including the delimiter
- character, but not including the null terminator.
- Returns -1 on failure (including an end of file condition), and errno will
- be set to contain more information.
- --*/
- {
- return getdelim(LinePointer, Size, '\n', Stream);
- }
- LIBC_API
- ssize_t
- getdelim (
- char **LinePointer,
- size_t *Size,
- int Delimiter,
- FILE *Stream
- )
- /*++
- Routine Description:
- This routine reads an entire line from the given stream, delimited by the
- given delimiter character. This routine will allocate or reallocate the
- given buffer so that the buffer is big enough.
- Arguments:
- LinePointer - Supplies a pointer that on input contains an optional pointer
- to a buffer to use to read the line. If the buffer ends up being not
- big enough, it will be reallocated. If no buffer is supplied, one will
- be allocated. On output, contains a pointer to the buffer containing
- the read line on success.
- Size - Supplies a pointer that on input contains the size in bytes of the
- supplied line pointer. On output, this value will be updated to contain
- the size of the buffer returned in the line buffer parameter.
- Delimiter - Supplies the delimiter to split the line on.
- Stream - Supplies the stream to read the line from.
- Return Value:
- On success, returns the number of characters read, including the delimiter
- character, but not including the null terminator.
- Returns -1 on failure (including an end of file condition), and errno will
- be set to contain more information.
- --*/
- {
- int Character;
- size_t LineSize;
- char *NewBuffer;
- size_t NewSize;
- LineSize = 0;
- if ((LinePointer == NULL) || (Size == NULL)) {
- errno = EINVAL;
- return -1;
- }
- if (Stream == NULL) {
- errno = EBADF;
- return -1;
- }
- //
- // Allocate an initial buffer if the caller passed one that is NULL or
- // too small.
- //
- if ((*LinePointer == NULL) || (*Size < GETLINE_INITIAL_BUFFER_SIZE)) {
- NewBuffer = realloc(*LinePointer, GETLINE_INITIAL_BUFFER_SIZE);
- if (NewBuffer == NULL) {
- return -1;
- }
- *LinePointer = NewBuffer;
- *Size = GETLINE_INITIAL_BUFFER_SIZE;
- }
- while (TRUE) {
- Character = fgetc(Stream);
- if (Character == EOF) {
- if (LineSize != 0) {
- break;
- }
- return -1;
- }
- if (LineSize + 2 > *Size) {
- assert(*Size != 0);
- NewSize = *Size;
- while (NewSize < LineSize + 2) {
- NewSize *= 2;
- }
- NewBuffer = realloc(*LinePointer, NewSize);
- if (NewBuffer == NULL) {
- return -1;
- }
- *LinePointer = NewBuffer;
- *Size = NewSize;
- }
- (*LinePointer)[LineSize] = Character;
- LineSize += 1;
- if (Character == Delimiter) {
- break;
- }
- }
- assert(*Size > LineSize);
- (*LinePointer)[LineSize] = '\0';
- return LineSize;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- void
- ClpGetpassSignalHandler (
- int Signal
- )
- /*++
- Routine Description:
- This routine is the signal handler installed during the getpass function.
- It simply tracks signals for replay later.
- Arguments:
- Signal - Supplies the signal number that fired.
- Return Value:
- None.
- --*/
- {
- assert((ClGetpassSignals != NULL) && (Signal < NSIG));
- ClGetpassSignals[Signal] += 1;
- return;
- }
|