1. 程式人生 > >Unix環境高級編程(四)數據系統文件和信息

Unix環境高級編程(四)數據系統文件和信息

fin 網絡 接口 protoent ati 返回 follow padding mktime

  本章主要介紹了Unix系統的正常運行要使用的與系統有關的數據文件和信息。如:口令文件,陰影文件、組文件、附加組、系統標識、時間和日期歷程。

  口令文件,即Unix系統用戶數據庫,存儲在/etc/passwd中,是一個ASCII文件,包含的字段信息在<pwd.h>定義的passwd數據結構中。  

  struct passwd {
   char *pw_name; /* username */
  char *pw_passwd; /* user password */
   uid_t pw_uid; /* user ID */


   gid_t pw_gid; /* group ID */
  char *pw_gecos; /* user information */
  char *pw_dir; /* home directory */
  char *pw_shell; /* shell program */
  };

  獲取口令文件函數,分別是根據用戶ID和用戶名。
  struct passwd *getpwuid(uid_t uid); //根據用戶ID
  struct passwd *getpwnam(const char *name); //根據用戶名


  查看整個口令文件,需要對口令文件進行遍歷。有如下函數:
  struct passwd *getpwent(void); //返回口令文件中的下一個記錄項
  void setpwent(void); //反繞文件,從文件頭開始
  void endpwent(void); //關閉文件

  可以用getpwent來實現getpwuid和getpwnam函數。寫個程序查看root用戶的相關信息及查看口令文件中所有用戶的用戶名,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <pwd.h>
 5 
 6 int main()
 7 {
 8     struct passwd *ppwd;
 9     struct passwd *ptr;
10     //獲取root用戶信息
11     ppwd = getpwnam("root");
12     if(ppwd == NULL)
13     {
14         perror("getpwnam() error");
15         exit(-1);
16     }
17     printf("root user information as follow:\n");
18     printf("user_name is: %s\n",ppwd->pw_name);
19     printf("user_passwd is: %s\n",ppwd->pw_passwd);
20     printf("user_uid is: %d\n",ppwd->pw_uid);
21     printf("user_gid is: %d\n",ppwd->pw_gid);
22     printf("user_gecos is: %s\n",ppwd->pw_gecos);
23     printf("user_dir is: %s\n",ppwd->pw_dir);
24     printf("user_shell is: %s\n",ppwd->pw_shell);
25     printf("*****************************\n");
26     //反轉口令文件,從文件頭開始
27     setpwent();
28     printf("Print all user name:\n");
29     //遍歷讀取口令文件
30     while((ptr = getpwent())!= NULL)
31     {
32       printf("%s\t",ptr->pw_name);
33     }
34     putchar(\n); 
35     //關閉口令文件
36     endpwent();
37     return 0;
38 }

測試結果如下:

技術分享圖片

  陰影文件,存放加密口令,至少包含有用戶名和加密口令。類似於口令文件,Unix在<shadow.h>都文件中針對陰影文件也提供類似的操作函數,但是只有超級用戶(root)才能調用訪問陰影文件的函數。陰影文件位於/etc/shadow文件中,文件結構及操作函數如下:

struct spwd {
char *sp_namp; /* Login name */
char *sp_pwdp; /* Encrypted password */
long sp_lstchg; /* Date of last change (measured in days since 1970-01-01 00:00:00 +0000 (UTC)) */
long sp_min; /* Min # of days between changes */
long sp_max; /* Max # of days between changes */
long sp_warn; /* # of days before password expire to warn user to change it */
long sp_inact; /* # of days after password expire until account is disabled */
long sp_expire; /* Date when account expires (measured in days since 1970-01-01 00:00:00 +0000 (UTC)) */
unsigned long sp_flag; /* Reserved */
};

