APUE学习笔记:第六章 系统数据文件和信息

6.1 引言node

UNIX系统的正常运行须要使用大量与系统有关的数据文件,针对这些数据文件的可移植接口是本章的主题。本章还介绍了系统标识函数、时间和日期函数linux

6.2 口令文件算法

UNIX系统的口令文件包含了下列各字段,这些字段包含在<pwd.h>中定义的passwd结构中shell

用户名    char *pw_name数据库

加密口令   char *pw_passwd数组

数值用户ID  uid_t pw_uid安全

数值组ID   gid_t pw_gidbash

注释字段    char *pw_gecos服务器

初始工做目录  char *pw_dir网络

初始shell     char *pw_shell

用户访问类   char *pw_class

下次更改口令时间  time_t pw_change

帐户到期时间    time_t pw_expire

 

因为历史缘由,口令文件存储在/etc/passwd中,并且是一个ASCII文件。

linux中,可能有下列四行   

root:x:0:0:root:/root:/bin/bash

squid:x:23:23: :/var/spool/squid:/dev/null

nobody:x:65534:65534:Nobody:/home:/bin/sh

sar:x:205:105:Stephen:/home/sar:/bin/bash

关于这些登录项请注意下列各点:

-一般一个用户名为root的登录项,其用户ID是0

-加密口令字段包含了一个占位字符(之前加密口令直接放在该字段,但因为安全问题,如今放在另外一位置)

-口令文件项中的某些字段多是口。若是加密口令字段为空,一般意味着该用户没有口令。

