從零開始UNIX環(huán)境高級(jí)編程(6):系統(tǒng)數(shù)據(jù)文件和信息

0. 思維導(dǎo)圖

系統(tǒng)數(shù)據(jù)文件和信息

1. 口令文件

從零開始UNIX環(huán)境高級(jí)編程(1)中,已經(jīng)介紹過口令文件(/etc/passwd),本小節(jié)主要學(xué)習(xí)如果通過函數(shù)去獲取口令文件的信息。

1.1 passed結(jié)構(gòu)體

存放口令文件信息的結(jié)構(gòu)體為passed,定義在pwd.h中,不同平臺(tái)實(shí)現(xiàn)的字段也會(huì)不一樣

  • Mac OS X中passwd結(jié)構(gòu)體

      struct passwd {
              char    *pw_name;               /* user name */
              char    *pw_passwd;             /* encrypted password */
              uid_t   pw_uid;                 /* user uid */
              gid_t   pw_gid;                 /* user gid */
              __darwin_time_t pw_change;              /* password change time */
              char    *pw_class;              /* user access class */
              char    *pw_gecos;              /* Honeywell login info */
              char    *pw_dir;                /* home directory */
              char    *pw_shell;              /* default shell */
              __darwin_time_t pw_expire;              /* account expiration */
      };
    
  • Ubuntu中passwd結(jié)構(gòu)體

         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 */
         };
    

1. 2 獲取passwd

1.2.1 獲取單個(gè)用戶信息

獲取passwd中單個(gè)用戶信息的函數(shù)有兩個(gè):使用getpwnam獲取用戶信息時(shí),需要傳入該用戶的名稱作為參數(shù);使用getpwuid獲取用戶信息時(shí),需要傳入該用戶的id作為參數(shù)。

   struct passwd *getpwnam(const char *name);
   struct passwd *getpwuid(uid_t uid);
  • 示例代碼

    以getpwnam函數(shù)為例,看下如何獲取單個(gè)用戶信息

#include "apue.h"
#include <pwd.h>

int main(int argc, char const *argv[])
{
    struct  passwd * info;
    info = getpwnam("ckt");
    if (info == NULL) {
        printf("get passwd fail\n");
        return 0;
    }
    printf("pw_uid = %d   pw_gid = %d   pw_gecos = %s   pw_dir = %s   pw_shell = %s \n"
        , info->pw_uid, info->pw_gid, info->pw_gecos , info->pw_dir , info->pw_shell);
    return 0;
}
  • 運(yùn)行結(jié)果

    使用getpwnam函數(shù)獲取到的信息和/etc/passwd中存放的信息一致

ckt@ubuntu:~/work/unix/code/chapter6$ ./passwdinfo_test
pw_uid = 1000   pw_gid = 1000   pw_gecos = ckt,,,   pw_dir = /home/ckt   pw_shell = /bin/bash 

ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/passwd | grep ckt
ckt:x:1000:1000:ckt,,,:/home/ckt:/bin/bash

1.2.2 遍歷整個(gè)口令文件

上面兩個(gè)函數(shù)只能獲取單個(gè)用戶信息,如果要得到整個(gè)口令文件,需要用到下面3個(gè)函數(shù),而getpwnam和getpwuid中也會(huì)對(duì)這3個(gè)函數(shù)進(jìn)行封裝。

   struct passwd *getpwent(void);

   void setpwent(void);

   void endpwent(void);
  • 示例代碼
    自定義一個(gè)getpwuid函數(shù)名為my_getpwuid,里面會(huì)調(diào)用到setpwent、getpwent、endpwent這3個(gè)函數(shù)
#include "apue.h"
#include <pwd.h>

struct passwd *my_getpwuid(uid_t uid)
{
    struct passwd * ptr;

    setpwent();

    while((ptr = getpwent()) != NULL)
        if (ptr->pw_uid == uid)
            break;

    endpwent();

    return ptr;
}

int main(int argc, char const *argv[])
{
    struct  passwd * info;
    info = my_getpwuid(1000);
    if (info == NULL) {
        printf("get passwd fail\n");
        return 0;
    }
    printf("pw_name = %s   pw_gid = %d   pw_gecos = %s   pw_dir = %s   pw_shell = %s \n"
        , info->pw_name, info->pw_gid, info->pw_gecos , info->pw_dir , info->pw_shell);
    return 0;
}
  • 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc myget_passwdinfo.c -o myget_passwdinfo
