[APUE]系統數據文件與信息

1、口令文件

UNIX口令文件包含下表中的各個字段,這些字段包含在<pwd.h>頭文件中定義的passwd結構體中。
node

因爲歷史緣由,口令文件是/bin/passwd,並且是一個文本文件,每一行都包括了上表中的七個字段,字段之間用":"分隔,例如一個文件中可能有如下三行:
root:jheVopR58x9Fx:0:1:The superuser:/:/bin/sh n o b o d y : * : 6 5 5 3 4 : 6 5 5 3 4 : : / : stevens:3hKVD8R58r9Fx:224:20:Richard Stevens:/home/stevens:/bin/ksh
對於這些登錄項須要注意如下幾點:算法

  • 加密口令字段是由單向不可逆算法加密產生的13個可打印字符(在64字符集中[a-zA-Z0-9./])。用nobody用戶ID和組ID都是你們均可讀寫的文件
  • 口令文件中某些字段多是空,若是密碼口令爲空則說明用戶沒有口令。nobody用戶有兩個空字段:註釋字段和初始shell字段,空註釋字段不產生任何影響,空shell字段表示取系統默認值,通常是/bin/sh。
  • 支持finger命令的某些系統支持註釋字段中的附加信息,其中各部分之間用逗號分隔:用戶姓名,用戶地址,用戶電話。若是註釋字段中的用戶姓名是"&",則它被替換爲登錄名。例如能夠有以下記錄:
stevens:3hKVD8R58r9Fx:224:20:Richard &, B232, 555-1111, 555-2222:
/ h o m e / s t e v e n s : / b i n / k s h

POISX.1只定義了兩個存取口令文件中信息的函數:參數爲用戶登陸名或者是數值ID:
```
#include <sys/types.h>
#include <pwd.h>shell

struct passwd getpwuid(uid_t uid);
struct passwd
getpwnam(const char *name);
返回值:成功則爲指針,出錯爲NULL
getpwuid由ls命令使用,用於從i節點中的數值用戶ID獲取用戶登陸名。getpwnam在輸入登陸名時由login程序使用。 可用如下三個函數查看整個口令文件:
#include <sys/types.h>
#include <pwd.h>bootstrap

struct passwd *getpwent(void);
返回值:成功返回指針,出錯或到達文件尾端返回NULL
void setpwent(void);
void endpwent(void);
```
調用getpwent時,返回口令文件中的下一個記錄,它返回一個由它填寫的passwd結構的指針,每次調用此函數都重寫該結構
setpwdent函數定位文件到開始處,endpwent關閉這些文件。再用getpwent查看完口令文件後必定要用endpwent關閉這些文件。getpwent函數並不知道什麼時候關閉這些文件。數組

2、陰影文件

有些系統爲了避免讓黑客獲得原始加密口令會將加密口令存放在另外一個一般被稱爲陰影口令的文件中(如:/etc/shadow)。該文件至少要包含用戶名和加密口令。與該口令相關的信息也能夠存放在該文件中。例若有些系統會要求用戶在必定時間間隔後修改口令,而這個時間間隔長度就存在陰影口令文件中。
陰影口令文件不該是通常用戶能夠讀取的。僅有少數幾個程序須要存取加密口令文件,例如login和passwd。這些程序一般設置-用戶-ID爲root。有了陰影口令後,普通口令文件/etc/passwd可由各用戶自由讀取。網絡

3、組文件

UNIX組文件包含了以下字段,這些字段包含在<grp.h>z中所定義的group結構中。
app

gr_mem是一個指針數組,其中的指針各指向一個屬於該組的用戶名,該數組以null結尾。
能夠由POSIX.1定義的兩個函數來查看組名或數值組ID。
```
#include <sys/types.h>
#include <grp.h>函數

struct group getgrgid(gid_t gid);
srruct group
getgrnam(const char *name);
返回值:成功返回group指針,出錯NULL
如同對口令文件進行操做的函數同樣,這兩個函數一般也返回指向一個**靜態變量**的指針,在每次調用時都重寫該靜態變量。 如下三個函數相似與針對口令文件的函數,用來查看整個組文件:
#include <sys/types.h>
#include <grp.h>學習

struct group *getgrent(void);
返回值:成功則爲指針,出錯或到達文件尾端則爲NULL
void setgrent(void);
void endgrent(void);
```ui

setgrent打開組文件並定位到文件開始,getgrent從組文件讀下一條記錄,若是該文件未打開則先打開它,endgrent關閉組文件。