-shell字段包含了一个可执行程序名,它被用做该用户的登录shell。若为空,则取默认值,一般为/bin/sh。(注意,squid登录项的该字段为/dev/null.显然,这是一个设备,不能  

 执行,所以将其设于此处的目的是,阻止任何人以用户squid的名义登陆到该系统

-为了阻止一个特定用户登录系统,除使用/dev/null以外,还有若干种替代方法。一种常见的方法是,将/bin/false用做登录shell。它简单地以不成功(非0)状态终止,该shell将此种状态判断为假。另外一种常见的方法是,用/bin/true禁止一个帐户。它所作的一切是以成功(0)状态终止。某些系统提供nologin命令,它打印可自定义的出错信息,而后以非0状态终止

-使用nobody用户名的目的是,使任何人均可登录至系统,但其用户ID(65534)和组ID(65534)不提供任何特权。该用户ID和组ID只能访问人人皆可读写的文件

-提供finger(1)命令的某些UNIX系统支持注释字段中的附加信息。

 

POSIX.1只定义了两个获取口令文件项的函数。在给出用户登录名或数值用户ID后,这两个函数就能查询相关项。

#include<pwd.h>

struct passwd *getpwuid(uid_t uid);

struct passwd *getpwnam(const char *name);
                //返回值:若成功则返回指针,若出错或到达文件结尾则返回NULL

getpwuid函数由ls(1)程序使用,它将i节点中的数值用户ID映射为用户登录名。在键入登录名时,getpwnam函数由login(1)程序使用。

 

若是要查看整个口令文件,下列三个函数则可用于此种目的

#include<pwd.h>
struct passwd *getpwent(void);
                //返回值:若成功则返回指针,若出错或到达文件结尾则返回NULL
void setpwent(void);
void endwent(void);

调用getpwent时,它返回口令文件中的下一个记录项。如同上面所述的两个POSIX.1函数同样,它返回一个由它填写好的password结构的指针。每次调用此函数时都重写该结构。在第一次调用该函数时,它打开它所使用的各个文件。

函数setpwent反饶它所使用的文件,endpwent则关闭这些文件。在使用getpwent查看完口令后,必定要调用endpwent关闭这些文件。getpwent知道什么时间它应当打开它所使用的文件(第一次被调用时),但它不知道什么时候关闭这些文件

 

实例:6_1  getpwnam函数的实现

 1 #include<pwd.h>
 2 #include<stddef.h>
 3 #include<string.h>
 4 struct passwd * getpwnam(const char *name)
 5 {
 6     struct passwd *ptr;
 7     setpwent();
 8     while((ptr=getpwent())!=NULL)
 9     if(strcmp(name,ptr->pw_name)==0)
10     break;
11     endpwent();
12     return(ptr);    //ptr is NULL if no match found
13 }

在程序开始处调用setpwent是自我保护性的措施,以便在调用者在此以前已经调用getpwent打开了有关文件的状况下,将有关文件定位到文件开始处。getpwnam和getpwuid调用完成后不该使有关文件仍处于打开状态,因此应调用endpwent关闭它们

 

6.3 阴影口令

加密口令是经单向加密算法处理过的用户口令副本。所以此算法是单向的,因此不能从加密口令猜想到原来的口令。但人们能够用试探办法猜想口令。。。

为使企图这样作的人难以得到原始资料(加密口令),如今,某些系统将加密口令存放在另外一个一般称为阴影口令的文件中。该文件至少要包含用户名和加密口令。

etc/shadow文件中的字段:

用户登录名      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

 

阴影口令文件不是通常用户能够读取的,仅有少数几个程序须要存取加密口令,例如login(1)和passwd(1),这些程序经常是设置用户ID为root的程序。有了阴影口令后,普通口令文件/etc/passwd可由用户自由读取

 

6.4 组文件

UNIX组文件中的字段包含在<grp.h>中所定义的group结构中

/etc/group文件中的字段:

  组名    char *gr_name

  加密口令  char *gr_passwd

  数值组ID  int gr_gid

  指向各用户的指针的数组  char **gr_mem

 

字段gr_mem是一个指针数组,其中每一个指针各指向一个属于该组的用户名。该数组以空指针结尾。

 

能够用下列两个由POSIX.1定义的函数来查看组名或数值组ID

#include<grp.h>

struct group *getgrgid(gid_t gid);

struct group *getgrnam(const char *name);

        //两个函数返回值:若成功则返回指针,所出错则返回NULL

 

若是须要搜索整个组文件,则需使用另外几个函数。下列三个函数相似于针对口令文件的三个函数

#include<grp.h>

struct group *getgrent(void);

        //返回值:若成功则返回指针,若出错或到达文件结尾则返回NULL
void setgrent(void);

void endgrent(void);

setgrent函数打开组文件(如若它还没有被打开)并反饶它。getgrent函数从组文件中读下一个记录,如若该文件还没有打开则先打开它。endgrent函数关闭组文件

 

6.5 附加组ID

引入了附加组ID的概念,咱们不只能够属于口令文件记录项中组ID所对应的组,也可属于多达16个另外的组。文件访问权限检查相应被修改成:不只将进程的有效组ID与文件的组ID相比较,并且也将全部附加组ID与文件组ID相比较。

使用附加组ID的优势是没必要再显示地常常更改组。一个用户会参加多个项目,所以也就要同时属于多个组。此类状况是常常有的。

 

为了获取和设置附加组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 grouplist[]);

#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将各附加组ID添些到数组grouplist中,该数组中存放的元素最多为gidsetsize个。实际填写到数组中的附加组ID数由函数返回。

做为一个特例,如若gidsetsize为0,则函数值返回附加组ID数,而对数组grouplist则不做修改(这使调用者能够肯定grouplist数组的长度,以便进行分配)

setgroups可由超级用户调用以便为调用进程设置附加组ID表。grouplist是组ID数组,而ngroups指定了数组中的元素个数,ngroups的值不能大于NGROUPS_MAX

 

6.6 实现的区别

在FreeBSD中,阴影口令文件是/etc/master.passwd,可使用特殊命令编辑该文件,它反过来会从阴影文件爱你产生/etc /passwd的一个副本。另外,还会产生该文件的散列版本。/etc/pwd.db是/etc/passwd的散列版本,/etc/spwd.db是 /etc/master.passwd的散列版本。这些为大型系统提供了更好的性能。

