File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / ttyname.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 9 09:29:52 2012 UTC (11 years, 9 months ago) by misho
Branches: sudo, MAIN
CVS tags: HEAD
sudo

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>