`getlogin()` 是通过读取`/var/run/utmp` 实现的吗?
Is `getlogin()` implemented by reading from `/var/run/utmp`?
来自https://unix.stackexchange.com/a/268388/674
logname
goes up the user that owns the tty
(by reading it from /var/run/utmp
)
在the source code of coreutils中,我发现logname.c
是基于LinuxAPI函数getlogin
:
实现的
#include <unistd.h>
char *getlogin(void);
我在 logname.c
中找不到 /var/run/tmp
。
getlogin()
是从/var/run/utmp
读取实现的吗?
谢谢。
在我的 Lubuntu 18.04 上,strace logname
输出:
openat(AT_FDCWD, "/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3
glibc code is a bit of a maze. But the easy way to determine what any program does at the userspace-kernel interface, which includes any reading of files, is the strace
实用程序。
刚才我运行strace logname
时,包含输出:
open("/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3
lseek(3, 0, SEEK_SET) = 0
...
read(3, "[=11=][=11=][=11=][=11=][=11=][=11=][=11=]~[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384
read(3, "[=11=][=11=][=11=]05[=11=][=11=][=11=]~[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384
read(3, "[=11=][=11=][=11=]2[=11=][=11=]tty1[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384
read(3, "[=11=][=11=][=11=]0[=11=][=11=]tty7[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384
read(3, "", 384) = 0
...
close(3) = 0
所以是的,/usr/bin/logname
从文件 /var/run/utmp
中读取。
getlogin
不是 Linux API 函数而是 libc 函数,在 GNU/Linux 和 glibc 上它是 implemented here:
/* Try to determine login name from /proc/self/loginuid and return 0
if successful. If /proc/self/loginuid cannot be read return -1.
Otherwise return the error number. */
int
attribute_hidden
__getlogin_r_loginuid (char *name, size_t namesize)
{
int fd = __open_nocancel ("/proc/self/loginuid", O_RDONLY);
[...]
从strace
也可以看出这一点:
openat(AT_FDCWD, "/proc/self/loginuid", O_RDONLY) = 3
read(3, "1000", 12) = 4
close(3) = 0
所以不,/usr/bin/logname
没有从文件 /var/run/utmp
中读取。
找出 logname
是否查看 /var/run/utmp
的最简单方法是 运行 它位于 strace
下,如下所示:
$ strace -e trace=open,openat logname 2>&1 | grep -Ev '\.so\.[0-9]+", O_RD'
-e trace=open,openat
部分使它只打印对 open
(和 openat
,glibc 非常喜欢在内部使用)的调用,而 grep
过滤掉共享库。这减少了输出,以至于我实际上可以连贯地解释它:
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/proc/self/loginuid", O_RDONLY) = 3
openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
zack
+++ exited with 0 +++
所以,在我的系统上,它打开的文件是 /etc/ld.so.cache
和 /usr/lib/locale/locale-archive
,这两个文件都包含与 getlogin
无关的通用数据; /proc/self/loginuid
,其中似乎包含我的用户 ID,我不知道记录在哪里; /etc/nsswitch.conf
,它告诉 C 库在哪里查找用户 ID 到名称的映射;和 /etc/passwd
,其中(在我的系统上)包含该映射。它没有在任何时候查看/var/run/utmp
。
(例如,如果您使用 su
模拟另一个用户,/proc/self/loginuid
中的值将不同于 getuid
返回的值;su root -c logname
仍然打印"zack" 对我来说。)
但是,如果我让 logname
无法查看 /proc/self/loginuid
(通过在 /proc
上临时绑定挂载一个空目录),那么我会得到一些不同的东西。 (我在这里有点作弊:我 运行 strace
没有任何 -e
选项,以便找出哪些系统调用是相关的。我还手动进一步编辑了输出。 )
$ strace -e trace=access,fstat,ioctl,open,openat,readlink,stat logname 2>&1 |
grep -Ev '\.so\.[0-9]+", O_RD'
...
openat(AT_FDCWD, "/proc/self/loginuid", O_RDONLY) = -1 ENOENT
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
readlink("/proc/self/fd/0", 0x7ffd2adfb030, 511) = -1 ENOENT
stat("/dev/pts/", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
openat(AT_FDCWD, "/dev/pts/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat("/dev/pts/1", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
close(3) = 0
access("/var/run/utmpx", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3
...
logname: no login name
+++ exited with 1 +++
如果它无法打开 /proc/self/loginuid
,那么它会花一些时间找到与它自己的 stdin 关联的终端的名称(简单的方法,readlink("/proc/self/fd/0")
,不起作用因为我为这个测试去掉了所有 /proc
)然后它 确实 在 /var/run/utmp
中查找它,在首先确定 /var/run/utmpx
没有之后'不存在。 (这是一个错误;测试你要打开的文件是否已经存在固有的 TOCTOU race。它应该只是打开它并检查打开是否失败。)它没有找到一个条目,所以它失败了.这是因为我使用的 Linux 发行版 (sp. Debian unstable) 已经决定每个终端 window 都有自己的 utmp 条目是愚蠢的,我的整个 X 会话应该只有一个,我是否打开了任何终端:
$ who
zack :0 2018-05-30 17:37 (:0)
打破传统,但我认为是明智的。
来自https://unix.stackexchange.com/a/268388/674
logname
goes up the user that owns thetty
(by reading it from/var/run/utmp
)
在the source code of coreutils中,我发现logname.c
是基于LinuxAPI函数getlogin
:
#include <unistd.h>
char *getlogin(void);
我在 logname.c
中找不到 /var/run/tmp
。
getlogin()
是从/var/run/utmp
读取实现的吗?
谢谢。
在我的 Lubuntu 18.04 上,strace logname
输出:
openat(AT_FDCWD, "/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3
glibc code is a bit of a maze. But the easy way to determine what any program does at the userspace-kernel interface, which includes any reading of files, is the strace
实用程序。
刚才我运行strace logname
时,包含输出:
open("/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3 lseek(3, 0, SEEK_SET) = 0
...
read(3, "[=11=][=11=][=11=][=11=][=11=][=11=][=11=]~[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384 read(3, "[=11=][=11=][=11=]05[=11=][=11=][=11=]~[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384 read(3, "[=11=][=11=][=11=]2[=11=][=11=]tty1[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384 read(3, "[=11=][=11=][=11=]0[=11=][=11=]tty7[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 384) = 384 read(3, "", 384) = 0
...
close(3) = 0
所以是的,/usr/bin/logname
从文件 /var/run/utmp
中读取。
getlogin
不是 Linux API 函数而是 libc 函数,在 GNU/Linux 和 glibc 上它是 implemented here:
/* Try to determine login name from /proc/self/loginuid and return 0
if successful. If /proc/self/loginuid cannot be read return -1.
Otherwise return the error number. */
int
attribute_hidden
__getlogin_r_loginuid (char *name, size_t namesize)
{
int fd = __open_nocancel ("/proc/self/loginuid", O_RDONLY);
[...]
从strace
也可以看出这一点:
openat(AT_FDCWD, "/proc/self/loginuid", O_RDONLY) = 3
read(3, "1000", 12) = 4
close(3) = 0
所以不,/usr/bin/logname
没有从文件 /var/run/utmp
中读取。
找出 logname
是否查看 /var/run/utmp
的最简单方法是 运行 它位于 strace
下,如下所示:
$ strace -e trace=open,openat logname 2>&1 | grep -Ev '\.so\.[0-9]+", O_RD'
-e trace=open,openat
部分使它只打印对 open
(和 openat
,glibc 非常喜欢在内部使用)的调用,而 grep
过滤掉共享库。这减少了输出,以至于我实际上可以连贯地解释它:
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/proc/self/loginuid", O_RDONLY) = 3
openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
zack
+++ exited with 0 +++
所以,在我的系统上,它打开的文件是 /etc/ld.so.cache
和 /usr/lib/locale/locale-archive
,这两个文件都包含与 getlogin
无关的通用数据; /proc/self/loginuid
,其中似乎包含我的用户 ID,我不知道记录在哪里; /etc/nsswitch.conf
,它告诉 C 库在哪里查找用户 ID 到名称的映射;和 /etc/passwd
,其中(在我的系统上)包含该映射。它没有在任何时候查看/var/run/utmp
。
(例如,如果您使用 su
模拟另一个用户,/proc/self/loginuid
中的值将不同于 getuid
返回的值;su root -c logname
仍然打印"zack" 对我来说。)
但是,如果我让 logname
无法查看 /proc/self/loginuid
(通过在 /proc
上临时绑定挂载一个空目录),那么我会得到一些不同的东西。 (我在这里有点作弊:我 运行 strace
没有任何 -e
选项,以便找出哪些系统调用是相关的。我还手动进一步编辑了输出。 )
$ strace -e trace=access,fstat,ioctl,open,openat,readlink,stat logname 2>&1 |
grep -Ev '\.so\.[0-9]+", O_RD'
...
openat(AT_FDCWD, "/proc/self/loginuid", O_RDONLY) = -1 ENOENT
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
readlink("/proc/self/fd/0", 0x7ffd2adfb030, 511) = -1 ENOENT
stat("/dev/pts/", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
openat(AT_FDCWD, "/dev/pts/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat("/dev/pts/1", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
close(3) = 0
access("/var/run/utmpx", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3
...
logname: no login name
+++ exited with 1 +++
如果它无法打开 /proc/self/loginuid
,那么它会花一些时间找到与它自己的 stdin 关联的终端的名称(简单的方法,readlink("/proc/self/fd/0")
,不起作用因为我为这个测试去掉了所有 /proc
)然后它 确实 在 /var/run/utmp
中查找它,在首先确定 /var/run/utmpx
没有之后'不存在。 (这是一个错误;测试你要打开的文件是否已经存在固有的 TOCTOU race。它应该只是打开它并检查打开是否失败。)它没有找到一个条目,所以它失败了.这是因为我使用的 Linux 发行版 (sp. Debian unstable) 已经决定每个终端 window 都有自己的 utmp 条目是愚蠢的,我的整个 X 会话应该只有一个,我是否打开了任何终端:
$ who
zack :0 2018-05-30 17:37 (:0)
打破传统,但我认为是明智的。