Annotation of embedaddon/sudo/src/ttyname.c, revision 1.1.1.3
1.1 misho 1: /*
1.1.1.3 ! misho 2: * Copyright (c) 2012-2013 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 misho 3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: #include <config.h>
18:
19: /* Large files not supported by procfs.h */
20: #if defined(HAVE_PROCFS_H) || defined(HAVE_SYS_PROCFS_H)
21: # undef _FILE_OFFSET_BITS
22: # undef _LARGE_FILES
23: #endif
24:
25: #include <sys/types.h>
26: #include <sys/stat.h>
27: #if defined(MAJOR_IN_MKDEV)
28: # include <sys/mkdev.h>
29: #elif defined(MAJOR_IN_SYSMACROS)
30: # include <sys/sysmacros.h>
31: #endif
32: #include <stdio.h>
33: #ifdef STDC_HEADERS
34: # include <stdlib.h>
35: # include <stddef.h>
36: #else
37: # ifdef HAVE_STDLIB_H
38: # include <stdlib.h>
39: # endif
40: #endif /* STDC_HEADERS */
41: #ifdef HAVE_STRING_H
42: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
43: # include <memory.h>
44: # endif
45: # include <string.h>
46: #endif /* HAVE_STRING_H */
47: #ifdef HAVE_STRINGS_H
48: # include <strings.h>
49: #endif /* HAVE_STRINGS_H */
50: #ifdef HAVE_UNISTD_H
51: # include <unistd.h>
52: #endif /* HAVE_UNISTD_H */
53: #include <errno.h>
54: #include <fcntl.h>
55: #include <limits.h>
56: #ifdef HAVE_DIRENT_H
57: # include <dirent.h>
58: # define NAMLEN(dirent) strlen((dirent)->d_name)
59: #else
60: # define dirent direct
61: # define NAMLEN(dirent) (dirent)->d_namlen
62: # ifdef HAVE_SYS_NDIR_H
63: # include <sys/ndir.h>
64: # endif
65: # ifdef HAVE_SYS_DIR_H
66: # include <sys/dir.h>
67: # endif
68: # ifdef HAVE_NDIR_H
69: # include <ndir.h>
70: # endif
71: #endif
72: #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) || defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
1.1.1.3 ! misho 73: # include <sys/param.h>
1.1 misho 74: # include <sys/sysctl.h>
75: #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
1.1.1.3 ! misho 76: # include <sys/param.h>
1.1 misho 77: # include <sys/sysctl.h>
78: # include <sys/user.h>
79: #endif
80: #if defined(HAVE_PROCFS_H)
81: # include <procfs.h>
82: #elif defined(HAVE_SYS_PROCFS_H)
83: # include <sys/procfs.h>
84: #endif
1.1.1.3 ! misho 85: #ifdef HAVE_PSTAT_GETPROC
! 86: # include <sys/param.h>
! 87: # include <sys/pstat.h>
! 88: #endif
1.1 misho 89:
90: #include "sudo.h"
91:
92: /*
93: * How to access the tty device number in struct kinfo_proc.
94: */
95: #if defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
96: # define SUDO_KERN_PROC KERN_PROC2
97: # define sudo_kinfo_proc kinfo_proc2
98: # define sudo_kp_tdev p_tdev
99: # define sudo_kp_namelen 6
100: #elif defined(HAVE_STRUCT_KINFO_PROC_P_TDEV)
101: # define SUDO_KERN_PROC KERN_PROC
102: # define sudo_kinfo_proc kinfo_proc
103: # define sudo_kp_tdev p_tdev
104: # define sudo_kp_namelen 6
105: #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
106: # define SUDO_KERN_PROC KERN_PROC
107: # define sudo_kinfo_proc kinfo_proc
108: # define sudo_kp_tdev ki_tdev
109: # define sudo_kp_namelen 4
110: #elif defined(HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV)
111: # define SUDO_KERN_PROC KERN_PROC
112: # define sudo_kinfo_proc kinfo_proc
113: # define sudo_kp_tdev kp_eproc.e_tdev
114: # define sudo_kp_namelen 4
115: #endif
116:
117: #if defined(sudo_kp_tdev)
118: /*
119: * Like ttyname() but uses a dev_t instead of an open fd.
120: * Caller is responsible for freeing the returned string.
121: * The BSD version uses devname()
122: */
123: static char *
124: sudo_ttyname_dev(dev_t tdev)
125: {
126: char *dev, *tty = NULL;
127: debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
128:
129: /* Some versions of devname() return NULL on failure, others do not. */
130: dev = devname(tdev, S_IFCHR);
131: if (dev != NULL && *dev != '?' && *dev != '#') {
132: if (*dev != '/') {
133: /* devname() doesn't use the /dev/ prefix, add one... */
134: size_t len = sizeof(_PATH_DEV) + strlen(dev);
135: tty = emalloc(len);
136: strlcpy(tty, _PATH_DEV, len);
137: strlcat(tty, dev, len);
138: } else {
139: /* Should not happen but just in case... */
140: tty = estrdup(dev);
141: }
142: }
143: debug_return_str(tty);
144: }
145: #elif defined(HAVE__TTYNAME_DEV)
146: extern char *_ttyname_dev(dev_t rdev, char *buffer, size_t buflen);
147:
148: /*
149: * Like ttyname() but uses a dev_t instead of an open fd.
150: * Caller is responsible for freeing the returned string.
151: * This version is just a wrapper around _ttyname_dev().
152: */
153: static char *
154: sudo_ttyname_dev(dev_t tdev)
155: {
156: char buf[TTYNAME_MAX], *tty;
157: debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
158:
159: tty = _ttyname_dev(tdev, buf, sizeof(buf));
160:
161: debug_return_str(estrdup(tty));
162: }
1.1.1.3 ! misho 163: #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__)
1.1 misho 164: /*
165: * Devices to search before doing a breadth-first scan.
166: */
167: static char *search_devs[] = {
168: "/dev/console",
169: "/dev/wscons",
170: "/dev/pts/",
171: "/dev/vt/",
172: "/dev/term/",
173: "/dev/zcons/",
174: NULL
175: };
176:
177: static char *ignore_devs[] = {
178: "/dev/fd/",
179: "/dev/stdin",
180: "/dev/stdout",
181: "/dev/stderr",
182: NULL
183: };
184:
185: /*
186: * Do a breadth-first scan of dir looking for the specified device.
187: */
1.1.1.3 ! misho 188: static char *
! 189: sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin)
1.1 misho 190: {
1.1.1.3 ! misho 191: DIR *d = NULL;
1.1 misho 192: char pathbuf[PATH_MAX], **subdirs = NULL, *devname = NULL;
193: size_t sdlen, d_len, len, num_subdirs = 0, max_subdirs = 0;
194: struct dirent *dp;
195: struct stat sb;
196: int i;
197: debug_decl(sudo_ttyname_scan, SUDO_DEBUG_UTIL)
198:
199: if (dir[0] == '\0' || (d = opendir(dir)) == NULL)
200: goto done;
201:
1.1.1.3 ! misho 202: sudo_debug_printf(SUDO_DEBUG_INFO, "scanning for dev %u in %s",
! 203: (unsigned int)rdev, dir);
! 204:
1.1 misho 205: sdlen = strlen(dir);
206: if (dir[sdlen - 1] == '/')
207: sdlen--;
208: if (sdlen + 1 >= sizeof(pathbuf)) {
209: errno = ENAMETOOLONG;
210: warning("%.*s/", (int)sdlen, dir);
211: goto done;
212: }
213: memcpy(pathbuf, dir, sdlen);
214: pathbuf[sdlen++] = '/';
215: pathbuf[sdlen] = '\0';
216:
217: while ((dp = readdir(d)) != NULL) {
218: /* Skip anything starting with "." */
219: if (dp->d_name[0] == '.')
220: continue;
221:
222: d_len = NAMLEN(dp);
223: if (sdlen + d_len >= sizeof(pathbuf))
224: continue;
225: memcpy(&pathbuf[sdlen], dp->d_name, d_len + 1); /* copy NUL too */
226: d_len += sdlen;
227:
228: for (i = 0; ignore_devs[i] != NULL; i++) {
229: len = strlen(ignore_devs[i]);
230: if (ignore_devs[i][len - 1] == '/')
231: len--;
232: if (d_len == len && strncmp(pathbuf, ignore_devs[i], len) == 0)
233: break;
234: }
235: if (ignore_devs[i] != NULL)
236: continue;
237: if (!builtin) {
238: /* Skip entries in search_devs; we already checked them. */
239: for (i = 0; search_devs[i] != NULL; i++) {
240: len = strlen(search_devs[i]);
241: if (search_devs[i][len - 1] == '/')
242: len--;
243: if (d_len == len && strncmp(pathbuf, search_devs[i], len) == 0)
244: break;
245: }
246: if (search_devs[i] != NULL)
247: continue;
248: }
249: # if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF)
1.1.1.3 ! misho 250: /*
! 251: * Convert dp->d_type to sb.st_mode to avoid a stat(2) if possible.
! 252: * We can't use it for links (since we want to follow them) or
! 253: * char devs (since we need st_rdev to compare the device number).
! 254: */
! 255: if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK && dp->d_type != DT_CHR)
1.1 misho 256: sb.st_mode = DTTOIF(dp->d_type);
257: else
258: # endif
259: if (stat(pathbuf, &sb) == -1)
260: continue;
261: if (S_ISDIR(sb.st_mode)) {
262: if (!builtin) {
263: /* Add to list of subdirs to search. */
264: if (num_subdirs + 1 > max_subdirs) {
265: max_subdirs += 64;
266: subdirs = erealloc3(subdirs, max_subdirs, sizeof(char *));
267: }
268: subdirs[num_subdirs++] = estrdup(pathbuf);
269: }
270: continue;
271: }
272: if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
273: devname = estrdup(pathbuf);
1.1.1.3 ! misho 274: sudo_debug_printf(SUDO_DEBUG_INFO, "resolved dev %u as %s",
! 275: (unsigned int)rdev, pathbuf);
! 276: goto done;
1.1 misho 277: }
278: }
279:
280: /* Search subdirs if we didn't find it in the root level. */
281: for (i = 0; devname == NULL && i < num_subdirs; i++)
282: devname = sudo_ttyname_scan(subdirs[i], rdev, false);
283:
284: done:
1.1.1.3 ! misho 285: if (d != NULL)
! 286: closedir(d);
1.1 misho 287: for (i = 0; i < num_subdirs; i++)
288: efree(subdirs[i]);
289: efree(subdirs);
290: debug_return_str(devname);
291: }
292:
293: /*
294: * Like ttyname() but uses a dev_t instead of an open fd.
295: * Caller is responsible for freeing the returned string.
296: * Generic version.
297: */
298: static char *
299: sudo_ttyname_dev(dev_t rdev)
300: {
301: struct stat sb;
302: size_t len;
303: char buf[PATH_MAX], **sd, *devname, *tty = NULL;
304: debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
305:
306: /*
1.1.1.3 ! misho 307: * First check search_devs for common tty devices.
1.1 misho 308: */
1.1.1.3 ! misho 309: for (sd = search_devs; tty == NULL && (devname = *sd) != NULL; sd++) {
1.1 misho 310: len = strlen(devname);
311: if (devname[len - 1] == '/') {
312: if (strcmp(devname, "/dev/pts/") == 0) {
1.1.1.3 ! misho 313: /* Special case /dev/pts */
1.1 misho 314: (void)snprintf(buf, sizeof(buf), "%spts/%u", _PATH_DEV,
315: (unsigned int)minor(rdev));
316: if (stat(buf, &sb) == 0) {
1.1.1.3 ! misho 317: if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev)
1.1 misho 318: tty = estrdup(buf);
319: }
1.1.1.3 ! misho 320: sudo_debug_printf(SUDO_DEBUG_INFO, "comparing dev %u to %s: %s",
! 321: (unsigned int)rdev, buf, tty ? "yes" : "no");
! 322: } else {
! 323: /* Traverse directory */
! 324: tty = sudo_ttyname_scan(devname, rdev, true);
1.1 misho 325: }
326: } else {
327: if (stat(devname, &sb) == 0) {
1.1.1.3 ! misho 328: if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev)
1.1 misho 329: tty = estrdup(devname);
330: }
331: }
332: }
333:
334: /*
335: * Not found? Do a breadth-first traversal of /dev/.
336: */
337: if (tty == NULL)
338: tty = sudo_ttyname_scan(_PATH_DEV, rdev, false);
339:
340: debug_return_str(tty);
341: }
342: #endif
343:
344: #if defined(sudo_kp_tdev)
345: /*
346: * Return a string from ttyname() containing the tty to which the process is
1.1.1.3 ! misho 347: * attached or NULL if the process has no controlling tty.
1.1 misho 348: */
349: char *
350: get_process_ttyname(void)
351: {
352: char *tty = NULL;
353: struct sudo_kinfo_proc *ki_proc = NULL;
354: size_t size = sizeof(*ki_proc);
1.1.1.3 ! misho 355: int mib[6], rc;
1.1 misho 356: debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
357:
358: /*
1.1.1.3 ! misho 359: * Lookup controlling tty for this process via sysctl.
! 360: * This will work even if std{in,out,err} are redirected.
1.1 misho 361: */
1.1.1.3 ! misho 362: mib[0] = CTL_KERN;
! 363: mib[1] = SUDO_KERN_PROC;
! 364: mib[2] = KERN_PROC_PID;
! 365: mib[3] = (int)getpid();
! 366: mib[4] = sizeof(*ki_proc);
! 367: mib[5] = 1;
! 368: do {
! 369: size += size / 10;
! 370: ki_proc = erealloc(ki_proc, size);
! 371: rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0);
! 372: } while (rc == -1 && errno == ENOMEM);
! 373: if (rc != -1) {
! 374: if (ki_proc->sudo_kp_tdev != (dev_t)-1) {
! 375: tty = sudo_ttyname_dev(ki_proc->sudo_kp_tdev);
! 376: if (tty == NULL) {
! 377: sudo_debug_printf(SUDO_DEBUG_WARN,
! 378: "unable to map device number %u to name",
! 379: ki_proc->sudo_kp_tdev);
1.1 misho 380: }
381: }
1.1.1.3 ! misho 382: } else {
! 383: sudo_debug_printf(SUDO_DEBUG_WARN,
! 384: "unable to resolve tty via KERN_PROC: %s", strerror(errno));
1.1 misho 385: }
386: efree(ki_proc);
387:
388: debug_return_str(tty);
389: }
390: #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV)
391: /*
392: * Return a string from ttyname() containing the tty to which the process is
1.1.1.3 ! misho 393: * attached or NULL if the process has no controlling tty.
1.1 misho 394: */
395: char *
396: get_process_ttyname(void)
397: {
398: char path[PATH_MAX], *tty = NULL;
399: struct psinfo psinfo;
400: ssize_t nread;
1.1.1.3 ! misho 401: int fd;
1.1 misho 402: debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
403:
404: /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
1.1.1.3 ! misho 405: snprintf(path, sizeof(path), "/proc/%u/psinfo", (unsigned int)getpid());
! 406: if ((fd = open(path, O_RDONLY, 0)) != -1) {
1.1 misho 407: nread = read(fd, &psinfo, sizeof(psinfo));
408: close(fd);
1.1.1.3 ! misho 409: if (nread == (ssize_t)sizeof(psinfo)) {
! 410: dev_t rdev = (dev_t)psinfo.pr_ttydev;
! 411: #if defined(_AIX) && defined(DEVNO64)
! 412: if (psinfo.pr_ttydev & DEVNO64)
! 413: rdev = makedev(major64(psinfo.pr_ttydev), minor64(psinfo.pr_ttydev));
! 414: #endif
! 415: if (rdev != (dev_t)-1)
! 416: tty = sudo_ttyname_dev(rdev);
1.1 misho 417: }
418: }
419:
420: debug_return_str(tty);
421: }
422: #elif defined(__linux__)
423: /*
424: * Return a string from ttyname() containing the tty to which the process is
1.1.1.3 ! misho 425: * attached or NULL if the process has no controlling tty.
1.1 misho 426: */
427: char *
428: get_process_ttyname(void)
429: {
1.1.1.3 ! misho 430: char path[PATH_MAX], *line = NULL, *tty = NULL;
1.1 misho 431: size_t linesize = 0;
432: ssize_t len;
1.1.1.3 ! misho 433: FILE *fp;
1.1 misho 434: debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
435:
1.1.1.3 ! misho 436: /* Try to determine the tty from tty_nr in /proc/pid/stat. */
! 437: snprintf(path, sizeof(path), "/proc/%u/stat", (unsigned int)getpid());
! 438: if ((fp = fopen(path, "r")) != NULL) {
1.1 misho 439: len = getline(&line, &linesize, fp);
440: fclose(fp);
441: if (len != -1) {
442: /* Field 7 is the tty dev (0 if no tty) */
443: char *cp = line;
444: int field = 1;
445: while (*cp != '\0') {
446: if (*cp++ == ' ') {
447: if (++field == 7) {
448: dev_t tdev = (dev_t)atoi(cp);
449: if (tdev > 0)
450: tty = sudo_ttyname_dev(tdev);
451: break;
452: }
453: }
454: }
455: }
1.1.1.3 ! misho 456: efree(line);
1.1 misho 457: }
458:
1.1.1.3 ! misho 459: debug_return_str(tty);
! 460: }
! 461: #elif HAVE_PSTAT_GETPROC
! 462: /*
! 463: * Return a string from ttyname() containing the tty to which the process is
! 464: * attached or NULL if the process has no controlling tty.
! 465: */
! 466: char *
! 467: get_process_ttyname(void)
! 468: {
! 469: struct pst_status pstat;
! 470: char *tty = NULL;
! 471: debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
1.1 misho 472:
1.1.1.3 ! misho 473: /* Try to determine the tty from psdev in struct pst_status. */
! 474: if (pstat_getproc(&pstat, sizeof(pstat), 0, (int)getpid()) != -1) {
! 475: if (pstat.pst_term.psd_major != -1 && pstat.pst_term.psd_minor != -1) {
! 476: tty = sudo_ttyname_dev(makedev(pstat.pst_term.psd_major,
! 477: pstat.pst_term.psd_minor));
! 478: }
! 479: }
1.1 misho 480: debug_return_str(tty);
481: }
482: #else
483: /*
484: * Return a string from ttyname() containing the tty to which the process is
1.1.1.3 ! misho 485: * attached or NULL if the process has no controlling tty.
1.1 misho 486: */
487: char *
488: get_process_ttyname(void)
489: {
490: char *tty;
491: debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
492:
493: if ((tty = ttyname(STDIN_FILENO)) == NULL) {
494: if ((tty = ttyname(STDOUT_FILENO)) == NULL)
495: tty = ttyname(STDERR_FILENO);
496: }
497:
498: debug_return_str(estrdup(tty));
499: }
500: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>