LoveStackover的博客

编程小子

系统相关信息和文件基础(一)

1. 基本概念

1.1 Password File And Shadow Passwords

Description struct passwd member
user name char *pw_name
encrypted password char *pw_passwd
numerical user ID uid_t pw_uid
numerical group ID gid_t pw_gid
comment field char *pw_gecos
initial working directory char *pw_dir
initial shell (user program) char *pw_shell

  在/etc/passwd中的条目如下所示:

root:x:0:0:root:/root:/bin/bash
squid:x:23:23::/var/spool/squid:/dev/null
nobody:x:65534:65534:Nobody:/home:/bin/sh
sar:x:205:105:Stephen Rago:/home/sar:/bin/bash

  对上述条目的解读遵循以下准则:

  • There is usually an entry with the user name root. This entry has a user ID of 0 (the superuser).
  • The encrypted password field contains a single character as a placeholder.
  • Some fields in a password file entry can be empty. If the encrypted password field is empty, it usually means that the user does not have a password. The entry for squid has one blank field: the comment field. An empty comment field has no effect.
  • The shell field contains the name of the executable program to be used as the login shell for the user. Note, however, that the entry for squid has /dev/null as the login shell. Obviously, this is a device and cannot be executed, so its use here is to prevent anyone from logging in to our system as user squid.
  • The nobody user name can be used to allow people to log in to a system, but with a user ID (65534) and group ID (65534) that provide no privileges. The only files that this user ID and group ID can access are those that are readable or writable by the world.
  • Some systems that provide the finger(1) command support additional
    information in the comment field. Each of these fields is separated by a comma:
    the user’s name, office location, office phone number, and home phone number.

  上述论述中comment field,分隔,可以附加用户名,办公室地址,办公室电话,家庭电话相关信息。并且这些信息够被finger指令直接使用。

#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
Both return: pointer if OK, NULL on error

struct passwd *getpwent(void);
Returns: pointer if OK, NULL on error or end of file
void setpwent(void);
void endpwent(void);

  getpwuid,getpwnamgetpwent都返回一个指向passwd结构体的指针,该结构体为静态变量,所以每次调用以上函数,其内容都会改变。getpwuid,getpwnam可以根据uid或者namepasswd文件中查找一个用户的相关信息,而getpwent,setpwent,endpwent三个函数可以用于遍历完整的passwd文件。
  getpwent每次返回passwd文件中的下一条记录,如果第一次调用该函数,则该函数自动打开它需要的文件,setpwent可以rewind其所打开的文件,也就是从头开始。endpwent关闭getpwent所打开的文件。特别的返回条目的顺序是不确定的:

There is no order implied when we use this function; the entries can be in any order, because some systems use a hashed version of the file /etc/passwd.

  为了防止获取原始密码(即使是加密后的),系统将加密后的密码存储在另外的文件,这个文件就是shadow password file

Description struct spwd member
user loginname char *sp_namp
encrypted password char *sp_pwdp
days since Epoch of last password change int sp_lstchg
days until change allowed int sp_min
days before change required int sp_max
days warning for expiration int sp_warn
days before account inactive int sp_inact
days since Epoch when account expires int sp_expire
reserved unsigned int sp_fla
  • The only two mandatory fields are the user’s login name and encrypted password. The other fields control how often the password is to change and how long an account is allowed to remain active.

  • The shadow password file should not be readable by the world.Only a few programs need to access encrypted passwords —login(1) and passwd(1), for example — and these programs are often set-user-ID root. With shadow passwords, the regular password file, /etc/passwd, can be left readable by the world.

  除了上面两点值得注意的,linux上访问该shadow password文件的API如下所示,其用法和上述无异:

#include <shadow.h>
struct spwd *getspnam(const char *name);
struct spwd *getspent(void);
Both return: pointer if OK, NULL on error
void setspent(void);
void endspent(void);

2. Group File And Supplementary Group IDs

