glibc 知:手册29:系统数据库和名称服务开关

1. 前言

The GNU C Library Reference Manual for version 2.35

2. 系统数据库和名称服务开关

System Databases and Name Service Switch

C 库中的各种功能需要配置才能在本地环境中正常工作。传统上,这是通过使用文件(例如,/etc/passwd)来完成的,但是其他名称服务(例如网络信息服务 (NIS) 和域名服务 (DNS))变得流行起来,并且通常被入侵到 C 库中 具有固定的搜索顺序。

GNU C 库包含一个更干净的解决方案来解决这个问题。它是根据 Sun Microsystems 在 Solaris 2 的 C 库中使用的一种方法设计的。GNU C 库沿用了它们的名称,并将此方案称为名称服务开关 (NSS)。

虽然界面可能与 Sun 的版本相似,但没有通用代码。我们从未见过 Sun 实现的任何源代码,因此内部接口不兼容。这也体现在我们使用的文件名中,稍后我们将看到。

2.1. NSS 基础

NSS Basics

基本思想是将提供的用于访问数据库的不同服务的实现放在单独的模块中。这有一些优点:

  • 贡献者可以添加新服务,而无需将它们添加到 GNU C 库中。
  • 模块可以单独更新。
  • C 库映像较小。

为了实现上述第一个目标,下面将描述模块的 ABI。为了正确实现新服务,了解模块中的函数是如何被调用的很重要。它们绝不是为程序员直接使用而设计的。相反,程序员应该只使用文档化和标准化的函数来访问数据库。

NSS 中可用的数据库是

aliases

邮件别名

ethers

以太网号码,

ethers

用户组,请参阅组数据库

gshadow

组密码哈希和相关信息。

hosts

主机名和编号,请参阅主机名

initgroups

补充组访问列表。

netgroup

网络范围的主机和用户列表,请参阅网络组数据库

networks

网络名称和编号,请参阅网络数据库

passwd

用户身份,请参阅用户数据库

protocols

网络协议,请参阅协议数据库

publickey

安全 RPC 的公钥。

rpc

远程过程调用名称和号码。

services

网络服务,请参阅服务数据库

shadow

用户密码哈希和相关信息。

以后可能会添加更多数据库。

2.2. NSS 配置文件

The NSS Configuration File

不知何故,必须将用户的意愿告知 NSS 代码。出于这个原因,有文件 /etc/nsswitch.conf。对于每个数据库,此文件包含查找过程应如何工作的规范。该文件可能如下所示:

# /etc/nsswitch.conf
#
# Name Service Switch configuration file.
#

passwd:     db files
shadow:     files
group:      db files

hosts:      files dns
networks:   files

ethers:     db files
protocols:  db files
rpc:        db files
services:   db files

第一列是数据库,您可以从上表中猜到。该行的其余部分指定查找过程的工作方式。请注意,您单独指定每个数据库的工作方式。这不能用旧的单片实现方式来完成。

每个数据库的配置规范可以包含两个不同的项目:

  • 服务规范,如文件、db 或 nis。
  • 对查找结果的反应,例如 [NOTFOUND=return]。

2.2.1. NSS 配置文件中的服务

Services in the NSS configuration File

上面的示例文件提到了五种不同的服务:files、db、dns、nis 和 nisplus。这并不意味着这些服务在所有站点上都可用,也不意味着这些服务将永远可用。

事实上,这些名称只是 NSS 代码用来查找隐式寻址函数的字符串。稍后将描述内部接口。对用户可见的是实现单个服务的模块。

假设服务名称将用于查找。此服务的代码在名为 libnss_name 的模块中实现。在支持共享库的系统上,这实际上是一个名为(例如)libnss_name.so.2 的共享库。末尾的数字是当前使用的界面版本,不会经常更改。通常用户不必知道这些文件,因为它们应该放置在自动找到它们的目录中。只有所有可用服务的名称很重要。

最后,一些系统软件可能会使用 NSS 配置文件来存储它们自己的配置以用于类似目的。这方面的示例包括 autofs 使用的 automount 服务。

2.2.2. NSS 配置中的操作

Actions in the NSS configuration

规范中的第二项使用户可以更好地控制查找过程。操作项放置在两个服务名称之间,并写在括号内。一般形式是

