linux系統編程:自己動手寫一個who命令

来源:https://www.cnblogs.com/ghostwu/archive/2018/01/08/8243534.html
-Advertisement-
Play Games

who命令的作用用於顯示當前有哪些用戶登錄到系統。 這個命令執行的原理是讀取了系統上utmp文件中記錄的所有登錄信息,直接顯示出來的 utmp文件在哪裡呢? man who的時候,在手冊下麵有這麼一段說明:意思就是不指定文件參數,那麼讀取的就是/var/run/utmp,到底是不是,驗證下 當我指定 ...


who命令的作用用於顯示當前有哪些用戶登錄到系統。

這個命令執行的原理是讀取了系統上utmp文件中記錄的所有登錄信息,直接顯示出來的

utmp文件在哪裡呢?

man who的時候,在手冊下麵有這麼一段說明:意思就是不指定文件參數,那麼讀取的就是/var/run/utmp,到底是不是,驗證下

If FILE is not specified, use /var/run/utmp.  /var/log/wtmp as FILE  is
       common.   If  ARG1  ARG2  given, -m presumed: 'am i' or 'mom likes' are
       usual.

當我指定file參數為/var/run/utmp或者省略這個參數的時候,結果都是一樣, 當我用一個錯誤的文件時,沒有任何結果,從這裡可以推斷,who命令確實從/var/run/utmp中讀取用戶登錄的信息

ghostwu@ubuntu:~$ who
ghostwu  tty7         2018-01-08 09:09 (:0)
ghostwu  pts/18       2018-01-08 12:59 (:0)
ghostwu  pts/19       2018-01-08 13:00 (:0)
ghostwu  pts/20       2018-01-08 13:03 (:0)
ghostwu@ubuntu:~$ who -b
         system boot  2018-01-08 09:08
ghostwu@ubuntu:~$ who -b /var/run/utmp 
         system boot  2018-01-08 09:08
ghostwu@ubuntu:~$ who -b /var/run/utmp2
ghostwu@ubuntu:~$ who -b /var/run/utmp3

那麼utmp到底在哪裡?

利用man -k utmp 查找所有的可能:    推斷--->  utmp (5) - login records  這裡的可能性比較大,描述說,這裡是記錄登錄信息的

ghostwu@ubuntu:~$ man -k utmp
endutent (3)         - access utmp file entries
endutxent (3)        - access utmp file entries
getutent (3)         - access utmp file entries
getutent_r (3)       - access utmp file entries
getutid (3)          - access utmp file entries
getutid_r (3)        - access utmp file entries
getutline (3)        - access utmp file entries
getutline_r (3)      - access utmp file entries
getutmp (3)          - copy utmp structure to utmpx, and vice versa
getutmpx (3)         - copy utmp structure to utmpx, and vice versa
getutxent (3)        - access utmp file entries
getutxid (3)         - access utmp file entries
getutxline (3)       - access utmp file entries
login (3)            - write utmp and wtmp entries
logout (3)           - write utmp and wtmp entries
pututline (3)        - access utmp file entries
pututxline (3)       - access utmp file entries
sessreg (1)          - manage utmpx/wtmpx entries for non-init clients
setutent (3)         - access utmp file entries
setutxent (3)        - access utmp file entries
systemd-update-utmp (8) - Write audit and utmp updates at bootup, runlevel ch...
systemd-update-utmp-runlevel.service (8) - Write audit and utmp updates at bo...
systemd-update-utmp.service (8) - Write audit and utmp updates at bootup, run...
utmp (5)             - login records
utmpdump (1)         - dump UTMP and WTMP files in raw format
utmpname (3)         - access utmp file entries
utmpx (5)            - login records
utmpxname (3)        - access utmp file entries

接下來,我們去 man 5 utmp 看下,會發現有這麼一段提示:

The file is a sequence of utmp structures, declared as follows in <utmp.h> (note that this
       is only one of several definitions around; details depend on the version of libc):

意思是utmp文件的信息是一系列utmp結構體數據, 這個結構體定義在utmp.h文件中,  每個linux發行版可能不一樣.

接下來,我用強大的find命令查找到了2個目標:

ghostwu@ubuntu:~$ find /usr/include -name "utmp.h"
/usr/include/x86_64-linux-gnu/bits/utmp.h
/usr/include/utmp.h

結構體的定義就在這個文件中( /usr/include/x86_64-linux-gnu/bits/utmp.h  )