Description struct group member
group name char *gr_name
encrypted password char *gr_passwd
numerical group ID int gr_gid
array of pointers to individual user names char **gr_mem
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
Both return: pointer if OK, NULL on error
struct group *getgrent(void);
Returns: pointer if OK, NULL on error or end of file
void setgrent(void);
void endgrent(void);

  group结构体中gr_mem指向属于一个数组,该数组的成员是指向用户名的指针,该数组以null指针结尾。getgrgidgetgrnam可以根据其gid或者name/etc/group获得一个条目,其他的相关操作都和前面passwd相仿。
  我们不仅可以属于一个在passwd文件中指定的组,也可以属于至多16个别的组。文件访问权限检查的时候也会参考supplementary group IDs

#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist[]);
Returns: number of supplementary group IDs if OK, −1 on error
#include <grp.h> /* on Linux */
int setgroups(int ngroups, const gid_t grouplist[]);
#include <grp.h> /* on Linux and Solaris */
int initgroups(const char *username, gid_t basegid);
Both return: 0 if OK, −1 on error

  getgroups函数使用supplementary group IDs填充gourplist所指向的数组,最多达到gidsetsize个,该函数返回实际存储的成员个数。如果该gidsetsize为0,则该函数只返回supplementary group IDs的个数。
  setgroups可以为当前进程设置其supplementary group IDs,此时调用该函数的进程必须具有管理员权限。initgroups其会读取整个group文件,寻找username所属的组,并调用setgroups函数,设置相应usernamesupplementary group IDs,需要注意以下引用:

initgroups includes basegid in the supplementary group ID list; basegid is the group ID from the password file for username.

1.3 Other Data Files And Login Accounting

  一般这些文件会具有以下函数来帮助操作,以下表格列举了一些常用系统数据文件和对应的函数:

  • A get function that reads the next record, opening the file if necessary.
  • A set function that opens the file, if not already open, and rewinds the file.
  • An end entry that closes the data file.
Description Data file Header Structure Additional keyed lookup
passwords /etc/passwd pwd.h passwd getpwnam, getpwuid
groups /etc/group grp.h group getgrnam, getgrgid
shadow /etc/shadow shadow.h spwd getspnam
hosts /etc/hosts netdb.h hostent getnameinfo, getaddrinfo
networks /etc/networks netdb.h netent getnetbyname, getnetbyaddr
protocols /etc/protocols netdb.h protoent getprotobyname, getprotobynumber
services /etc/services netdb.h servent getservbyname, getservbyport

  utmp记录当前登录的用户,wtmp记录所有的用户登录,注销信息:

