diff --git a/src/ttyname.c b/src/ttyname.c index 32f093c..d8858f7 100644 --- a/src/ttyname.c +++ b/src/ttyname.c @@ -414,53 +414,80 @@ get_process_ttyname(void) } #elif defined(__linux__) /* - * Return a string from ttyname() containing the tty to which the process is - * attached or NULL if there is no tty associated with the process (or its - * parent). First tries field 7 in /proc/pid/stat, then /proc/ppid/stat. - * Falls back on ttyname of std{in,out,err} if that fails. + * Store the name of the tty to which the process is attached in name. + * Returns name on success and NULL on failure, setting errno. */ char * get_process_ttyname(void) { - char *line = NULL, *tty = NULL; - size_t linesize = 0; - ssize_t len; - int i; + const char path[] = "/proc/self/stat"; + char *cp, buf[1024]; + char *ret = NULL; + int serrno = errno; + ssize_t nread; + int fd; debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) - /* Try to determine the tty from tty_nr in /proc/pid/stat. */ - for (i = 0; tty == NULL && i < 2; i++) { - FILE *fp; - char path[PATH_MAX]; - (void)snprintf(path, sizeof(path), "/proc/%u/stat", - i ? (unsigned int)getppid() : (unsigned int)getpid()); - if ((fp = fopen(path, "r")) == NULL) - continue; - len = getline(&line, &linesize, fp); - fclose(fp); - if (len != -1) { + /* + * Try to determine the tty from tty_nr in /proc/self/stat. + * Ignore /proc/self/stat if it contains embedded NUL bytes. + */ + if ((fd = open(path, O_RDONLY | O_NOFOLLOW)) != -1) { + cp = buf; + while ((nread = read(fd, cp, buf + sizeof(buf) - cp)) != 0) { + if (nread == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + break; + } + cp += nread; + if (cp >= buf + sizeof(buf)) + break; + } + if (nread == 0 && memchr(buf, '\0', cp - buf) == NULL) { /* * Field 7 is the tty dev (0 if no tty). - * Since the process name at field 2 "(comm)" may include spaces, - * start at the last ')' found. + * Since the process name at field 2 "(comm)" may include + * whitespace (including newlines), start at the last ')' found. */ - char *cp = strrchr(line, ')'); - int field = 2; - while (*cp != '\0') { - if (*cp++ == ' ') { - if (++field == 7) { - dev_t tdev = (dev_t)atoi(cp); - if (tdev > 0) - tty = sudo_ttyname_dev(tdev); - break; + *cp = '\0'; + cp = strrchr(buf, ')'); + if (cp != NULL) { + char *ep = cp; + int field = 1; + + while (*++ep != '\0') { + if (*ep == ' ') { + *ep = '\0'; + if (++field == 7) { + dev_t tdev = (dev_t)strtol(cp, NULL, 10); + if (tdev == 0 || errno == ERANGE || errno == EINVAL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s: tty device %s: %s", path, cp, strerror(errno)); + } + if (tdev > 0) { + errno = serrno; + ret = sudo_ttyname_dev(tdev); + goto done; + } + break; + } + cp = ep + 1; } } } } } - efree(line); + errno = ENOENT; - debug_return_str(tty); +done: + if (fd != -1) + close(fd); + if (ret == NULL) + sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "unable to resolve tty via %s", path); + + debug_return_str(ret); } #else /*