《UNIX环境高级编程》 第6章 系统数据文件和信息

系统数据文件和信息

6.1 引言

UNIX系统的正常运作需要使用大量的与系统有关的数据文件,例如口令文件/etc/passwd和组文件/etc/group就是经常被多个程序频繁使用的两个文件。每次用户登录系统,以及每次执行ls -l命令都需要使用口令文件。
由于历史原因,这些数据文件都是ASCII文件文件,并使用标准IO库读取这些文件。但是对于较大的系统,顺序扫描口令文件很花费时间,我们需要能够以非ASCII文件格式存放这些文件,但仍向使用其他文件格式的应用程序提供接口。对于这些数据文件(系统数据文件)的可移植接口是本章的主题。本章还包括了系统标识函数、时间和日期函数。

6.2 口令文件

UNIX系统口令文件包含了以下各字段,这些字段包含在pwd.h中的passwd结构中。

说明struct passwd成员
用户名char *pw_name
加密口令char *pw_passwd
数值用户IDuid_t pw_uid
数值组IDgid_t pw_gid
注释字段char *pw_gecos
初始工作目录char *pw_dir
初始shellchar *pw_shell
用户访问类char *pw_class
下次更改口令时间time_t pw_change
账户有效期时间time_t pw_expire

由于历史原因,口令文件/etc/passwd是一个ASCII文件。每行包含以上表格字段,字段之间用冒号分隔。


通过UID和username查看口令文件相关项:

#include <pwd.h>
struct passwd *getpwuid(uid_t uid);//通过用户ID获取口令文件结构
struct passwd *getpwnam(char *name);//通过用户名获得口令文件结构

查看整个口令文件:

#include <pwd.h>
struct passwd *getpwent(void);//获得一条口令文件记录。
void setpwent(void);//反绕口令文件,用于回到第一条。
void endpwend(void);//关闭口令文件。

example:实现getpwname函数:

#include <pwd.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);
}

6.3 阴影口令

加密口令是经单向加密算法处理过的用户口令副本。因为此算法是单向的,所以不能从加密口令猜测到原来的口令。但是可以对口令进行猜测,然后与用户的加密口令比较来猜测到用户口令。
为了使有这样企图的人难以获得原始资料(加密口令),系统通常将加密口令存储在一个称为阴影口令(shadow password)的文件中(/etc/shadow)。这个文件一般用户不能读取,只有少数几个程序需要访问加密口令,如login和passwd,这些程序常常设置用户ID为root。有了阴影口令后普通口令文件/etc/passwd就可以由用户自由读取了。
/etc/shadow中的字段:

说明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
保留unsigend int sp_flag

类似于口令文件,有一组访问/etc/shadow的函数:

#include <shadow.h>
struct spwd *getspnam(const char *name);//通过用户名取得shadow结构

struct spwd *getspent(void);//获取单条记录
void setspent(void);//反绕,回到首条记录
void endspent(void);//关闭shadow文件

6.4 组文件

UNIX组文件包含了以下字段,这些字段的定义在grp.h文件中:

说明struct group成员
组名char *gr_name
加密口令char *gr_passwd
数值组IDint gr_gid
指向各用户名指针的数组char **gr_mem

字段gr_mem是一个数组指针,其中每个指针指向一个属于该组的用户名,该数组以NULL指针结尾。
下面两个文件通过组名和数组组ID来获得组结构信息,它们都是返回一个静态变量的指针,每次调用时都会重新该静态变量:

#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);

如果要历遍整个组文件,可以使用类似历遍口令文件的方法:

#include <grp.h>
struct group *getgrent(void);       //从组文件中读取下一个记录
void setgrent(void);                //打开并反绕,回到第一条记录
void endgrent(void);                //关闭组文件

6.5 附属组ID

很多时候,一个进程不仅可以属于口令文件记录项中组ID所对应的组,也可以属于多至16个(具体数量定义在常量NGROUP_MAX中)另外的组。因此访问权限的检查规则改变为:不仅将进程的有效组ID与文件的组ID相比较,而且也将所有附属组ID与文件组ID相比较。
使用附属组ID的优点是进程不需要显式地进程更改组,一个用户会参与多个项目,因此也就要同时属于多个组。获得附属组ID,使用下列3个函数:

#include <unistd.h>
int getgroups(int gidsetsize ,gid_t grouplist[]);

#include <grp.h>
int setgroups(int ngroups,const gid_t grouplist[]);
int initgroups(const char *username ,gid_t basegid);

getgroups将进程所属用户的各个附属组ID填写到数组grouplist中,最多填入个数为gissetsize,实际数量有返回值确定。如果gidsetsize为0,则返回实际附属组ID数量,不修改grouplist。
setgroups用户设置组ID,grouplist是组ID数组,ngroups是数量,但不能超过NGROUP_MAX规定的上限。
(以下很重要,比较难理解)
initgroups:读取整个组文件(/etc/group)根据username确定属于哪几个组,再将这些组ID和basegid一起存到进程附属组ID表中,完成初始化附属组ID表的工作。

