第六章 系统数据文件和信息
1、主要的系统数据
包括但不限于passwd(5), shadow(5), group(5), hosts(5), networks(5), protocols(5), services(5)等,它们的文档位于 man 手册的第 5 部分。它们均存于/etc 目录下。Unix 系统提供了相应的数据结构和函数对它们进行读取。但是这一块根据书中的表 6-1 可以看出,POSIX 对此并没有作出特别具体的定义。对于Linux,可以参考LSB中的相关定义或查看当前系统中的man 手册
A. 口令文件 passwd
对于文件 passwd(5),值得注意的一点是,如果里面注册的某个用户只打算用于守护进程之类而不打算给予登录 shell,应将其 home directory 相应字段设置为/dev/null 或者/bin/false等。
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
这两个函数分别通过UID 和用户名取得其 passwd结构。
#include <pwd.h>
struct passwd *getpwent(void);
void setpwent(void);
void endpwent(void);
getpwent 返回 passwd(5)文件下一个记录项;
setpwent 重置 getpwent 的当前位置到开始处;
endpwent 关闭 getpwent 打开的文件(必须);
B. 阴影口令文件 shadow
管理 shadow 文件的相应 API主要包括以下,用法类似 passwd 的函数:
#include <shadow.h>
struct spwd *getspnam(const char *name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);
C. 组文件 group
管理 group(5)文件的相应 API主要包括以下,用法也类似 passwd 的函数:
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
struct group *getgrent(void);
void setgrent(void);
void endgrent(void);
对于进程的附加组操作,还包括以下函数:
#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist]();
#include <grp.h> /* on Linux*/
int setgroups(int ngroups, const gid_t grouplist[]);
int initgroups(const char *username, gid_t basegid);
getgroups 和 setgroups 用于读取/设置当前用户进程的附加组 id,并返回实际读取/设置的附加组id 数量;
initgroups 初始化附加组 id 表,basegid 为用户进程的主 GID。
D. service(5)、networks(5)、protocols(5)等数据可通过 getxxxbyxxx 或 getaddrinfo(3)等函数取得。具体见“第十六章 网络 IPC”或 Richard Stevens 的《UNIX 网络编程》第一卷;
2、utmp 文件和 wtmp 文件
utmp(5)位于/var/run 下,记录当前登录的用户,为 who(1)的数据源;wtmp(5)位于/var/log下,记录上次登录的用户,为last(1)的数据源。另外还/var/log还存在一个btmp文件,用于记录失败的登录操作,为lastb(1)的数据源,但btmp 本身没有手册页。
POSIX 对这些文件及其结构都没有作专门要求;使用时应查看具体系统上的手册页;
3、系统标识 uname
#include <sys/utsname.h>
int uname(stuct ustname *name);
该函数将与操作系统相关的信息读到指定的utsname 结构指针中,POSIX 要求ustname 至少包括sysname(内核名,如:Linux)、nodename(主机名,但手册称这个字段基本上没有意义,可用gethostname(2)代替)、release(发行名,如:Ubuntu)、version(版本号,如:2.6.24)、machine(体系结构,如:i686)这几个字段。对应的 shell 命令也是 uname(1)。
4、时间和日期
本节提及的库函数除了 gettimeofday(2)外,都属于 ISO C的一部分。时间日期的数据结构包括:time_t(标准格式,以秒为单位)、struct timeval(日期格式,包括年月日时分秒)、struct tm(时间格式,包括秒和微秒);查询时间日期相应的 shell 系统命令包括 date(1)、cal(1)等。
A. 读取时间值
#include <time.h>
time_t time(time_t *calptr);
该函数返回以秒为单位的当前Unix 标准时间值,如果指定了指针calptr,也同时将该值存入其中;time(2)同时也是从内核取得时间值的基本系统调用;注意另有一个 POSIX 函数 times(2)用于取进程时间,它使用是 tms 和 clock_t结构。
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
该函数读取当前时间的timeval 结构到指针 fp 处,并用tzp 调整(时区,若指定)。timeval 结构定义了毫秒级的精度;
B. 转换时间值
#include <time.h>
struct tm *gmtime(const time_t *calptr);
struct tm *localtime(const time_t *calptr);
该函数将指定的Unix 标准时间值转换为格林尼治时间或本地时间并存到tm 结构中返回其指针;
#include <time.h>
time_t mktime(struct tm *tmptr);
该函数将tm 结构的时间转换为Unix 标准时间;
#include <time.h>
char *asctime(const struct tm *tmptr);
char *ctime(const time_t *calptr);
以上两个函数将参数指定的时间转换为常见的时间日期格式字符串,受环境变量 TZ影响;
C. 格式化时间字符串
#include <time.h>
size_t strftime(char *restrict buf, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr);
该函数将tmptr 转换为format 指定的格式,并存放在长度为buf 中,buf 长度为maxsize(注意字符串最后一个字符为'\0')。buf 长度不足时返回 0,否则返回存放的字符数(不包括'\0')。
format 的格式化用法类似 printf 等函数,但使用不同的格式化标记。如:
strftime(buf, maxsize, "%Y 年%m 月%d 日 %H:%M:%S %Z", localtime(&time_t_obj));
要注意大小写。
5、proc 文件系统
这是位于内存中的各种内核数据结构信息挂载在/文件系统下的一个虚拟文件系统,可以像对普通文件一样对其进行读写操作。其内容不是 POSIX 的一部分,书中也几乎未提及这部分。对于Linux,是通过构建一个 procfs 并用 VFS 接口实现的。这里是我以前对 Linux 文件系统诸信息的一个笔记。