4、添加組ID

在UNIX中,對組的使用已經作了一些修改。在V7中一個用戶任什麼時候候只能屬於一個組,當用戶登陸時,系統就按照口令文件中用戶相關聯的組ID賦給他實際組ID。能夠在任什麼時候候執行netgrp更改組。若是newgrp命令執行成功則實際組更改成新的組,它將用於後續文件權限檢查。執行不帶任何權限的newgrp則可返回到原來的組。
這種組的成員關係一直持續到1983年左右,此時4.2BSD引入了添加組ID的概念,咱們不只能夠屬於口令記錄中組ID所對應的組,還能夠屬於多至16個另外的組。文件權限檢查並修改成:不只將進程的有效組ID與文件的zuID比較,並且也將全部添加組ID與文件的組ID比較
使用添加組ID的一個優點就是不用顯式的修改用戶組ID。
爲了存取和設置添加組ID,一般使用如下三個函數:
```
#include <sys/types.h>
#include <unistd.h>

int getgroups(int gidsetsize, gid_t grouplist[]);
返回值:成功則爲添加的組數量,出錯爲-1.
int setgroups(int ngroups, const gid_t grouplist[]);
int initgroups(const char *username, gid_t basegid);
兩個函數返回值:成功0,出錯-1.
```
getgroups將進程所屬用戶的各添加組填到數組grouplist中,填入該數組的ID數最多gidsetsize。實際填入的數量由該函數返回。若是系統常數NGROUPS_MAX爲0,則返回0,這並不表示錯誤。
setgroups可由root用戶調用爲調用進程設置添加組ID表,grouplist是組ID數組,ngroups是數組元素數。
一般只有initgroups調用setgroups,initgroups讀整個組文件而後對username肯定其組的成員關係。而後它調用setgroups爲該用戶初始化添加組ID表。由於initgroups調用setgroups,因此只有root用戶才能調用initgroups,除了在組文件中找username是成員的組,initgroups也在添加組ID表中包括了basegid。basegid是username在口令文件中的組ID。

5、其餘數據文件

除了之上咱們學習的口令文件和組文件,UNIX系統還有其餘一下數據文件。對於這些數據文件的界面都與上述對口令文件和組文件的類似。
通常狀況下數據文件都有三個函數:

  • get函數,讀下一個記錄,這種函數一般返回一個指向靜態存儲類結構的指針,若是要保存其內容則須要複製它。當到達文件尾端時返回空指針。
  • set函數:打開數據文件並將指針移到文件起始位置
  • end函數:關閉數據文件。
    下表中列出了一些SVR4和4.3+BSD支持的例程。在表中列出了針對口令文件和組文件的函數,也列出了一些與網絡有關的函數。

6、登陸會計

大多數UNIX系統都提供一下兩個數據文件:

  • utmp文件,該文件記錄當前登陸進系統的各個用戶;
  • wtmp文件,該文件跟蹤各個登陸和註銷事件。
    在V7中,包含下列結構的一個二進制記錄寫入這兩個文件中:
struct utmp {
  char ut_line[8]; /* tty line: "ttyh0","ttyd1","ttyp0"...... */
  char ut_name[8]; /* login name */
  long ut_time; /* second  since Epoch */
}

登陸時,login程序填寫這樣一個結構而後填入utmp文件中,同時填入到wtmp中。註銷時,init進程將utmp文件中相應的記錄擦除(每一個字節都爲0),並將一個新紀錄填入到wtmp中。讀wtmp中該註銷記錄,其ut_name清除爲0。在系統再啓動時,以及更改系統日期和時間的先後,都在wtmp中填寫特殊的記錄項。who命令讀utmp文件,並以可讀格式打印其內容。後來的UNIX系統版本提供last命令,它讀wtmp文件並打印所選擇的記錄。

7、系統標識

