从零开始UNIX环境高级编程(6):系统数据文件和信息

0. 思维导图

《从零开始UNIX环境高级编程(6):系统数据文件和信息》 系统数据文件和信息

1. 口令文件

从零开始UNIX环境高级编程(1)中,已经介绍过口令文件(/etc/passwd),本小节主要学习如果通过函数去获取口令文件的信息。

1.1 passed结构体

存放口令文件信息的结构体为passed,定义在pwd.h中,不同平台实现的字段也会不一样

  • Mac OS X中passwd结构体

      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结构体

         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 获取单个用户信息

获取passwd中单个用户信息的函数有两个:使用getpwnam获取用户信息时,需要传入该用户的名称作为参数;使用getpwuid获取用户信息时,需要传入该用户的id作为参数。

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

    以getpwnam函数为例,看下如何获取单个用户信息

#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;
}
  • 运行结果

    使用getpwnam函数获取到的信息和/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 遍历整个口令文件

上面两个函数只能获取单个用户信息,如果要得到整个口令文件,需要用到下面3个函数,而getpwnam和getpwuid中也会对这3个函数进行封装。

   struct passwd *getpwent(void);

   void setpwent(void);

   void endpwent(void);
  • 示例代码
    自定义一个getpwuid函数名为my_getpwuid,里面会调用到setpwent、getpwent、endpwent这3个函数
#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;
}
  • 运行结果
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. 阴影文件

系统将登陆密码进行加密,并将加密后的口令存放在阴影文件/etc/shadow中,查看阴影文件需要root权限

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

2.1 spwd结构体

和口令文件一样,阴影文件也有对应的结构体spwd,spwd结构体定义在 <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结构体函数

   // 通过用户名称获取单个用户的spwd信息
   struct spwd *getspnam(const char *name);

   //以下三个函数用户获取整个/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;
}
  • 运行结果
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用来存放系统中所有的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结构体

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

3.2 获取group函数

   //通过组名称获取当个group信息
   struct group *getgrnam(const char *name);
   //通过组ID获取当个group信息
   struct group *getgrgid(gid_t gid);

   //使用以下3个函数,获取整个/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;
}
  • 运行结果
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. 附属组

一个用户可以属于多个组,通过id命令可以查看用户的主组和附属组

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

4.1 getgroups函数

使用getgroups函数,可以获取进程所属用户的主组和附属组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;
}
  • 运行结果

分别获取用户ckt和root的主组ID和附属组ID,和使用命令id得到的结果一致

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. 其他数据文件

除了上面介绍的一些文件,系统中还有很多其他类型的数据文件,它们的操作方式都是类似的。各个数据的文件名称、结构体和操作函数,如下表:

《从零开始UNIX环境高级编程(6):系统数据文件和信息》 系统数据文件

6. 登录账户记录

utmp文件(/var/run/utmp)记录当前登录到系统的各个用户;wtmp文件(/var/log/wtmp)跟踪各个登录和注销事件

我们可以使用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. 系统标识

使用uname命令可以查看操作系统有关信息,同样我们也可以通过函数去获取这些信息。

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命令可以获取主机名

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

7.1 uname结构体

uname结构体用来存放操作系统有关信息,定义在#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函数

通过uname函数获取信息并返回这些值到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;
}
  • 运行结果
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函数

 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;
}
  • 运行结果
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. 时间和日期历程

《从零开始UNIX环境高级编程(6):系统数据文件和信息》 各个函数直接的关系

8.1 获取UTC时间

8.1.1 time函数

time函数定义在<time.h>中,用来获取UTC时间

time_t time(time_t *t);

8.1.2 clock_gettime函数

当clk_id设置为CLOCK_REALTIME时,clock_gettime的功能和time类似,在系统支持高精度时间值的情况下, clock_gettime可能比time函数得到更高精度的时间值

int clock_gettime(clockid_t clk_id, struct timespec *tp);
  • clk_id参数说明
标识符说明
CLOCK_REALTIME实时系统时间
CLOCK_MONOTONIC不带负跳数的实时系统时间
CLOCK_PROCESS_CPUTIME_ID调用进程的CPU时间
CLOCK_THREAD_CPUTIME_ID调用线程的CPU时间
  • timespec结构体
    struct timespec {
           time_t   tv_sec;        /* seconds */
           long     tv_nsec;       /* nanoseconds */
       };

8.1.3 gettimeofday函数

和time函数功能类似,gettimeofday提供了更高的精度(微秒级)

int gettimeofday(struct timeval *tv, struct timezone *tz);
  • timeval结构体
struct timeval {
           time_t      tv_sec;     /* seconds */
           suseconds_t tv_usec;    /* microseconds */
       };

8.2 将日历时间转换成分解的时间

调用gmtime和localtime函数可以将time_t格式的UTC时间转换成分解的时间,存放在tm结构体中

   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 格式化输出时间

调用strftime将tm格式的时间值格式化输出,strftime需要通过TZ环境变量指定时区

 size_t strftime(char *s, size_t max, const char *format,
                   const struct tm *tm);
  • TZ环境变量

TZ环境变量用来设置时区,设置方法如下:

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;
}
  • 运行结果
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 环境高级编程 第3版
    原文作者:伤口不该结疤
    原文地址: https://www.jianshu.com/p/55428371b561
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