From 019cefd3fbd256dd04289ce181b0a8330e11b292 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Thu, 31 Aug 2017 13:37:28 -0400 Subject: [PATCH] Add support for askpass program for reading answers from the user. --- platform-independent/cli-c/cli/mpw-cli.c | 109 ++++++++++++++++++++--- 1 file changed, 97 insertions(+), 12 deletions(-) diff --git a/platform-independent/cli-c/cli/mpw-cli.c b/platform-independent/cli-c/cli/mpw-cli.c index e3d28998..5d27ce8f 100644 --- a/platform-independent/cli-c/cli/mpw-cli.c +++ b/platform-independent/cli-c/cli/mpw-cli.c @@ -117,36 +117,103 @@ static const char *mpw_getenv(const char *variableName) { return envBuf? strdup( envBuf ): NULL; } +char *mpw_read_fd(int fd); +char *mpw_read_file(FILE *file); + +/** Use the askpass program to prompt the user. + * @return A newly allocated string or NULL if askpass is not supported or an error occurred. */ +static char *mpw_askpass(const char *prompt) { + + const char *askpass = mpw_getenv( "MPW_ASKPASS" ); + if (!askpass) + return NULL; + + int pipes[2]; + if (pipe( pipes ) == ERR) { + wrn( "Couldn't pipe: %s\n", strerror( errno ) ); + return NULL; + } + + pid_t pid = fork(); + if (pid == ERR) { + wrn( "Couldn't fork for askpass:\n %s: %s\n", askpass, strerror( errno ) ); + return NULL; + } + + if (!pid) { + // askpass fork + close( pipes[0] ); + if (dup2( pipes[1], STDOUT_FILENO ) == ERR) + ftl( "Couldn't connect pipe to process: %s\n", strerror( errno ) ); + + else if (execl( askpass, askpass, prompt, NULL ) == ERR) + ftl( "Couldn't execute askpass:\n %s: %s\n", askpass, strerror( errno ) ); + + exit( EX_SOFTWARE ); + } + + close( pipes[1] ); + char *answer = mpw_read_fd( pipes[0] ); + close( pipes[0] ); + int status; + if (waitpid( pid, &status, 0 ) == ERR) { + wrn( "Couldn't wait for askpass: %s\n", strerror( errno ) ); + mpw_free_string( &answer ); + return NULL; + } + + if (WIFEXITED( status ) && WEXITSTATUS( status ) == EXIT_SUCCESS && answer && strlen( answer )) { + // Remove trailing newline. + if (answer[strlen( answer ) - 1] == '\n') + answer[strlen( answer ) - 1] = '\0'; + return answer; + } + + mpw_free_string( &answer ); + return NULL; +} + /** Ask the user a question. * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ static const char *mpw_getline(const char *prompt) { + // Get answer from askpass. + char *answer = mpw_askpass( prompt ); + if (answer) + return answer; + + // Get password from terminal. fprintf( stderr, "%s ", prompt ); - char *buf = NULL; size_t bufSize = 0; - ssize_t lineSize = getline( &buf, &bufSize, stdin ); + ssize_t lineSize = getline( &answer, &bufSize, stdin ); if (lineSize <= 1) { - free( buf ); + mpw_free_string( &answer ); return NULL; } - // Remove the newline. - buf[lineSize - 1] = '\0'; - return buf; + // Remove trailing newline. + answer[lineSize - 1] = '\0'; + return answer; } /** Ask the user for a password. * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ static const char *mpw_getpass(const char *prompt) { - char *passBuf = getpass( prompt ); - if (!passBuf) + // Get password from askpass. + const char *password = mpw_askpass( prompt ); + if (password) + return password; + + // Get password from terminal. + char *answer = getpass( prompt ); + if (!answer) return NULL; - char *buf = strdup( passBuf ); - bzero( passBuf, strlen( passBuf ) ); - return buf; + password = strdup( answer ); + bzero( answer, strlen( answer ) ); + return password; } /** Get the absolute path to the mpw configuration file with the given prefix name and file extension. @@ -230,9 +297,27 @@ static bool mpw_mkdirs(const char *filePath) { return success; } +/** Read until EOF from the given file descriptor. + * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ +char *mpw_read_fd(int fd) { + + char *buf = NULL; + size_t blockSize = 4096, bufSize = 0, bufOffset = 0; + ssize_t readSize = 0; + while ((mpw_realloc( &buf, &bufSize, blockSize )) && + ((readSize = read( fd, buf + bufOffset, blockSize )) > 0)); + if (readSize == ERR) + dbg( "While reading: %s\n", strerror( errno ) ); + + return buf; +} + /** Read the file contents of a given file. * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ -static char *mpw_read_file(FILE *file) { +char *mpw_read_file(FILE *file) { + + if (!file) + return NULL; char *buf = NULL; size_t blockSize = 4096, bufSize = 0, bufOffset = 0, readSize = 0;