這裡有兩個巨集要註意下( ut_time和UTMP_FILE ), 下麵的程式會用到

#ifndef _NO_UT_TIME
/* We have a problem here: `ut_time' is also used otherwise.  Define
   _NO_UT_TIME if the compiler complains.  */
# define ut_time    ut_tv.tv_sec
#endif
ghostwu@ubuntu:~$ grep "UTMP_FILE" /usr/include/utmp.h 
#define UTMP_FILE    _PATH_UTMP
#define UTMP_FILENAME    _PATH_UTMP
ghostwu@ubuntu:~$ grep "_PATH_UTMP" /usr/include/utmp.h
#define UTMP_FILE    _PATH_UTMP
#define UTMP_FILENAME    _PATH_UTMP
ghostwu@ubuntu:~$ grep "_PATH_UTMP" /usr/include/x86_64-linux-gnu/bits/utmp.h 
ghostwu@ubuntu:~$ grep "_PATH_UTMP" /usr/include/*.h
/usr/include/paths.h:#define    _PATH_UTMP    "/var/run/utmp"
/usr/include/utmp.h:#define UTMP_FILE    _PATH_UTMP
/usr/include/utmp.h:#define UTMP_FILENAME    _PATH_UTMP
/usr/include/utmpx.h:# define UTMPX_FILE    _PATH_UTMPX
/usr/include/utmpx.h:# define UTMPX_FILENAME    _PATH_UTMPX
ghostwu@ubuntu:~$

UTMP_FILE的查找思路: 首先grep兩個目錄下麵的文件utmp.h,在/usr/include/utmp.h找到一個巨集定義 _PATH_UTMP,下一步就是確定 _PATH_UTMP到底是什麼,利用grep "_PATH_UTMP" /usr/include/*.h

最終在paths.h頭文件中,發現了他的真面目

who命令書寫思路:

1)從/var/run/utmp讀取文件,每次讀取一個struct utmp結構體這麼大,如果長度每次都有這麼大,繼續讀取

2)格式化4個信息:用戶名,主機,地址,時間

3)只列印當前活動的用戶(當前登錄的用戶)

4)格式化時間( 小時,分鐘,秒, >10的補0, <10的原樣返回 )

源代碼

  1 /*================================================================
  2 *   Copyright (C) 2018 . All rights reserved.
  3 *   
  4 *   文件名稱:mywho.c
  5 *   創 建 者:ghostwu(吳華)
  6 *   創建日期:2018年01月08日
  7 *   描    述:
  8 *
  9 ================================================================*/
 10 
 11 #include <stdio.h>
 12 #include <utmp.h>
 13 #include <sys/types.h>
 14 #include <sys/stat.h>
 15 #include <fcntl.h>
 16 #include <stdlib.h>
 17 #include <unistd.h>
 18 #include <time.h>
 19 #include <string.h>
 20 
 21 #ifndef UTMP_FILE
 22 #define UTMP_FILE "/var/run/utmp"
 23 #endif
 24 
 25 int count = 0;
 26 
 27 //格式化時間, <10 就補0, >10 原樣返回
 28 char* format_time( char* s, const char *time ) {
 29     if( strlen( time ) < 2 ) {
 30         return strcat( s, time );
 31     }
 32     return strcpy( s, time );
 33 }
 34 
 35 void show_info( struct utmp* t_utmp ) {
 36     if ( t_utmp->ut_type != USER_PROCESS ) //不顯示 非活躍的用戶信息
 37       return;
 38 
 39     printf( "%-8.8s", t_utmp->ut_user ); 
 40     printf( " " );
 41     printf( "%-8.8s", t_utmp->ut_line );
 42     printf( " " );
 43 
 44     //printf( " " );
 45     //printf( "%12.12s", ctime( (time_t*)&(t_utmp->ut_time) ) + 4 ); //+4--->去除天(day)和後面的空格
 46     
 47     /*測試localtime用法
 48     //當前時間
 49     time_t now;
 50     struct tm* pNow;
 51     time( &now );
 52     pNow = localtime( &now );
 53     printf( "%d-%d-%d %d:%d", pNow->tm_year + 1900, pNow->tm_mon + 1, pNow->tm_mday, pNow->tm_hour, pNow->tm_min );
 54     */
 55 
 56     struct tm* ptm;
 57     time_t u_time = t_utmp->ut_time;
 58     ptm = localtime( &u_time );
 59     int ihour = ptm->tm_hour;
 60     int imin = ptm->tm_min;
 61 
 62     char hour[3] = "0";
 63     char hour2[3] = "0";
 64     sprintf( hour2, "%d", ihour );
 65     format_time( hour, hour2 );
 66 
 67     char min[3] = "0";
 68     char min2[3] = "0";
 69     sprintf( min2, "%d", imin );
 70     format_time( min, min2 );
 71 
 72     //printf( "%d-%d-%d %d:%d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ihour, imin );
 73     printf( "%d-%d-%d %s:%s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, hour, min );
 74 
 75     printf( " " );
 76     printf( "%-8.8s", t_utmp->ut_host );
 77 
 78     printf( "\n" );
 79 }
 80 
 81 int main(int argc, char *argv[])
 82 {
 83     struct utmp myutmp;
 84     int fd = -1;
 85     int reclen = sizeof( myutmp );
 86 
 87     fd = open( UTMP_FILE, O_RDONLY );
 88     
 89     if( -1 == fd ) {
 90         perror( "open utmp" );
 91         exit( -1 );
 92     }
 93 
 94     //printf( "fd = %d\n", fd );
 95     
 96     while( read( fd, &myutmp, reclen ) == reclen ) {
 97         count++;
 98         show_info( &myutmp );    
 99     }
100     printf( "文件讀取的次數:%d\n", count );
101     close( fd );
102 
103 
104     return 0;
105 }
View Code

 

總結:

 一個非常小的功能,囊括以下知識點:

1)文件讀取

