系统数据文件和信息

系统数据文件和信息

Unix 系统的正常运行需要使用大量与系统有关的数据文件,例如,口令文件 /etc/passwd 和组文件 /etc/group 都是经常被多个程序频繁使用的两个文件。用户每次登录 Unix 系统以及每次执行 ls -l 命令时都要使用口令文件。

/etc/passwd 文件

Unix 系统口令文件中的每一行记录包含了以下的字段

在这里插入图片描述

这些字段都在 passwd 结构体中,该结构体的具体形式:

struct passwd {char   *pw_name;       /* username */char   *pw_passwd;     /* user password */uid_t   pw_uid;        /* user ID */gid_t   pw_gid;        /* group ID */char   *pw_gecos;      /* user information */char   *pw_dir;        /* home directory */char   *pw_shell;      /* shell program */
};

由于历史原因,口令文件是 /etc/passwd,而且是一个 ASCII 文件,每一行包含了结构中的各种字段,字段之间用冒号分隔。

虽然可以在此文件获取到用户和用户组的信息,但并不是每个 Linux 系统都是使用此文件保存口令信息。POSIX.1 定义了两个获取口令文件的函数,函数原型如下:

#include <sys/types.h>
#include <pwd.h>struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);

通过指定的用户名或用户 id,返回 passwd 结构的指针(passwd 结构通常是函数内部的静态变量),即可获取这个用户的所有相关信息。

pw_shell 成员保存的是该用户的登录 shell,如果此字段为空,则默认使用 /bin/sh。如果想要阻止一个特定用户登录系统,可以将将此字段改为 /dev/null/bin/false

获取某个文件的所属用户名

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>int main(int argc, char *argv[]) {if (1 == argc) {fprintf(stderr, "Usage: %s filename ...\n", argv[0]);exit(EXIT_FAILURE);}struct passwd *passwd_line = NULL;for (int i = 0; i < argc; ++i) {struct stat stat_buf;if (-1 == stat(argv[i], &stat_buf)) {perror("stat() error");exit(EXIT_FAILURE);}passwd_line = getpwuid(stat_buf.st_uid);printf("%s name is %s\n", argv[i], passwd_line->pw_name);}return 0;
}

如果要获取整个口令文件,则需要使用以下的函数

#include <sys/types.h>
#include <pwd.h>struct passwd *getpwent(void);  // 返回口令文件中的下一个记录项
void setpwent(void);            // 反绕它所使用的文件
void endpwent(void);            // 关闭该文件

在使用 getpwent 查看完口令文件后,一定要调用 endpwent 文件。gerpwent 知道什么时候应当打开它所使用的文件(第一次被调用时),但是不知道何时关闭这些文件。在函数开始调用 setpwent 是自我保护性的措施,以便确保如果调用者在此之前已经调用 getpwent 打开了文件情况下,反绕有关文件使它们定位到文件开始处。getpwnamgetpwuid 完成后不应使有关文件仍处于打开状态,所以应当调用 endpwent 关闭它们。

getpwnam 的一个简单实现

#include <pwd.h>
#include <stddef.h>
#include <string.h>struct passwd* getpwnam(const char *name) {struct passwd *ptr;setpwent(); // 打开口令文件while ((ptr = getpwent()) != NULL)  // 逐一获取口令文件中的每一行并比较if (strcmp(name, ptr->pw_name) == 0)break;endpwent();   // 关闭口令文件return(ptr);  // 返回用户名
}

/etc/group 文件

用户组的相关信息可以在 /etc/group 文件中读取到,该文件中的每行数据都保存在 group 结构中,包含以下字段

在这里插入图片描述

这些字段都保存在 group 结构中,该结构的具体形式如下:

struct group {char   *gr_name;        /* group name */char   *gr_passwd;      /* group password */gid_t   gr_gid;         /* group ID */char  **gr_mem;         /* NULL-terminated array of pointersto names of group members */
};

POSXI.1 定义两个函数来获取用户组的信息,其函数原型如下:

#include <sys/types.h>
#include <grp.h>// 成功返回 group 指针,失败返回 NULL
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);

获取某个文件的所属用户组

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <grp.h>int main(int argc, char *argv[]) {if (1 == argc) {fprintf(stderr, "Usage: %s filename ...\n", argv[0]);exit(EXIT_FAILURE);}struct group *group_line = NULL;for (int i = 1; i < argc; ++i) {struct stat stat_buf;if (-1 == stat(argv[i], &stat_buf)) {perror("stat() error");exit(EXIT_FAILURE);}group_line = getgrgid(stat_buf.st_gid);printf("%s belong to %s\n", argv[i], group_line->gr_name);}return 0;
}

