UNIX环境高级编程 学习笔记 第六章 系统数据文件和信息

本文主要是介绍UNIX环境高级编程 学习笔记 第六章 系统数据文件和信息,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

UNIX系统口令文件(POSIX.1称其为用户数据库)包含以下字段,这些字段也包含在头文件pwd.h中定义的passwd结构中:
在这里插入图片描述
由于历史原因,口令文件/etc/passwd是一个ASCII文件,其中的每行都包含以上各字段,字段之间用冒号分隔。

/etc/passwd文件的例子:
在这里插入图片描述
有关口令文件的登录项的注意点:
1.通常有一个用户名为root的登录项,用户ID为0,是超级用户。

2.加密口令字段是一个占位符x,较早期的UNIX系统版本中,该字段存放加密后的密码,但将加密后的密码放在一个人人可读的文件中是一个安全漏洞,所以现在将加密口令放在另一个文件中。

3.某些字段可能为空(连续两个冒号表示两个冒号之间的字段为空)。如加密口令字段为空,则用户没有口令。注释字段为空没有任何影响,如上图的squid登录项。

4.shell字段包含一个可执行程序名,它被用作该用户的登录shell,此字段为空时,默认值为/bin/sh。用户squid的登录项的shell字段为/dev/null,这是一个设备而非可执行文件,目的是阻止任何人以用户squid登录到系统。

5.shell字段填为/bin/false时也可阻止特定用户登录系统,它简单地直接以不成功(非0)状态终止;shell字段也可填为/bin/true来阻止特定用户登录,它直接以成功(0)状态终止。某些系统提供nologin命令,它打印可定制的出错信息,然后以非0状态终止。

6.nobody用户在系统中被特别设计用于运行一些服务或进程,而不是为了与用户交互,它的用户ID(65534)和组ID(65534)不提供任何特权。该用户ID和组ID只能访问人人都可读写的文件(假设用户65534和组65534不拥有任何文件,实际就应该如此)。

7.提供finger命令的某些UNIX系统支持返回注释字段中的附加信息给调用者,finger命令要求注释字段按顺序包含各部分内容:用户姓名、办公室地点、办公室电话号、家庭电话号,各部分之间用逗号分隔,如果注释字段中的用户姓名字段是一个&,则它会被替换为登录名:
在这里插入图片描述
注释字段只是一个注释,不由系统实用程序解释。

某些系统提供vipw命令,允许管理员通过此命令编辑口令文件,此命令用于编辑/etc/passwd和/etc/shadow文件,这两个文件分别存储了系统用户和密码的信息,它以一个安全的方式来修改系统用户的密码和其他信息,因为它会在编辑时锁定这两个文件,这样其他进程就不能修改它们,保证了数据的一致性和完整性。vipw命令会启动一个编辑器(默认是vi或vim),你可以在其中编辑/etc/passwd和/etc/shadow文件,修改用户的信息,比如用户名、UID、GID、Shell、密码等。注意,修改这些文件需要root权限,所以只有系统管理员可以执行这个命令。执行vipw命令时,还会检查/etc/passwd和/etc/shadow文件的语法和格式是否正确,如果发现错误会给出相应的提示。系统也常常由图形用户界面(GUI)提供类似的功能。

POSIX.1定义了两个获取口令文件项的函数,给出用户登录名或UID后,就能查看相关项:
在这里插入图片描述
ls命令会使用getpwuid函数,将文件i节点中的数字用户ID映射为用户登录名。login命令会使用getpwnam函数。

以上函数返回的passwd结构通常是函数内部的静态变量,再次调用其内容会被重写。

SUS的XSI扩展中的有关口令文件的函数,可期望所有UNIX实现都提供这些函数,但POSIX.1标准没有定义这三个函数(POSIX标准是SUS标准的组成部分):
在这里插入图片描述
每次调用getpwent时,它返回口令文件中的下一个记录项(首次调用时,在返回第一条口令文件项前,还会做打开口令文件操作),每次调用此函数时都重写返回的passwd结构。对此函数返回的口令文件项的顺序无要求,有些系统使用散列算法对口令文件中各项排序。

setpwent函数rewind到口令文件的开头,使得下次调用getpwent时返回第一条口令文件项。

endpwent函数会关闭getpwent函数打开的口令文件,用这个函数关闭打开的口令文件的原因是getpwent函数不知道何时应该关闭口令文件。

