UNIX系统的正常运作需要用到大量与系统有关的数据文件,例如系统用户账号、用户密码、用户组等文件。出于历史原因,这些数据文件都是ASCII文本文件,并且使用标准I/O库函数来读取。

口令文件

/etc/passwd文件是UNIX安全的关键文件之一。该文件用于用户登录时校验用户的口令,文件中每行的一般格式为:

用户名:x:用户ID:用户组ID:说明信息:个人主目录:SHELL

对于第二项x来说是密码,但由于安全原因密码已经被移至其他文件,因此使用x来代替。

UNIX系统提供了两个用于获取passwd文件中条目的函数,在给出用户名或者用户ID之后,这两个函数即可查看相关信息。其头文件及函数原型如下:

#include <pwd.h>

struct passwd* getpwuid(uid_t __uid);
struct passwd* getpwnam(const char *__name);

函数成功是返回相应指针,出错返回NULL。第一个函数使用用户UID,第二个函数使用用户名。返回的指针指向一个静态变量,因此只要再调用任一相关的函数就会改写指针所指向的变量内容。

上面两个函数一次只能查看一个,而且必须提供用户名或者用户UID,如果想要查看所有账户信息或者事先不了解用户名及用户UID,则可以通过下面几个函数来查看。其头文件及函数原型如下:

#include <pwd.h>

struct passwd *getpwent(void);
void setpwent(void);
void endpwent(void);

getpwent( )函数用于读取passwd文件,setpwent( )用于设置从passwd文件头部开始读,endpwent( )用于关闭文件。

阴影口令

阴影口令文件/etc/shadow用于存储加密后的密码,该密码加密方式是单向不可逆。shadow文件普通用户不允许读取,访问shadow文件的一组函数与访问passwd文件函数类似。其头文件及函数原型如下:

#include <shadow.h>

struct spwd *getspnam(const char *__name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);

函数成功是返回相应指针,出错返回NULL。

组文件

组文件/etc/group包含了系统用户组相关的信息,UNIX系统提供了两个函数来查看。其头文件及函数原型如下:

#include <grp.h>

struct group *getgrgid (gid_t __gid);
struct group *getgrnam (const char *__name);

函数成功是返回相应指针,出错返回NULL。

如果需要搜索整个组文件,则类似passwd文件函数。其头文件及函数原型如下:

#include <grp.h>

struct group *getgrent(void);
extern void setgrent(void);
extern void endgrent(void);

附属组ID

现有的UNIX都支持一个用户加入多个组,对于除了创建用户时产生的自带用户组之外,用户额外加入的其他组则为附属组。为了获取和设置附加组ID,UNIX提供了3个函数。其头文件及函数原型如下:

#include <unistd.h>
#include <grp.h> int getgroups (int __size, gid_t __list[]);
int setgroups (size_t __n, const gid_t *__groups);
int initgroups (const char *__user, gid_t __group);

登录账户记录

UNIX系统还提供了一些事件簿记功能,该功能可以查看当前登录到系统的各个用户,还能跟踪登录注销等事件。UNIX中利用一个二进制结构来填写记录。登录时,login程序将信息写入到utmp文件和wtmp文件中。注销时,init进程将utmp文件中的记录移除,并添加一个新记录到wtmp文件中。

系统标识

UNIX系统提供了一个函数用于返回与主机和操作系统有关的信息。其头文件及函数原型如下:

#include <sys/utsname.h>

int uname(struct utsname *__name);

函数成功时返回非负值,失败返回-1。

时间和日期例程

正如第一章所述,UNIX为运行在它之上的程序提供各种服务,其中就包括提供时间这一服务。UNIX系统提供的时间服务是计算自协调世界时1970年1月1日以来所经过的秒数,该秒数使用time_t类型表示。在UNIX系统中,可以使用time( )函数来返回当前日期和时间。其头文件及函数原型如下:

#include <time.h>

time_t time (time_t *__timer);

函数成功返回时间值,失败返回-1。如果__timer指针非空,那么__timer指针所指的对象也被设置为相应的值。

time( )函数只能用来获取当前时间,UNIX系统还提供了一个函数用于获取指定时钟开始之后的秒数。其头文件及函数原型如下:

#include <time.h>

int clock_gettime (clockid_t __clock_id, struct timespec *__tp);

函数成功时返回0,失败返回-1。该函数可以用来计时,比如从当前程序开始执行开始到程序停止执行为止统计总计花费了多少时间。

当我们得到时间之后,这个时间是一个自1970年以来经过的秒数,对人来说它无意义,必须转换为人类可读的时间。转换又分为两步,第一步是转换为年月日时分秒,第二步是格式化输出。如果仅仅用于程序的计算,那么第一步转换已经满足,但为了打印输出让人可读,还必须进行格式化输出。UNIX系统提供了两个函数用于从time_t转换为年月日时分秒的时间结构类型,提供了一个逆向从年月日时分秒的时间结构类型转到time_t的函数,还提供了两个格式化输出的函数。其头文件及函数原型如下:

