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


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);


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);

  我们不仅可以属于一个在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


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



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


int main(void)
    time_t t;
    struct tm * tmp;
    char buf1[16];
    char buf2[64];
    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");
        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");
        printf("%s\n", buf2);


$ ./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.

个人分类: UNIX C编程
想对作者说点什么? 我来说一句


2015年10月29日 1.04MB 下载


2008年09月30日 113KB 下载