从零开始UNIX环境高级编程(6):系统数据文件和信息
0. 思维导图
系统数据文件和信息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. 其他数据文件
除了上面介绍的一些文件,系统中还有很多其他类型的数据文件,它们的操作方式都是类似的。各个数据的文件名称、结构体和操作函数,如下表:
系统数据文件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. 时间和日期历程
各个函数直接的关系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版