2)man手冊與系統命令使用技巧

3)指針用法

4)字元串函數用法

5)時間函數用法

6)巨集與typedef的用法

   


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第一次用adb,一開始只是想試試看能不能解析出,沒有看網上的現有解析方式。 需要安卓機開啟usb 調試+電腦運行。打開跳一跳的界面 點擊程式 【開始】按鈕即可開始,別的按鈕都是調試用的 主要流程是用abd 截圖傳到電腦開始解析,用顏色計算出起點,然後通過起點計算出終點。然後用adb模擬按壓屏幕。 小 ...
  • 打開vs2017開發者命令提示符 切換至安裝下的指定目錄 執行下麵的命令就可以了 需要註意的是一定要用vs2017的開發人員命令提示符 別用cmd ...
  • 最近有幸閱讀了陳希章花了一年時間為國內開發者貢獻的《Office 365 開發入門指南》。 雖然早期接觸過SharePoint的開發,2007年之後就再也沒有接觸SharePoint的開發,這次閱讀這本書讓我重新認識了Office的系統開發技術,讓我意識到現在的Office 開發也是.NET Cor... ...
  • 1.下載mysql資料庫 鏈接https://dev.mysql.com/downloads/mysql/ 2.解壓到自己喜歡的文件夾下麵,我自己的建在D:\DataBase\mysql-5.7.20-winx64 3.配置環境變數: 4.然後在解壓目錄新建my-default.ini配置文件,配置 ...
  • cp命令的基本用法: cp 源文件 目標文件 如果目標文件不存在 就創建, 如果存在就覆蓋 實現一個cp命令其實就是讀寫文件的操作: 對於源文件: 把內容全部讀取到緩存中,用到的函數read 對於目標文件: 把緩存中的內容全部寫入到目標文件,用到的函數creat 1 /* 2 * Copyright ...
  • Python基礎 對於Python,一切事物都是對象,對象基於類創建 所以,以下這些值都時對象:"zhurui"、22、['北京','上海','深圳'],並且是根據不同的類生成的對象。 一、基本數據類型 數字(int) 如:21、45、56 每一個整數都具備如下功能: 字元串(str) 1、name ...
  • 在上一章,我們添加了nor,nand啟動後,uboot啟動出如下圖所示: 上面的Flash: *** failed *** 是屬於uboot第二階段函數board_init_r()里的代碼, 代碼如下所示(位於arch/arm/lib/board.c): 從上面代碼看出, board_init_r( ...
  • Note: ‘小白’我本人的挖坑總結,記錄一次悲催自作孽的安裝過程 > 安裝基本Follow:http://www.linuxidc.com/Linux/2016-06/132051.htm > 一些區別 及 “踩坑”記錄: a. 註意下麵提示文字,版本不同進入編輯的指令也不同,eg:2017.8版 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...