UNIX再学习 -- 系统数据文件和信息
生活随笔
收集整理的這篇文章主要介紹了
UNIX再学习 -- 系统数据文件和信息
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
UNIX 系統的正常運作需要使用大量與系統有關的數據文件,例如,口令文件 /etc/passwd 和組文件 /ect/group 就是經常被多個程序頻繁使用的兩個文件。用戶每次登陸 UNIX 系統,以及每次執行 ls -l 命令時都要使用口令文件。
一、口令文件
1、口令文件簡介
UNIX 系統口令文件包含的字段,而這些字段包含在 /usr/include/pwd.h 中的定義的 passwd 結構中。 /* The passwd structure. */ struct passwd {char *pw_name; /* Username. */char *pw_passwd; /* Password. */__uid_t pw_uid; /* User ID. */__gid_t pw_gid; /* Group ID. */char *pw_gecos; /* Real name. */char *pw_dir; /* Home directory. */char *pw_shell; /* Shell program. */ }; 由于歷史原因,口令文件是 /etc/passwd,而且是一個 ASCII 文件。 這部分講過,這話說的我都自信滿滿了。參看:Hi3516A開發--/etc/passwd # cat /etc/passwd root:x:0:0:root:/root:/bin/bash tarena:x:1000:1000:tarena,,,:/home/tarena:/bin/bash nobody:x:65534:65534:nobody:/nonexistent:/bin/sh 各字段含義就不講了,關于這些登錄項,請注意下列各點: (1)通常有一個用戶名為 root 的登錄項,其用戶 ID 是 0(超級用戶)。 (2)加密口令字段包含了一個占位符。 (3)口令文件項中的某些字段可能是空。 (4)shell 字段包含了一個可執行程序名,它被用作該用戶的登錄 shell。 (5)阻止一個特定用戶登錄系統的方法: ? ? ? ?使用 /dev/null ? ? ? ?將 /bin/false 用作登錄 shell。它簡單地以不成功(非 0)狀態終止,該 shell 將此種狀態判斷位假。 ? ? ? ?用 /bin/true 禁止一個賬戶。它所做的一切是以成功(0)狀態終止。 (6)使用 nobody 用戶名的目的是,使任何人都可以登錄至系統,但其用戶 ID (65534)和組 ID(65534)不提供任何特權。該用戶 ID 和組 ID只能訪問人人皆可讀、寫的文件。 (7)提供 finger 命令的某些 UNIX 系統支持注釋字段中的附加信息。 # finger -p tarena Login: tarena Name: tarena Directory: /home/tarena Shell: /bin/bash Last login Tue Aug 2 11:40 2016 (CST) on pts/3 from ubuntu.local No mail.POSIX.1 定義了兩個獲取口令文件項的函數,在給出用戶登錄名或者數值用戶ID后,這兩個函數就能查詢相關項。2、函數 getpwuid 和 getpwnam
#include <pwd.h> struct passwd *getpwuid(uid_t uid); struct passwd *getpwnam(const char *name);(1)函數解析
這兩個函數都返回一個指向 passwd 結構的指針,該結構已由這兩個函數在執行時填入信息。passwd 結構通常是函數內部的靜態變量,只要調用任一相關函數,其內容就會被重寫。(2)示例說明
#include <stdio.h> #include <stdlib.h> #include <pwd.h>int main (void) {struct passwd *pwd;if ((pwd = getpwuid (1000)) == NULL)perror ("fail to getpwuid"), exit (1);printf("%s,%s,%ld,%ld,%s,%s,%s\n",pwd->pw_name, pwd->pw_passwd,pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); if ((pwd = getpwnam ("tarena")) == NULL)perror ("fail to getpwnam"), exit (1);printf("%s,%s,%ld,%ld,%s,%s,%s\n",pwd->pw_name, pwd->pw_passwd,pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); return 0; } 輸出結果: tarena,x,1000,1000,tarena,,,,/home/tarena,/bin/bash tarena,x,1000,1000,tarena,,,,/home/tarena,/bin/bash3、函數 getpwent、setpwent 和 endpwent
#include <pwd.h> struct passwd* getpwent(void); void setpwent(void); void endpwent(void);(1)函數解析
用于查看 登錄名和用戶 ID。 調用 getpwent 時,它返回口令文件中的下一個記錄項。它返回一個由它填寫好的 passwd 結構的指針。每次調用此函數時都重寫該結構。在第一次調用該函數時,它打開所使用的各個文件。在使用本函數時,對口令文件中各個記錄項的安排順序并無要求。某些系統采用散列算法對 /etc/passwd 文件中各項排序。 函數 setpwent 反繞它所使用的文件,endpwent 則關閉這些文件。在使用 getpwent 查看完口令文件后,一定要調用 endpwent 關閉這些文件。getpwent 知道什么時間應當打開它所使用的文件(第一次被調用時),但是它并不知道何時關閉這些文件。(2)示例說明
#include <pwd.h> #include <sys/types.h> #include <stdio.h>int main (void) {struct passwd *user;setpwent ();while ((user = getpwent ()) != NULL){if (strcmp ("tarena", user->pw_name) == 0)break;}printf ("%s:%d:%d:%s:%s:%s\n",user->pw_name,user->pw_uid,user->pw_gid,user->pw_gecos,user->pw_dir,user->pw_shell);endpwent ();return 0; } 輸出結果: tarena:1000:1000:tarena,,,:/home/tarena:/bin/bash(3)示例總結
在函數開始處調用 setpwent 是自我保護性的措施,以便確保如果調用者在此之前已經調用 getpwent 打開了有關文件情況下,反繞有關文件使他們定位到文件開始處。getpwnam 和 getpwuid 完成后不應使有關文件仍處于打開狀態。所以應調用 endpwent 關閉它們。 簡單來說, setpwent 可以定位到文件開始處。如果不使用 setpwent 的情況下舉個例子:#include<pwd.h> #include<sys/types.h> #include <stdio.h> int main (void) {struct passwd *user;int i;for(i = 0; i < 4; i++) {user = getpwent ();printf ("%s :%d :%d :%s:%s:%s\n", user->pw_name, user->pw_uid, user->pw_gid,user->pw_gecos, user->pw_dir, user->pw_shell);} // setpwent();user = getpwent ();printf ("=============================\n");printf ("%s :%d :%d :%s:%s:%s\n", user->pw_name, user->pw_uid, user->pw_gid,user->pw_gecos, user->pw_dir, user->pw_shell);endpwent();return 0; } 輸出結果: root :0 :0 :root:/root:/bin/bash daemon :1 :1 :daemon:/usr/sbin:/bin/sh bin :2 :2 :bin:/bin:/bin/sh sys :3 :3 :sys:/dev:/bin/sh ============================= sync :4 :65534 :sync:/bin:/bin/sync二、陰影口令
加密口令是經單向加密算法處理過的用戶口令副本。因為此算法是單向的,所以不能從加密口令猜測到原來的口令。 現在,某些系統將加密口令存放在令一個通常稱為陰影口令文件中。該文件至少要包含用戶名和加密口令。與該口令相關的其他信息也可存放在該文件中。 陰影口令包含的字段包含在 /usr/include/shadow.h 中的定義的 spwd 結構中。 /* Structure of the password file. */ struct spwd{char *sp_namp; /* Login name. */char *sp_pwdp; /* Encrypted password. */long int sp_lstchg; /* Date of last change. */long int sp_min; /* Minimum number of days between changes. */long int sp_max; /* Maximum number of days between changes. */long int sp_warn; /* Number of days to warn user to changethe password. */long int sp_inact; /* Number of days the account may beinactive. */long int sp_expire; /* Number of days since 1970-01-01 untilaccount expires. */unsigned long int sp_flag; /* Reserved. */};陰影口令文件是 /etc/shadow # cat /etc/shadow root:$6$MNQKabSO$UcLm09JPH7JdppbRZBrj4XoWUQIWRhqhwdzJ9F2mGlwSXo5V.ylP4.gaReQSw3sZSSuczM1iqnMKBrwnbDlgz/:17131:0:99999:7::: tarena:$6$kea9L4dJ$PeK8uyZ8MesNW6zG1fMMnJIol4icj0nLKAg7Vq78sLJJhEs1Sr6M/VpxBvy.kGeMVDr2SC/EN8Utx5OSIx8Fs/:16630:0:99999:7::: nobody:*:15453:0:99999:7:::1、陰影口令函數
#include <shadow.h> struct spwd *getspnam(const char *name); struct spwd *getspent(void); void setspent(void); void endspent(void); 返回值:成功返回指針,失敗返回 NULL(1)函數功能
getspnam,訪問 shadow 口令。 getspent,是獲得訪問影子密碼文件的接口。(2)示例說明
#include <stdio.h> #include <shadow.h> #include <stdlib.h>int main (void) { struct spwd* spwd; if ((spwd = getspnam("tarena")) == NULL) perror("getspnam"), exit (1); printf("%s,%s,%ld,%ld,%ld,%ld,%ld,%ld,%lu\n",spwd->sp_namp,spwd->sp_pwdp,spwd->sp_lstchg,spwd->sp_min,spwd->sp_max,spwd->sp_warn,spwd->sp_inact,spwd->sp_expire,spwd->sp_flag); setspent(); while ((spwd = getspent()) != NULL){if (strcmp ("tarena", spwd->sp_namp) == 0)break;}printf("%s,%s,%ld,%ld,%ld,%ld,%ld,%ld,%lu\n",spwd->sp_namp,spwd->sp_pwdp,spwd->sp_lstchg,spwd->sp_min,spwd->sp_max,spwd->sp_warn,spwd->sp_inact,spwd->sp_expire,spwd->sp_flag); endspent(); return 0; } 輸出結果: tarena,$6$kea9L4dJ$PeK8uyZ8MesNW6zG1fMMnJIol4icj0nLKAg7Vq78sLJJhEs1Sr6M/VpxBvy.kGeMVDr2SC/EN8Utx5OSIx8Fs/,16630,0,99999,7,-1,-1,4294967295 tarena,$6$kea9L4dJ$PeK8uyZ8MesNW6zG1fMMnJIol4icj0nLKAg7Vq78sLJJhEs1Sr6M/VpxBvy.kGeMVDr2SC/EN8Utx5OSIx8Fs/,16630,0,99999,7,-1,-1,42949672952、思考
這讓我想起,在Ubuntu 10.04版本上,設置 超級用戶 root 登錄密碼了。 $sudo passwd root Enter new UNIX password: <--- 新的Root用戶 Password: <--- 輸入你當前用戶davinci的密碼 密碼 Retype new UNIX password: <--- 重復新的Root用戶密碼 passwd:已成功更新密碼(1)可以寫個程序驗證密碼是不是 root:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(void) { char *passwd; char key[] = "root"; passwd = crypt(key, "$6$MNQKabSO$"); printf("root: %s\n", passwd); return 0; } 編譯:gcc test -lcrypt 輸出結果: root: $6$MNQKabSO$UcLm09JPH7JdppbRZBrj4XoWUQIWRhqhwdzJ9F2mGlwSXo5V.ylP4.gaReQSw3sZSSuczM1iqnMKBrwnbDlgz/(2)示例總結
該示例證明,我的超級用戶 root的密碼確實是 root。注意到示例中出現了一個新的函數 crypt。 參看:百度百科 -- crypt 函數 #define _XOPEN_SOURCE /* See feature_test_macros(7) */ #include <unistd.h> char *crypt(const char *key, const char *salt);#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <crypt.h> char *crypt_r(const char *key, const char *salt, struct crypt_data *data);Link with -lcrypt. 返回值:返回一個以 NULL 結尾的密碼字符串注意,編譯時需要鏈接 -lcrypt,否則會出現?undefined reference to `crypt' 錯誤。1)參數解析
第一個參數:要加密的明文。必需。規定要編碼的字符串。
第二個參數:密鑰
可選。用于增加被編碼字符數目的字符串,以使編碼更加安全。如果未提供 salt 參數,則每次調用該函數時會隨機生成一個。 (1)salt 這個字符串如果以$1$開頭,以$結尾,那么這表示讓crypt用MD5的方式加密,加密后出來的密文格式就 是 $1$...$<密文正文> ,夾在$1$和$之間的字符串就是我們指定的密鑰文字。這個密鑰文字最多不能超過8個字符。 (2)如果salt字符串不是(1)方式的格式,那默認就用DES加密方法。DES加密時,salt只能取兩個字符,也就是 說,salt最多不能 超過2個字符,多出的字符會被丟棄,用DES加密出來的密文前兩個字符就是密鑰。后面緊跟著的 就是真正的密文。
(3)參數 salt 為兩個字符組成的字符串,由 a-z,A-Z,0-9,".",和 "/" 所組成。
2)函數功能
crypt() 函數返回加密的字符串。它使用的是一種單向算法。解密函數是沒有的。擴展:getpass 函數通常會與 crypt 加密函數一同使用 #include <unistd.h> char *getpass( const char *prompt); 函數功能: getpass() 函數用于從控制臺輸入一行字符串,關閉了回顯(輸入時不顯示輸入的字符串),適用于用密碼的輸入。 函數說明: getpass() 會顯示參數 prompt 所指的字符串,然后從 /dev/tty 中讀取所輸入的密碼,若無法從 /dev/tty 中讀取則會轉從標準輸入設備中讀取密碼。所輸入的密碼長度限制在 128 個字符,包含結束字符 NULL, 超過長度的字符及換行字符 /n 將會被忽略。在輸入密碼時 getpass() 會關閉字符回應,并忽略一些信號如 CTRL-C 或 CTRL-Z 所產生的信號
返回值: 返回一個指向以NULL結尾的密碼字符串
附加說明: 為了系統安全考慮,在般在使用getpass()輸入密碼后,該密碼最好盡快處理完畢,然后將該密碼字符串清除
示例說明: //示例一 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #include <crypt.h> int main() { char passwd[13]; char *key; char slat[2]; key = getpass("Input first passward:"); slat[0] = key[0]; slat[1] = key[1]; strcpy(passwd,crypt(key,slat)); key = getpass("Input second passward:"); slat[0] = passwd[0]; slat[1] = passwd[1]; printf("After crypt(),1st passwd:%s\n",passwd); printf("After crypt(),2st passwd:%s\n",passwd); return 0; } 編譯: # gcc test.c -lcrypt輸出結果: Input first passward://root Input second passward://root After crypt(),1st passwd:roK20XGbWEsSM After crypt(),2st passwd:roK20XGbWEsSM
三、組文件
UNIX 組文件包含的字段包含在?/usr/include/grp.h 中所定義的 group 結構中。/* The group structure. */ struct group{char *gr_name; /* Group name. */char *gr_passwd; /* Password. */__gid_t gr_gid; /* Group ID. */char **gr_mem; /* Member list. */};其中,字段 gr_mem 是一個指針數組,其中每個指針指向一個屬于該組的用戶名。該數組以 null 指針結尾。組文件是 /etc/group# cat /etc/group root:x:0: tarena:x:1000:對組文件的訪問函數如下,形式基本與口令文件和陰影文件相同。#include <grp.h> struct group *getgrgid(gid_t gid); struct group *getgrnam(const char *name); //兩個函數返回值:如果成功返回指針,出錯則返回NULL。 struct group *getgrent(void); //如果成功返回指針,出錯或者到達文件結尾則返回NULL void setgrent(void); void endgrent(void);四、附屬組 ID
我們不僅可以屬于口令文件記錄項中組 ID 所對應的組,也可屬于多至 16 個另外的組。文件訪問權限檢查相應被修改為:不僅將進程的有效組 ID 相計較,而且也將所有附屬組 ID 與文件的組 ID 進行比較。為了獲取和設置附屬組 ID,提供了下列 3 個函數。#include <sys/types.h> #include <unistd.h> int getgroups(int size, gid_t list[]);#include <grp.h> int setgroups(size_t size, const gid_t *list); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): setgroups(): _BSD_SOURCE#include <sys/types.h> #include <grp.h> int initgroups(const char *user, gid_t group); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): initgroups(): _BSD_SOURCE1、函數解析
(1)getgroups 將進程所屬用戶的各附屬組 ID 填寫到數組 grouplist 中,填寫入數組的附屬組 ID 數最多為 gidsetsize 個。實際填寫到數組中的附屬組 ID 數由函數返回。返回值:成功返回附屬組 ID 數量,若出錯,返回 -1.(2)setgroups 由root調用,為進程設置附加組ID表,grouplist是組ID數組,ngroups指定了數組中的元素個數。返回值:如果成功返回0,出錯則返回-1.(3)initgroups 用來從組文件(/etc/group)讀取整個組文件,然后對username確定其組的成員關系,然后調用setgroups。為該用戶初始化附加組ID表。除了在組文件中找到username是成員的所有組,initgroups也在附加組ID表中包括了?basegid,basegid是username在口令文件中的組ID。返回值:如果成功返回0,出錯則返回-1.五、其他數據文件
一般情況下,對于每個數據文件至少有 3 個函數:(1)get 函數:讀下一個記錄,如果需要,還會打開該文件。此種函數通常返回指向一個結構的指針。當已達到文件尾端時返回空指針。大多數 get 函數返回指向一個靜態存儲類結構的指針,如果要保存其內容,則需復制它。(2)set 函數:打開相應數據文件(如果尚未打開),然后反繞該文件。如果希望在相應文件起始處開始處理,則調用此函數。(3)end 函數:關閉相應數據文件。如前所述,在結束了對應數據文件的讀、寫操作后,總應調用此函數以關閉相關文件。 下面列出了 UNIX 常用的文件對應的函數:六、系統標識
1、uname 函數,返回與主機和操作系統有關的信息。
#include <sys/utsname.h> int uname(struct utsname * name); 返回值:成功返回非負值,失敗返回 -1(1)參數解析
通過該函數的參數向其傳遞一個 utsname 結構的地址,然后該函數填寫此結構。 其結構定義在?/usr/include/i386-linux-gnu/sys/utsname.h? /* Structure describing the system and machine. */ struct utsname{/* Name of the implementation of the operating system. */char sysname[_UTSNAME_SYSNAME_LENGTH];/* Name of this node on the network. */char nodename[_UTSNAME_NODENAME_LENGTH];/* Current release level of this implementation. */char release[_UTSNAME_RELEASE_LENGTH];/* Current version level of this release. */char version[_UTSNAME_VERSION_LENGTH];/* Name of the hardware type the system is running on. */char machine[_UTSNAME_MACHINE_LENGTH];#if _UTSNAME_DOMAIN_LENGTH - 0/* Name of the domain of this node on the network. */ # ifdef __USE_GNUchar domainname[_UTSNAME_DOMAIN_LENGTH]; # elsechar __domainname[_UTSNAME_DOMAIN_LENGTH]; # endif #endif};(2)示例說明
每個字符串都以 null 字節結尾。#include <stdio.h> #include <sys/utsname.h> #include <stdlib.h>int main (void) {struct utsname uts;if (uname (&uts) == -1)perror ("fail to uname"), exit (1);elseprintf ("%s %s %s %s %s\n", uts.sysname, uts.nodename, uts.release, uts.version, uts.machine);return 0; } 輸出結果: Linux ubuntu 3.2.0-23-generic-pae #36-Ubuntu SMP Tue Apr 10 22:19:09 UTC 2012 i686(3)Linux 下的 uname 命令
uname 命令用于打印當前系統相關信息(內核版本號、硬件架構、主機名稱和操作系統類型等)。選項:
-a或--all:顯示全部的信息; -m或--machine:顯示電腦類型; -n或-nodename:顯示在網絡上的主機名稱; -r或--release:顯示操作系統的發行編號; -s或--sysname:顯示操作系統名稱; -v:顯示操作系統的版本; -p或--processor:輸出處理器類型或"unknown"; -i或--hardware-platform:輸出硬件平臺或"unknown"; -o或--operating-system:輸出操作系統名稱; --help:顯示幫助; --version:顯示版本信息。示例:
# uname -a Linux ubuntu 3.2.0-23-generic-pae #36-Ubuntu SMP Tue Apr 10 22:19:09 UTC 2012 i686 i686 i386 GNU/Linux2、gethostname 函數
#include <unistd.h> int gethostname(char * name, int namelen); 返回:若成功則為0,若出錯則為-1(1)函數解析
該函數只返回主機名,該名字通常是 TCP/IP 網絡上主機的名字。namelen 參數指定 name 緩沖區長度,如若提供足夠的空間,則通過 name 返回的字符串以 null 字節結尾。如若沒有提供足夠的空間,則沒有說明通過 name 返回的字符串是否以 null 結尾。現在,gethostname 函數已在 POSIX.1 中定義,它指定的最大主機名長度是 HOST_NAME_MAX 不小于255參看:sysconf(3) - Linux man pageHOST_NAME_MAX - _SC_HOST_NAME_MAX Max length of a hostname, not including the terminating null byte, as returned by gethostname(2). Must not be less than _POSIX_HOST_NAME_MAX (255).而 uname 函數,查看結果為:#define _UTSNAME_LENGTH 65 ?即最大名字長度為 65# grep "_UTSNAME_LENGTH" * -rn i386-linux-gnu/sys/utsname.h:33:# define _UTSNAME_SYSNAME_LENGTH _UTSNAME_LENGTH i386-linux-gnu/sys/utsname.h:36:# define _UTSNAME_NODENAME_LENGTH _UTSNAME_LENGTH i386-linux-gnu/sys/utsname.h:39:# define _UTSNAME_RELEASE_LENGTH _UTSNAME_LENGTH i386-linux-gnu/sys/utsname.h:42:# define _UTSNAME_VERSION_LENGTH _UTSNAME_LENGTH i386-linux-gnu/sys/utsname.h:45:# define _UTSNAME_MACHINE_LENGTH _UTSNAME_LENGTH i386-linux-gnu/sys/utsname.h:77:# define SYS_NMLN _UTSNAME_LENGTH i386-linux-gnu/bits/utsname.h:24:#define _UTSNAME_LENGTH 65 i386-linux-gnu/bits/utsname.h:29:#define _UTSNAME_DOMAIN_LENGTH _UTSNAME_LENGTH(2)示例說明
#include <stdio.h> #include <unistd.h>int main (void) {char name[65];gethostname(name, sizeof(name));printf("hostname = %s\n", name);return 0; } 輸出結果: hostname = ubuntu(3)Linux 下的 hostname 命令
hostname 命令用于顯示和設置系統的主機名稱。環境變量 HOSTNAME 也保存了當前的主機名。在使用 hostname 命令設置主機名后,系統并不會永久保存新的主機名,重新啟動機器之后還是原來的主機名。如果需要永久修改主機名,需要同時修改/etc/hosts和/etc/sysconfig/network的相關內容。選項:
-v:詳細信息模式; -a:顯示主機別名; -d:顯示DNS域名; -f:顯示FQDN名稱; -i:顯示主機的ip地址; -s:顯示短主機名稱,在第一個點處截斷; -y:顯示NIS域名。示例:
# hostname ubuntu七、時間和日期例程
參看:C語言再學習 -- 時間函數八、未講部分
組文件 (簡單介紹)附屬組 ID (簡單介紹)登錄賬戶記錄時間和日期例程總結
以上是生活随笔為你收集整理的UNIX再学习 -- 系统数据文件和信息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分布式Zookeeper安装搭建详解
- 下一篇: java信息管理系统总结_java实现科