150 lines
3.4 KiB
C
150 lines
3.4 KiB
C
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
/*
|
|
* 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;
|
|
}
|