昨天看完了,今天来看看第六章。感觉第六章的内容不是非常重要。简单看看吧
6.2 口令文件
口令文件其实就是/etc文件夹下的passwd文件,但处于安全性的考虑,我们无法直接读取它。就是通过直接限制权限的方式对其进行保护,passwd文件具体权限如下:
-rw-r--r-- 1 root root
可以看到只有root用户具有读写权限,与root同组的用户与其他用户仅具有读权限。
不过为了解决以上问题,Linux中给出了一系列数据结构与函数帮助我们操纵口令文件,首先是关键数据结构,定义位于/include/pwd.h
struct passwd
{
char *pw_name; /* Username. */
char *pw_passwd; /* Password. */
__uid_t pw_uid; /* User ID. */
__gid_t pw_gid; /* Group ID. */
char *pw_gecos; /* Real name. */
char *pw_dir; /* Home directory. */
char *pw_shell; /* Shell program. */
};
给出用户登录名与数值用户ID后,通过以下两个函数就能获得passwd结构体,该结构体中包含有我们需要的信息。
#include <pwd.h>
extern struct passwd *getpwuid (__uid_t __uid);
extern struct passwd *getpwnam (const char *__name);
getpwuid函数由ls程序使用。getpwnam函数由login程序使用。passwd结构通常是函数内部的静态变量,只要调用任一相关函数,其内容就会被重写。
以上两个函数仅能查看某一个用户的口令信息,但如果不知道其他用户的登录名或数值ID,则无法获得这些信息。因此Linux中又给出了以下三个函数来查看整个口令文件。
#include <pwd.h>
extern struct passwd *getpwent (void); //若成功,返回指针,指向口令文件中的下一个记录项。第一次调用时,它打开所使用的各个文件。
extern void setpwent (void); //回到口令文件开头
extern void endpwent (void); //关闭口令文件
6.3节中所提到的阴影口令文件感觉与口令文件的内容并没有多大的差距,在此就先不深入研究了,以后遇到了再说。
6.4组文件
还是相同的思路,如果用户程序想访问组相关信息,这些信息虽然存放在/etc/group中,但还是由于权限的限制导致一般的用户无法直接访问这一问题。Linux中给出的方法是通过相关的函数与数据结构进行访问,首先来看关键数据结构。
struct group
{
char *gr_name; /* Group name. */
char *gr_passwd; /* Password. */
__gid_t gr_gid; /* Group ID. */
char **gr_mem; /* Member list. */
};
通过组名与数值ID可以获得这一数据结构。
#include <grp.h>
extern struct group *getgrgid (__gid_t __gid);
extern struct group *getgrnam (const char *__name);
搜索整个口令文件则需要以下三个函数,思路也是一样的。
#include <grp.h>
extern void setgrent (void);
extern void endgrent (void);
extern struct group *getgrent (void);
6.5 附属组
引入附属组的一个原因是:一个用户会参与多个项目,因此也就要同时属于多个组。为了解决上述问题,4.2BSD引入了附属组的概念,用户不再简单的属于一个组,也可以属于多至16个另外的组。
用户程序可通过以下函数获取和设置附属组ID。
extern int getgrouplist (const char *__user, __gid_t __group,
__gid_t *__groups, int *__ngroups);
extern int setgroups (size_t __n, const __gid_t *__groups) __THROW;
extern int initgroups (const char *__user, __gid_t __group);
其中setgroups需要超级用户权限调用,而由于initgroups需要调用setgroups,因此initgroups也同样只有超级用户才能调用。
6.7其他数据文件
除了上面提到的口令文件和组文件,Linux还有多个具有类似概念的文件。书中给出了一个表对上述内容进行总结。
说明 | 数据文件 | 头文件 | 结构 | 附加的减搜索函数 |
口令 | /etc/passwd | <pwd.h> | passwd | getpwnam/getpwuid |
组 | /etc/group | <grp.h> | group | getgrnam/getgrgid |
阴影口令 | /etc/shadow | <shadow.h> | spwd | getspnam |
主机 | /etc/hosts | <netdb.h> | hostnet | getnameinfo/getaddrinfo |
网络 | /etc/networks | <netdb.h> | netent | getnetbyname/getnetbyaddr |
协议 | /etc/protocols | <netdb.h> | protonet | getprotobyname/getprotobynumber |
服务 | /etc/services | <netdb.h> | servent | getserbyname/getserbyport |
6.8登录账户记录
Linux通过“/var/run/utmp”对当前登录到系统的各个用户进行记录;通过“/var/log/wtmp”文件跟踪各个登录和注销事件。以上两个文件是二进制文件,不能直接打开。
Linux通过以下几个函数与数据结构对以上两个文件进行修改。首先来看看关键数据结构,定义位于/usr/include/x86_64-linux-gnu/bits/utmp.h中
struct utmp
{
short int ut_type; /* Type of login. */
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE]; /* Devicename. */
char ut_id[4]; /* Inittab ID. */
char ut_user[UT_NAMESIZE]; /* Username. */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login. */
struct exit_status ut_exit; /* Exit status of a process marked
as DEAD_PROCESS. */
/* The ut_session and ut_tv fields must be the same size when compiled
32- and 64-bit. This allows data files and shared memory to be
shared between 32- and 64-bit applications. */
#ifdef __WORDSIZE_TIME64_COMPAT32
int32_t ut_session; /* Session ID, used for windowing. */
struct
{
int32_t tv_sec; /* Seconds. */
int32_t tv_usec; /* Microseconds. */
} ut_tv; /* Time entry was made. */
#else
long int ut_session; /* Session ID, used for windowing. */
struct timeval ut_tv; /* Time entry was made. */
#endif
int32_t ut_addr_v6[4]; /* Internet address of remote host. */
char __glibc_reserved[20]; /* Reserved for future use. */
};
再来看看相关函数:
extern void login (const struct utmp *__entry) __THROW;
登录时,login程序填写此类型结构,然后将其写入到utmp文件中,同时也将其添写到wtmp文件中。通过login函数的参数也可以验证这一点。
注销时,init进程将utmp文件中相应的记录擦除,并将一个新记录添写到wtmp文件中。
6.9 系统标识
POSIX.1 定义了uname函数,它返回与主机和操作系统有关的信息。定义位于/usr/include/x86_64-linux-gnu/sys/utsname.h。
extern int uname (struct utsname *__name) __THROW; //__name即是输入参数也是输出参数。若程序运行成功返回非负值,若出错返回-1。
参数定义如下:
struct utsname
{
/* Name of the implementation of the operating system. */
char sysname[_UTSNAME_SYSNAME_LENGTH];
/* Name of this node on the network. */
char nodename[_UTSNAME_NODENAME_LENGTH];
/* Current release level of this implementation. */
char release[_UTSNAME_RELEASE_LENGTH];
/* Current version level of this release. */
char version[_UTSNAME_VERSION_LENGTH];
/* Name of the hardware type the system is running on. */
char machine[_UTSNAME_MACHINE_LENGTH];
#if _UTSNAME_DOMAIN_LENGTH - 0
/* Name of the domain of this node on the network. */
# ifdef __USE_GNU
char domainname[_UTSNAME_DOMAIN_LENGTH];
# else
char __domainname[_UTSNAME_DOMAIN_LENGTH];
# endif
#endif
};
utsname结构体中的信息通常可用uname命令打印。我的机器上uname的返回结果非常简单
Linux
仅此而已,再无其他。
还有一个返回主机名的函数,该名字通常就是TCP/IP网络上主机的名字。
extern int gethostname (char *__name, size_t __len) __THROW __nonnull ((1));
其中name即是输入参数也是输出参数。
hostname命令可用来获取和设置主机名。主机名通常在系统自举时设置,它由/etc/rc或init取自一个启动文件。
6.10 时间和日期例程
由UNIX内核提供的基本时间服务是计算自协调世界时(Coordinated Universal Time,UTC)公元1970年1月1日00:00:00这一特定时间以来经过的秒数。这一秒数通过以下函数获得:
#include <time.h>
extern time_t time (time_t *__timer) __THROW;
通过clock_gettime同样可以获得这一秒数,但其时间精度更高:
#include <time.h>
extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __THROW;
time与clock_gettime函数的结果可以直接相互转化。
time_t类型的参数可以通过gmtime函数转化为协调统一时间的年、月、日、时、分、秒、周日分解。这一格式的时间通过以下结构体表示。
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
# ifdef __USE_MISC
long int tm_gmtoff; /* Seconds east of UTC. */
const char *tm_zone; /* Timezone abbreviation. */
# else
long int __tm_gmtoff; /* Seconds east of UTC. */
const char *__tm_zone; /* Timezone abbreviation. */
# endif
};
这一类型需要通过strftime转化为字符串模式并输出。
extern size_t strftime (char *__restrict __s, size_t __maxsize,
const char *__restrict __format,
const struct tm *__restrict __tp) __THROW;
一个格式化的时间字符串可通过以下函数转化为结构体struct tm。
extern char *strptime (const char *__restrict __s,
const char *__restrict __fmt, struct tm *__tp)
__THROW;
struct tm结构体又可以通过mktime函数转化为time_t类型参数。
extern time_t mktime (struct tm *__tp) __THROW;
最后给大家附上各时间结构体之间的转化关系。