#ifdef HAVE_CONFIG_H #include #endif #include #include #include /* * getlogin/getlogin_r: the login name associated with the calling * process — the user who owns its controlling terminal. * * POSIX.1-2008: getlogin() returns a pointer to the login name, or NULL * when the process is not associated with a terminal; getlogin_r may * fail with ENOTTY when the calling process has no controlling terminal. * The "controlling terminal" is the terminal of the session the process * belongs to; Linux reports it as field 7 (tty_nr) of /proc/self/stat * (0 when the process has none). * * Resolving the NAME of that terminal's user requires the session/utmp * database, which this libc does not yet carry (the passwd/group todo * owns /etc/passwd and the session records). This stage therefore never * fabricates a name: it distinguishes the no-terminal case (ENOTTY, * fully specified by POSIX) from the has-a-terminal-but-no-database * case (ENOENT), and getlogin() reports both as NULL with errno set. * The name resolution is completed by the session-database todo. */ /* Static buffer for getlogin(); 256 bytes comfortably holds a login name. */ #define VLIBC_UNISTD_LOGIN_BUF 256 static char getlogin_buf[VLIBC_UNISTD_LOGIN_BUF]; /* * Read field 7 (tty_nr) of /proc/self/stat. After the parenthesized * comm field — located by scanning back from the end for the last ')' — * the fields run state(3) ppid(4) pgrp(5) session(6) tty_nr(7), so * tty_nr is the token after four more whitespace-separated tokens. * Returns 1 and the value when the file could be read, 0 otherwise. */ static int login_tty_nr(long *tty_out) { char buf[512]; char *p; ssize_t n; int fd; int tok; long value = 0; int sign = 1; int have_digit = 0; fd = open("/proc/self/stat", O_RDONLY); if (fd < 0) { return 0; } n = read(fd, buf, sizeof(buf) - 1); close(fd); if (n <= 0) { return 0; } buf[n] = '\0'; p = buf + n; while (p > buf && *p != ')') { p--; } if (*p != ')') { return 0; } p++; for (tok = 0; tok < 4; tok++) { while (*p == ' ' || *p == '\t') { p++; } while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '\n') { p++; } if (*p == '\0') { return 0; } } while (*p == ' ' || *p == '\t') { p++; } if (*p == '-') { sign = -1; p++; } while (*p >= '0' && *p <= '9') { value = value * 10 + (*p - '0'); p++; have_digit = 1; } if (!have_digit) { return 0; } *tty_out = sign * value; return 1; } int getlogin_r(char *name, size_t namesize) { long tty_nr; if (name == NULL || namesize == 0) { errno = EINVAL; return EINVAL; } if (!login_tty_nr(&tty_nr) || tty_nr == 0) { /* No controlling terminal (or procfs cannot be read). */ errno = ENOTTY; return ENOTTY; } /* A controlling terminal exists, but no session database to resolve * its owner's login name yet — never fabricate one. */ errno = ENOENT; return ENOENT; } char * getlogin(void) { int err = getlogin_r(getlogin_buf, sizeof(getlogin_buf)); if (err != 0) { errno = err; return NULL; } return getlogin_buf; }