UNIX系统的正常运作需要使用大量与系统有关的数据文件,例如,口令文件/etc/passwd和组文件/etc/group就是经常被多个程序频繁使用的两个文个件。用户每次登录UNIX系统,以及每次执行ls -l命令时都要使用口令文件。
UNIX系统的口令文件包含如下字段,且这些字段包含在
用户名(char *pw_name)、加密口令(char* pw_passwd)、数值用户ID(uid_t pw_uid)、数值组ID(gid_t pw_gid)、注释字段(char* pw_gecos)、初始工作目录(char* pw_dir)、初始shell(用户程序)(char* pw_shell)、用户访问类(char* pw_class)、下次更改口令时间(time_t pw_change)、账户有效期时间(time_t pw_expire)。
举例:tyliang:x:1000:1000:tyliang,,,:/home/tyliang:/bin/bash
值得注意地方:
i、通常有一个用户名为root的登录项,其用户ID为0
ii、加密口令字段包含了一个占位符。我们无法看见密码的,嘿嘿!
iii、shell字段包含了一个可执行程序名,它被用作该用户的登录shell。
iv、为了阻止一个特定用户登录系统。除使用/dev/null外,其他替代方法:将/bin/false用作登录shell(以不成功状态终止);用/bin/true禁止一个账户(以成功状态终止);用nologin命令,打印可定制的出错信息,然后以非0状态终止。
v、finger命令支持注释字段中的附加信息。如:finger -p tyliang 从而可以查看tyliang用信息。
某些系统将加密口令存放在一个通常称为阴影口令的文件中。该文件至少包含用户名和加密口令。只有root权限才有资格查看。
说明 | 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 |
UNIX组文件包含如下表的所示字段。这此字段包含在
说明 | struct group成员 |
组名 | char* gr_name |
加密口令 | char* gr_passwd |
数值组ID | int gr_gid |
指向各用户名指针的数组 | char** gr_mem |
字段gr_mem是一个指针数组,其中每个指针指向一个属于该组的用户名。该数组以null指针结尾。
说明 | 数据文件 | 头文件 | 结构 | 附加的键搜索函数 |
口令 | /etc/passwd | passwd | getpwnam、getpwuid | |
组 | /etc/group | group | getgrnam、getgrgid | |
阴影 | /etc/shadow | spwd | getspnam | |
主机 | /etc/hosts | hostent | getnameinfo、getaddrinfo | |
网络 | /etc/networks | netent | getnetbyname、getnetbyaddr | |
协议 | /etc/protocols | protoent | getprotobyname、getprotobynumber | |
服务 | /etc/services | servent | getservbyname、getservbyport |
i、UNIX系统提供下列两个数据文件:utmp文件记录当前登录到系统的各个用户;wtmp文个把跟踪各个登录和注销事件。
一般而言,利用who命令读取utmp文件,last命令读取wtmp文件。
ii、uname函数返回与主机和操作系统有关的信息
#include
int uname(struct utsname* name);
//返回值:若成功,返回非负值,若出错,返回-1
struct utsname {
char sysname[];/* name of the operating system*/
char nodename[];/* name of this node */
char release[];/* current release of operating system*/
char version[];/* current version of this release */
char machine[];/* name of hardware type */
};
utsname结构中的信息通常可用uname命令打印。
gethostname函数:只返回主机名,该名字通常就是tcp/ip网络上主机的名字。
#include
int gethostname(char* name, int namelen);
//返回值:若成功,返回非负值,若出错,返回-1
namelen参数指定name缓冲区长度,如若提供足够的空间,则通过name返回的字符串以null字节结尾。如若没有提供足够的空间,则没有说明通过name返回的字符串是否以null结尾。
hostname命令可用来获取和设置主机名。(超级用户用一个类似的函数sethostname来设置主机名。)主机名通常在系统自举时设置,它由/etc/rc或init取自一个启动文件。
日历时间:以1970年1月1日为准,经过的时间以秒来计算,其类型为time_t。
分解时间:即存储年月日时分秒,通过一定组合可以转换成我们熟悉的时间表示。
i、time函数返回当前时间和日期。
#include
time_t time(time_t *calptr);
//返回值:若成功,返回时间值,若出错,返回-1
时间值作为函数值返回。如果参数非空,则时间值也存放在由calptr指向的单元内。
mktime函数将本地时间的年月日等作为参数,转变为time_t值。
#include
time_t mktime(struct tm* tmptr);
//返回值:若成功,返回日历时间;若出错,返回-1
ii、两个函数localtime和gmtime将日历时间转换成分解的时间,并将这些存放在一个tm结构中。
#include
struct tm* gmtime(const time_t * calptr);
struct tm* localtime(const time_t * calptr);
//两个函数的返回值:指向分解的tm结构的指针;若出错,返回NULL
localtime和gmtime之间的区别是:localtime将日历时间转换成本地时间(考虑到本地时区和夏令时标志),而gmtime则将日历时间转换成协调统一时间的年、月、日、时、分、秒、周日分解结构。
iii、strftime是一个类似于printf的时间值函数。可通过可用的多个参数来定制产生的字符串。
#include
size_t strftime(char* restrict buf, size_t maxsize, const char *restrict format,
const struct tm* restrict tmptr);//将分解时间转换成字符串,其中tmptr参数由localtime函数获取
size_t strftime_l(char* restrict buf, size_t maxsize, const char *restrict format,
const struct tm* restrict tmptr,locale_t locale);
//两个函数的返回值:若有空间,返回存入数组的字符数;否则,返回0
strftime_l允许调用者将区域指定为参数。strftime使用通过TZ环境变量指定的区域。
tmptr参数是要格式化的时间值,由一个指向分解时间值tm结构的指针说明。
format参数控制时间值的格式。如同printf函数一样,转换说明的形式是百分号之后跟一个特定字符。
该部分具体参考书P154-P156。
下图是各个函数之间的转换关系。
以下是获取本时间的程序:
#include"apue.h"
#include
int main(){
time_t caltime;
struct tm *tm;
char line[MAXLINE];
if((caltime=time(NULL))==-1)//调用time函数返回日历时间
err_sys("time_error");
if((tm=localtime(&caltime))==NULL)//将日历时间转换为本地的分解时间
err_sys("localtime error");
if(strftime(line,MAXLINE,"%a %b %X %Z %Y\n",tm)==0)//将分解时间格式化为字符串
err_sys("strftime error");
fputs(line,stdout);//将字符串输出到标准输出中
exit(0);
}