☰
SerenityOS whoami 命令完全指南:从使用到 LibC 底层实现
2026/10/10 10:13:50 网站建设 项目流程

SerenityOS whoami 命令完全指南:从使用到 LibC 底层实现

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

whoami是 SerenityOS 中最基础也最常用的命令之一,用于打印与当前有效 UID(Effective UID)关联的用户名。本文以 whoami 手册页 为核心,结合Userland/Utilities/whoami.cpp源码与 LibC 层getlogin()的实现,从命令行用法、权限模型到源码级原理逐层展开,读完后你不仅能熟练使用该命令,还能理解 SerenityOS 中"进程身份 → 用户名"的完整解析链路。

命令概览:功能与语法

手册页中对whoami的定位非常明确:

whoami - print effective user name(打印有效用户名)

其语法极简,不接受任何参数与选项:

$ whoami

在 SerenityOS 中,whoami的实际作用是输出与当前有效 UID相关联的用户名。这里的"有效 UID"与真实 UID 相对:当程序以 setuid 方式运行或进程主动调用身份切换接口(如setuid、seteuid)后,有效 UID 可能与启动时的真实 UID 不同,而whoami始终以有效身份为准作答。

与它功能互补的是id(1) 命令,后者能够同时输出 UID、GID 及全部附加组信息。从两个手册页的 "See also" 交叉引用可以看出,二者在 SerenityOS 的 man 体系中是一组紧密关联的身份查询工具。

实战示例:普通用户与超级用户

手册页给出了两个典型运行场景:

$ whoami anon # whoami root
  • 在默认登录为anon用户的图形会话或终端中,whoami输出anon,对应 UID 100(参见id(1) 示例中的uid=100(anon))。
  • 在以root(UID 0)身份运行的 Shell 中(例如通过su切换或从 root 会话启动),输出root。

这一对比清晰展示了whoami在权限调试中的价值:当不确定当前进程实际以何种身份运行、或者怀疑 setuid 程序是否生效时,执行whoami即可立即得到有效身份的答案。

源码实现:一个极简但安全的工具

whoami的实现位于 Userland/Utilities/whoami.cpp,完整代码仅有十余行:

#include <LibCore/System.h> #include <LibMain/Main.h> #include <stdio.h> #include <unistd.h> ErrorOr<int> serenity_main(Main::Arguments) { TRY(Core::System::pledge("stdio rpath")); TRY(Core::System::unveil("/etc/passwd", "r")); TRY(Core::System::unveil(nullptr, nullptr)); puts(getlogin()); return 0; }

这段代码展示了 SerenityOS 用户态工具的标准写法,其中包含三个值得深入的技术点:

1. 入口约定:serenity_main

所有 SerenityOS 用户态程序都通过LibMain提供的serenity_main作为入口(而非裸的main),其返回ErrorOr<int>使得TRY宏可以便捷地传播错误。这保证了工具在资源受限、初始化失败时能统一、干净地退出。

2. 安全加固:pledge 与 unveil

  • Core::System::pledge("stdio rpath")声明该进程只承诺使用标准 I/O 与只读路径解析能力;
  • Core::System::unveil("/etc/passwd", "r")将进程可访问的文件系统视野收缩到/etc/passwd这一个文件的只读访问;
  • unveil(nullptr, nullptr)用于锁定上述声明,此后无法再新增任何可见路径。

这两层"承诺 + 揭幕"机制是 SerenityOS 的纵深防御哲学在最小工具上的体现:即使whoami被攻破,攻击者能做的也仅限于读取/etc/passwd并输出文本。

3. 身份查询:getlogin()

关键一行是puts(getlogin())。getlogin()是 LibC(<unistd.h>)提供的接口,其实现位于 Userland/Libraries/LibC/unistd.cpp。从源码(第 876-886 行附近)可以看到,它内部通过查询用户数据库(getpwuid路径)取得当前有效 UID 对应的passwd记录,再返回其中的pw_name字段,并使用静态缓冲区缓存结果:

static Optional<ByteString> getlogin_buffer {}; char* getlogin() { if (!getlogin_buffer.has_value()) { // ... 解析 /etc/passwd 中与当前有效 UID 匹配的条目 getlogin_buffer = ByteString(passwd->pw_name); } return const_cast<char*>(getlogin_buffer->characters()); }

这正是whoami与id -un行为一致的底层原因:两者最终都归结于"有效 UID → passwd 条目 → 用户名"的同一条解析链路。同时,/etc/passwd的只读权限(普通用户无法修改)保证了用户名查询结果的可靠性。

常见问题与使用注意

  1. 为什么whoami不接受--help之类的选项?手册页的 Synopsis 中明确只允许裸调用,这是刻意保持的最小化设计。若需要更丰富的身份信息,请使用id(1)(如id -un打印用户名、id -u打印 UID、id -g打印 GID)。

  2. 普通用户能查询到 root 吗?不能。whoami只反映当前进程自身的有效身份,它不会也不应该泄露其他用户的信息;要改变身份,需要借助su/setuid等提权途径。

  3. 输出内容来自哪个文件?用户名解析基于/etc/passwd(这正是源码中unveil只放行该文件的原因)。SerenityOS 默认的常规用户为anon,超级用户为root。

小结

whoami虽然只有一行输出,却是理解 SerenityOS 进程身份模型的最佳入口:通过 手册页 掌握用法,通过 whoami.cpp 学习标准工具骨架与 pledge/unveil 安全实践,再通过 LibC 的getlogin()深入"有效 UID → 用户名"的解析实现,三者串联起来便构成了一个完整、可验证的权限查询知识闭环。

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询