在清理服務器時想看一下有多少服務器最近是沒登錄過的,因為服務器登錄信息會記錄到/var/log/wtmp文件,我們可以自己寫腳本來讀取wtmp文件,通過utmp函數,可以實現我們的需求。
最近突然要清理閑置服務器,最簡單的指標當然是看下有多少服務器是最近沒有人登錄過的。當登錄服務器的時候,init, tty等會將登錄和登出信息記錄到/var/log/wtmp文件中,通過last命令可以查詢服務器的登錄情況。
但是直接到服務器上執行last命令有幾個問題,一是希望查詢一個時間段內的登錄情況,而last命令的-t參數只能設置最近時間點;另一個是服務器上的last比較老,不支持-F參數,顯示的事件沒有年字段,計算的時候可能會存在問題。
最方便的解決方法當然是自己來讀取wtmp文件了,linux提供了utmp.h頭文件,里面包含了utmp數據結構和操作函數,關于utmp這個結構,可以通過man utmp進行查詢,其中比較常用的幾個屬性有:ut_type(登錄/登出類型,具體上面手冊中有詳細的宏定義說明)、ut_tv(操作的事件)、ut_user(操作用戶)、ut_host(操作主機名/ip).
手冊中強調了,登錄和登出都會單獨記錄一條,唯一的區別是登出不記錄ut_user等字段,所以估計last命令中每次登錄的事件計算,應該是根據每次登錄和之后的一次登出的ut_line匹配上進行計算的.
再來說下utmp的操作函數,主要操作流程為:setutent -> getutent -> endutent,感覺和文件讀寫順序差不多,首先需要通過setutent重新定位到utmp的文件頭,循環調用getutent獲取每一次的utmp記錄,最后通過endutent結束讀取,從api可以看出,所有的記錄都要順序讀出,然后再進行過濾和運算.
需求是在給定事件段內打印出最后一次登錄的時間,所以不去判斷那次登錄是否有登出操作,大致的實現,代碼如下:
- time_t from = atol(argv[1]);
- time_t to = atol(argv[2]);
- struct utmp *line = NULL;
- struct utmp *result = NULL;
- time_t timestamp;
- utmpname("/var/log/wtmp");
- setutent();
- while( (line = getutent()) != NULL) {
- if (line->ut_type == USER_PROCESS ) {
- timestamp = line->ut_tv.tv_sec;
- if(timestamp >= from && timestamp <= to) {
- if(result != NULL) {
- free(result);
- }
- result = malloc(sizeof (*result));
- memcpy (result, line, sizeof (*result));
- } //Vevb.com
- }
- }
- if(result != NULL) {
- timestamp = result->ut_tv.tv_sec;
- printf("%s %s %s %s", result->ut_line, result->ut_user, result->ut_host, asctime(localtime(×tamp)));
- }
- endutent();
獲取的參數是兩個時間段,為了方便運算,直接要求輸入時間對應的unix時間戳,可以通過date命令獲取,遍歷所有記錄,只讀取USER_PROCESS類型的,然后比較這次登錄時間是否在給定時間段內,因為這個順序剛好是登錄時間的順序,所以最后一次登錄的時間,就是最后一條符合if判斷的時間,最后打印出來就OK了.
新聞熱點
疑難解答