You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.2 KiB

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
/*