diff --git a/misc.h b/misc.h index bcc34f980..7421fbdf9 100644 --- a/misc.h +++ b/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.81 2019/09/03 08:32:11 djm Exp $ */ +/* $OpenBSD: misc.h,v 1.82 2019/11/12 22:34:20 djm Exp $ */ /* * Author: Tatu Ylonen @@ -178,8 +178,13 @@ int opt_match(const char **opts, const char *term); #define RP_ALLOW_EOF 0x0004 #define RP_USE_ASKPASS 0x0008 +struct notifier_ctx; + char *read_passphrase(const char *, int); int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); +struct notifier_ctx *notify_start(int, const char *, ...) + __attribute__((format(printf, 2, 3))); +void notify_complete(struct notifier_ctx *); #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) diff --git a/readpass.c b/readpass.c index 7e52cae9c..33fc03bb7 100644 --- a/readpass.c +++ b/readpass.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readpass.c,v 1.54 2019/06/28 13:35:04 deraadt Exp $ */ +/* $OpenBSD: readpass.c,v 1.55 2019/11/12 22:34:20 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -197,3 +197,87 @@ ask_permission(const char *fmt, ...) return (allowed); } + +struct notifier_ctx { + pid_t pid; + void (*osigchld)(int); +}; + +struct notifier_ctx * +notify_start(int force_askpass, const char *fmt, ...) +{ + va_list args; + char *prompt = NULL; + int devnull; + pid_t pid; + void (*osigchld)(int); + const char *askpass; + struct notifier_ctx *ret; + + va_start(args, fmt); + xvasprintf(&prompt, fmt, args); + va_end(args); + + if (fflush(NULL) != 0) + error("%s: fflush: %s", __func__, strerror(errno)); + if (!force_askpass && isatty(STDERR_FILENO)) { + (void)write(STDERR_FILENO, "\r", 1); + (void)write(STDERR_FILENO, prompt, strlen(prompt)); + (void)write(STDERR_FILENO, "\r\n", 2); + free(prompt); + return NULL; + } + if (getenv("DISPLAY") == NULL || + (askpass = getenv("SSH_ASKPASS")) == NULL || *askpass == '\0') { + debug3("%s: cannot notify", __func__); + free(prompt); + return NULL; + } + osigchld = signal(SIGCHLD, SIG_DFL); + if ((pid = fork()) == -1) { + error("%s: fork: %s", __func__, strerror(errno)); + signal(SIGCHLD, osigchld); + free(prompt); + return NULL; + } + if (pid == 0) { + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) + fatal("%s: open %s", __func__, strerror(errno)); + if (dup2(devnull, STDIN_FILENO) == -1 || + dup2(devnull, STDOUT_FILENO) == -1) + fatal("%s: dup2: %s", __func__, strerror(errno)); + closefrom(STDERR_FILENO + 1); + setenv("SSH_ASKPASS_PROMPT", "none", 1); /* hint to UI */ + execlp(askpass, askpass, prompt, (char *)NULL); + fatal("%s: exec(%s): %s", __func__, askpass, strerror(errno)); + /* NOTREACHED */ + } + if ((ret = calloc(1, sizeof(*ret))) == NULL) { + kill(pid, SIGTERM); + fatal("%s: calloc failed", __func__); + } + ret->pid = pid; + ret->osigchld = osigchld; + free(prompt); + return ret; +} + +void +notify_complete(struct notifier_ctx *ctx) +{ + int ret; + + if (ctx == NULL || ctx->pid <= 0) { + free(ctx); + return; + } + kill(ctx->pid, SIGTERM); + while ((ret = waitpid(ctx->pid, NULL, 0)) == -1) { + if (errno != EINTR) + break; + } + if (ret == -1) + fatal("%s: waitpid: %s", __func__, strerror(errno)); + signal(SIGCHLD, ctx->osigchld); + free(ctx); +}