1 |
diff --git a/src/ttyname.c b/src/ttyname.c |
2 |
index 32f093c..d8858f7 100644 |
3 |
--- a/src/ttyname.c |
4 |
+++ b/src/ttyname.c |
5 |
@@ -414,53 +414,80 @@ get_process_ttyname(void) |
6 |
} |
7 |
#elif defined(__linux__) |
8 |
/* |
9 |
- * Return a string from ttyname() containing the tty to which the process is |
10 |
- * attached or NULL if there is no tty associated with the process (or its |
11 |
- * parent). First tries field 7 in /proc/pid/stat, then /proc/ppid/stat. |
12 |
- * Falls back on ttyname of std{in,out,err} if that fails. |
13 |
+ * Store the name of the tty to which the process is attached in name. |
14 |
+ * Returns name on success and NULL on failure, setting errno. |
15 |
*/ |
16 |
char * |
17 |
get_process_ttyname(void) |
18 |
{ |
19 |
- char *line = NULL, *tty = NULL; |
20 |
- size_t linesize = 0; |
21 |
- ssize_t len; |
22 |
- int i; |
23 |
+ const char path[] = "/proc/self/stat"; |
24 |
+ char *cp, buf[1024]; |
25 |
+ char *ret = NULL; |
26 |
+ int serrno = errno; |
27 |
+ ssize_t nread; |
28 |
+ int fd; |
29 |
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) |
30 |
|
31 |
- /* Try to determine the tty from tty_nr in /proc/pid/stat. */ |
32 |
- for (i = 0; tty == NULL && i < 2; i++) { |
33 |
- FILE *fp; |
34 |
- char path[PATH_MAX]; |
35 |
- (void)snprintf(path, sizeof(path), "/proc/%u/stat", |
36 |
- i ? (unsigned int)getppid() : (unsigned int)getpid()); |
37 |
- if ((fp = fopen(path, "r")) == NULL) |
38 |
- continue; |
39 |
- len = getline(&line, &linesize, fp); |
40 |
- fclose(fp); |
41 |
- if (len != -1) { |
42 |
+ /* |
43 |
+ * Try to determine the tty from tty_nr in /proc/self/stat. |
44 |
+ * Ignore /proc/self/stat if it contains embedded NUL bytes. |
45 |
+ */ |
46 |
+ if ((fd = open(path, O_RDONLY | O_NOFOLLOW)) != -1) { |
47 |
+ cp = buf; |
48 |
+ while ((nread = read(fd, cp, buf + sizeof(buf) - cp)) != 0) { |
49 |
+ if (nread == -1) { |
50 |
+ if (errno == EAGAIN || errno == EINTR) |
51 |
+ continue; |
52 |
+ break; |
53 |
+ } |
54 |
+ cp += nread; |
55 |
+ if (cp >= buf + sizeof(buf)) |
56 |
+ break; |
57 |
+ } |
58 |
+ if (nread == 0 && memchr(buf, '\0', cp - buf) == NULL) { |
59 |
/* |
60 |
* Field 7 is the tty dev (0 if no tty). |
61 |
- * Since the process name at field 2 "(comm)" may include spaces, |
62 |
- * start at the last ')' found. |
63 |
+ * Since the process name at field 2 "(comm)" may include |
64 |
+ * whitespace (including newlines), start at the last ')' found. |
65 |
*/ |
66 |
- char *cp = strrchr(line, ')'); |
67 |
- int field = 2; |
68 |
- while (*cp != '\0') { |
69 |
- if (*cp++ == ' ') { |
70 |
- if (++field == 7) { |
71 |
- dev_t tdev = (dev_t)atoi(cp); |
72 |
- if (tdev > 0) |
73 |
- tty = sudo_ttyname_dev(tdev); |
74 |
- break; |
75 |
+ *cp = '\0'; |
76 |
+ cp = strrchr(buf, ')'); |
77 |
+ if (cp != NULL) { |
78 |
+ char *ep = cp; |
79 |
+ int field = 1; |
80 |
+ |
81 |
+ while (*++ep != '\0') { |
82 |
+ if (*ep == ' ') { |
83 |
+ *ep = '\0'; |
84 |
+ if (++field == 7) { |
85 |
+ dev_t tdev = (dev_t)strtol(cp, NULL, 10); |
86 |
+ if (tdev == 0 || errno == ERANGE || errno == EINVAL) { |
87 |
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
88 |
+ "%s: tty device %s: %s", path, cp, strerror(errno)); |
89 |
+ } |
90 |
+ if (tdev > 0) { |
91 |
+ errno = serrno; |
92 |
+ ret = sudo_ttyname_dev(tdev); |
93 |
+ goto done; |
94 |
+ } |
95 |
+ break; |
96 |
+ } |
97 |
+ cp = ep + 1; |
98 |
} |
99 |
} |
100 |
} |
101 |
} |
102 |
} |
103 |
- efree(line); |
104 |
+ errno = ENOENT; |
105 |
|
106 |
- debug_return_str(tty); |
107 |
+done: |
108 |
+ if (fd != -1) |
109 |
+ close(fd); |
110 |
+ if (ret == NULL) |
111 |
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, |
112 |
+ "unable to resolve tty via %s", path); |
113 |
+ |
114 |
+ debug_return_str(ret); |
115 |
} |
116 |
#else |
117 |
/* |