#include <time.h>

struct tm *gmtime(const time_t *timer);
struct tm *localtime(const time_t *timer);
time_t mktime(struct tm *tp);
size_t strftime(char* s, size_t maxsize, const char* format, const struct tm* tp);
size_t strftime_l(char* s, size_t maxsize, const char* format, const struct tm* tp, locale_t loc);

对于前两个函数,成功返回相应指针,失败返回NULL;对于第三个函数,成功返回时间,失败返回-1;对于最后两个函数,成功返回字符数,失败返回0。

习题

6.1 如果系统使用阴影文件,那么如何取得加密口令?

无法获取,因为shadow文件只允许root用户访问,普通用户无法取得。

6.2 假设你有超级用户权限,并且系统使用了阴影口令,重新考虑上一道习题。

#include <iostream>
#include <shadow.h> int main (int argc, char *argv[])
{
string name;
cout << "input your user name: ";
cin >> name;
struct spwd *ptr = nullptr;
ptr = getspnam(name.c_str()); if (ptr)
{
cout << ptr->sp_pwdp << endl;
} return ;
}

代码编译后,使用root权限运行。

6.3 编写一程序,它调用uname并输出utsname结构中的所有字段,将该输出与uname(1)命令的输出结果进行比较。

#include <sys/utsname.h>
#include <iostream> using namespace std; int main (int argc, char *argv[])
{
utsname p;
if (uname(&p) >= )
{
cout << p.sysname << ' '
<< p.nodename << ' '
<< p.release << ' '
<< p.version << ' '
<< p.machine << endl;
}
return ;
}

与uname(1)命令的输出结果相比少三个输出:处理器类型、硬件平台、操作系统。因为UNIX系统并不负责定义硬件及具体实现,UNIX只定义标准规范。

6.4  计算可由time_t数据类型表示的最近时间。如果超出了这一时间将会如何?

C++11标准规定long类型最少占32位,在我的计算机上,系统使用long int来实现time_t,实际使用64位来表示long类型,因此其取值值范围为 -9223372036854775808~9223372036854775807,由于该值特别大,2900亿年后才会溢出,此时宇宙可能都不存在了。对于某些32位系统或者旧的程序,它们的time_t类型是使用32位int来实现的,而int取值范围为-2147483648~2147483647,我们可以利用localtime( )函数来分解该值,并用strftime( )函数来打印,程序如下:

#include <iostream>
#include <climits>
#include <ctime> int main (int argc, char *argv[])
{
time_t tm_t = INT_MAX;
tm* tmp = nullptr;
char buf[] = {};
if (tmp = localtime(&tm_t))
{
strftime(buf, , "%Y/%m/%e %H:%M:%S \n", tmp);
}
std::cout << buf << std::endl; return ;
}

程序运行结果如下:

由于我的本地北京时间有8个小时的时差提前,因此真正的溢出时间为2038年1月19日03:14:07。如果到这一天将会溢出,由于int是有符号数,而C和C++标准都对有符号数溢出行为未定义,因此程序的行为无法预计,可能崩溃,可能复位,可能溢出后是个无法估计的值。下面是个动态演示图:

2038年问题演示:第一行是“time_t”数字的二进制表示;第二行是其十进制表示;第三行是受影响的计算机理解的时间;第四行是实际的时间。

上面图片来自wikipedia.org。

6.5 编写一程序,获取当前时间,并使用strftime将输出结果转换为类似于date(1)命令的默认输出。将环境变量TZ设置为不同值,观察输出结果。

#include <iostream>
#include <climits>
#include <ctime> int main (int argc, char *argv[])
{
time_t tm_t = time(nullptr);
tm* tmp = nullptr;
char buf[] = {};
if (tmp = localtime(&tm_t))
{
strftime(buf, , "%Y年 %m月 %e日 星期%u %H:%M:%S %Z\n", tmp);
}
std::cout << "\n个人程序:";
std::cout << '\n' << buf << std::endl; return ;
}

编译代码后在不同时区执行结果如下:

Africa Abidjan:

America Adak:

Asia Shanghai:

UNIX环境高级编程 第6章 系统数据文件和信息的更多相关文章

  1. UNIX系统高级编程——第六章-系统数据文件和信息-总结

    口令文件: /* The passwd structure. */ struct passwd { char *pw_name; /* Username. */ char *pw_passwd; /* ...

  2. apue学习笔记(第六章 系统数据文件和信息)

    UNIX系统的正常运作需要使用大量与系统有关的数据文件,例如,口令文件/etc/passwd和组文件/etc/group就是经常被多个程序频繁使用的两个文件. 口令文件 UNIX系统口令文件包含如下字 ...

  3. UNIX环境高级编程 第13章 守护进程

    守护进程daemon是一种生存周期很长的进程.它们通常在系统引导时启动,在系统关闭时终止.守护进程是没有终端的,它们一直在后台运行. 守护进程的特征 在Linux系统中,可以通过命令 ps -efj ...

  4. UNIX环境高级编程 第5章 标准I/O库

    本章是关于C语言标准I/O库的,之所以在UNIX类系统的编程中会介绍C语言标准库,主要是因为UNIX和C之间具有密不可分的关系.由于UNIX系统存在很多实现,而每个实现都有自己的标准I/O库,为了统一 ...

  5. UNIX环境高级编程 第16章 网络IPC:套接字

    上一章(15章)中介绍了UNIX系统所提供的多种经典进程间通信机制(IPC):管道PIPE.命名管道FIFO.消息队列Message Queue.信号量Semaphore.共享内存Shared Mem ...

  6. unix环境高级编程第六章笔记

    口令文件 阴影口令 组文件 附属组ID 登录账户记录 系统标识 口令文件<\h2> /etc/passwd文件是UNIX安全的关键文件之一.该文件用于用户登录时校验用户的口令,文件中每行的 ...

  7. UNIX环境高级编程 第8章 进程控制

    本章是UNIX系统中进程控制原语,包括进程创建.执行新程序.进程终止,另外还会对进程的属性加以说明,包括进程ID.实际/有效用户ID. 进程标识 每个进程某一时刻在系统中都是独一无二的,它们之间是用一 ...

  8. UNIX环境高级编程 第7章 进程环境

    本章涉及C/C++程序中main函数是如何被调用的.命令行参数如何传递给main函数.程序的内存空间布局.程序如何使用环境变量.程序如何终止退出. main函数 C程序或C++程序总是从main函数开 ...

  9. UNIX环境高级编程 第9章 进程关系

    在第8章学习了进程的控制原语,通过各种进程原语可以对进程进行控制,包括新建进程.执行新程序.终止进程等.在使用fork( )产生新进程后,就出现了进程父子进程的概念,这是进程间的关系.本章更加详细地说 ...

随机推荐

  1. Fortinet Security Fabric

    Fortinet Security Fabric 这个世界从不固步自封.在技术方面,这意味着解决方案供应商必须保持不断创新和探索才能实现生存与发展. 在网络安全领域,这更是至理名言.许多黑客都是才华横 ...

  2. BZOJ 4753 [Jsoi2016]最佳团体 | 树上背包 分数规划

    BZOJ 4753 [Jsoi2016]最佳团体 | 树上背包 分数规划 又是一道卡精度卡得我头皮发麻的题-- 题面(--蜜汁改编版) YL大哥是24OI的大哥,有一天,他想要从\(N\)个候选人中选 ...

  3. 设置Linux终端字体颜色

    系统启动后,环境变量加载的顺序为:/etc/profile → /etc/profile.d/*.sh → ~/.bash_profile → ~/.bashrc → /etc/bashrc 想要修改 ...

  4. 用C语言获取任意文件的长度(可能大于2GB)#define _FILE_OFFSET_BITS 64

    打开文件后用 fseek() 函数把文件位置指针移动到文件的末尾,用 ftell() 获得这时位置指针距文件头的字节数,这个字节数就是文件的长度.但是这样做也会受到下面的限制:ftell() 函数的返 ...

  5. Java的基本类型

    基本数据类型的加载和存储 极客时间深入理解Java虚拟机读后感,有错误还请指正 虚拟机中的Boolean类型 在Java语言规范中,boolean类型的值只有两种可能,那就是"true&qu ...

  6. 18华南理工校赛 K 小马哥的超级盐水

    https://www.nowcoder.com/acm/contest/94/K sum(ai)/sum(bi) = x/y <=> sum(ai*yi-bi*x) = 0 跟这题有点类 ...

  7. eclipse 支持 Robot framework 编辑环境

    一.配置python 环境     1. 设置pydev库         打开Help -> Install New Software, 点击'Add',设置Name为“Pydev”,Loca ...

  8. O(1)时间复杂度求栈中最小元素

    import java.util.Stack; /** * 功能:O(1)时间复杂度求栈中最小元素 * 思路:空间换取时间,使用两个栈,stack1栈存储数据,stack2栈存储最小值: * stac ...

  9. lumen 单元测试的一些问题

    1.一个 test 多个请求 如 $this->post,然后又  $this->post,我们会发现第二个请求中的请求参数是和第一个请求的参数是完全一样的,然后在 Controller ...

  10. 开源分布式工作流任务调度系统Easy Scheduler Release 1.0.2发布

    Easy Scheduler Release 1.0.2===Easy Scheduler 1.0.2是1.x系列中的第三个版本.此版本增加了调度开放接口.worker分组(指定任务运行的机器组).任 ...