ckt@ubuntu:~/work/unix/code/chapter6$ ./myget_passwdinfo
pw_name = ckt   pw_gid = 1000   pw_gecos = ckt,,,   pw_dir = /home/ckt   pw_shell = /bin/bash 

ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/passwd | grep 1000
ckt:x:1000:1000:ckt,,,:/home/ckt:/bin/bash

2. 陰影文件

系統(tǒng)將登陸密碼進(jìn)行加密,并將加密后的口令存放在陰影文件/etc/shadow中,查看陰影文件需要root權(quán)限

ckt@ubuntu:~/work/unix/code/chapter6$ sudo cat /etc/shadow | grep ckt
ckt:$1$6BZmOQTp$RV1GboT5SxQR1hV3ZauQ..:16401:0:99999:7:::

2.1 spwd結(jié)構(gòu)體

和口令文件一樣,陰影文件也有對(duì)應(yīng)的結(jié)構(gòu)體spwd,spwd結(jié)構(gòu)體定義在 <shadow.h> 中

       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 expires
                                 to warn user to change it */
           long  sp_inact;    /* # of days after password expires
                                 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 */
       };

2.2 獲取spwd結(jié)構(gòu)體函數(shù)

   // 通過用戶名稱獲取單個(gè)用戶的spwd信息
   struct spwd *getspnam(const char *name);

   //以下三個(gè)函數(shù)用戶獲取整個(gè)/etc/shadow信息
   struct spwd *getspent(void);

   void setspent(void);

   void endspent(void);
  • 示例代碼
#include "apue.h"
#include <shadow.h>

int main(int argc, char const *argv[])
{
    struct  spwd * info;
    info = getspnam("ckt");
    if (info == NULL) {
        printf("get passwd fail\n");
        return 0;
    }
    printf("sp_pwdp = %s   sp_lstchg = %ld   sp_min = %ld   sp_max = %ld   sp_warn = %ld\
        sp_inact = %ld  sp_expire = %ld  sp_flag = %ld \n", info->sp_pwdp, info->sp_lstchg,
        info->sp_min , info->sp_max , info->sp_warn , info->sp_inact , info->sp_expire , info->sp_flag);
    return 0;
}
  • 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ sudo ./shadowinfo_test
sp_pwdp = $1$6BZmOQTp$RV1GboT5SxQR1hV3ZauQ..   sp_lstchg = 16401   sp_min = 0   sp_max = 99999   sp_warn = 7        sp_inact = -1  sp_expire = -1  sp_flag = -1 
ckt@ubuntu:~/work/unix/code/chapter6$ sudo cat /etc/shadow | grep ckt
ckt:$1$6BZmOQTp$RV1GboT5SxQR1hV3ZauQ..:16401:0:99999:7:::

3. 組文件

組文件/etc/group用來存放系統(tǒng)中所有的group信息
/etc/group文件格式:group_name:password:GID:user_list

ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/group | grep ckt
ckt:x:1000:

3.1 group結(jié)構(gòu)體

group結(jié)構(gòu)體定義在<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 */
   };

3.2 獲取group函數(shù)

   //通過組名稱獲取當(dāng)個(gè)group信息
   struct group *getgrnam(const char *name);
   //通過組ID獲取當(dāng)個(gè)group信息
   struct group *getgrgid(gid_t gid);

   //使用以下3個(gè)函數(shù),獲取整個(gè)/etc/group信息
   struct group *getgrent(void);

   void setgrent(void);

   void endgrent(void);
  • 示例代碼
#include "apue.h"
#include <grp.h>

int main(int argc, char const *argv[])
{
    struct group * info;
    info = getgrnam("zhm");
    if (info == NULL) {
        printf("get group fail\n");
        return 0;
    }

    printf("gr_name = %s  gr_passwd = %s  gr_gid = %d  gr_mem = %s \n", 
        info->gr_name, info->gr_passwd, info->gr_gid, *info->gr_mem);
    return 0;
}
  • 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc groupinfo_test.c -o groupinfo_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./groupinfo_test
gr_name = zhm  gr_passwd = x  gqr_gid = 1001  gr_mem = ckt 
ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/group | grep zhm
zhm:x:1001:ckt

4. 附屬組

一個(gè)用戶可以屬于多個(gè)組,通過id命令可以查看用戶的主組和附屬組

ckt@ubuntu:~/work/code/mt6737$ id ckt
uid=1000(ckt) gid=1000(ckt) groups=1000(ckt),1001(zhm)

4.1 getgroups函數(shù)

使用getgroups函數(shù),可以獲取進(jìn)程所屬用戶的主組和附屬組ID

int getgroups(int size, gid_t list[]);
  • 示例代碼