POSIX.1定義了uname函數,返回與主機和操做系統相關的信息。
#include <sys/utsname.h> int uname(struct utsname *name); 返回值: 成功爲非負值,出錯爲-1.
經過參數向該函數傳一個struct uname結構的地址,而後該函數填寫該參數。POSIX.1只定義了該結構至少須要的字段(都是字符數組),每一個數組的長度由實現來決定。 歷史上,V系統爲每一個數組分配9個字節,其中最後一個字節是null結束符。
struct utsname{ char sysname[9]; /* name of the operating system */ char nodename[9]; /* name of this node */ char release[9]; /* current release of operating system */ char version[9]; /* current version of operating system */ char machine[9]; /* name of hardware type */ }
utsname結構中的信息一般由uname命令打印。
伯克利類的版本提供了gethostname函數,只返回主機名,該名字一般就是TCP/IP網絡上主機的名字。
#include <unistd.h> int gethostname(char *name, int namelen); 返回值:成功0,出錯-1.
經過name返回的字符串以null結束(除非沒有提供足夠的空間)。<sys/param.h>中的常數MAXHOSTNAMELEN規定了此名字的最大長度(一般是64字節)。
hostname命令能夠用來存取和設置主機名(root用戶用一個相似的函數sethostname來設置主機名),主機名一般在系統自舉(bootstrapping)時設置,它由/etc/rc取自一個啓動文件。

8、時間和日期例程

time函數返回當前日期和時間
#include <time.h> time_t time(time_t *calptr); 返回值:成功爲時間值(時間戳),出錯爲-1.
若是參數不是NULL,則返回的時間值也保存在calptr指向的單元中
一旦取得時間戳後,一般要調用另一個時間函數將其轉換爲人們可讀的時間和日期。下圖說明了各時間函數之間的關係。(圖中虛線標註的函數一般受環境變量TZ的影響)

兩個函數localtime和gmtime將時間戳轉換爲以年、月、日、時、分、秒、週日表示的時間,並將這些存放在tm結構中。
struct tm { /* a broken down time */ int tm_sec; /* seconds after the minute:[0,61] */ int tm_min; /* minutes after the hour:[0,59] */ int tm_hour; /* hours after the midnight:[0,23] */ int tm_mday; /* day of the month:[1,31] */ int tm_mon; /* month of the year:[0,11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday:[0,6] */ int tm_yday; /* days since January 1:[0,365] */ int tm_isdst; /* daylight saving time flag:<0, 0 >0 夏時制標誌值 */ }
秒能夠超過59的緣由是能夠表示潤秒。若是夏時制生效,則tm_isdst爲正,若是已非夏時制則爲0,若是此信息不可用則爲負。
#include <time.h> struct tm *gmtime(const time_t *calptr); struct tm *localtime(const time_t *calptr);
localtime和gmtime的區別在於localtime將時間戳轉換爲本地時間(考慮到本時區和夏時制標誌)。而gmtime轉爲了國際標準時間。
函數mktime以本地時間的年、月、日、時、分、秒等做爲參數將其轉爲時間戳:
#include <time.h> time_t mktime(struct tm *tmptr);
asctime和ctime函數產生形式的26字節字符串,這與date命令的系統默認輸出形式相似;
Tue Jan 14 10:15:03 1992\n\0

#include <time.h>
char *asctime(const struct tm *tmptr);
char *ctime(const time_t *calptr);
返回值:指向null結尾的字符串

最後一個時間函數是strftime,它是很是複雜的printf類的時間值函數
#include <time.h> size_t strftime(char *buf, size_t maxsize, const char *format, const struct tm *tmptr); 返回值:如有空間,則存入數組的字符數,不然爲0.
最後一個參數是要格式化的時間指針。格式化結果存放在一個長度爲maxsize個字符的buf數組中,若是buf長度足以存放格式化結果及一個null終止符,則該函數返回在buf中存放的字符數(不包括null終止符),不然該函數返回0。
format參數控制時間值的格式,如同printf函數同樣,變換格式說明是百分號後面跟一個特定字符。format中其餘自負原樣輸出。兩個連續的百分號在輸出中產生一個百分號。與printf函數不一樣的是,每一個變換說明產生一個定長輸出字符串,在format字符串中沒有字段寬度修飾符。下表列出了21種ANSI C規定的變換說明

表中第三列的數據來自於SVR4,對應於下列日期與時間,執行strftime函數所得的結果爲:
Tue Jan 14 10:15:30 MST 1992 表中%U是相應日期在該年中所屬週數,包含該年中第一個星期日的周是第一週。%W也是相應日期在該年中所屬的週數,不一樣的是包含第一個星期一的周爲第一週。 若是定義了環境變量TZ,則localtime、mktime、ctime、strftime(即時間函數關係圖中虛線標準的函數)將使用其值代替系統默認時區。若是定義TZ爲空串(即TZ=),則使用國際標準時間。TZ的值常相似於TZ=EST5ETD,可是POSIX.1容許更爲詳細的說明。

相關文章
相關標籤/搜索