struct spwd *getspnam(const char *name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);
寫個程序查看root用戶的加密口令及所有用戶的用戶名及加密口令如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <shadow.h>
 5 
 6 int main()
 7 {
 8     struct spwd *pspwd;
 9     struct passwd *ptr;
10     pspwd = getspnam("root");
11     if(pspwd == NULL)
12     {
13         perror("getspnam() error");
14         exit(-1);
15     }
16     printf("root user information as follow:\n");
17     printf("user_name is: %s\n",pspwd->sp_namp);
18     printf("user_passwd is: %s\n",pspwd->sp_pwdp);
19     printf("*****************************\n");
20     setspent();
21     while((pspwd = getspent()) != NULL)
22     {
23         printf("user_name is: %s\n",pspwd->sp_namp);
24         printf("user_passwd is: %s\n",pspwd->sp_pwdp);
25     }
26     endspent();
27     return 0;
28 }

只能在超級用戶下運行此程序,在普用戶下提升權限不夠。執行結果如下所示:

技術分享圖片

組文件,即組數據庫文件,存儲在/etc/group中,結構及操作函數包含在<grp.h>頭文件中。具體結構和操作函數如下:

struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* group members */
};

struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
搜索整個文件組函數:
struct group *getgrent(void);
void setgrent(void);
void endgrent(void);
寫個程序,打印出組id為0的組名稱及遍歷整個組文件,輸出組名稱及組id。程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <grp.h>
 5 #include <errno.h>
 6 
 7 int main()
 8 {
 9     struct group *pgrg;
10     struct group *ptr;
11     gid_t gid = 0;
12     char *username = NULL;
13     //根據gid進行查詢
14     pgrg = getgrgid(gid);
15     if(pgrg == NULL)
16     {
17         perror("getgrgid() error");
18         exit(-1);
19     }
20     printf("group name is: %s\n",pgrg->gr_name);
21     printf("group passwd is: %s\n",pgrg->gr_passwd);
22     printf("group gid is: %d\n",pgrg->gr_gid);
23     setgrent();
24     //遍歷整個組文件
25     while((ptr = getgrent()) != NULL)
26     {
27         printf("group name is: %s\t",ptr->gr_name);
28         printf("group gid is: %d\n",ptr->gr_gid);
29     }
30     endgrent();
31     return 0;
32 }

程序執行結果如下所示:

技術分享圖片

  附加組,一個用戶可以屬於多個組,這樣可以參加多項工作,優點是不必顯式地經常更改組。用戶登錄時候,系統按照口令文件記錄項中的數值組ID,賦給實際組ID,可以在任何時候通過newgrp更改組ID。為了獲取和設置附加組ID,提供操作函數如下:

int getgroups(int size, gid_t list[]); //將各個附加組ID填寫到數組grouplist中
int setgroups(size_t size, const gid_t *list); //由超級用戶調用,以便為調用進程設置附加組ID表
int initgroups(const char *user, gid_t group); //調用setgroups,確定其組的成員關系

其他數據文件 除了口令文件和組文件外,Linux也使用很多其他文件,一般情況下,這些文件都至少支持三個函數:
(1)get函數:讀文件中的下一個記錄項。
(2)set函數:將文件偏移量設置到文件起始處。
(3)end函數:關閉系統文件。
如果該文件支持關鍵字檢索,例如口令文件支持基於用戶名和用戶ID的檢索,因此實現了接口getpwnam和getpwuid函數,就會支持相應的函數。

存取系統數據文件的類似例程
說明 數據文件 頭文件 結構 附加的關鍵字查找函數
口令 /etc/passwd <pwd.h> passwd getpwnam 、getpwuid
/etc/group <grp.h> group getpwnam、getpwuid
陰影 /etc/shadow <shadow.h> spwd getspnam
主機 /etc/hosts <netdb.h> hostent gethostbyname、gethostbyaddr
網絡 /etc/networks <netdb.h> netent getnetbyname、getnetbyaddr
協議 /etc/protocols <netdb.h> protoent getprotobyname、getprotobyaddr
服務 /etc/services <netdb.h> servent getservbyname、getservbyad

  系統標識,uname函數返回與當前主機和操作系統有關的信息,函數字在<sys/utsname.h>頭文件中定義。utsname結構信息和操作函數如下:

  struct utsname {
  char sysname[]; /* Operating system name (e.g., "Linux") */
   char nodename[]; /* Name within "some implementation-defined network" */
  char release[]; /* OS release (e.g., "2.6.28") */
  char version[]; /* OS version */
  char machine[]; /* Hardware identifier */
   #ifdef _GNU_SOURCE
   char domainname[]; /* NIS or YP domain name */
   #endif
  };

  int uname(struct utsname *buf);