[ ( !? status = action )+ ]

在哪里

status ⇒ success | notfound | unavail | tryagain
action ⇒ return | continue

关键字的大小写无关紧要。状态值是调用特定服务的查找函数的结果。他们的意思:

‘success’

没有发生错误,并且返回了想要的条目。对此的默认操作是返回。

‘notfound’

查找过程正常,但未找到所需的值。默认操作是继续。

‘unavail’

该服务永久不可用。这可能意味着所需的文件不可用,或者对于 DNS,服务器不可用或不允许查询。默认操作是继续。

‘tryagain’

该服务暂时不可用。这可能意味着文件被锁定或服务器当前无法接受更多连接。默认操作是继续。

动作值意味着:

‘return’

如果状态匹配,则停止此服务规范的查找过程。如果条目可用,请将其提供给应用程序。如果发生错误,请将其报告给应用程序。在之前的“合并”操作的情况下,数据将与之前的查找结果相结合,如下所述。

‘continue’

如果状态匹配,则在下一个条目处继续查找过程,丢弃当前查找的结果(以及任何合并的数据)。一个例外是“initgroups”数据库和“成功”状态,其中“继续”的行为类似于下面的合并。

‘merge’

继续查找过程,保留当前查找结果。此操作仅对“成功”状态有用。如果后续服务查找成功并且具有匹配的“返回”规范,则合并结果,查找过程结束,并将合并的结果返回给应用程序。如果以下服务具有匹配的“合并”操作,则查找过程将继续,保留此查找和任何先前查找的组合数据。

合并操作后,后续查找的错误将被忽略,并且将返回到目前为止收集的数据。

“合并”仅适用于“成功”状态。它目前是为“组”数据库及其组成员字段“gr_mem”实现的。如果为其他数据库指定,则会导致查找失败(如果状态匹配)。

在处理“组”成员身份的“合并”时,两个条目的组 GID 和名称必须相同。如果只有一个或另一个匹配,则行为未定义。

如果我们有一行

ethers: nisplus [NOTFOUND=return] db files

这相当于

ethers: nisplus [SUCCESS=return NOTFOUND=return UNAVAIL=continue
                 TRYAGAIN=continue]
        db      [SUCCESS=return NOTFOUND=continue UNAVAIL=continue
                 TRYAGAIN=continue]
        files

(除了它必须写在一行上)。操作的默认值通常是您想要的,只有在特殊情况下才需要更改。

如果可选!放置在状态之前,这意味着以下操作用于所有状态,但状态本身除外。IE。,!是 C 语言(和其他语言)中的否定。

在我们解释使该操作项成为必要的异常之前,请再说明一点:显然,在文件服务之后添加另一个操作项是没有意义的。由于在该操作之后没有其他服务始终是返回。

现在,为什么这个 [NOTFOUND=return] 操作有用?要理解这一点,我们应该知道 nisplus 服务通常是完整的;即,如果一个条目在 NIS+ 表中不可用,则它在其他任何地方都不可用。这就是这个行动项目所表达的:检查进一步的服务是没有用的,因为它们不会给我们一个结果。

如果 NIS+ 服务不可用,因为机器正在引导,情况会有所不同。在这种情况下,查找函数的返回值不是未找到,而是不可用。正如您在上面的完整表格中看到的那样:在这种情况下,使用了 db 和 files 服务。整洁,不是吗?系统管理员无需特别注意系统未完全准备好工作的时间(在启动或关闭或网络问题时)。

2.2.3. NSS 配置文件注意事项

Notes on the NSS Configuration File

最后还有一些提示。如果 /etc/nsswitch.conf 不存在,NSS 实现也不是完全无能为力。对于所有受支持的数据库,都有一个默认值,因此即使文件损坏或丢失,通常也应该可以让系统运行。

对于主机和网络数据库,默认值为文件 dns。即,本地配置将覆盖域名系统 (DNS) 的内容。

passwd、group 和 shadow 数据库传统上以特殊方式处理。已读取 /etc 目录中的相应文件,但如果发现名称以 + 字符开头的条目,则使用 NIS。这种查找已被删除,现在服务的默认值是文件。libnss_compat 不再依赖于 libnsl,并且可以在没有 NIS 的情况下使用。

