[单刷APUE系列]第一章——Unix基础知识[1]
[单刷APUE系列]第一章——Unix基础知识[2]
[单刷APUE系列]第二章——Unix标准及实现
[单刷APUE系列]第三章——文件I/O
[单刷APUE系列]第四章——文件和目录[1]
[单刷APUE系列]第四章——文件和目录[2]
[单刷APUE系列]第五章——标准I/O库
[单刷APUE系列]第六章——系统数据文件和信息
使用过Unix系统的朋友应该知道 /etc/passwd
文件,这就是Unix系统口令文件,在POSIX1.x规范中规定Unix系统必须存在用户数据库,这些用户数据库里每个用户都需要包含一些字段,具体需要查看 <pwd.h>
头文件。
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 */ };
在很多Unix系统中, passwd
是一个ASCII文件,每一行是一个用户,然后每个用户都包含这些字段,字段之间使用冒号分隔,但是在苹果系统中,则是使用 opendirectory
守护进程来存储用户数据库等数据。
系统提供了两个获取口令文件项的函数
struct passwd *getpwuid(uid_t uid); struct passwd *getpwnam(const char *login);
这两个函数都会返回passwd结构体,而且需要注意的是,这个结构体指针实际上是指向一块静态区域的指针,也就是说,不同通过保留指针来让后面使用。
struct passwd *getpwent(void); void setpwent(void); void endpwent(void);
getpwent函数则是一项一项读取,然后将其以passwd结构体返回,非常适合需要读取全部内容的情况。setpwent和endpwent清理读取的缓存,其中endpwent则是关闭所有的文件,当使用getpwent后一定要使用endpwent关闭文件。
在最初的Unix系统实现中,密码是存放在 /etc/passwd
文件的第二个字段中,但是由于有很多程序读取这个文件,所以这个字段被专门移出来放在 /etc/shadow
中,通常情况下,只有root权限才能读取。在Solaris和Linux中,有一组函数用于访问阴影口令文件,但是,非常遗憾的是,苹果系统中没有,所以这里也就只能讲一讲,而不能列出实际的代码。
struct group { char *gr_name; /* [XBD] group name */ char *gr_passwd; /* [???] group password */ gid_t gr_gid; /* [XBD] group id */ char **gr_mem; /* [XBD] group members */ };
上面是苹果系统定义的 group
结构体
struct group *getgrgid(gid_t gid); struct group *getgrnam(const char *name); struct group *getgrent(void); void setgrent(void); void endgrent(void);
就如同口令文件的函数一样,上面的函数用法也是一样的。
在早期Unix系统实现中,是不存在附属组的。直到后来BSD的一个版本引入了附属组ID的概念,这样在权限检查的时候也需要检查附属组ID的权限
int getgroups(int gidsetsize, gid_t grouplist[]); int setgroups(int ngroups, const gid_t *gidset); int initgroups(const char *name, int basegid);
getgroups
函数将进程所属用户的附属组ID放入grouplist参数中,groupsize参数用于确定放入的大小,但是实际上,我们可以将groupsize设置为0,然后函数会返回实际的附属组个数,然后就可以很方便的分配grouplist数组,用不着去猜测究竟应该分配多少。
setgroups
是一个root权限操作,用于为进程设置附属组ID。
initgroups
是一系列操作的集合,实际上用到的机会极少,如果有需要的朋友可以自行观看系统手册。
除了上面讲到的两个文件以外,系统还有很多其他的文件,但是基本上都是提供了同样的接口(get,set,end),而且和前面的用法基本一样,所以这里也不再讲解。
在实际的运维中,经常会需要查看用户的登录日志,所以系统也有相关的数据文件和接口函数用于开发使用。原著中讲解了utemp结构体的内容,这里不再多讲,但是在苹果系统中,实际上已经被utempx等结构体所取代
struct utmpx { char ut_user[_UTX_USERSIZE]; /* login name */ char ut_id[_UTX_IDSIZE]; /* id */ char ut_line[_UTX_LINESIZE]; /* tty name */ pid_t ut_pid; /* process id creating the entry */ short ut_type; /* type of this entry */ struct timeval ut_tv; /* time entry was created */ char ut_host[_UTX_HOSTSIZE]; /* host name */ __uint32_t ut_pad[16]; /* reserved for future use */ }; struct lastlogx { struct timeval ll_tv; /* time entry was created */ char ll_line[_UTX_LINESIZE]; /* tty name */ char ll_host[_UTX_HOSTSIZE]; /* host name */ };
其实,不光是苹果系统,其他的Unix系统的字段也已经被修改扩充了,而且其实这个并不重要,所以只需要了解即可。
POSIX定义了uname函数,用于返回主机和操作系统相关的信息,uname命令就是使用了这个函数。
int uname(struct utsname *name); struct utsname { char sysname[_SYS_NAMELEN]; /* [XSI] Name of OS */ char nodename[_SYS_NAMELEN]; /* [XSI] Name of this network node */ char release[_SYS_NAMELEN]; /* [XSI] Release level */ char version[_SYS_NAMELEN]; /* [XSI] Version level */ char machine[_SYS_NAMELEN]; /* [XSI] Hardware type */ };
所有的成员都是以null字节结尾,并且BSD派生系统也提供了gethostname函数
int gethostname(char *name, size_t namelen);
实际上还有一个sethostname函数,但是这里也不多讲,除此以外,上面的一些函数实际上有一些已经被移到标准C库中了,所以也能作为跨平台开发使用。
Unix系统都使用的是Unix时间戳,也就是UTC时间1970年1月1日0时0分0秒以来的秒数,在前面我们提到过它,这个就是日历时间,并且是使用 time_t
类型存储的。
time_t time(time_t *tloc);
time函数就是很简单的获取日历时间,在一般的情况下,都是传入null然后获得函数返回值来使用。原著中也讲到了指定时间的获取,但是非常不幸,苹果系统又没有这些方法,所以这里也只能不提及了。