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.
117 lines
3.2 KiB
117 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 |
|
/*
|
|
|