对于所有其他数据库,默认值为 files。

第二点是用户应该尝试优化查找过程。不同的服务有不同的响应时间。对本地文件进行简单的文件查找可能很快,但如果文件很长并且所需的条目接近文件末尾,则可能需要相当长的时间。在这种情况下,最好使用允许快速本地访问大型数据集的 db 服务。

通常情况是必须使用一些全局信息,如 NIS。所以使用 nis 等服务条目是不可避免的。但是如果可能的话,应该避免这样的慢服务。

2.3. NSS 模块内部

NSS Module Internals

现在是时候描述模块的样子了。模块中包含的函数由它们的名称标识。即,没有跳转表等。如何做到这一点在这里并不重要。对这个主题感兴趣的人应该阅读动态链接。

2.3.1. NSS 模块的命名方案

The Naming Scheme of the NSS Modules

每个函数的名称由不同的部分组成:

_nss_service_function

service 当然对应这个函数所在的模块的名称。4 函数部分来源于C库本身的接口函数。如果用户调用函数 gethostbyname 并且使用的服务是文件函数

       _nss_files_gethostbyname_r

在模块中

       libnss_files.so.2

用来。你看,上面解释的并不是全部真相。事实上,NSS 模块只包含查找函数的可重入版本。即,如果用户调用 gethostbyname_r 函数,这也将以上述函数结束。对于所有用户界面函数,C 库将此调用映射到对可重入函数的调用。对于可重入函数,这是微不足道的,因为接口(几乎)相同。对于不可重入版本,库保留用于替换用户提供的缓冲区的内部缓冲区。

即,可重入函数可以有对应的。没有一个服务模块被迫拥有所有数据库和所有类型的功能来访问它们。如果一个函数不可用,它被简单地视为该函数将返回不可用(请参阅 NSS 配置中的操作)。

文件名 libnss_files.so.2 将位于 Solaris 2 系统 nss_files.so.2 上。这就是上面所说的区别。Sun 的 NSS 模块可用作仅间接加载的模块。

GNU C 库中的 NSS 模块本身已准备好用作普通库。不过,目前这不是真的。但是,模块中名称空间的组织并不像 Solaris 那样不可能。现在您可以看到为什么模块仍然是库。

2.3.2. NSS模块中的函数接口

The Interface of the Function in NSS Modules

现在我们了解了模块中包含的功能。现在是描述类型的时候了。当我们提到上述函数的可重入版本时,这意味着有一些额外的参数(与标准的不可重入版本相比)。上述函数的不可重入和可重入版本的原型是:

struct hostent *gethostbyname (const char *name)

int gethostbyname_r (const char *name, struct hostent *result_buf,
                     char *buf, size_t buflen, struct hostent **result,
                     int *h_errnop)

在这种情况下,NSS 模块中函数的实际原型是

enum nss_status _nss_files_gethostbyname_r (const char *name,
                                            struct hostent *result_buf,
                                            char *buf, size_t buflen,
                                            int *errnop, int *h_errnop)

即接口函数实际上是可重入函数,只是改变了返回值,省略了result参数,增加了errnop参数。当用户级函数返回一个指向结果的指针时,可重入函数返回一个枚举 nss_status 值:

NSS_STATUS_TRYAGAIN

数值 -2

NSS_STATUS_UNAVAIL

数值 -1

NSS_STATUS_NOTFOUND

数值 0

NSS_STATUS_SUCCESS

数值 1

现在您可以看到 /etc/nsswitch.conf 文件的操作项在哪里使用了。

如果你研究源代码你会发现有第五个值:NSS_STATUS_RETURN。这是一个仅供内部使用的值,在无法使用上述值的地方由少数函数使用。如有必要,应检查源代码以了解详细信息。

如果接口函数必须返回错误,则将正确的错误代码存储在 *errnop 中很重要。一些返回状态值只有一个相关的错误代码,其他的有更多。

NSS_STATUS_TRYAGAIN	    EAGAIN      使用的功能之一暂时耗尽资源或服务当前不可用。
                        ERANGE      提供的缓冲区不够大。
                                    应该使用更大的缓冲区再次调用该函数。
