一份关于用基于 SQLite 后端的共享库替换旧版二进制系统日志格式 lastlog、btmp、utmp 和 wtmp 的提案(RFC)已发布在 linux-api 邮件列表中,供大家讨论。该提案旨在解决一系列问题,包括 32 位时间计数器的 2038 溢出、缺乏可扩展性、查询性能差以及写入时缺乏原子性等。
目前,用于存储有关会话和身份验证尝试的数据 Linux 使用以下具有固定结构的二进制文件:
- /var/log/lastlog — 上次登录时间(结构体“struct lastlog”,字段“ll_time”为 32 位 time_t 类型);
- /var/log/btmp — 登录失败尝试;
- /var/run/utmp — 当前会话;
- /var/log/wtmp — 登录和注销历史记录。
这些文件的格式是几十年前开发的,存在一些根本性的局限性:
- “utmpx”结构中的“tv_sec”字段和“lastlog”中的“ll_time”字段均为“int32_t”类型,基于这些字段的时间计数器的值将于2038年1月19日溢出。由于ABI兼容性要求,即使在64位系统上,这些字段仍然是32位的,因此该问题将影响所有安装。 Linux.
- 固定的记录大小不允许在不完全更改格式并重新编译所有实用程序的情况下添加新字段(例如容器 ID、服务名称、IP 地址)。
- last、lastb、who 和 lastlog 等工具只能线性遍历文件内容。对于大型日志,如果没有索引来有效过滤记录,I/O 负载和查询延迟将变得无法接受。
- 向二进制文件写入数据并非原子操作。如果写入失败,写入的数据可能会部分损坏。
- 为了避免多个进程(例如 sshd 和 login)同时写入日志时发生冲突,使用了集群锁,但集群锁不能保证原子性,并且可能导致死锁。
RFC 作者提议完全放弃二进制格式,转而使用基于 SQLite 的专用共享库。针对每种日志类型,都将创建一个具有统一 C 接口的独立库:liblastlog2、libbtmp2、libutmp2 和 libwtmp2。所有库都适用于包含 64 位时间戳(INTEGER 类型)以及用户和时间索引的数据库模式。可以通过 ALTER TABLE 语句添加新字段而不会破坏兼容性。
支持使用 SQLite 的一些论点包括:使用 64 位 INTEGER 类型存储纪元时间;使用索引通过选择性访问记录而不是完全扫描来减少 I/O;能够在不更改现有记录的情况下添加新字段;支持 ACID 事务;WAL(预写式日志)模式允许在不使用锁的情况下进行并发访问;以及 SQLite 久经考验的可靠性。
为确保平稳过渡,提出了一种双写策略:
- 写入二进制文件的程序(login、sshd、sudo、cron 等)经过修改,可以通过相应的库同时写入旧的二进制文件和新的 SQLite 数据库。
- 我们正在开发新版本的实用程序(last2、lastb2、who2、lastlog2),它们使用索引从 SQLite 数据库读取数据,从而提高性能。旧版实用程序仍可继续处理相同的文件。
- 几年后,当绝大多数系统都更新换代后,对旧格式录制的支持可能会被禁用,旧的实用程序也可能会被宣布过时。
可供进一步讨论的问题:
- 将库拆分成单独的库还是将它们合并成一个库(例如 libsession2)是明智之举。
- 为库和实用程序选择名称(保留历史名称或改用更通用的名称)。
- 数据库文件的位置(/var/lib/ 用于应用程序状态,/var/log/ 用于日志)。
- 模式版本控制和迁移机制。
- 不同场景下的 SQLite 性能参数(伺服器(嵌入式系统)。
- 为 SQLite 可能过于复杂的系统(例如,内存严重受限的嵌入式设备)提供备用后端,以轻量级二进制格式存储日志。
来源: opennet.ru
