#endif /* lint */
#ifndef TCSASOFT
-#define TCSASOFT 0
-#endif /* TCSASOFT */
+# define TCSASOFT 0
+#endif
+#ifndef ECHONL
+# define ECHONL 0
+#endif
+
+#ifndef _POSIX_VDISABLE
+# ifdef VDISABLE
+# define _POSIX_VDISABLE VDISABLE
+# else
+# define _POSIX_VDISABLE 0
+# endif
+#endif
/*
* Abstract method of getting at the term flags.
# endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */
+static int signo; /* XXX - should be volatile sig_atomic_t */
+
static char *tgetline __P((int, char *, size_t, int));
+static void handler __P((int));
/*
* Like getpass(3) but with timeout and echo flags.
int timeout;
int flags;
{
- struct TERM term, oterm;
- int input, output;
+ struct sigaction sa, saveint, savehup, savequit, saveterm, savetstp;
static char buf[SUDO_PASS_MAX + 1];
+ int input, output, save_errno;
+ struct TERM term, oterm;
+ char *pass;
+restart:
/* Open /dev/tty for reading/writing if possible else use stdin/stderr. */
if ((flags & TGP_STDIN) ||
(input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) {
if (prompt)
(void) write(output, prompt, strlen(prompt));
- /* Turn echo off/on as specified by flags. */
- (void) term_getattr(input, &oterm);
- (void) memcpy(&term, &oterm, sizeof(term));
- if ((flags & TGP_ECHO) && !(term.tflags & ECHO))
- term.tflags |= ECHO;
- else if (!(flags & TGP_ECHO) && (term.tflags & ECHO))
- term.tflags &= ~ECHO;
- (void) term_setattr(input, &term);
+ /*
+ * Catch signals that would otherwise cause the user to end
+ * up with echo turned off in the shell. Don't worry about
+ * things like SIGALRM and SIGPIPE for now.
+ */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; /* don't restart system calls */
+ sa.sa_handler = handler;
+ (void)sigaction(SIGINT, &sa, &saveint);
+ (void)sigaction(SIGHUP, &sa, &savehup);
+ (void)sigaction(SIGQUIT, &sa, &savequit);
+ (void)sigaction(SIGTERM, &sa, &saveterm);
+ (void)sigaction(SIGTSTP, &sa, &savetstp);
- buf[0] = '\0';
- tgetline(input, buf, sizeof(buf), timeout);
+ /* Turn echo off/on as specified by flags. */
+ if (term_getattr(input, &oterm) == 0) {
+ (void) memcpy(&term, &oterm, sizeof(term));
+ if (!(flags & TGP_ECHO))
+ term.tflags &= ~(ECHO | ECHONL);
+#ifdef VSTATUS
+ term.c_cc[VSTATUS] = _POSIX_VDISABLE;
+#endif
+ (void) term_setattr(input, &term);
+ } else {
+ memset(&term, 0, sizeof(term));
+ memset(&oterm, 0, sizeof(oterm));
+ }
- /* Restore old tty flags. */
- (void) term_setattr(input, &oterm);
+ pass = tgetline(input, buf, sizeof(buf), timeout);
+ save_errno = errno;
- if (!(flags & TGP_ECHO))
+ if (!(term.tflags & ECHO))
(void) write(output, "\n", 1);
+ /* Restore old tty settings and signals. */
+ if (memcmp(&term, &oterm, sizeof(term)) != 0)
+ (void) term_setattr(input, &oterm);
+ (void) sigaction(SIGINT, &saveint, NULL);
+ (void) sigaction(SIGHUP, &savehup, NULL);
+ (void) sigaction(SIGQUIT, &savequit, NULL);
+ (void) sigaction(SIGTERM, &saveterm, NULL);
+ (void) sigaction(SIGTSTP, &savetstp, NULL);
if (input != STDIN_FILENO)
(void) close(input);
- return(buf);
+ /*
+ * If we were interrupted by a signal, resend it to ourselves
+ * now that we have restored the signal handlers.
+ */
+ if (signo) {
+ kill(getpid(), signo);
+ if (signo == SIGTSTP) {
+ signo = 0;
+ goto restart;
+ }
+ }
+
+ errno = save_errno;
+ return(pass);
}
/*
size_t bufsiz;
int timeout;
{
- size_t left;
- int n;
fd_set *readfds = NULL;
struct timeval tv;
- char c;
+ size_t left;
char *cp;
+ char c;
+ int n;
- if (bufsiz == 0)
+ if (bufsiz == 0) {
+ errno = EINVAL;
return(NULL); /* sanity */
+ }
cp = buf;
left = bufsiz;
/* Make sure there is something to read (or timeout) */
while ((n = select(fd + 1, readfds, 0, 0, &tv)) == -1 &&
- (errno == EINTR || errno == EAGAIN))
+ errno == EAGAIN)
;
- if (n == 0) {
+ if (n <= 0) {
free(readfds);
- return(NULL); /* timeout */
+ return(NULL); /* timeout or interrupt */
}
/* Read a character, exit loop on error, EOF or EOL */
}
*cp = '\0';
- return(cp == buf ? NULL : buf);
+ return(n == -1 ? NULL : buf);
+}
+
+static void handler(s)
+ int s;
+{
+ signo = s;
}