NSS_STATUS_UNAVAIL	    ENOENT      找不到必要的输入文件。
NSS_STATUS_NOTFOUND	    ENOENT      请求的条目不可用。
NSS_STATUS_NOTFOUND     SUCCESS     没有条目。
                                    使用它来避免返回可能在以后启用的非活动服务的错误。
                                    这与服务暂时不可用不同。

这些是建议值。可以有其他错误代码,并且描述的错误代码可以具有不同的含义。有一个例外:当返回 NSS_STATUS_TRYAGAIN 时,错误代码 ERANGE 必须意味着用户提供的缓冲区太小。其他一切都是非关键的。

在静态链接的程序中,主应用程序和 NSS 模块不共享同一个线程局部变量 errno,这就是为什么存在显式 errnop 函数参数的原因。

上面的函数有一些特别之处,几乎所有其他模块函数都没有。有一个参数 h_errnop。这指向一个变量,如果由于某种原因函数执行失败,该变量将填充错误代码。(在静态链接的程序中,线程局部变量 h_errno 不与主应用程序共享。)

getXXXbyYYY 函数是 NSS 模块中最重要的函数。但是还有其他实现访问系统数据库的其他方法(例如,对于用户数据库,有 setpwent、getpwent 和 endpwent)。这些将在后面更详细地描述。这里我们给出一个确定模块函数签名的通用方法:

  • 返回值为枚举 nss_status;

  • 名称(参见 NSS 模块的命名方案);

  • 第一个参数与不可重入函数的参数相同;

    接下来的四个参数是:

    STRUCT_TYPE *result_buf

    指向存储结果的缓冲区的指针。STRUCT_TYPE 通常是一个对应于数据库的结构。

    char *buffer

    指向缓冲区的指针,函数可以在其中存储结果的附加数据等。

    size_t buflen

    buffer 指向的缓冲区的长度。

    int *errnop

    返回给应用程序的低级错误代码。如果返回值不是 NSS_STATUS_SUCCESS,*errnop 需要设置为非零值。NSS 模块不应该将 *errnop 设置为零。如上所述,ERANGE 值是特殊的。

  • 可能是最后一个参数 h_errnop,用于主机名和网络名查找函数。如果返回值不是 NSS_STATUS_SUCCESS,*h_errnop 需要设置为非零值。一个通用错误代码是 NETDB_INTERNAL,它指示调用者检查 *errnop 以获取更多详细信息。(这包括 ERANGE 特例。)

该表适用于除 set…ent 和 end…ent 功能之外的所有功能。

2.4. 扩展 NSS

Extending NSS

上面提到的 NSS 的优点之一是它可以很容易地扩展。可以通过两种方式进行扩展:添加另一个数据库或添加另一个服务。前者通常仅由 C 库开发人员完成。这里唯一重要的是要记住添加另一个数据库独立于添加另一个服务,因为服务不需要支持所有数据库或查找功能。

因此,新服务的设计者/实施者可以自由选择他/她感兴趣的数据库,并将其余的留待以后使用(或完全放在一边)。

2.4.1. 向 NSS 添加另一个服务

Adding another Service to NSS

新服务的源代码不需要(也不应该)是 GNU C 库本身的一部分。开发人员保留对源及其开发的完全控制。C 库和新服务模块之间的链接仅由接口函数组成。

每个模块都是按照特定的接口规范设计的。目前版本是 2(版本 1 中的接口不够用),这体现在 NSS 模块的共享库对象的版本号中:它们的扩展名为 .2。如果接口以不兼容的方式再次更改,则此数字将增加。使用旧界面的模块仍然可以使用。

新服务的开发人员必须确保他们的模块是使用正确的接口号创建的。这意味着文件本身必须具有正确的名称,并且在 ELF 系统上,soname(共享对象名称)也必须具有此编号。使用 GNU CC 从 ELF 系统上的一堆目标文件构建一个模块可以这样完成:

gcc -shared -o libnss_NAME.so.2 -Wl,-soname,libnss_NAME.so.2 OBJECTS

GNU CC 中的链接选项,以了解有关此命令行的更多信息。

要使用新模块,库必须能够找到它。这可以通过使用动态链接器的选项来实现,以便它将搜索放置二进制文件的目录。对于 ELF 系统,这可以通过将所需目录添加到 LD_LIBRARY_PATH 的值来完成。

