From: Todd C. Miller Date: Wed, 23 May 2012 16:46:39 +0000 (-0400) Subject: Rototill code to determine the tty. For Linux, we now look up the X-Git-Tag: SUDO_1_7_10~96 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8ff1629ff050aedb6ca1c3688ae0b435d3e62a50;p=sudo Rototill code to determine the tty. For Linux, we now look up the tty device in /proc/pid/stat instead of trying to open /proc/pid/fd/[0-2]. The sudo_ttyname_dev() function maps the given device number to a string. On BSD, we can use devname(). On Solaris, _ttyname_dev() does what we want. For others we do a breadth-first search of /dev. --HG-- branch : 1.7 --- diff --git a/config.h.in b/config.h.in index e3d38cb16..652f48ce9 100644 --- a/config.h.in +++ b/config.h.in @@ -389,6 +389,9 @@ /* Define to 1 if you have the `posix_openpt' function. */ #undef HAVE_POSIX_OPENPT +/* Define to 1 if you have the header file. */ +#undef HAVE_PROCFS_H + /* Define to 1 if you have the header file. */ #undef HAVE_PROJECT_H @@ -534,6 +537,9 @@ /* Define to 1 if `p_tdev' is a member of `struct kinfo_proc'. */ #undef HAVE_STRUCT_KINFO_PROC_P_TDEV +/* Define to 1 if `pr_ttydev' is a member of `struct psinfo'. */ +#undef HAVE_STRUCT_PSINFO_PR_TTYDEV + /* Define to 1 if the system has the type `struct timespec'. */ #undef HAVE_STRUCT_TIMESPEC @@ -563,6 +569,9 @@ */ #undef HAVE_SYS_NDIR_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PROCFS_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SELECT_H @@ -630,6 +639,9 @@ /* Define to 1 if you have the `_innetgr' function. */ #undef HAVE__INNETGR +/* Define to 1 if you have the `_ttyname_dev' function. */ +#undef HAVE__TTYNAME_DEV + /* Define to 1 if your crt0.o defines the __progname symbol for you. */ #undef HAVE___PROGNAME @@ -664,6 +676,14 @@ /* The user or email address that sudo mail is sent to. */ #undef MAILTO +/* Define to 1 if `major', `minor', and `makedev' are declared in . + */ +#undef MAJOR_IN_MKDEV + +/* Define to 1 if `major', `minor', and `makedev' are declared in + . */ +#undef MAJOR_IN_SYSMACROS + /* The max number of chars per log file line (for line wrapping). */ #undef MAXLOGFILELEN diff --git a/configure b/configure index f8332105a..1b80062b3 100755 --- a/configure +++ b/configure @@ -2087,6 +2087,63 @@ fi } # ac_fn_c_check_header_mongrel +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member + # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache @@ -2319,63 +2376,6 @@ rm -f conftest.val } # ac_fn_c_compute_int -# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES -# ---------------------------------------------------- -# Tries to find if the field MEMBER exists in type AGGR, after including -# INCLUDES, setting cache variable VAR accordingly. -ac_fn_c_check_member () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 -$as_echo_n "checking for $2.$3... " >&6; } -if eval \${$4+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (sizeof ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - eval "$4=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$4 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_member - # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR @@ -15161,6 +15161,56 @@ $as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/types.h defines makedev" >&5 +$as_echo_n "checking whether sys/types.h defines makedev... " >&6; } +if ${ac_cv_header_sys_types_h_makedev+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +return makedev(0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_header_sys_types_h_makedev=yes +else + ac_cv_header_sys_types_h_makedev=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_types_h_makedev" >&5 +$as_echo "$ac_cv_header_sys_types_h_makedev" >&6; } + +if test $ac_cv_header_sys_types_h_makedev = no; then +ac_fn_c_check_header_mongrel "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_mkdev_h" = xyes; then : + +$as_echo "#define MAJOR_IN_MKDEV 1" >>confdefs.h + +fi + + + + if test $ac_cv_header_sys_mkdev_h = no; then + ac_fn_c_check_header_mongrel "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_sysmacros_h" = xyes; then : + +$as_echo "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h + +fi + + + fi +fi + for ac_header in malloc.h netgroup.h paths.h spawn.h utime.h sys/sockio.h sys/bsdtypes.h sys/select.h sys/stropts.h sys/sysmacros.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` @@ -15174,6 +15224,47 @@ fi done +for ac_header in procfs.h sys/procfs.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + ac_fn_c_check_member "$LINENO" "struct psinfo" "pr_ttydev" "ac_cv_member_struct_psinfo_pr_ttydev" "$ac_includes_default +#ifdef HAVE_PROCFS_H +#include +#endif +#ifdef HAVE_SYS_PROCFS_H +#include +#endif + +" +if test "x$ac_cv_member_struct_psinfo_pr_ttydev" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_PSINFO_PR_TTYDEV 1 +_ACEOF + +for ac_func in _ttyname_dev +do : + ac_fn_c_check_func "$LINENO" "_ttyname_dev" "ac_cv_func__ttyname_dev" +if test "x$ac_cv_func__ttyname_dev" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE__TTYNAME_DEV 1 +_ACEOF + +fi +done + +fi + +break +fi + +done + # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; diff --git a/configure.in b/configure.in index 89ea2dea2..7407550f4 100644 --- a/configure.in +++ b/configure.in @@ -1941,7 +1941,17 @@ dnl AC_HEADER_STDC AC_HEADER_DIRENT AC_HEADER_TIME +AC_HEADER_MAJOR AC_CHECK_HEADERS(malloc.h netgroup.h paths.h spawn.h utime.h sys/sockio.h sys/bsdtypes.h sys/select.h sys/stropts.h sys/sysmacros.h) +AC_CHECK_HEADERS([procfs.h] [sys/procfs.h], [AC_CHECK_MEMBERS(struct psinfo.pr_ttydev, [AC_CHECK_FUNCS(_ttyname_dev)], [], [AC_INCLUDES_DEFAULT +#ifdef HAVE_PROCFS_H +#include +#endif +#ifdef HAVE_SYS_PROCFS_H +#include +#endif +])] +break) dnl dnl Check for large file support. HP-UX 11.23 has a broken sys/type.h dnl when large files support is enabled so work around it. diff --git a/ttyname.c b/ttyname.c index e27b3f89d..6e6a03831 100644 --- a/ttyname.c +++ b/ttyname.c @@ -16,9 +16,20 @@ #include +/* Large files not supported by procfs.h */ +#if defined(HAVE_PROCFS_H) || defined(HAVE_SYS_PROCFS_H) +# undef _FILE_OFFSET_BITS +# undef _LARGE_FILES +#endif + #include #include #include +#if defined(MAJOR_IN_MKDEV) +# include +#elif defined(MAJOR_IN_SYSMACROS) +# include +#endif #include #ifdef STDC_HEADERS # include @@ -42,12 +53,34 @@ #endif /* HAVE_UNISTD_H */ #include #include +#include +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) || defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV) # include #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV) # include # include #endif +#if defined(HAVE_PROCFS_H) +# include +#elif defined(HAVE_SYS_PROCFS_H) +# include +#endif #include "sudo.h" @@ -76,7 +109,227 @@ # define sudo_kp_namelen 4 #endif -#ifdef sudo_kp_tdev +#if defined(sudo_kp_tdev) +/* + * Like ttyname() but uses a dev_t instead of an open fd. + * Caller is responsible for freeing the returned string. + * The BSD version uses devname() + */ +static char * +sudo_ttyname_dev(dev_t tdev) +{ + char *dev, *tty = NULL; + + /* Some versions of devname() return NULL on failure, others do not. */ + dev = devname(tdev, S_IFCHR); + if (dev != NULL && *dev != '?' && *dev != '#') { + if (*dev != '/') { + /* devname() doesn't use the /dev/ prefix, add one... */ + size_t len = sizeof(_PATH_DEV) + strlen(dev); + tty = emalloc(len); + strlcpy(tty, _PATH_DEV, len); + strlcat(tty, dev, len); + } else { + /* Should not happen but just in case... */ + tty = estrdup(dev); + } + } + return tty; +} +#elif defined(HAVE__TTYNAME_DEV) +extern char *_ttyname_dev(dev_t rdev, char *buffer, size_t buflen); + +/* + * Like ttyname() but uses a dev_t instead of an open fd. + * Caller is responsible for freeing the returned string. + * This version is just a wrapper around _ttyname_dev(). + */ +static char * +sudo_ttyname_dev(dev_t tdev) +{ + char buf[TTYNAME_MAX], *tty; + + tty = _ttyname_dev(tdev, buf, sizeof(buf)); + + return estrdup(tty); +} +#else +/* + * Devices to search before doing a breadth-first scan. + */ +static char *search_devs[] = { + "/dev/console", + "/dev/wscons", + "/dev/pts/", + "/dev/vt/", + "/dev/term/", + "/dev/zcons/", + NULL +}; + +static char *ignore_devs[] = { + "/dev/fd/", + "/dev/stdin", + "/dev/stdout", + "/dev/stderr", + NULL +}; + +/* + * Do a breadth-first scan of dir looking for the specified device. + */ +static +char *sudo_ttyname_scan(dir, rdev, builtin) + const char *dir; + dev_t rdev; + int builtin; +{ + DIR *d; + char pathbuf[PATH_MAX], **subdirs = NULL, *devname = NULL; + size_t sdlen, d_len, len, num_subdirs = 0, max_subdirs = 0; + struct dirent *dp; + struct stat sb; + int i; + + if (dir[0] == '\0' || (d = opendir(dir)) == NULL) + goto done; + + sdlen = strlen(dir); + if (dir[sdlen - 1] == '/') + sdlen--; + if (sdlen + 1 >= sizeof(pathbuf)) { + errno = ENAMETOOLONG; + warning("%.*s/", (int)sdlen, dir); + goto done; + } + memcpy(pathbuf, dir, sdlen); + pathbuf[sdlen++] = '/'; + pathbuf[sdlen] = '\0'; + + while ((dp = readdir(d)) != NULL) { + /* Skip anything starting with "." */ + if (dp->d_name[0] == '.') + continue; + + d_len = NAMLEN(dp); + if (sdlen + d_len >= sizeof(pathbuf)) + continue; + memcpy(&pathbuf[sdlen], dp->d_name, d_len + 1); /* copy NUL too */ + d_len += sdlen; + + for (i = 0; ignore_devs[i] != NULL; i++) { + len = strlen(ignore_devs[i]); + if (ignore_devs[i][len - 1] == '/') + len--; + if (d_len == len && strncmp(pathbuf, ignore_devs[i], len) == 0) + break; + } + if (ignore_devs[i] != NULL) + continue; + if (!builtin) { + /* Skip entries in search_devs; we already checked them. */ + for (i = 0; search_devs[i] != NULL; i++) { + len = strlen(search_devs[i]); + if (search_devs[i][len - 1] == '/') + len--; + if (d_len == len && strncmp(pathbuf, search_devs[i], len) == 0) + break; + } + if (search_devs[i] != NULL) + continue; + } +# if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF) + /* Use d_type to avoid a stat() if possible. */ + /* Convert d_type to stat-style type bits but follow links. */ + if (dp->d_type != DT_LNK && dp->d_type != DT_CHR) + sb.st_mode = DTTOIF(dp->d_type); + else +# endif + if (stat(pathbuf, &sb) == -1) + continue; + if (S_ISDIR(sb.st_mode)) { + if (!builtin) { + /* Add to list of subdirs to search. */ + if (num_subdirs + 1 > max_subdirs) { + max_subdirs += 64; + subdirs = erealloc3(subdirs, max_subdirs, sizeof(char *)); + } + subdirs[num_subdirs++] = estrdup(pathbuf); + } + continue; + } + if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) { + devname = estrdup(pathbuf); + break; + } + } + closedir(d); + + /* Search subdirs if we didn't find it in the root level. */ + for (i = 0; devname == NULL && i < num_subdirs; i++) + devname = sudo_ttyname_scan(subdirs[i], rdev, false); + +done: + for (i = 0; i < num_subdirs; i++) + efree(subdirs[i]); + efree(subdirs); + return devname; +} + +/* + * Like ttyname() but uses a dev_t instead of an open fd. + * Caller is responsible for freeing the returned string. + * Generic version. + */ +static char * +sudo_ttyname_dev(rdev) + dev_t rdev; +{ + struct stat sb; + size_t len; + char buf[PATH_MAX], **sd, *devname, *tty = NULL; + + /* + * First check search_devs. + */ + for (sd = search_devs; (devname = *sd) != NULL; sd++) { + len = strlen(devname); + if (devname[len - 1] == '/') { + /* Special case /dev/pts */ + if (strcmp(devname, "/dev/pts/") == 0) { + (void)snprintf(buf, sizeof(buf), "%spts/%u", _PATH_DEV, + (unsigned int)minor(rdev)); + if (stat(buf, &sb) == 0) { + if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) { + tty = estrdup(buf); + break; + } + } + continue; + } + /* Traverse directory */ + tty = sudo_ttyname_scan(devname, rdev, 1); + } else { + if (stat(devname, &sb) == 0) { + if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) { + tty = estrdup(devname); + break; + } + } + } + } + + /* + * Not found? Do a breadth-first traversal of /dev/. + */ + if (tty == NULL) + tty = sudo_ttyname_scan(_PATH_DEV, rdev, 0); + + return tty; +} +#endif + +#if defined(sudo_kp_tdev) /* * 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 @@ -108,19 +361,8 @@ get_process_ttyname() rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0); } while (rc == -1 && errno == ENOMEM); if (rc != -1) { - char *dev = devname(ki_proc->sudo_kp_tdev, S_IFCHR); - /* Some versions of devname() return NULL, others do not. */ - if (dev != NULL && *dev != '?' && *dev != '#') { - if (*dev != '/') { - /* devname() doesn't use the /dev/ prefix, add one... */ - size_t len = sizeof(_PATH_DEV) + strlen(dev); - tty = emalloc(len); - strlcpy(tty, _PATH_DEV, len); - strlcat(tty, dev, len); - } else { - /* Should not happen but just in case... */ - tty = estrdup(dev); - } + if (ki_proc->sudo_kp_tdev != (dev_t)-1) { + tty = sudo_ttyname_dev(ki_proc->sudo_kp_tdev); } } } @@ -136,36 +378,114 @@ get_process_ttyname() return tty; } -#else +#elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) /* * 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 std{in,out,err} then falls back to the parent's /proc - * entry. We could try following the parent all the way to pid 1 but - * /proc/%d/status is system-specific (text on Linux, a struct on Solaris). + * parent). First tries /proc/pid/psinfo, then /proc/ppid/psinfo. + * Falls back on ttyname of std{in,out,err} if that fails. */ char * get_process_ttyname() { char path[PATH_MAX], *tty = NULL; - pid_t ppid; + struct stat sb; + struct psinfo psinfo; + ssize_t nread; int i, fd; - if ((tty = ttyname(STDIN_FILENO)) == NULL && - (tty = ttyname(STDOUT_FILENO)) == NULL && - (tty = ttyname(STDERR_FILENO)) == NULL) { - /* No tty for child, check the parent via /proc. */ - ppid = getppid(); - for (i = STDIN_FILENO; i < STDERR_FILENO && tty == NULL; i++) { - snprintf(path, sizeof(path), "/proc/%d/fd/%d", (int)ppid, i); - fd = open(path, O_RDONLY|O_NOCTTY, 0); - if (fd != -1) { - tty = ttyname(fd); - close(fd); + /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */ + for (i = 0; tty == NULL && i < 2; i++) { + (void)snprintf(path, sizeof(path), "/proc/%u/psinfo", + i ? (unsigned int)getppid() : (unsigned int)getpid()); + if ((fd = open(path, O_RDONLY, 0)) == -1) + continue; + nread = read(fd, &psinfo, sizeof(psinfo)); + close(fd); + if (nread == (ssize_t)sizeof(psinfo) && psinfo.pr_ttydev != (dev_t)-1) { + tty = sudo_ttyname_dev(psinfo.pr_ttydev); + } + } + + /* If all else fails, fall back on ttyname(). */ + if (tty == NULL) { + if ((tty = ttyname(STDIN_FILENO)) != NULL || + (tty = ttyname(STDOUT_FILENO)) != NULL || + (tty = ttyname(STDERR_FILENO)) != NULL) + tty = estrdup(tty); + } + + return tty; +} +#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. + */ +char * +get_process_ttyname() +{ + char *line = NULL, *tty = NULL; + size_t linesize = 0; + ssize_t len; + int i; + + /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */ + 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) { + /* Field 7 is the tty dev (0 if no tty) */ + char *cp = line; + int field = 1; + while (*cp != '\0') { + if (*cp++ == ' ') { + if (++field == 7) { + dev_t tdev = (dev_t)atoi(cp); + if (tdev > 0) + tty = sudo_ttyname_dev(tdev); + break; + } + } } } } + efree(line); + + /* If all else fails, fall back on ttyname(). */ + if (tty == NULL) { + if ((tty = ttyname(STDIN_FILENO)) != NULL || + (tty = ttyname(STDOUT_FILENO)) != NULL || + (tty = ttyname(STDERR_FILENO)) != NULL) + tty = estrdup(tty); + } + + return tty; +} +#else +/* + * 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. + * parent). + */ +char * +get_process_ttyname() +{ + char *tty; + + if ((tty = ttyname(STDIN_FILENO)) == NULL) { + if ((tty = ttyname(STDOUT_FILENO)) == NULL) + tty = ttyname(STDERR_FILENO); + } return estrdup(tty); } -#endif /* sudo_kp_tdev */ +#endif