如同口令文件一样,如果获取全部的用户组信息,也有对应的处理函数,使用的原理也基本相同。

#include <sys/types.h>
#include <grp.h>struct group *getgrent(void);
void setgrent(void);
void endgrent(void);

/etc/shadow 文件

加密口令是经单向加密算法处理过的用户口令副本,因为此算法是单向的,所以不能从加密口令猜测到原来的口令。某些系统将加密口令存放在一个称为阴影口令的文件中,如 /etc/shadow,该文件至少要包含用户名和加密口令。我们也可以如同上面的两个文件,通过登录用户名获取文件中对应的字段信息,函数原型如下:

#include <shadow.h>struct spwd *getspnam(const char *name);
struct spwd *getspent(void);

spwd 结构的具体形式如下:

struct spwd {char *sp_namp;     /* Login name */char *sp_pwdp;     /* Encrypted password */long  sp_lstchg;   /* Date of last change(measured in days since1970-01-01 00:00:00 +0000 (UTC)) */long  sp_min;      /* Min # of days between changes */long  sp_max;      /* Max # of days between changes */long  sp_warn;     /* # of days before password expiresto warn user to change it */long  sp_inact;    /* # of days after password expiresuntil account is disabled */long  sp_expire;   /* Date when account expires(measured in days since1970-01-01 00:00:00 +0000 (UTC)) */unsigned long sp_flag;  /* Reserved */
};

此文件不应是一般用户可以读取的,仅有少数几个程序需要访问加密口令,如 login(1)passwd(1),这些程序常常设置用户 ID 为 root 的程序。

使用 crypt() 函数可以获取加密口令,函数原型如下:

#include <crypt.h>char *crypt(const char *phrase, const char *setting);

获取加密口令

#include <crypt.h>
#include <shadow.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main(int argc, char *argv[]) {if (2 != argc) {fprintf(stderr, "Usage: %s <username>\n", argv[0]);exit(EXIT_FAILURE);}struct spwd *shadow_line = NULL;shadow_line = getspnam(argv[1]);char *input_pass = getpass("PassWord: "); // 获取密码输入并且不在终端回显char *crypt_pass = crypt(input_pass, shadow_line->sp_pwdp);if (0 == strcmp(shadow_line->sp_pwdp, crypt_pass))puts("ok!");elseputs("failed");return 0;
}

附属组 ID 和登录账户记录

附属组 ID

当用户登录时,系统就按口令文件记录项中的数值 ID,赋给他实际组 ID,还可以属于 16 个另外的组。因此访问权限检查不仅将进程的有效组 ID 与文件的组 ID 比较,而且也将所有附属组 ID 和文件的组 ID 进行比较。相关函数如下:

#include <sys/types.h>
#include <unistd.h>
#include <grp.h>int getgroups(int size, gid_t list[]);
int setgroups(size_t size, const gid_t *list);
int initgroups(const char *user, gid_t group);

登录账户记录

大多数的 Unix 系统都提供这两个数据文件: utmp 文件记录当前登录到系统的各个用户;wtmp 文件跟踪各个登录和注销事件。

struct utmp {short   ut_type;              /* Type of record */pid_t   ut_pid;               /* PID of login process */char    ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */char    ut_id[4];             /* Terminal name suffix,or inittab(5) ID */char    ut_user[UT_NAMESIZE]; /* Username */char    ut_host[UT_HOSTSIZE]; /* Hostname for remote login, orkernel version for run-levelmessages */struct  exit_status ut_exit;  /* Exit status of a processmarked as DEAD_PROCESS; notused by Linux init (1 *//* The ut_session and ut_tv fields must be the same size whencompiled 32- and 64-bit.  This allows data files and sharedmemory to be shared between 32- and 64-bit applications. */
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32int32_t ut_session;           /* Session ID (getsid(2)),used for windowing */struct {int32_t tv_sec;           /* Seconds */int32_t tv_usec;          /* Microseconds */} ut_tv;                      /* Time entry was made */
#elselong   ut_session;           /* Session ID */struct timeval ut_tv;        /* Time entry was made */
#endifint32_t ut_addr_v6[4];        /* Internet address of remotehost; IPv4 address usesjust ut_addr_v6[0] */char __unused[20];            /* Reserved for future use */
};