getpwnam函数的一个实现:

#include <pwd.h>
#include <string.h>struct passwd *getpwnam(const char *name) {struct passwd *ptr;setpwent();    // 确保调用者在以前已经调用getpwent打开了口令文件的情况下,rewind到文件开始处while ((ptr = getpwent()) != NULL) {if (strcmp(name, ptr->pw_name) == 0) {break;}}endpwent();return ptr;    // ptr is NULL if no match found
}

加密口令是经单向加密算法处理过的用户口令副本,不能从加密口令反变换到原来的口令,但可对口令进行猜测,将猜测的口令经单向算法变换成加密形式,然后将其与用户的加密口令相比较。

某些系统将加密口令存放在阴影口令文件中,该文件至少要包含用户名和加密口令,与该口令相关的其他信息也可放在该文件中:
在这里插入图片描述
阴影口令文件不应是一般用户可以读取的,仅有少数几个程序需要访问加密口令,如login、passwd,这些程序常常是设置用户ID为root的程序。

Linux 3.2.0和Solaris 10中访问阴影口令文件的函数:
在这里插入图片描述
FreeBSD 8.0和Mac OS X 10.6.8中没有阴影口令结构,这两个系统中附加的账户信息(如账户有效期)存放在口令文件中。

UNIX组文件(POSIX.1称其为组数据库)包含以下字段,这些字段包含在头文件grp.h定义的group结构中:
在这里插入图片描述
字段gr_mem是一个指针数组,其中每个指针指向一个属于该组的用户名,该数组以null指针结尾。

POSIX.1定义的返回指定组的group结构的函数:
在这里插入图片描述
这两个函数也常返回指向一个静态变量的指针,每次调用时都重写该静态变量。

搜索整个组文件(由SUS的XSI扩展定义,不是基本POSIX.1组成,所有UNIX都提供这3个函数):
在这里插入图片描述
setgrent函数打开组文件(如它尚未打开)并rewind它。

用户执行newgrp命令可更改用户所在的实际组ID,执行不带参数的newgrp命令可回到与该用户名相同的预设组。

4.2 BSD引入了附属组ID,一个用户除了属于一个组外,还可属于至多16个另外的附属组。文件访问权限也修改为:不仅将进程的有效组ID与文件的组ID相比,也将所有附属组ID与文件的组ID比较。

POSIX.1早期版本中附属组ID特性是可选的。常量NGROUPS_MAX规定了附属组ID的数量,常用值为16。

获取和设置附属组ID的函数:
在这里插入图片描述
POSIX.1只说明了getgroups函数,因为setgroups和initgroups函数是特权操作,所以它们并非POSIX.1的组成部分。Mac OS X 10.6.8中,initgroupus函数的basegid参数的类型为int。

getgroups函数将进程所属用户的各附属组ID填到grouplist参数中,填入数量最多是参数gidsetsize个,返回实际填写到数组中的附属组ID数。如果参数gidsetsize为0,则函数不修改grouplist参数,但会返回附属组ID数,这样可确定grouplist参数数组的长度,以便进行分配空间。

setgroups函数可由root用户调用为调用进程设置附属组ID表,参数grouplist是组ID数组,参数ngroups说明了数组中的元素数(此值不能大于NGROUPS_MAX)。

通常只有initgroups函数调用setgroups函数(因此initgroups函数也需要超级用户权限),initgroups函数一般读整个组文件,然后用组文件中数据初始化username参数表示的用户的附属组,参数basegid也会加入用户的附属组中。login命令在用户登录时会调用此函数。

不同平台存储用户和组信息的方式:
在这里插入图片描述
Mac OS X只在单用户模式下使用/etc/passwd和/etc/master.passwd(单用户模式下可执行系统的维护任务,通常此模式下会关闭系统服务,如目录服务守护进程),在正常的多用户模式期间,目录服务守护进程提供对用户和组账户信息的访问。

对于阴影口令文件中的不活动字段,Solaris将其定义为自用户上次登录后到下次账户自动失效之间的天数,Linux将其定义为达到口令失效的尚余天数。

很多系统中,用户和组数据库是用网络信息服务(Network Information Service,NIS)实现的,管理员可编辑数据库的主副本,然后将它自动分发到组织中所有服务器上,客户联系服务器查看用户和组的有关信息,NIS+轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP)提供了类似功能。很多客户通过配置文件/etc/nsswitch.conf控制管理每一类信息的方法。

