0. 思維導(dǎo)圖
1. 口令文件
在從零開始UNIX環(huán)境高級編程(1)中,已經(jīng)介紹過口令文件(/etc/passwd),本小節(jié)主要學(xué)習(xí)如果通過函數(shù)去獲取口令文件的信息。
1.1 passed結(jié)構(gòu)體
存放口令文件信息的結(jié)構(gòu)體為passed,定義在pwd.h中,不同平臺實現(xiàn)的字段也會不一樣
-
Mac OS X中passwd結(jié)構(gòu)體
struct passwd { char *pw_name; /* user name */ char *pw_passwd; /* encrypted password */ uid_t pw_uid; /* user uid */ gid_t pw_gid; /* user gid */ __darwin_time_t pw_change; /* password change time */ char *pw_class; /* user access class */ char *pw_gecos; /* Honeywell login info */ char *pw_dir; /* home directory */ char *pw_shell; /* default shell */ __darwin_time_t pw_expire; /* account expiration */ };
-
Ubuntu中passwd結(jié)構(gòu)體
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 */ };
1. 2 獲取passwd
1.2.1 獲取單個用戶信息
獲取passwd中單個用戶信息的函數(shù)有兩個:使用getpwnam獲取用戶信息時轧坎,需要傳入該用戶的名稱作為參數(shù);使用getpwuid獲取用戶信息時泽示,需要傳入該用戶的id作為參數(shù)缸血。
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
-
示例代碼
以getpwnam函數(shù)為例蜜氨,看下如何獲取單個用戶信息
#include "apue.h"
#include <pwd.h>
int main(int argc, char const *argv[])
{
struct passwd * info;
info = getpwnam("ckt");
if (info == NULL) {
printf("get passwd fail\n");
return 0;
}
printf("pw_uid = %d pw_gid = %d pw_gecos = %s pw_dir = %s pw_shell = %s \n"
, info->pw_uid, info->pw_gid, info->pw_gecos , info->pw_dir , info->pw_shell);
return 0;
}
-
運行結(jié)果
使用getpwnam函數(shù)獲取到的信息和/etc/passwd中存放的信息一致
ckt@ubuntu:~/work/unix/code/chapter6$ ./passwdinfo_test
pw_uid = 1000 pw_gid = 1000 pw_gecos = ckt,,, pw_dir = /home/ckt pw_shell = /bin/bash
ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/passwd | grep ckt
ckt:x:1000:1000:ckt,,,:/home/ckt:/bin/bash
1.2.2 遍歷整個口令文件
上面兩個函數(shù)只能獲取單個用戶信息,如果要得到整個口令文件捎泻,需要用到下面3個函數(shù)飒炎,而getpwnam和getpwuid中也會對這3個函數(shù)進行封裝。
struct passwd *getpwent(void);
void setpwent(void);
void endpwent(void);
- 示例代碼
自定義一個getpwuid函數(shù)名為my_getpwuid笆豁,里面會調(diào)用到setpwent郎汪、getpwent、endpwent這3個函數(shù)
#include "apue.h"
#include <pwd.h>
struct passwd *my_getpwuid(uid_t uid)
{
struct passwd * ptr;
setpwent();
while((ptr = getpwent()) != NULL)
if (ptr->pw_uid == uid)
break;
endpwent();
return ptr;
}
int main(int argc, char const *argv[])
{
struct passwd * info;
info = my_getpwuid(1000);
if (info == NULL) {
printf("get passwd fail\n");
return 0;
}
printf("pw_name = %s pw_gid = %d pw_gecos = %s pw_dir = %s pw_shell = %s \n"
, info->pw_name, info->pw_gid, info->pw_gecos , info->pw_dir , info->pw_shell);
return 0;
}
- 運行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc myget_passwdinfo.c -o myget_passwdinfo
ckt@ubuntu:~/work/unix/code/chapter6$ ./myget_passwdinfo
pw_name = ckt pw_gid = 1000 pw_gecos = ckt,,, pw_dir = /home/ckt pw_shell = /bin/bash
ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/passwd | grep 1000
ckt:x:1000:1000:ckt,,,:/home/ckt:/bin/bash
2. 陰影文件
系統(tǒng)將登陸密碼進行加密闯狱,并將加密后的口令存放在陰影文件/etc/shadow中煞赢,查看陰影文件需要root權(quán)限
ckt@ubuntu:~/work/unix/code/chapter6$ sudo cat /etc/shadow | grep ckt
ckt:$1$6BZmOQTp$RV1GboT5SxQR1hV3ZauQ..:16401:0:99999:7:::
2.1 spwd結(jié)構(gòu)體
和口令文件一樣,陰影文件也有對應(yīng)的結(jié)構(gòu)體spwd哄孤,spwd結(jié)構(gòu)體定義在 <shadow.h> 中
struct spwd {
char *sp_namp; /* Login name */
char *sp_pwdp; /* Encrypted password */
long sp_lstchg; /* Date of last change (measured
in days since 1970-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 expires
to warn user to change it */
long sp_inact; /* # of days after password expires
until account is disabled */
long sp_expire; /* Date when account expires (measured
in days since 1970-01-01 00:00:00 +0000 (UTC)) */
unsigned long sp_flag; /* Reserved */
};
2.2 獲取spwd結(jié)構(gòu)體函數(shù)
// 通過用戶名稱獲取單個用戶的spwd信息
struct spwd *getspnam(const char *name);
//以下三個函數(shù)用戶獲取整個/etc/shadow信息
struct spwd *getspent(void);
void setspent(void);
void endspent(void);
- 示例代碼
#include "apue.h"
#include <shadow.h>
int main(int argc, char const *argv[])
{
struct spwd * info;
info = getspnam("ckt");
if (info == NULL) {
printf("get passwd fail\n");
return 0;
}
printf("sp_pwdp = %s sp_lstchg = %ld sp_min = %ld sp_max = %ld sp_warn = %ld\
sp_inact = %ld sp_expire = %ld sp_flag = %ld \n", info->sp_pwdp, info->sp_lstchg,
info->sp_min , info->sp_max , info->sp_warn , info->sp_inact , info->sp_expire , info->sp_flag);
return 0;
}
- 運行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ sudo ./shadowinfo_test
sp_pwdp = $1$6BZmOQTp$RV1GboT5SxQR1hV3ZauQ.. sp_lstchg = 16401 sp_min = 0 sp_max = 99999 sp_warn = 7 sp_inact = -1 sp_expire = -1 sp_flag = -1
ckt@ubuntu:~/work/unix/code/chapter6$ sudo cat /etc/shadow | grep ckt
ckt:$1$6BZmOQTp$RV1GboT5SxQR1hV3ZauQ..:16401:0:99999:7:::
3. 組文件
組文件/etc/group用來存放系統(tǒng)中所有的group信息
/etc/group文件格式:group_name:password:GID:user_list
ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/group | grep ckt
ckt:x:1000:
3.1 group結(jié)構(gòu)體
group結(jié)構(gòu)體定義在<grp.h>
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* group members */
};
3.2 獲取group函數(shù)
//通過組名稱獲取當個group信息
struct group *getgrnam(const char *name);
//通過組ID獲取當個group信息
struct group *getgrgid(gid_t gid);
//使用以下3個函數(shù)照筑,獲取整個/etc/group信息
struct group *getgrent(void);
void setgrent(void);
void endgrent(void);
- 示例代碼
#include "apue.h"
#include <grp.h>
int main(int argc, char const *argv[])
{
struct group * info;
info = getgrnam("zhm");
if (info == NULL) {
printf("get group fail\n");
return 0;
}
printf("gr_name = %s gr_passwd = %s gr_gid = %d gr_mem = %s \n",
info->gr_name, info->gr_passwd, info->gr_gid, *info->gr_mem);
return 0;
}
- 運行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc groupinfo_test.c -o groupinfo_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./groupinfo_test
gr_name = zhm gr_passwd = x gqr_gid = 1001 gr_mem = ckt
ckt@ubuntu:~/work/unix/code/chapter6$ cat /etc/group | grep zhm
zhm:x:1001:ckt
4. 附屬組
一個用戶可以屬于多個組,通過id命令可以查看用戶的主組和附屬組
ckt@ubuntu:~/work/code/mt6737$ id ckt
uid=1000(ckt) gid=1000(ckt) groups=1000(ckt),1001(zhm)
4.1 getgroups函數(shù)
使用getgroups函數(shù)瘦陈,可以獲取進程所屬用戶的主組和附屬組ID
int getgroups(int size, gid_t list[]);
- 示例代碼
#include "apue.h"
#include <sys/types.h>
#include <unistd.h>
#define MAX_GROUP_SIZE 5
int main(int argc, char const *argv[])
{
int i = 0, group_size = 0;
int group_list[MAX_GROUP_SIZE];
if ((group_size = getgroups(MAX_GROUP_SIZE, group_list)) > 0)
{
for (i = 0; i < group_size; i++)
{
printf("group_list[%d] : %d \n", i , group_list[i]);
}
}
else
printf("get groups fail\n");
return 0;
}
- 運行結(jié)果
分別獲取用戶ckt和root的主組ID和附屬組ID凝危,和使用命令id得到的結(jié)果一致
ckt@ubuntu:~/work/unix/code/chapter6$ cc getgroups_test.c -o getgroups_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./getgroups_test
group_list[0] : 1000
group_list[1] : 1001
ckt@ubuntu:~/work/unix/code/chapter6$ sudo ./getgroups_test
group_list[0] : 0
ckt@ubuntu:~/work/unix/code/chapter6$ id ckt
uid=1000(ckt) gid=1000(ckt) groups=1000(ckt),1001(zhm)
ckt@ubuntu:~/work/unix/code/chapter6$ id root
uid=0(root) gid=0(root) groups=0(root)
5. 其他數(shù)據(jù)文件
除了上面介紹的一些文件,系統(tǒng)中還有很多其他類型的數(shù)據(jù)文件晨逝,它們的操作方式都是類似的蛾默。各個數(shù)據(jù)的文件名稱、結(jié)構(gòu)體和操作函數(shù)捉貌,如下表:
6. 登錄賬戶記錄
utmp文件(/var/run/utmp)記錄當前登錄到系統(tǒng)的各個用戶支鸡;wtmp文件(/var/log/wtmp)跟蹤各個登錄和注銷事件
我們可以使用who命令讀取utmp文件,使用last命令讀取wtmp文件
ckt@ubuntu:~/work/unix/code/chapter6$ who
ckt tty7 2017-03-02 17:00
ckt pts/0 2017-03-02 18:04 (:0)
ckt pts/1 2017-03-02 20:58 (:0)
ckt pts/2 2017-03-02 21:37 (:0)
ckt@ubuntu:~/work/unix/code/chapter6$ last
ckt pts/2 :0 Thu Mar 2 21:37 still logged in
ckt pts/1 :0 Thu Mar 2 20:58 still logged in
ckt pts/0 :0 Thu Mar 2 18:04 still logged in
reboot system boot 3.13.0-32-generi Thu Mar 2 16:58 - 00:26 (07:28)
ckt pts/2 :0 Thu Mar 2 03:19 - 03:45 (00:26)
ckt pts/1 :0 Thu Mar 2 00:46 - 03:19 (02:33)
ckt pts/1 :0 Thu Mar 2 00:40 - 00:40 (00:00)
ckt pts/0 :0 Thu Mar 2 00:17 - 03:45 (03:28)
reboot system boot 3.13.0-32-generi Thu Mar 2 00:16 - 03:45 (03:29)
ckt pts/1 :0 Thu Mar 2 00:08 - crash (00:07)
7. 系統(tǒng)標識
使用uname命令可以查看操作系統(tǒng)有關(guān)信息昏翰,同樣我們也可以通過函數(shù)去獲取這些信息苍匆。
ckt@ubuntu:~/work/unix/code/chapter6$ uname -a
Linux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:51:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
使用hostname命令可以獲取主機名
ckt@ubuntu:~/work/unix/code/chapter6$ hostname
ubuntu
7.1 uname結(jié)構(gòu)體
uname結(jié)構(gòu)體用來存放操作系統(tǒng)有關(guān)信息刘急,定義在#include <sys/utsname.h>
struct utsname {
char sysname[]; /* Operating system name (e.g., "Linux") */
char nodename[]; /* Name within "some implementation-defined
network" */
char release[]; /* OS release (e.g., "2.6.28") */
char version[]; /* OS version */
char machine[]; /* Hardware identifier */
#ifdef _GNU_SOURCE
char domainname[]; /* NIS or YP domain name */
#endif
};
7.2 uname函數(shù)
通過uname函數(shù)獲取信息并返回這些值到buf中
int uname(struct utsname *buf);
- 示例代碼
#include "apue.h"
#include <sys/utsname.h>
int main(int argc, char const *argv[])
{
struct utsname info;
if (uname(&info) == -1)
{
printf("get uname fail\n");
return 0;
}
printf("%s %s %s %s %s", info.sysname, info.nodename, info.release, info.version, info.machine);
#ifdef _GNU_SOURCE
printf(" %s", info.domainname);
#endif
printf("\n");
return 0;
}
- 運行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc getuname_test.c -o getuname_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./getuname_test
Linux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:51:20 UTC 2014 x86_64
ckt@ubuntu:~/work/unix/code/chapter6$ uname -a
Linux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:51:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
7.3 gethostname函數(shù)
int gethostname(char *name, size_t len);
- 示例代碼
#include "apue.h"
#include <limits.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
char hostname[HOST_NAME_MAX];
int i = 0;
if (gethostname(hostname, HOST_NAME_MAX) == -1)
printf("get hostname fail\n");
while(hostname[i] != '\0')
{
printf("%c", hostname[i]);
i++;
}
printf("\n");
return 0;
}
- 運行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc gethostname_test.c -o gethostname_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./gethostname_test
ubuntu
ckt@ubuntu:~/work/unix/code/chapter6$ hostname
ubuntu
8. 時間和日期歷程
8.1 獲取UTC時間
8.1.1 time函數(shù)
time函數(shù)定義在<time.h>中棚菊,用來獲取UTC時間
time_t time(time_t *t);
8.1.2 clock_gettime函數(shù)
當clk_id設(shè)置為CLOCK_REALTIME時,clock_gettime的功能和time類似叔汁,在系統(tǒng)支持高精度時間值的情況下统求, clock_gettime可能比time函數(shù)得到更高精度的時間值
int clock_gettime(clockid_t clk_id, struct timespec *tp);
- clk_id參數(shù)說明
標識符 | 說明 |
---|---|
CLOCK_REALTIME | 實時系統(tǒng)時間 |
CLOCK_MONOTONIC | 不帶負跳數(shù)的實時系統(tǒng)時間 |
CLOCK_PROCESS_CPUTIME_ID | 調(diào)用進程的CPU時間 |
CLOCK_THREAD_CPUTIME_ID | 調(diào)用線程的CPU時間 |
- timespec結(jié)構(gòu)體
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
8.1.3 gettimeofday函數(shù)
和time函數(shù)功能類似,gettimeofday提供了更高的精度(微秒級)
int gettimeofday(struct timeval *tv, struct timezone *tz);
- timeval結(jié)構(gòu)體
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
8.2 將日歷時間轉(zhuǎn)換成分解的時間
調(diào)用gmtime和localtime函數(shù)可以將time_t格式的UTC時間轉(zhuǎn)換成分解的時間据块,存放在tm結(jié)構(gòu)體中
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
struct tm {
int tm_sec; /* seconds */
int tm_min; /* minutes */
int tm_hour; /* hours */
int tm_mday; /* day of the month */
int tm_mon; /* month */
int tm_year; /* year */
int tm_wday; /* day of the week */
int tm_yday; /* day in the year */
int tm_isdst; /* daylight saving time */
};
8.3 格式化輸出時間
調(diào)用strftime將tm格式的時間值格式化輸出码邻,strftime需要通過TZ環(huán)境變量指定時區(qū)
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
- TZ環(huán)境變量
TZ環(huán)境變量用來設(shè)置時區(qū),設(shè)置方法如下:
ckt@ubuntu:~$ export TZ=Asia/Shanghai
ckt@ubuntu:~$ date
Tue Mar 7 09:15:24 CST 2017
ckt@ubuntu:~$ export TZ=Asia/Tokyo
ckt@ubuntu:~$ date
Tue Mar 7 10:15:46 JST 2017
8.4 示例代碼
#include "apue.h"
#include <time.h>
int main(int argc, char const *argv[])
{
time_t t;
struct tm *tmp = NULL;
char buf1[64];
if (time(&t) == -1)
{
printf("get time error\n");
return 0;
}
tmp = localtime(&t);
if (tmp == NULL)
{
printf("get localtime error\n");
return 0;
}
if (strftime(buf1, 64, "time and date : %r, %a %b %d, %Y", tmp) == 0)
printf("buffer len 64 is too small\n");
else
printf("%s\n", buf1);
return 0;
}
- 運行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter6$ cc getdate_test.c -o getdate_test
ckt@ubuntu:~/work/unix/code/chapter6$ ./getdate_test
time and date : 11:00:08 PM, Wed Mar 08, 2017
參考
- UNIX 環(huán)境高級編程 第3版