登录时,login 程序填写此类型结构,然后将其写入到 utmp 文件中,同时也将其添写到 wtmp 文件中。注销时,init 进程将 utmp 文件中相应的记录擦除(每个字节都填以 null 字节),并将一个新记录添写到 wtmp 文件中。在 wtmp 文件的注销记录中,ut_name 字段清除为 0。在系统再启动时,以及更改系统时间和日期的前后,都在 wtmp 文件中追加写特殊的记录项。who(1) 程序读取 utmp 文件,并以可读格式打印其内容。后来的 Unix 版本提供 last(1) 命令,它读 wtmp 文件并打印所选择的记录。

时间和日期例程

Unix 内核提供的基本时间服务是计算自协调世界时公元 1970 年 1 月 1 日 00:00::00 这一特定时间以来经过的秒数。返回当前时间的函数如下:

#include <time.h>time_t time(time_t *tloc);

如果 tloc 为非空指针,则将时间值存储在 tloc 所指向的内存;如果是空指针,则直接返回一个整型时间值;如果失败,则返回 (time_t) -1 值,并用 errno 指明错误类型。

当取得从特定时间经过秒数的整型时间后,这是计算机喜欢的方式,我们程序无法理解其具体时间。因此,通常要调用函数将其转换为分解的时间结构,使其成为人们可读的时间和日期。主要有以下几个函数:

#include <time.h>// 下面的两个函数会将整型的时间分解成 tm 结构的指针,如果出错,返回 NULL
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);// 下面的函数将 tm 结构的时间转换成整型的时间
time_t mktime(struct tm *tm);

上述 tm 结构的具体形式如下:

struct tm {int tm_sec;    /* Seconds (0-60) */int tm_min;    /* Minutes (0-59) */int tm_hour;   /* Hours (0-23) */int tm_mday;   /* Day of the month (1-31) */int tm_mon;    /* Month (0-11) */int tm_year;   /* Year - 1900 */int tm_wday;   /* Day of the week (0-6, Sunday = 0) */int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */int tm_isdst;  /* Daylight saving time */
};

将整型的时间转换成年月日时分秒的形式gmtimelocaltime 之间的区别是: localtime 将日历时间转换成本地时间(考虑本地时间和夏令时标志),我们中国时区一般使用的是 localtime。而 gmtime 则将日历时间转换成协调统一的年、月、日、时、分、周日分解结构。

#include <stdio.h>
#include <time.h>int main(int argc, const char *argv[]) {time_t time_now = time(NULL);struct tm *ltime_now = localtime(&time_now);printf("------localtime------\n");printf("%d 年 %d 月 %d 日 %d 时 %d 分 %d 秒\n", ltime_now->tm_year + 1900, ltime_now->tm_mon + 1, ltime_now->tm_mday,ltime_now->tm_hour, ltime_now->tm_min, ltime_now->tm_sec);printf("%d 天 %d 日 %d \n", ltime_now->tm_yday, ltime_now->tm_wday, ltime_now->tm_isdst);struct tm *gtime_now = gmtime(&time_now);printf("------gtime------\n");printf("%d 年 %d 月 %d 日 %d 时 %d 分 %d 秒\n", gtime_now->tm_year + 1900, gtime_now->tm_mon + 1, gtime_now->tm_mday,gtime_now->tm_hour, gtime_now->tm_min, gtime_now->tm_sec);printf("%d 天 %d 日 %d \n", gtime_now->tm_yday, gtime_now->tm_wday, gtime_now->tm_isdst);return 0;
}

输出内容如下:

------localtime------
2024611158416220
------gtime------
202461178416220

man 手册中的 asctimectime 能用于产生一个 26 字节的可打印的字符串,类似于 date(1) 命令的默认输出,但是这些函数已经被弃用。

上面程序中要输出指定的时间内容需要自己通过成员访问调用,十分麻烦,Unix 有函数可以实现格式化时间转换,如同 printf 一样,函数原型如下:

#include <time.h>// 将 tm 结构的时间按匹配的格式保存
/*** @param*   s:保存转换后的字符串空间*   max:内存的最大大小*   format:格式化转换的方式*   tm:分解过后的时间* @return:内存空间足够,返回存入内存中数据的大小,否则返回 0*/
size_t strftime(char *s, size_t max, const char *format,const struct tm *tm);// 将指定的时间格式转换成 tm 结构
#define _XOPEN_SOURCE       /* See feature_test_macros(7) */
char *strptime(const char *s, const char *format, struct tm *tm);