寫個程序獲取本機當前主機和操作系統信息,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/utsname.h>
 5 #include <errno.h>
 6 
 7 int main()
 8 {
 9     struct utsname  name;
10     if(uname(&name) == -1)
11     {
12         perror("uname() error");
13         exit(-1);
14     }
15     printf("system name is: %s\n",name.sysname);
16     printf("node name is: %s\n",name.nodename);
17     printf("release is: %s\n",name.release);
18     printf("version is: %s\n",name.version);
19     printf("machine is: %s\n",name.machine);
20     return 0;
21 }

程序執行結果如下:

技術分享圖片

  時間和日期,Unix內核提供的基本時間服務是計算自國際標準時間公元1970年1月1日00:00:00以來經過的秒數,基本數據類型是time_t,稱為日歷時間,包括時間和日期,將時間和日期作為一個量值進行保存。類型定義如下:

#ifndef __TIME_T #define __TIME_T /* 避免重復定義 time_t */ typedef long time_t; /* 時間值time_t 為長整型的別名*/ #endif 相關操作函數有: #include <time.h>

time_t time(time_t *t); //返回當前時間及日期

#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz); //相比time提供更高的分辨率,微妙級

int settimeofday(const struct timeval *tv, const struct timezone *tz);

struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};

取得了這種以秒計的整型時間後,通常調用另外一個時間函數將其轉化為人們可讀的時間和日期。時間的結構及操作函數有:

struct tm {
int tm_sec; /* seconds */
int tm_min; /* minutes */
int tm_hour; /* hours */
int tm_mday; /* day of the month */
int tm_mon; /* month */
int tm_year; /* year */
int tm_wday; /* day of the week */
int tm_yday; /* day in the year */
int tm_isdst; /* daylight saving time */
};

char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
struct tm *gmtime(const time_t *timep); //轉換為國際標準時間
struct tm *localtime(const time_t *timep); //轉換為本地實際
time_t mktime(struct tm *tm);
size_t strftime(char *s, size_t max, const char *format,const struct tm *tm); //對tm進行格式化輸出到一個字符串
函數之間的關系如下圖:

技術分享圖片

寫一個程序鞏固時間函數,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <time.h>
 5 #include <errno.h>
 6 #include <string.h>
 7 
 8 int main()
 9 {
10     time_t now;
11     struct tm *ptime;
12     char *ptstr;
13     char timebuf[100];
14     memset(timebuf,0,100);
15    //獲取時間,秒數
16     now = time(&now);
17     printf("Global time is:\n");
18    //轉化為國際時間
19     ptime = gmtime(&now);
20     ptime->tm_year += 1900;
21     ptime->tm_mon += 1;
22     printf("%d-%d-%d %d:%d:%d\n",ptime->tm_year,ptime->tm_mon,ptime->tm_mday,
23            ptime->tm_hour,ptime->tm_min,ptime->tm_sec);
24     printf("Local time is:\n");
25     //轉化為本地時間
26     ptime = localtime(&now);
27     ptime->tm_year += 1900;
28     ptime->tm_mon += 1;
29     printf("%d-%d-%d %d:%d:%d\n",ptime->tm_year,ptime->tm_mon,ptime->tm_mday,
30            ptime->tm_hour,ptime->tm_min,ptime->tm_sec);
31     //將tm結構轉換為字符串
32     ptstr = asctime(ptime);
33     printf("tm time stirng is: %s",ptstr);
34     //將time_t類型轉換為字符串
35     ptstr = ctime(&now);
36     printf("time_t time string is: %s",ptstr);
37     //date 格式化輸出時間
38     strftime(timebuf,100,"%YYear %mMonth %dDay %A %X",ptime);
39     printf("time buf is:%s\n",timebuf);
40     return 0;
41 }

程序執行結果如下所示:

技術分享圖片

總結:加深對Unix的系統數據文件及時間日期的認識,能夠調用系統函數獲取系統相關數據。

Unix環境高級編程(四)數據系統文件和信息