struct utmp {
char ut_line[8]; /* tty line: "ttyh0", "ttyd0", "ttyp0", ... */
char ut_name[8]; /* login name */
long ut_time; /* seconds since Epoch * /
};

  记录该信息的细节如下:

On login, one of these structures was filled in and written to the utmp file by the login program, and the same structure was appended to the wtmp file. On logout, the entry in the utmp file was erased—filled with null bytes—by the init process, and a new entry was appended to the wtmp file. This logout entry in the wtmp file had the ut_name field zeroed out. Special entries were appended to the wtmp file to indicate when the system was rebooted and right before and after the system’s time and date was changed. Linux 3.2.0, the utmp(5) manual page gives the format of their versions ofthese login records. The pathnames of these two files are /var/run/utmp and/var/log/wtmp.

1.4 System Identification

#include <sys/utsname.h>
int uname(struct utsname *name);
Returns: non-negative value if OK, −1 on error
int gethostname(char *name, int namelen);
Returns: 0 if OK, −1 on error

struct utsname {
char sysname[]; /* name of the operating system */
char nodename[]; /* name of this node */
char release[]; /* current release of operating system */
char version[]; /* current version of this release */
char machine[]; /* name of hardware type * /
};

  utsname成员都是null字符结尾,uname命令使用的就是该结构体。gethostname函数在socket编程中使用的比较频繁,通过这个函数得到的主机名是fully qualified domain name,一般该主机位于TCP/IP网络上,但是注意uname在POSIX中规定如下:

POSIX.1 warns that the nodename element may not be adequate to reference the host on a communications network.

1.5 Time and Date Routines

#include <time.h>
time_t time(time_t *calptr);
Returns: value of time if OK, −1 on error
int clock_gettime(clockid_t clock_id, struct timespec *tsp);
Returns: 0 if OK, −1 on error
int clock_getres(clockid_t clock_id, struct timespec *tsp);
Returns: 0 if OK, −1 on error
int clock_settime(clockid_t clock_id, const struct timespec *tsp);
Returns: 0 if OK, −1 on error
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
Returns: 0 always

struct tm { /* a broken-down time */
int tm_sec; /* seconds after the minute: [0 - 60] */
int tm_min; /* minutes after the hour: [0 - 59] */
int tm_hour; /* hours after midnight: [0 - 23] */
int tm_mday; /* day of the month: [1 - 31] */
int tm_mon; /* months since January: [0 - 11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday: [0 - 6] */
int tm_yday; /* days since January 1: [0 - 365] */
int tm_isdst; /* daylight saving time flag: <0, 0, >0 */
};
struct tm *gmtime(const time_t *calptr);
struct tm *localtime(const time_t *calptr);
Both return: pointer to broken-down time, NULL on error
time_t mktime(struct tm *tmptr);
Returns: calendar time if OK, −1 on error

  以下引用出自MAN PAGE:

time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). If t is non-NULL, the return value is also stored in the memory pointed to by t.

Identifier Option Description
CLOCK_REALTIME real system time
CLOCK_MONOTONIC _POSIX_MONOTONIC_CLOCK real system time with no negative jumps
CLOCK_PROCESS_CPUTIME_ID _POSIX_CPUTIME CPU time for calling process
CLOCK_THREAD_CPUTIME_ID _POSIX_THREAD_CPUTIME CPU time for calling thread

  clock_gettime可以获得指定时钟的当前时间,并存储在tsp所指的变量中。当clock_idCLOCK_REALTIME,该函数作用与time相同。clock_getres将得到clock_id指定的时钟的精准度,并将该值存在tsp所指的结构体中。gettimeofday作用与time相似,但是其提供更高的精度,精确到ms

  localtimegmtime都是将calptr所指的时间转换为tm,但是前者将其转化为本地时间,后者还是UTC。mktimelocaltime作用恰好相反。

size_t strftime(char *restrict buf, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr);
size_t strftime_l(char *restrict buf, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr, locale_t locale);
Both return: number of characters stored in array if room, 0 otherwise
char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tmptr);
Returns: pointer to one character past last character parsed, NULL otherwise

  strftime函数将tmptr所指的tm所指的时间格式化输出到buf所指的数组中,如果maxsize的数组能够容下转换的结果,则返回实际存储的字节数,不含空字符。如果该数组的大小容乃不下,就返回0。format格式化输出参数,具体细节使用的时候再细究。strptimestrftime相似,两者格式转换符有区别,看看APUE中的一个例子:

int main(void)
{
    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);
}

  结果如下:

$ ./a.out
buffer length 16 is too small
time and date: 11:12:35 PM, Thu Jan 19, 2012

  localtime, mktime, strftime三个函数受TZ环境变量影响,如果该变量被定义,则这三个函数将参照该环境变量进行输出,如果该变量为NULL,则默认使用UTC。以上函数的关系如下图所示:
这里写图片描述

2. 实验

Write a program to obtain the current time and print it using strftime, so that it looks like the default output from date(1). Set the TZ environment variable to different values and see what happens.

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LoveStackover/article/details/79234202
个人分类: UNIX C编程
想对作者说点什么? 我来说一句

一张图介绍所有IPv6基础

2015年10月29日 1.04MB 下载

\查找角色的相关信息分析一

2008年09月30日 113KB 下载

没有更多推荐了,返回首页

不良信息举报

系统相关信息和文件基础(一)

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