5.1 口令文件
口令文件包含了下表中所示的各字段,这些字段包含在<pwd.h>中定义的passwd结构中。
说 明 | struct passwd 成员 |
---|---|
用户名 | 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 |
口令文件是/etc/passwd,而且是一个文本文件。每一行包含上表中所示的 7个字段,字段之间用冒号相分隔。例如,该文件中可能有下列三行:
root:jheVopR58x9Fx:0:1:The superuser:/:/bin/sh
nobody:*:65534:65534::/:
stevens:3hKVD8R58r9Fx:224:20:Richard Stevens:/home/stevens:/bin/ksh
关于这些登录项请注意下列各点:
- 通常有一个登录项,其用户名为root,其用户ID是0 (超级用户)。
- 加密口令字段包含了经单向密码算法处理过的用户口令副本。因为此算法是单向的,所 以不能从加密口令猜测到原来的口令。
- 口令文件中的某些字段可能是空。如果密码口令字段为空,这通常就意味着该用户没有口令。nobody有两个空白字段:注释字段和初始shell字段。空白注释字段不产生任何影响。空白shell字段则表示取系统默认值,通常是/bin/sh。
定义了两个存取口令文件中信息的函数。在给出用户登录名或数值用户ID后,这两个函数就能查看相关记录。
#include <sys/types.h>
#include <pwd.h>//两个函数返回:若成功则为指针,若出错则为NULL
struct passwd *getpwuid(uid_t uid) ;
struct passwd *getpwnam(const char *name) ;
如果要查看的只是一个登录名或用户ID,那么这两个函数能满足要求,但是也有些程序要查看整个口令文件。下列三个函数则可用于此。
#include <sys/types.h>
#include <pwd.h>//返回:若成功则为指针,若出错或到达文件尾端则为NULL
struct passwd *getpwent(void);void setpwent(void);
void endpwent(void);
5.2 组文件
组文件包含了下表中所示字段。这些字段包含在<grp.h> 中所定义的group结构中。
说 明 | struct group 成员 |
---|---|
组名 | char *gr_name |
加密口令 | char *gr_passwd |
数字组ID | int gr_gid |
指向各用户名指针的数组 | char **gr_mem |
字段gr_mem是一个指针数组,其中的指针各指向一个属于该组的用户名。该数组以null结尾。
可以用下列两个函数来查看组名或数值组ID。
#include <sys/types.h>
#include <grp.h>//两个函数返回:若成功则为指针,若出错则为NULL
struct group *getgrgid(gid_t gid) ;
struct group *getgrnam(const char * name) ;
如同对口令文件进行操作的函数一样,这两个函数通常也返回指向一个静态变量的指针,在每次调用时都重写该静态变量。
如果需要搜索整个组文件,则须使用另外几个函数。下列三个函数类似于针对口令文件的三个函数。
#include <sys/types.h>
#include <grp.h>//返回:若成功则为指针,若出错或到达文件尾端则为NULL
struct group *getgrent(void);void setgrent(void);
void endgrent(void);
setgrent打开组文件(如若它尚末被打开)并反绕它。getgrent从组文件中读下一个记录,如若该文件尚未打开则先打开它。endgrent关闭组文件。
5.3 添加组ID
为了存取和设置添加组I D提供了下列三个函数:
#include <sys/types.h>
#include <unistd.h>//返回:若成功则为添加的组ID数,若出错则为-1
int getgroups(int gidsetsize, gid_t grouplist[]) ;//两个函数返回:若成功则为 0,若出错则为-1
int setgroups(int ngroups, const gid_t grouplist[]) ;
int initgroups(const char *username, gid_t basegid) ;
5.4 其它数据文件
UNIX系统还使用很多其他文件。例如, BSD网络软件有一个记录各网络服务器所提供的服务的数据文件 (/etc/services ),有一个记录协议信息的数据文件(/etc/protocols),还有一个则是记录网络信息的数据文件(/etc/networks)。幸运的是,对于这些数据文件的界面都与上述对口令文件和组文件的相似。
一般情况下每个数据文件至少有三个函数:
(1)get函数:读下一个记录,如果需要还打开该文件。此种函数通常返回指向一个结构的指针。当已达到文件尾端时返回空指针。大多数 get函数返回指向一个静态存储类结构的指针,如果要保存其内容,则需复制它。
(2)set函数:打开相应数据文件 (如果尚末打开),然后反绕该文件。如果希望在相应文件起始处开始处理,则调用此函数。
(3) end函数:关闭相应数据文件。正如前述,在结束了对相应数据文件的读、写操作后, 总应调用此函数以关闭所有相关文件。
5.5 登录会计
大多数UNIX系统都提供下列两个数据文件:utmp文件,它记录当前登录进系统的各个用 户;wtmp文件,它跟踪各个登录和注销事件。
包含下列结构的一个二进制记录写入这两个文件中:
struct utmp {
char ut_line[8]; /* tty line: "ttyh0", "ttyd0", "ttyp0", ... */
char ut_name[8]; /* login name */
long ut_time; /* seconds since Epoch */
} ;
5.6 系统标识
定义了uname函数,它返回与主机和操作系统有关的信息。
#include <sys/utsname.h>//返回:若成功则为非负值,若出错则为- 1
int uname(struct utsname *name) ;
struct utsname {
char sysname[9]; /* name of the operating system */
char nodename[9]; /* name of this node */
char release[9]; /* current release of operating system */
char version[9]; /* current version of this release */
char machine[9]; /* name of hardware type */
} ;
伯克利类的版本提供gethostname函数,它只返回主机名,该名字通常就是TCP/IP网络上主 机的名字。
#include <unistd.h>//返回:若成功则为0,若出错则为-1
int gethostname(char *name, int namelen) ;
通过name返回的字符串以null结尾 (除非没有提供足够的空间 )。<sys/param.h>中的常数 MAXHOSTNAMELEN规定了此名字的最大长度(通常是64字节)。如果宿主机联接到TCP/IP网络中,则此主机名通常是该主机的完整域名。
5.7 时间和日期例程
由UNIX内核提供的基本时间服务是国际标准时间公元 1970年1月1日00 : 00 : 00以来经过的秒数。这种秒数是以数据类型 time_t表示的。我们称它们为日历时间。日历时间包括时间和日期。UNIX在这方面与其他操作系统的区别是:
( 1 )以国际标准时间而非本地时间计时;
( 2 )可自动进行转换,例如变换到夏日制;
( 3 )将时间和日期作为一个量值保存。time函数 返回当前时间和日期。
#include <time.h>//返回:若成功则为时间值,若出错则为- 1
time_t time(time_t *calptr) ;
时间值作为函数值返回。如果参数非null,则时间值也存放在由calptr指向的单元内。
一旦取得这种以秒计的很大的时间值后,通常要调用另一个时间函数将其变换为人们可读的时间和日期。下图中说明了各种时间函数之间的关系。(图中以虚线表示的四个函数localtime、mktime、ctime和strftime都受到环境变量T Z的影响。)
两个函数localtime和gmtime将日历时间变换成以年、月、日、时、分、秒、周日表示的时 间,并将这些存放在一个tm结构中。
struct tm { /* a broken-down time */int tm_sec; /* seconds after the minute: [0, 61] */int tm_min; /* minutes after the hour: [0, 59] */int tm_hour; /* hours after midnight: [0, 23] */int tm_mday; /* day of the month: [1, 31] */int tm_mon; /* month of the year: [0, 11] */int tm_year; /* years since 1900 */int tm_wday; /* days since Sunday: [0, 6] */int tm_yday; /* days since January 1: [0, 365] */int tm_isdst; /* daylight saving time flag: <0, 0, >0 */
}
#include <time.h>//两个函数返回:指向tm结构的指针
struct tm *gmtime(const time_t *calptr) ;
struct tm *localtime(const time_t *calptr) ;
localtime和gmtime之间的区别是: localtime将日历时间变换成本地时间 (考虑到本地时区和夏时 制标志),而gmtime则将日历时间变换成国际标准时间的年、月、日、时、分、秒、周日。
函数mktime以本地时间的年、月、日等作为参数,将其变换成 time_t值。
#include <time.h>//返回:若成功则为日历时间,若出错则为- 1
time_t mktime(struct tm *tmptr) ;
asctime和ctime函数产生形式的26字节字符串,这与date(1)命令的系统默认输出形式类似:
Tue Jan 14 17:49:03 1992\n\0
#include <time.h>//两个函数返回:指向null结尾的字符串
char *asctime(const struct tm *tmptr) ;
char *ctime(const time_t *calptr) ;
asctime的参数是指向年、月、日等字符串的指针,而 ctime的参数则是指向日历时间的指针。
最后一个时间函数是strftime,它是非常复杂的printf类的时间值函数。
#include <time.h>//返回:若有空间,则存入数组的字符数,否则为 0
size_t strftime(char *buf, size_t maxsize, const char * format, const struct tm *tmptr) ;