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

相關文章
相關標籤/搜索