#include "apue.h"
#include <sys/types.h>
#include <unistd.h>

#define MAX_GROUP_SIZE 5

int main(int argc, char const *argv[])
{
    int i = 0, group_size = 0;
    int group_list[MAX_GROUP_SIZE];

    if ((group_size = getgroups(MAX_GROUP_SIZE, group_list)) > 0)
    {
        for (i = 0; i < group_size; i++)
        {
            printf("group_list[%d] : %d \n", i , group_list[i]);
        }
    }
    else 
        printf("get groups fail\n");

    return 0;
}
  • 運(yùn)行結(jié)果

分別獲取用戶ckt和root的主組ID和附屬組ID,和使用命令id得到的結(jié)果一致

ckt@ubuntu:~/work/unix/code/chapter6$ cc getgroups_test.c -o getgroups_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./getgroups_test
group_list[0] : 1000 
group_list[1] : 1001 
ckt@ubuntu:~/work/unix/code/chapter6$ sudo ./getgroups_test
group_list[0] : 0 

ckt@ubuntu:~/work/unix/code/chapter6$ id ckt
uid=1000(ckt) gid=1000(ckt) groups=1000(ckt),1001(zhm)
ckt@ubuntu:~/work/unix/code/chapter6$ id root
uid=0(root) gid=0(root) groups=0(root)

5. 其他數(shù)據(jù)文件

除了上面介紹的一些文件,系統(tǒng)中還有很多其他類型的數(shù)據(jù)文件,它們的操作方式都是類似的。各個(gè)數(shù)據(jù)的文件名稱、結(jié)構(gòu)體和操作函數(shù),如下表:

系統(tǒng)數(shù)據(jù)文件

6. 登錄賬戶記錄

utmp文件(/var/run/utmp)記錄當(dāng)前登錄到系統(tǒng)的各個(gè)用戶;wtmp文件(/var/log/wtmp)跟蹤各個(gè)登錄和注銷事件

我們可以使用who命令讀取utmp文件,使用last命令讀取wtmp文件

ckt@ubuntu:~/work/unix/code/chapter6$ who
ckt      tty7         2017-03-02 17:00
ckt      pts/0        2017-03-02 18:04 (:0)
ckt      pts/1        2017-03-02 20:58 (:0)
ckt      pts/2        2017-03-02 21:37 (:0)

ckt@ubuntu:~/work/unix/code/chapter6$ last
ckt      pts/2        :0               Thu Mar  2 21:37   still logged in   
ckt      pts/1        :0               Thu Mar  2 20:58   still logged in   
ckt      pts/0        :0               Thu Mar  2 18:04   still logged in   
reboot   system boot  3.13.0-32-generi Thu Mar  2 16:58 - 00:26  (07:28)    
ckt      pts/2        :0               Thu Mar  2 03:19 - 03:45  (00:26)    
ckt      pts/1        :0               Thu Mar  2 00:46 - 03:19  (02:33)    
ckt      pts/1        :0               Thu Mar  2 00:40 - 00:40  (00:00)    
ckt      pts/0        :0               Thu Mar  2 00:17 - 03:45  (03:28)    
reboot   system boot  3.13.0-32-generi Thu Mar  2 00:16 - 03:45  (03:29)    
ckt      pts/1        :0               Thu Mar  2 00:08 - crash  (00:07)    

7. 系統(tǒng)標(biāo)識(shí)

使用uname命令可以查看操作系統(tǒng)有關(guān)信息,同樣我們也可以通過函數(shù)去獲取這些信息。

ckt@ubuntu:~/work/unix/code/chapter6$ uname -a
Linux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:51:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

使用hostname命令可以獲取主機(jī)名

ckt@ubuntu:~/work/unix/code/chapter6$ hostname 
ubuntu

7.1 uname結(jié)構(gòu)體

uname結(jié)構(gòu)體用來存放操作系統(tǒng)有關(guān)信息,定義在#include <sys/utsname.h>

   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
   };

7.2 uname函數(shù)

通過uname函數(shù)獲取信息并返回這些值到buf中

int uname(struct utsname *buf);
  • 示例代碼
#include "apue.h"
#include <sys/utsname.h>

int main(int argc, char const *argv[])
{
    struct utsname info;
    if (uname(&info) == -1)
    {
        printf("get uname fail\n");
        return 0;
    }

    printf("%s %s %s %s %s", info.sysname, info.nodename, info.release, info.version, info.machine);

#ifdef _GNU_SOURCE
    printf(" %s", info.domainname);
#endif

    printf("\n");

    return 0;
}
  • 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc getuname_test.c -o getuname_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./getuname_test