6.6 实现区别

这里比较了4中不同UNIX系统平台下的系统文件存储情况,包括FreeBSD 8.0 、Linux 3.2.0 、Mac OS X 10.6.8 和Solaris 10。我们只关心Linux部分。

信息路径
账户信息/etc/passwd
加密口令/etc/shadow
组信息/etc/group

6.7 其他数据文件

处理口令文件和组文件这两个系统数据文件,在日常操作中UNIX系统还有很多其他文件。
一般情况下,每个数据文件至少有3个函数:
(1)get函数:读取下一个记录,如果需要还会打开该文件。这种函数一般返回一个结构指针。当达到文件尾时返回空指针。大多数get函数返回一个静态存储类结构的指针,如果要保存该其内容,则需要复制它。
(2)set函数:打开相应的数据文件(如果尚未打开),然后反绕该文件。
(3)end函数:关闭相应数据文件。
另外,如果数据文件支持某种形式的搜索,则也提供搜索指定字段的记录的方法。
以下列出常用的系统参数数据文件,处理3个处理函数外,另外列出了附加的搜索函数。

说明数据文件头文件结构附加搜索函数
口令文件/etc/passwdpasswdgetpwnam、getpwuid
组文件/etc/groupgroupgetgrnam、getgrgid
加密口令文件/etc/shadowspwdgetspnam
主机/etc/hostshostentgetnameinfo、getaddrinfo
网络/etc/networksnetentgetnetbyname、getnetbyaddr
协议/etc/prptocolsprotoentgetprotobyname、getprotobynumber
服务/etc/servicesserventgetservbyname、getservbyport

6.8 登录账户记录

大多数UNIX系统都提供下列两个数据文件:utmp文件(/var/run/utmp)记录当前登录到系统的各个用户;wtmp文件(/var/log/wtmp)跟踪各个登录和注销事件。
每次写入这两个文件中的是包含下列结构的一个二进制记录:

struct utmp {
    char ut_line[8]; //tty line 
    char ut_name[8]; //login name
    long ut_time;    //seconds since epoch
};

登录时,login填充此类型结构,然后将其写入到utmp文件中,同时也添写到wtmp文件中。
注销时,init进程将utmp文件中的相应记录擦除,并将一个新记录写到wtmp中。在wtmp文件的注销记录中,ut_name字段清除为0。在系统启动、更改时间等操作前后,都会在wtmp文件中追加特殊记录项。
**who程序读取utmp文件,并以可读格式打印其内容。
last程序读取wtmp文件并打印记录。**

6.9 系统标识

POSIX.1定义了uname函数,它返回主机和操作系统有关想信息。

#include <sys/utsname.h>
int uname(struct utsname *name);

POSIX.1定义了utsname结构体的最小结构,具体的实现会比这更多:

struct utsname{
    char sysname[];    //name of the operating system
    char nodename[];   //name of this node,网络上的主机
    char release[];    //current release  of the operating system
    char version[];    //current version of this release
    char machine[];    //name of hardware type
    };

其中每个成员都是字符串,其最大长度受系统限制。


hostname函数只返回主机名,该名字通常就是TCP/IP网络上主机的名字(通常是该主机在网络上的完整域名)。

#include <unistd.h>
int gethostname(char *name,int namelen);

shell中hostname命令可以用来获取或设置主机名。(超级用户用一个类似sethostname来设置主机名)主机名通常在系统自举时设置,它由/etc/rc或init取自一个启动文件。

6.10 时间和日期例程

UNIX内核提供的基本事件服务是计算从协调世界时间(coordinated universal time,UTC)1970年1月1日00:00:00这一特点时间以来经过的秒数。
这种秒数是以time_t类型定义的,称为日历时间。
time函数返回当前时间和日期:

#include <time.h>
time_t time(time_t *calptr);    //返回时间值,若参数非空也填充参数

现在操作系统支持多个系统时钟,时钟类型通过clockid_t类型来标识:

时钟类型标识符选项说明
CLOCK_REALTIME实时系统时间
CLOCK_MONOTONIC_POSIX_CLOCK_MONOTONIC不带负跳数的实时系统时间
CLOCK_PROCESS_CPUTIME_ID_POSIX_CPUTIME调用进程的CPU时间
CLOCK_THREAD_CPUTIME_ID_POSIX_THREAD_CPUTIME调用线程的CPU时间

clock_gettime函数用于获取指定的时钟时间,返回到timespec结构中。

#include <sys/time.h>
int clock_gettime(clockid_t clock_id,struct timespec *tsp); 

UNIX还有多个时间函数,就不一一看了,可以查阅UNIX环境高级编程第6章10节。

6.11 小结

  • 所有的UNIX系统都是用口令文件合组文件,我们说明了这些文件的各种函数。我们也介绍了阴影口令,它可以增加系统的安全性。
  • 附属组提供了一个用户同时参加多个组的方法。
  • 系统相关数据文件访问函数提供了访问系统属性的方法。
  • 时间函数提供了访问时钟时间的方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值