可是Mac OS X只以单用户模式使用/etc/passwd和/etc/master.passwd。在维护系统时,单用户模式一般意味着不能提供任何系统服务。正常运行期间的多用户方式即netinfo目录服务提供对用户和组帐户信息的访问。

虽 然Linux和Solaris支持相似的阴影口令接口,但二者之间存在某些微妙的区别。例如,gr_uid在Solaris中定义为int类型,在 Linux中则定义为long int。另外一个区别是帐户不活动字段。Solaris将其定义为用户上次登陆依赖所通过的天数,而Linux则将其定义为到口令过时的尚余天数。

在 不少系统中,用户和组数据库是用网络信息服务(Network Information Service,NIS)实现的。这使管理员可编辑数据库的主副本,而后将它自动分发到组织中的全部服务器上。客户端系统能够联系服务器以查看用户和组的 有关嘻嘻。NIS+和轻量级目录访问协议(Lightweight Directory Access Protocal,LDAP)提供了相似功能。不少系统经过配置文件/etc/nsswitch.conf来控制管理每一类信息的方法。

 

6.7 其余数据文件

除口令文件和组文件外,UNIX系统还使用不少其余文件。例如,BSD网络软件有一个记录各网络服务器所提供服务的数据文件(/etc/services)

通常状况下,对于每一个数据文件至少有三个函数:

(1)get函数:读下一个记录,若是须要,还可打开该文件。

(2)set函数:打开相应数据文件(若是还没有打开),而后反饶该文件。

(3)end函数:关闭相应数据文件

另外,若是数据文件支持某种形式的关键字搜索,则会提供搜索具备指定关键字记录的例程

 

6.8 登录帐户信息

大多数UNIX系统都提供下列两个数据文件:utmp文件,它记录当前登录进系统的各个用户,wtmp文件,它跟踪各个登录和注销时间。

struct utmp{
    char ut_line[8];
    char ut_name[8];
    long ut_time;
};

登录时,login程序添些此类型结构,而后将其写入到utmp文件中,同时也将其填写到wtmp文件中。注销时,init进程将utmp文件中相应的记录擦除(每一个字节都填以0),并将一个新纪录填写到wtmp文件中。在wtmp文件的注销记录中,将ut_name字段清零。在系统从新启动时,以及更改系统时间和日期的先后,都在wtmp文件中添些特殊的记录项。

 

6.9 系统标识

POSIX.1定义了uname函数,它返回与当前主机和操做系统有关的信息

#include<sys/utsname.h>

int uname(struct utsname *name);

                //返回值:若成功则返回非负值,若出错则返回-1

经过该函数的参数想起传递一个utsname结构的地址,而后该函数填写此结构

struct utsname{
    char sysname[];
    char nodename[];
    char release[];
    char version[];
    char machine[];
};

 

6.10 时间和日期例程

time函数返回当前时间和日期

#include<time.h>

time_h time(time_t *calptr);

            //返回值:若成功则返回时间值,若出错则返回-1

 

与time函数相比,gettimeofday提供了更高的分辨率(最高为微秒级)。这对某些应用很重要

#include<sys/time.h>

int gettimeofday(struct timeval *restrict tp,void *restrict tzp);

                返回值:老是返回0

gettimeofday函数将当前时间存放在tp指向timeval结构中,而该结构存储秒和微秒。

struct timeval{

  time_t tv_sec;

  long tv_usec;

}

 

localtime和gmtime将日历时间转换。

#include<time.h>

struct tm *gmtime(const time *calptr);

struct tm *localtime(const time_t *calptr);

        //两个函数返回值:指向tm结构的指针

localtime和gmtime之间的区别是:localtime将日历时间转换成本地时间,而gmtime则将日历时间转换称国际标准时间的年、月、、、

 

函数mktime以本地时间做为参数,将其转换称time_t

#include<time.h>
time_t mktime(struct tm *tmptr);
      返回值:若成功则返回日历时间,若出错则返回-1

 

此外还有asctime,ctime,,mktime,strftime

相关文章
相关标签/搜索