zoukankan      html  css  js  c++  java
  • APUE学习笔记:第六章 系统数据文件和信息

    6.1 引言

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

    6.2 口令文件

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

    用户名    char *pw_name

    加密口令   char *pw_passwd

    数值用户ID  uid_t pw_uid

    数值组ID   gid_t pw_gid

    注释字段    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

  • 相关阅读:
    [bbk2908]第4集 Chapter 03 介绍RAC的体系结构
    [bbk3011]第8集 Chapter 05 介绍RAC安装过程概述
    [bbk3100]第7集 Chapter 04 介绍RAC中CVU工具的使用
    [bbk2907]第3集 Chapter 02 RAC的安装过程中需要注意的要点
    [bbk2905]第1集 Chapter 01 介绍RAC概述
    [bbk2906]第2集 Chapter 02 介绍RAC概述
    RAC之CRS架构简介
    NOIP普及组2017比赛总结
    struct和typedef
    KMP详解(转)
  • 原文地址:https://www.cnblogs.com/wbingeek/p/3841972.html
Copyright © 2011-2022 走看看