转换说明有多种形式,大约有 30 多种转换说明,如下所示;

在这里插入图片描述

实现一个简单的 date 命令

#include <time.h>
#include <stdio.h>#define BUFFERSIZE 4096int main() {// 获取整型的时间值time_t time_now = time(NULL);// 对整型值进行分解struct tm *ltime_now = localtime(&time_now);// 格式化转换时间char buf[BUFFERSIZE] = {0};int res = strftime(buf, BUFFERSIZE, "%G %m %d %A %T %Z", ltime_now);if (0 == res)puts("failed");elseputs(buf);return 0;
}

这些函数之间的关系如下图所示:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1550495.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【设计模式-命令】

定义 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使您能够使用不同的请求、排队请求或记录请求&#xff0c;并支持可撤销的操作。该模式通过将请求与其执行分离&#xff0c;使得请求者和接收者之间…

第十四周学习周报

目录 摘要Abstract1. LSTM的代码实现2. 序列到序列模型3. 梯度与方向导数总结 摘要 在上周的学习基础之上&#xff0c;本周学习的内容有LSTM的代码实现&#xff0c;通过对代码的学习进一步加深了对LSTM的理解。为了切入到transformer的学习&#xff0c;本文通过对一些应用例子…

AGI时代存内计算芯片的语音识别之旅 —— 知存科技开发板体验与感悟

目录 一、简介 二、活动内容 2.1 多模态时代&#xff0c;存内计算框架的应用与发展 1、多模态时代计算需求 2、存内计算技术 3、知存科技存内产品 2.2 分布式环境下深度学习任务的高效可靠执行研究 基于强化学习的流水线分布式训练调度方案&#xff08;PG-MPSS&#xf…

前端工程规范-4:Git提交前代码规范检查(Husky + Lint-staged)

Git提交前代码规范检查 在前端项目开发中&#xff0c;规范git提交信息&#xff0c;也是经常使用的手段&#xff0c;如何确保团队成员都遵循ESint规则&#xff0c;且不会将不符合规范的代码推送到Git仓库&#xff1f; 答案是&#xff1a;使用带有git hooks功能的husky。git hoo…

【LLM大模型】Ollama 运行 GGUF 模型

Ollama 默认直接支持很多模型&#xff0c;只需要简单的使用 ollama run命令&#xff0c;示例如下&#xff1a; ollama run gemma:2b就可安装、启动、使用对应模型。 通过这样方式直接支持的模型我们可以通过https://ollama.com/library 找到。 在https://huggingface.co/mod…

大端、小端区分与判断

大小端的判断是根据系统如何存储二进制数据来判断的 大端顾名思义&#xff0c;以数据的高位做开端的操作系统、小端也是以数据的低位做开端的操作系统 用最简单的例子&#xff1a; 对于数据0x01来说&#xff0c;高位为0低位为1&#xff0c;转十进制&#xff1a; 0x01 0 * 1…

RK3588主板PCB设计学习(五)

DDR中的一组信号线如何进行走线&#xff1a; 高亮这一组的焊盘&#xff0c;按照DDR4的走线要求&#xff0c;TOP层贴元器件的话从最下面的层开始走线&#xff1a; 先行CPU这一端把线拉出来&#xff0c;怎么顺怎么拉&#xff1a;如果在第六层拉不出来的话&#xff0c;在表层进行…

STM32DMA学习日记

STM32 DMA学习日记 写于2024/9/28晚 文章目录 STM32 DMA学习日记1. DMA简介2. I/O方式2.1 程序查询方式2.2 程序中断方式2.3 DMA方式 3.DMA框图4. 相关寄存器4.1 DMA中断状态寄存器&#xff08;DMA_ISR&#xff09;4.2 DMA中断标志清除寄存器&#xff08;DMA_IFCR&#xff09;…

[CSP-J 2022] 解密

题目来源&#xff1a;洛谷题库 [CSP-J 2022] 解密 题目描述 给定一个正整数 k k k&#xff0c;有 k k k 次询问&#xff0c;每次给定三个正整数 n i , e i , d i n_i, e_i, d_i ni​,ei​,di​&#xff0c;求两个正整数 p i , q i p_i, q_i pi​,qi​&#xff0c;使 n …

verilog实现FIR滤波系数生成(阶数,FIR滤波器类型及窗函数可调)