但这并不总是可行的,因为一些程序(那些在不属于用户的 ID 下运行的程序)忽略了这个变量。因此,模块的稳定版本应放置在动态链接器搜索的目录中。通常这应该是目录 $prefix/lib,其中 $prefix 对应于使用 --prefix 选项配置的值。但要小心:只有在明确模块不会造成任何伤害的情况下才应该这样做。系统管理员应该小心。

2.4.2. NSS 模块功能的内部

Internals of the NSS Module Functions

到目前为止,我们只为 NSS 模块中的函数提供了语法接口。事实上,我们可以说的不多,因为每个功能的实现显然是不同的。但是所有功能都必须遵循一些一般规则。

实际上,界面中可能会出现四种不同的功能。所有这些都源自用于系统数据库的传统数据库。下表中的 db 通常是数据库的缩写(例如,它是用户数据库的 pw)。

enum nss_status _nss_database_setdbent (void)

此功能为以下操作准备服务。对于简单的基于文件的查找,这意味着可以打开文件,对于其他服务,此功能只是一个 noop。

这个函数的一个特殊情况是它需要一些数据库的附加参数(即接口是 int setdbent (int))。主机名,描述 sethostent 函数。

返回值应为 NSS_STATUS_SUCCESS 或根据上表以防出错(请参阅 NSS 模块中的函数接口)。

enum nss_status _nss_database_enddbent (void)

此函数只是关闭所有仍然打开的文件或删除缓冲区缓存。如果没有要删除的文件或缓冲区,这又是一个简单的 noop。

除了 NSS_STATUS_SUCCESS 之外,通常没有返回值。

enum nss_status _nss_database_getdbent_r (STRUCTURE *result, char *buffer, size_t buflen, int *errnop)

由于此函数将连续多次调用以逐个检索一个条目,因此它必须保持某种状态。但这也意味着这些函数并不是真正可重入的。它们是可重入的,仅因为同时调用此函数不会尝试将检索到的数据写入同一位置(就像不可重入函数的情况一样);相反,它写入结果参数指向的结构。但是这些调用共享一个共同的状态,在文件访问的情况下,这意味着它们返回文件中的相邻条目。

buffer 指向的长度为 buflen 的缓冲区可用于存储一些额外的结果数据。不能保证下次调用此函数时会传递相同的缓冲区。因此,不得滥用此缓冲区来保存从一次调用到另一次调用的某些状态信息。

在函数返回失败代码之前,实现应该将本地 errno 变量的值存储在指向 errnop 的变量中。这对于保证模块在静态链接程序中工作很重要。存储的值不能为零。

如上所述,这个函数也可以有一个额外的最后一个参数。这取决于使用的数据库;它只发生在主机和网络上。

只要有更多条目,该函数就应返回 NSS_STATUS_SUCCESS。当最后一个条目被读取时,它应该返回 NSS_STATUS_NOTFOUND。当作为参数给出的缓冲区太小而无法返回数据时,应返回 NSS_STATUS_TRYAGAIN。当服务之前没有通过调用 _nss_DATABASE_setdbent 进行初始化时,此函数允许的所有返回值也可以在此处返回。

enum nss_status _nss_DATABASE_getdbbyXX_r (PARAMS, STRUCTURE *result, char *buffer, size_t buflen, int *errnop)

该函数应返回由 PARAMS 寻址的数据库中的条目。这些论点的类型和数量各不相同。它必须通过查看用户级界面功能来单独确定。赋予不可重入版本的所有参数在此处由 PARAMS 描述。

结果必须存储在 result 指向的结构中。如果有额外的数据要返回(比如字符串,结果结构只包含指针),函数必须使用长度为 buflen 的缓冲区。不得有任何对非常量全局数据的引用。

只要有意义,这个函数的实现就应该尊重 setDBent 函数设置的保持打开标志。

在函数返回之前,实现应该将本地 errno 变量的值存储在 errnop 指向的变量中。这对于保证模块在静态链接的程序中工作很重要。

同样,此函数为主机和网络数据库采用额外的最后一个参数。

返回值应始终遵循上面给出的规则(请参阅 NSS 模块中的函数接口)。

3. 参考

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值