Linux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:51:20 UTC 2014 x86_64

ckt@ubuntu:~/work/unix/code/chapter6$ uname -a
Linux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:51:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

7.3 gethostname函數(shù)

 int gethostname(char *name, size_t len);
  • 示例代碼
#include "apue.h"
#include <limits.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    char hostname[HOST_NAME_MAX];
    int i = 0;

    if (gethostname(hostname, HOST_NAME_MAX) == -1)
        printf("get hostname fail\n");

    while(hostname[i] != '\0')
    {
        printf("%c", hostname[i]);
        i++;
    }
    printf("\n");
    return 0;
}
  • 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc gethostname_test.c -o gethostname_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./gethostname_test
ubuntu
ckt@ubuntu:~/work/unix/code/chapter6$ hostname 
ubuntu

8. 時(shí)間和日期歷程

各個(gè)函數(shù)直接的關(guān)系

8.1 獲取UTC時(shí)間

8.1.1 time函數(shù)

time函數(shù)定義在<time.h>中,用來獲取UTC時(shí)間

time_t time(time_t *t);

8.1.2 clock_gettime函數(shù)

當(dāng)clk_id設(shè)置為CLOCK_REALTIME時(shí),clock_gettime的功能和time類似,在系統(tǒng)支持高精度時(shí)間值的情況下, clock_gettime可能比time函數(shù)得到更高精度的時(shí)間值

int clock_gettime(clockid_t clk_id, struct timespec *tp);
  • clk_id參數(shù)說明
標(biāo)識(shí)符 說明
CLOCK_REALTIME 實(shí)時(shí)系統(tǒng)時(shí)間
CLOCK_MONOTONIC 不帶負(fù)跳數(shù)的實(shí)時(shí)系統(tǒng)時(shí)間
CLOCK_PROCESS_CPUTIME_ID 調(diào)用進(jìn)程的CPU時(shí)間
CLOCK_THREAD_CPUTIME_ID 調(diào)用線程的CPU時(shí)間
  • timespec結(jié)構(gòu)體
    struct timespec {
           time_t   tv_sec;        /* seconds */
           long     tv_nsec;       /* nanoseconds */
       };

8.1.3 gettimeofday函數(shù)

和time函數(shù)功能類似,gettimeofday提供了更高的精度(微秒級(jí))

int gettimeofday(struct timeval *tv, struct timezone *tz);
  • timeval結(jié)構(gòu)體
struct timeval {
           time_t      tv_sec;     /* seconds */
           suseconds_t tv_usec;    /* microseconds */
       };

8.2 將日歷時(shí)間轉(zhuǎn)換成分解的時(shí)間

調(diào)用gmtime和localtime函數(shù)可以將time_t格式的UTC時(shí)間轉(zhuǎn)換成分解的時(shí)間,存放在tm結(jié)構(gòu)體中

   struct tm *gmtime(const time_t *timep);
   struct tm *localtime(const time_t *timep);

   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 */
   };

8.3 格式化輸出時(shí)間

調(diào)用strftime將tm格式的時(shí)間值格式化輸出,strftime需要通過TZ環(huán)境變量指定時(shí)區(qū)

 size_t strftime(char *s, size_t max, const char *format,
                   const struct tm *tm);
  • TZ環(huán)境變量

TZ環(huán)境變量用來設(shè)置時(shí)區(qū),設(shè)置方法如下:

ckt@ubuntu:~$ export TZ=Asia/Shanghai
ckt@ubuntu:~$ date
Tue Mar  7 09:15:24 CST 2017
ckt@ubuntu:~$ export TZ=Asia/Tokyo
ckt@ubuntu:~$ date
Tue Mar  7 10:15:46 JST 2017

8.4 示例代碼

#include "apue.h"
#include <time.h>

int main(int argc, char const *argv[])
{
    time_t t;
    struct tm *tmp = NULL;
    char buf1[64];

    if (time(&t) == -1)
    {
        printf("get time error\n");
        return 0;
    }

    tmp = localtime(&t);

    if (tmp == NULL)
    {
        printf("get localtime error\n");
        return 0;
    }

    if (strftime(buf1, 64, "time and date : %r, %a %b %d, %Y", tmp) == 0)
        printf("buffer len 64 is too small\n");
    else
        printf("%s\n", buf1);

    return 0;
}
  • 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc getdate_test.c -o getdate_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./getdate_test
time and date : 11:00:08 PM, Wed Mar 08, 2017

參考

  • UNIX 環(huán)境高級(jí)編程 第3版
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容