在以往采用 FPGA 实现的 FIR 滤波功能&#xff0c;滤波器系数是通过 matlab 计算生成&#xff0c;然后作为固定参数导入到 verilog 程序中&#xff0c;这尽管简单&#xff0c;但灵活性不足。在某些需求下&#xff08;例如捕获任意给定台站信号&#xff09;需要随时修改滤波器的…

【教程】57帧! Mac电脑流畅运行黑神话悟空

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、先安装CrossOver。网上有许多和谐版&#xff0c;可自行搜索。&#xff08;pd虚拟机里运行黑神话估计够呛的&#xff09; 2、运行CrossOver&#xf…

12、echarts 没有显示折线图

一、问题描述 echarts 没有显示折线图&#xff0c;但是&#xff0c;有数据显示&#xff1a; 看图表展示&#xff0c;y轴数据全部没有显示&#xff0c;直接可以判定是数据结构出问题了。 检查 series.data[] 数据结构&#xff1a; dataList [{"dateStr":"202…

鸿蒙开发(NEXT/API 12)【硬件(传感器开发3)】传感器服务

场景介绍 当设备需要获取传感器数据时&#xff0c;可以使用sensor模块&#xff0c;例如&#xff1a;通过订阅方向传感器数据感知用户设备当前的朝向&#xff0c;通过订阅计步传感器数据统计用户的步数等。 函数说明 名称描述OH_Sensor_GetInfos(Sensor_Info **infos, uint32…

GAMES101(作业8)

作业8 题目&#xff1a; 模拟绳子动画&#xff0c;包括基于物理的&#xff0c;和非物理的&#xff0c;应该修改的函数是:rope.cpp 中的void Rope::simulateEuler(... Rope::rope(...)&#xff0c;&#xff0c;void Rope::simulateVerlet(...) 代码框架&#xff1a; main:负…

调试分析:[跳数度量]更改为[距离度量]后的 routing_bellmanford 算法

回顾复习2023年8月的《★修改Exata6.2源码&#xff1a;〔修改Bellmanford最短路径路由的衡量标准从【路由跳数】改为【“路由器节点间的物理距离”】&#xff0c;并动画演示〕》&#xff0c;VS2015调试Exata&#xff0c;跟踪调试修改后的[ routing_bellmanford.cpp ]源码&#…

Redis-常见数据类型(修改ing)

1. 预备知识 redis按照键值对的方式存储数据 1.1 基本全局命令 KEYS 返回所有满⾜样式&#xff08;pattern&#xff09;的key,⽀持如下统配样式: h?llo 匹配hello,hallo,hxlloh*llo 匹配hllo,heeeelloh[ae]llo 只匹配hallo helloh[^e]llo 匹配除hello,heee..llo以外的h[a…

Java-数据结构-Map和Set-(二)-哈希表 |ू・ω・` )

文本目录&#xff1a; ❄️一、哈希表&#xff1a; ☑ 1、概念&#xff1a; ☑ 2、冲突-概念&#xff1a; ☑ 3、冲突-避免&#xff1a; ☞ 1&#xff09;、避免冲突-哈希函数的设计&#xff1a; ☞ 2&#xff09;、避免冲突-负载因子调节(重点)&#xff1a; ☑ 4、冲突-解决&…

宠物空气净化器该怎么选?希喂、美的、有哈这三款有推荐的吗?

终于要到国庆了&#xff0c;这可是打工人除春节外最长的假期&#xff01;在外上班后&#xff0c;回家的次数越来越少了&#xff0c;这次国庆肯定要回去陪陪父母。这票是真难买啊&#xff0c;候补了我一个多星期才买到。本来以为最困难的问题已经解决了&#xff0c;又想到我家猫…

有通话质量更好的蓝牙耳机推荐吗?高品质的平价开放式耳机推荐

个人认为开放式耳机在通话方面还是表现不错的&#xff0c;主要有以下几个原因&#xff1a; 首先&#xff0c;在麦克风设计与配置方面&#xff1a; 拥有高品质麦克风硬件。优质的开放式耳机往往会配备高性能的麦克风&#xff0c;这些麦克风灵敏度较高&#xff0c;能够精准地捕捉…

情感短视频素材下载推荐

在制作热门的情感短视频时&#xff0c;优质的素材是不可或缺的。作为一名资深视频剪辑师&#xff0c;今天我将为你推荐几个可以下载高清无水印情感视频素材的网站&#xff0c;助你轻松找到创作灵感。 蛙学网 蛙学网是国内领先的视频素材平台&#xff0c;专注于情感和治愈类视频…