对于除口令文件和组文件外的一些系统数据文件,一般为每个数据文件至少提供3个函数:
1.get函数:读下一条记录,如果要读的文件未打开,则先打开该文件,此函数通常返回指向一个结构的指针,该指针指向一个静态存储类结构,当到达文件尾端时返回空指针。

2.set函数:打开相应数据文件(如果该文件未打开),然后rewind该文件。

3.end函数:关闭相应数据文件。

除以上3个函数外,如果数据文件支持某种形式的键搜索,则也提供搜索指定键对应数据的例程,如口令文件有getpwnam函数搜索指定用户名的记录和getpwuid函数搜索指定用户ID的记录。
在这里插入图片描述
Solaris中,上图里的最后4个文件都是符号链接,链接到/etc/inet下的同名文件上。

FreeBSD中,阴影口令文件是/etc/master.passwd,可使用特殊命令编辑该文件,该命令会产生阴影口令文件的一个副本,该副本名为/etc/passwd;另外,此命令也产生一个/etc/master.passwd文件的散列副本(/etc/spwd.db)和一个/etc/passwd的散列副本(/etc/pwd.db),这些散列副本为更大的系统提供更好的性能。

大多UNIX系统都提供下列两个数据文件:utmp文件记录当前登录到系统的各个用户;wtmp文件跟踪各个登录和注销事件。在V7中,这两个文件中写入的是以下结构的二进制记录:
在这里插入图片描述
登录时,login程序填写此类型结构,然后将其写入utmp文件和wtmp文件,注销时,init进程将utmp文件中的相应记录擦除(每个字节都填为null字节),并将一个新纪录(此记录的ut_name字段为0)添加到wtmp文件中。在系统重启动后和更改系统时间前后,都会在wtmp文件追加特殊的记录项。who命令读取utmp,并以可读形式打印其中内容(当前登录用户信息);last命令读wtmp文件并打印其中记录。

utmp结构中的信息在V7中有20字节,在SVR2中被扩充为36字节,而在SVR4中被扩充为多于350字节。

Solaris中,utmp和wtmp文件在/var/adm目录下;FreeBSD 8.0和Linux 3.2.0中,这两个文件的路径为/var/run/utmp和/var/log/wtmp;Mac OS X 10.6.8中,utmp和wtmp文件不存在。

POSIX.1定义的返回与主机和操作系统相关信息的函数:
在这里插入图片描述
此函数执行时会填写传入到name参数的utsname结构,POSIX.1只定义了该结构中最少需提供的字段,这些字段都是以null结尾的字符数组,每个数组的长度由实现确定:
在这里插入图片描述
utsname结构中的内容可由uname命令打印。

utsname结构没有给出POSIX.1版本的信息,应使用宏_POSIX_VERSION获得此信息。

BSD派生的系统提供gethostname函数,它只返回主机名,该名字通常是TCP/IP网络上主机的名字:
在这里插入图片描述
namelen参数指定name参数的长度,如果空间足够,则返回的字符串以null结尾,否则不确定。

现在POSIX.1也定义了gethostname函数,该标准指定最大主机名长度为HOST_NAME_MAX。
在这里插入图片描述
hostname命令可获取和设置主机名,主机名通常在系统自举时设置,文件名由程序/etc/rc或init取自一个启动文件。

UNIX内核提供的基本时间服务是计算自协调世界时(Coordinated Universal Time,UTC)的19700101 00:00:00时间以来经过的秒数,这个秒数存放于数据类型time_t,称其为日历时间。

返回当前的UNIX时间戳:
在这里插入图片描述
时间戳作为函数值返回,如果参数calptr非空,时间戳同时也存放在该参数指向的空间。

POSIX.1的实时扩展增加了对多个系统时钟的支持,在SUS v4中,控制这些时钟的接口从可选组移到基本组。时钟通过clockid_t类型进行标识:
在这里插入图片描述
以下函数可获取特定时钟的时间:
在这里插入图片描述
当参数clock_id设为CLOCK_REALTIME时,由于timespec结构把时间表示为秒和纳秒,因此在系统支持高精度时间值的情况下,colck_gettime函数比time函数的精度更高。

