本章学习系统数据文件的接口,以及系统标识函数、时间和日期等信息函数。
口令文件:/etc/passwd
#include <pwd.h>
struct passwd{
char *pw_name; //用户名 *
char *pw_passwd; //加密口令
uid_t pw_uid; //user id *
gid_t pw_gid; //group id *
char *pw_gecos; //注释
char *pw_dir; //初始目录 *
char *pw_shell; //shell menu *
char *pw_class; //用户访问类
time_t pw_change; //下次更改口令时间
time_t pw_expire; //账户到期时间
};
*指的是POSIX.1规定的必须项;
使用finger命令可解析指定用户的相关信息。
相关函数(POSIX):
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
通过name或uid获得passwd结构的内容。返回的指针其实指向一个静态变量,所以每次调用都会覆盖上次的结果,注意保存副本,下同。
SUS扩展:
struct passwd *getpwent(void); //返回口令文件下一记录项
void setpwent(); //打开口令文件;若已经打开,则偏移量调回开始处。rewind,书中翻译为反绕
void endpwent();//关闭口令文件
用户密码段在/etc/passwd中仅有一个占位符,真正的密码存放在/etc/shadow中,前者任何人均可读取,后者却需要管理员权限。
struct spwd{
char *sp_namp; //登录名 *
char *sp_pwdp; //加密口令 *
int sp_lstchg; //上次更改口令以来经过的时间
int sp_min; //经过多少天后允许更改
int sp_max; //要求更改尚余天数
int sp_warn; //到期警告天数
int sp_inact; //账户不活动之前尚余天数
int sp_expire; //账户到期天数
unsigned int sp_flag; //保留
};
sp是shadow password的缩写,其中只有*的两项是必须的,其余都是附加字段。相关函数
#include <shadow.h>
struct spwd *getspnam(const char *name);
struct spwd *getspent();
void setspent();
void endspent();
木有通过UID获取的函数;以上函数在Linux和Solaris上可用;但是freeBSD和MacOS上口令就不是用这种存放方式了。
组文件:/etc/group
#include <grp.h>
struct group{
char *gr_name; //组名 *
char *gr_passwd; //加密口令
int gr_gid; //组ID *
char **gr_mem; //字符串数组,指向各用户名 *
};
除口令外皆POSIX.1定义的必须项。
相关函数:
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
//SUS:
struct group *getgrent();
void setgrent();
void endgrent();
附加组ID
常量NGROUPS_MAX规定了附加组的数量限制。相关函数:
#include <unistd.h>
ing getgroups(int gidsetsize, gid_t grouplist[]); //填充附加组ID,POSIX.1. 若gidsetsize=0,返回附加组ID数;
//然后再调用一次,gidsetsize可作为限制。
#include <grp.h> //on linux, SUS,以下都是su权限操作
#include <unistd.h> //others
int setgroups(int ngroups, const gid_t grouplist[]); //给调用进程设置附加组ID表
int initgroups(const char *username, gid_t basegid); //初始化附加组ID表,一般很少调用。
其他信息
其他信息,包括服务(/etc/services),网络(/etc/networks)和协议(/etc/protocols)等等,相关函数有形式上的相似性,包括:
get函数:读下一个记录,返回一个指向静态结构的指针(大多数情况)
set函数:打开文件,或rewind已经打开的文件
end函数:关闭文件
还有若干关键字搜索的函数:getxxbyname和getxxbyaddr等等。具体的可以查阅相关头文件。如netdb.h
登录账户记录
utmp文件:记录当前登录进系统各个用户;shell: who
wtmp:记录各个登录和注销事件;shell: last
在Linux中,可以用man 5 utmp获得相关信息
系统标识
#include <sys/utsname.h>
int uname(struct ustname *name);
struct utsname{
char sysname[]; //系统名称
char nodename[]; //当前结点名称
char release[]; //发行版编号
char version[]; //当前发行版版本号
char machine[]; //机器名称
};
如果在TCP/IP网络中,可以使用gethostname(char *name,int namelen)获取主机名称(完全限定名),shell: hostname。
时间和日期相关
time函数返回当前时间(日历时间)。但是SUS木有规定如何设置当前时间…╮(╯Д╰)╭
#include <sys/time.h>
struct timval{
time_t tv_sec; //seconds
long tv_usec; //microseconds
};
int gettimeofday(struct timeval *restrict tp, void *restrict tzp); //SUS
总是返回0;
tzp只能是NULL(囧),否则不可移植。本函数提供比time函数更高精度的填充值。
time_t可以经过多个函数转化为可读性更好的时间值,包括:
#include <time.h>
struct tm *gmtime(const time_t *calptr); //转为国际标准时间UTC
struct tm *localtime(const time_t *calptr); //转为本地时间CST
struct tm{
int tm_sec; //0~60,考虑到闰秒
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year; //years since 1900
int tm_wday; //周几,从周日开始
int tm_yday; //从1月1号开始过了几天
int tm_isdst; //夏时制
};
mktime进行struct tm*到time_t的反相转换;
asctime和ctime分别将struct tm *和time_t *转为字符串形式,形式上同shell: date的输出;
strftime具有非常复杂的格式化输出…类似printf:
#include <time.h>
size_t strftime(char *restrict buf, size_t maxsize,
const char *restrict format,
const struct tm *restrict tmptr);
format指定格式,格式化结果存放在长度为maxsize的buf中。返回存放字符数(不含\0),如果buf长度不足,返回0;
以上函数中,除了asctime和gmtime外都受到时区设置(环境变量TZ)的影响;
这一部分除了SUS外,都是ISO C规定的函数(time.h)
习题里面最后一题,貌似没有办法直接生成汉字的输出,给的答案也不行,估计要自行格式化了…