首页 > 解决方案 > `getlogin()` 是通过读取`/var/run/utmp` 来实现的吗?

问题描述

来自https://unix.stackexchange.com/a/268388/674

logname上升拥有 的用户tty(通过从 中读取/var/run/utmp

coreutils 的源代码中,我发现它logname.c是基于 Linux API 函数实现的getlogin

#include <unistd.h>
char *getlogin(void);

/var/run/tmplogname.c.

getlogin()通过读取来实现的/var/run/utmp吗?

谢谢。


在我的 Lubuntu 18.04 上,strace logname是否输出:

openat(AT_FDCWD, "/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3

标签: clinuxgnu-coreutils

解决方案


找出是否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

(例如,如果您用来模拟另一个用户,则 in 的值/proc/self/loginuid将与返回的值不同;仍然为我打印“zack”。)getuidsusu root -c logname

但是,如果我无法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,那么它会花费一定的时间来查找与其自己的标准输入相关联的终端名称(简单的方法,readlink("/proc/self/fd/0")不起作用,因为我为这个测试全部涂白了)/proc然后它看起来/var/run/utmp首先确定 /var/run/utmpx不存在之后。(这是一个错误;测试您要打开的文件是否已经存在具有固有的TOCTOU 竞赛。它应该打开它并检查是否打开失败。)它没有找到条目,所以它失败了。这是因为我使用的 Linux 发行版(sp。Debian 不稳定)已经决定每个终端窗口都有自己的 utmp 条目是愚蠢的,我的整个 X 会话应该只有一个,无论我是否打开任何终端:

$ who
zack     :0           2018-05-30 17:37 (:0)

我认为这是一种与传统的决裂,但也是一种明智的做法。


推荐阅读