系统数据文件操作函数介绍

    UNIX 中有个和用户登录密切相关的口令文件 /etc/passwd,POSIX.1 定义了下面两个可获取口令文件项的函数。
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
                        /* 返回值:若成功,都返回指针;否则,都返回 NULL */

struct passwd{
    char    *pw_name;       // 用户名
    char    *pw_passwd;     // 加密口令
    uid_t   *pw_uid;        // 数值用户 ID
    gid_t   *pw_gid;        // 数值组 ID
    char    *pw_gecos;      // 注释字段
    char    *pw_dir;        // 初始工作目录
    char    *pw_shell;      // 初始 shell(用户程序)
    char    *pw_class;      // 用户访问类
    time_t  *pw_change;     // 下次更改口令时间
    time_t  *pw_expire;     // 账户有效期时间
};

    getpwuid 函数由 ls 程序使用,它将 i 节点中的数字用户 ID 映射为用户登录名。而在键入登录名时,getpwnam 函数由 login 程序使用。这两个函数都返回一个指向 passwd 结构的指针,该结构包含了组成 /etc/passwd 文件的各个字段,它通常是函数内部的静态变量,所以只要调用任一相关函数,其内容就会被重写。
    如果要查看的只是登录名或用户 ID,那么这两个函数能满足要求,而如果要查看整个口令文件时,则可使用 XSI 扩展中提供的下列 3 个函数。
#include <pwd.h>
struct passwd *getpwent(void);
           /* 返回值:若成功,返回指针;若出错或到达文件尾端,返回 NULL */
void setpwent(void);
void endpwent(void);

    调用 getpwent 时,它返回口令文件中的下一个记录项。每次调用此函数都会重写 passwd 结构。在第一次调用时,它就会打开它所使用的各个文件。函数 setpwent 打开(如果文件尚未打开)并反绕它所使用的文件,endpwent 则关闭这些文件。在使用 getpwent 查看完口令文件后,一定要调用 endpwent 关闭来这些文件。因为 getpwent 知道什么时间应当打开它所使用的文件(第一次被调用时),但是却不知道何时关闭它们。
    下面是一个利用这 3 个函数实现的一个 getpwnam 程序。
#include <pwd.h>
#include <stddef.h>
#include <string.h>

struct passwd *
getpwnam(const char *name){
	struct passwd	*ptr;

	setpwent();
	while((ptr=getpwent()) != NULL){
		if(strcmp(name, ptr->pw_name) == 0)
			break;
	}
	endpwent();
	return ptr;		// ptr is NULL if no match found.
}

    在函数开始处调用 setpwent 是自我保护性的措施,以便确保如果在此之前已经调用 getpwent 打开了有关文件的情况下,反绕这些文件使它们定位到开始处。getpwnam 和 getpwuid 完成后不应使有关文件仍处于打开状态,所以要调用 endpwent 关闭它们。
    不过为了安全,加密口令一般是存放在阴影口令文件 /etc/shadow 中,该文件至少要包含用户名和加密口令,与该口令相关的其它信息也可存放在该文件中。一般用户是不可以读取该文件的,仅有少数几个程序需要访问加密口令,如 login 和 passwd 等,这些程序常常是设置用户 ID 为 root 的程序。在 Linux 3.2.0 和 Solaris 10 中,与访问口令文件的一组函数相类似,有另一组函数可用于访问阴影口令文件(在 FreeBSD 8.0 和 Mac OS X 10.6.8 中,没有阴影口令结构,附加的账户信息存放在口令文件中)。
#include <shadow.h>
struct spwd *getspnam(const char *name);
struct spwd *getspent(void);
                  /* 返回值:若成功,都返回指针;否则,都返回 NULL */
void setspent(void);
void endspent(void);

struct spwd{
    char    *sp_namp;        // 用户登录名
    char    *sp_pwdp;        // 加密口令
    int     sp_lstchg;       // 上次更改口令以来经过的时间
    int     sp_min;          // 经多少天后允许修改
    int     sp_max;          // 要求更改尚余天数
    int     sp_warn;         // 超期警告天数
    int     sp_inact;        // 账户不活动之前尚余天数
    int     sp_expire;       // 账户超期天数
    unsigned int sp_flag;    // 保留
};

    UNIX 中的每个账户除了这些个人信息外,还拥有所属组的相关信息,这些信息保存在 /etc/group 文件中。UNIX 也提供了类似上面的一组函数来操作该文件。
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
                         /* 返回值:若成功,都返回指针;否则,都返回 NULL */

struct group *getgrent(void);
                  /* 返回值:若成功,返回指针;若出错或到达文件尾端,返回 NULL */
void setgrent(void);
void endgrent(void);

struct group{
    char *gr_name;            // 组名
    char *gr_passwd;          // 加密口令
    int gr_gid;               // 数值组 ID
    char **gr_mem;            // 指向各用户名指针的数组,以 null 指针结尾
};

    当用户登录时,系统就按口令文件记录项中的数值组 ID 赋给他实际组 ID(可以在任何时候使用 newgrp 更改组 ID,执行不带参数的 newgrp 则可返回到原来的组)。在 4.2 BSD 引入了附属组 ID 的概念后,一个账户还可属于多至 16(由 NGROUPS_MAX 规定)个另外的组。所以文件访问权限检查时不仅将进程的有效组 ID 与文件的组 ID 相比较,而且也将所有附属组 ID 与文件的组 ID 进行比较。
    可使用下面 3 个函数来获取和设置附属组 ID。
#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist[]);
                        /* 返回值:若成功,返回附属组 ID 数量;否则,返回 -1 */
#include <grp.h>        // on Linux
#include <unistd.h>     // on FreeBSD, Mac OS X, and Solaris
int setgroups(int ngroups, const gid_t groupslist[]);
#include <grp.h>        // on Linux and Solaris
#include <unistd.h>     // on FreeBSD and Mac OS X
int initgroups(const char *username, gid_t basegid);
                         /* 返回值:若成功,都返回 0;否则,都返回 -1 */

    getgroups 将进程所属用户的各附属组填写到数组 grouplist 中,填写入的附属组 ID 数最多为 gidsetsize 个,实际填写的附属组 ID 数由函数返回。特殊地,如若 gidsetsize 为 0,则函数只返回附属组 ID 数,而不修改 grouplist(这使得调用者可以确定 grouplist 的长度,以便进行分配)。
    setgroups 可由超级用户调用以便为调用进程设置附属组 ID 表。grouplist 是组 ID 数组,而 ngroups 说明了数组中的元素数,ngroups 的值不能大于 NGROUPS_MAX。
    通常,只有 initgroups 函数调用 setgroups,initgroups 使用 getgrent 等函数读整个组文件,然后对 username 确定其组的成员关系,然后调用 setgroups,以便为该用户初始化附属组 ID 表。除了在组文件中找到 username 是成员的所有组,initgroups 也在附属组 ID 表中包括了 basegid,basegid 是 username 在口令文件中的组 ID。只有 login 等少数几个程序会调用 initgroups 函数。
    除了口令文件和组文件外,UNIX 系统还使用很多其他文件。比如,记录各网络服务器所提供服务的数据文件 /etc/services,记录协议信息的数据文件 /etc/protocols,以及记录网络信息的数据文件 /etc/networks 等。这些数据文件一般都至少提供了类似的 get、set 和 end 函数。如果数据文件支持某种形式的键搜索,则也提供类似于口令文件中的 getpwnam 和 getpwuid 之类的可搜索具有指定键的记录的例程。下表列出了 UNIX 常用的这类例程。

猜你喜欢

转载自aisxyz.iteye.com/blog/2389296