获取特定时钟的精度:
在这里插入图片描述
如果参数clock_id表示的时钟的精度为1毫秒,则tsp参数指向的timespec结构的tv_sec字段为0,tv_nsec字段为1000000。

设置特定时钟的时间:
在这里插入图片描述
更改时钟值需要特权,有些时钟是不能修改的。

System V派生的系统使用stime函数设置系统时间;BSD派生的系统使用settimeofday函数设置系统时间。

SUS v4指定gettimeofday函数已经弃用,但还有很多程序使用它,因为它相比于time函数能提供更高的精度(微秒级):
在这里插入图片描述
tzp参数的唯一合法值是NULL,其他值会产生不确定的结果,某些平台用tzp参数说明时区,这完全依实现而定,SUS对此没有定义。

gettimeofday函数返回以timeval结构表示的UNIX时间戳,该结构将UNIX时间戳表示为秒和微秒。

取得UNIX时间戳后,通常调用函数将其转换为tm结构:
在这里插入图片描述
然后通常调用另一函数将tm结构转换为人可读的时间和日期,各个时间函数之间的关系:
在这里插入图片描述
上图中用虚线表示的函数受环境变量TZ的影响,它们会用TZ的值代替系统默认时区,如果TZ定义为空串,则使用UTC(世界各地的时区都是用与UTC的正负偏移量来表示的)。

tm结构中秒的最大值超过59,因为这样可以表示闰秒;除月、日外,tm结构中其他字段都以0开始;如果夏令时生效,则夏令时标志为正,如果为非夏令时时间,则该标志为0,如果此信息不可用,则其值为负。

SUS的以前版本允许双闰秒,tm_sec字段的最大值为61,UTC的正式定义不允许双闰秒,因此现在tm_sec值最大值定义为60。

将UNIX时间戳转换为tm结构:
在这里插入图片描述
localtime函数可将日历时间转换为本地时间(即考虑时区和夏令时标志);gmtime函数将日历时间转换成UTC的tm结构。

将tm结构转换为日历时间:
在这里插入图片描述
将tm结构转换为格式化字符串:
在这里插入图片描述
函数asctime和ctime能用于产生一个26字节的可打印字符串,类似date命令的输出,但这些函数已被标记为弃用,因为它们容易出现缓冲区溢出的问题。

strftime_l函数允许调用者指定时区。strftime使用TZ环境变量指定时区。

参数tmptr指向一个tm结构,是要格式化的时间值,格式化的结果存放在参数buf表示的数组中,数组的大小由maxsize参数指明(单位为字节数),如果缓冲区能容纳格式化的结果和一个null终止符,则该函数返回在buf中存放的字节数(不含null),否则函数返回0。

format参数控制输出的格式,其中的“转换说明”会转换为特定的时间,其他字符原样输出,ISO C规定的“转换说明”如下:
在这里插入图片描述
strftime函数的每个转换说明产生一个定长的输出字符串,因此format参数字符串中没有字段宽度修饰符。

上图中%U表示该日期在该年中所属的周数,包含第一个星期日的周是第一周。%W与%U类似,但它将包含第一个星期一的周看做第一周。%V认为如果包含了1月1日的那周包含了新一年的4天及以上的天数,那么该周为一年中的第一周,否则该周被认为是上一年的最后一周。

strftime函数对某些转换说明支持修饰符,可使用E和O修饰符产生本地支持的另一种格式,某些系统支持另一些非标准的扩充支持。

使用strftime函数:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main() {time_t t;struct tm *tmp;char buf1[16];char buf2[64]; time(&t);tmp = localtime(&t);if (strftime(buf1, 16, "time and date: %r, %a %b %d %Y", tmp) == 0) {printf("buffer length 16 is too small\n");} else {printf("%s\n", buf1);}if (strftime(buf2, 64, "time and date: %r, %a %b %d %Y", tmp) == 0) {printf("buffer length 64 is too small\n");} else {printf("%s\n", buf2);}exit(0);
}

运行它:
在这里插入图片描述
strptime函数将字符串时间分解为tm结构:
在这里插入图片描述
参数format给出了buf参数指向的缓冲区内的字符串的格式,format参数中的转换说明与strftime函数的转换说明稍有不同:
在这里插入图片描述
加密口令要从阴影文件中获取而不能从口令文件中获取(在FreeBSD 8.0中,仅当调用者的有效用户ID为0时,即是root用户时,getpwnam或getpwuid函数返回的口令文件项的加密口令字段pw_passwd含加密口令;Mac OS X 10.6.8中,加密口令无法通过本章中的函数获取)。

在系统Linux 3.2.0和Solaris 10上,有超级用户权限时,可用以下程序从阴影口令文件中获取加密口令,如没有超级用户权限,调用getspnam会返回EACCESS错误:

#include <stdio.h>
#include <shadow.h>
#include <stdlib.h>int main() {    // Linux/Solaris versionstruct spwd *ptr;if ((ptr = getspnam("sar")) == NULL) {printf("getspnam error\n");exit(1);}printf("sp_pwdp = %s\n", (ptr->sp_pwdp == NULL || ptr->sp_pwdp[0] == 0) ? "(null)" : ptr->sp_pwdp);exit(0);
}

在FreeBSD 8.0中,在具有超级用户权限时,可用以下程序从口令文件中输出加密口令,如果没有超级用户权限,则加密字段为星号:

#include <pwd.h>
#include <stdlib.h>
#include <stdio.h>int main() {struct passwd *ptr;if ((ptr = getpwnam("sar")) == NULL) {printf("getpwnam error\n");exit(1);}printf("pw_passwd = %s\n", (ptr->pw_passwd == NULL || ptr->pw_passwd[0] == 0) ? "(null)" : ptr->pw_passwd);exit(0);
}

以上程序在Mac OS X 10.6.8上,不管运行时用户是什么权限都输出星号。

使用strftime函数输出类似date命令的格式化串:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define MAXLINE 1024int main() {time_t caltime;struct tm *tm;char line[MAXLINE];if ((caltime = time(NULL)) == -1) {printf("time error\n");exit(1);}if ((tm = localtime(&caltime)) == NULL) {printf("localtime error\n");exit(1);}if (strftime(line, MAXLINE, "%a %b %d %X %z %Y\n", tm) == 0) {printf("strftime error\n");exit(1);}fputs(line, stdout);exit(0);
}

运行它:
在这里插入图片描述
可见strftime函数输出TZ环境变量表示的时区的时间。

这篇关于UNIX环境高级编程 学习笔记 第六章 系统数据文件和信息的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/940654

相关文章

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

什么是cron? Linux系统下Cron定时任务使用指南

《什么是cron?Linux系统下Cron定时任务使用指南》在日常的Linux系统管理和维护中,定时执行任务是非常常见的需求,你可能需要每天执行备份任务、清理系统日志或运行特定的脚本,而不想每天... 在管理 linux 服务器的过程中,总有一些任务需要我们定期或重复执行。就比如备份任务,通常会选在服务器资

TP-LINK/水星和hasivo交换机怎么选? 三款网管交换机系统功能对比

《TP-LINK/水星和hasivo交换机怎么选?三款网管交换机系统功能对比》今天选了三款都是”8+1″的2.5G网管交换机,分别是TP-LINK水星和hasivo交换机,该怎么选呢?这些交换机功... TP-LINK、水星和hasivo这三台交换机都是”8+1″的2.5G网管交换机,我手里的China编程has

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

Python中列表的高级索引技巧分享

《Python中列表的高级索引技巧分享》列表是Python中最常用的数据结构之一,它允许你存储多个元素,并且可以通过索引来访问这些元素,本文将带你深入了解Python列表的高级索引技巧,希望对... 目录1.基本索引2.切片3.负数索引切片4.步长5.多维列表6.列表解析7.切片赋值8.删除元素9.反转列表

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

使用Python检查CPU型号并弹出警告信息

《使用Python检查CPU型号并弹出警告信息》本教程将指导你如何编写一个Python程序,该程序能够在启动时检查计算机的CPU型号,如果检测到CPU型号包含“I3”,则会弹出一个警告窗口,感兴趣的小... 目录教程目标方法一所需库步骤一:安装所需库步骤二:编写python程序步骤三:运行程序注意事项方法二

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

基于Qt实现系统主题感知功能

《基于Qt实现系统主题感知功能》在现代桌面应用程序开发中,系统主题感知是一项重要的功能,它使得应用程序能够根据用户的系统主题设置(如深色模式或浅色模式)自动调整其外观,Qt作为一个跨平台的C++图形用... 目录【正文开始】一、使用效果二、系统主题感知助手类(SystemThemeHelper)三、实现细节

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE