Linux下who命令之C語言實現


Linux下who命令之C語言實現

Step1:前期准備

  • 首先要有一個清楚的認識:linux中一切皆文件

  • 實現who命令,who命令也是Linux中的一個文件,那我們怎么找到它呢?我們可以“找男人”(man),終端下執行命令:man who

  • 此處我裝了漢譯版的man手冊,查看到這樣一段提示如下:

  • 所以退出man手冊,執行命令:info who

  • 找到如下圖相關信息:

  • 也可以找到下面這樣的一段話,也就是說:who命令可以這樣實現,讀取utmp里邊的內容,然后顯示記錄,最后關閉utmp,who命令就是一個很簡單的體現吧,打開文件,保存文件id,根據文件id讀取用戶的登陸信息,顯示在終端。

  • 我們嘗試地打開一下/var/run/utmp這個文件得到如下畫面,發現是一串亂碼,但還是能看出相關who的信息的

  • 既然不清楚utmp是干嘛的,我們可以使用man -k來檢索一下相關信息,得到信息如下

  • 經過篩選,發現只有utmpx(5)是符合我們要求的,其中后面顯示的是login records,而who命令不就是用來輸出登錄信息的;man的第五個類型表示的是文件格式和規范,說明此處可能存儲了登錄記錄的數據結構

  • 使用指令:man 5 utmpx,可以得到如下有用信息:

  • 也可以使用輸出重定向指令man 5 utmpx > utmp.txt,得到文本格式的utmp數據結構體如下:

struct utmp {
    short   ut_type;              /* Type of record */
    pid_t   ut_pid;               /* PID of login process */
    char    ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
    char    ut_id[4];             /* Terminal name suffix,
                                     or inittab(5) ID */
    char    ut_user[UT_NAMESIZE]; /* Username */
    char    ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
                                     kernel version for run-level
                                     messages */
    struct  exit_status ut_exit;  /* Exit status of a process
                                     marked as DEAD_PROCESS; not
                                     used by Linux init (1 */
    /* The ut_session and ut_tv fields must be the same size when
       compiled 32- and 64-bit.  This allows data files and shared
       memory to be shared between 32- and 64-bit applications. */
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
    int32_t ut_session;           /* Session ID (getsid(2)),
                                     used for windowing */
    struct {
        int32_t tv_sec;           /* Seconds */
        int32_t tv_usec;          /* Microseconds */
    } ut_tv;                      /* Time entry was made */
#else
    long   ut_session;           /* Session ID */
    struct timeval ut_tv;        /* Time entry was made */
#endif

    int32_t ut_addr_v6[4];        /* Internet address of remote
                                     host; IPv4 address uses
                                     just ut_addr_v6[0] */
    char __unused[20];            /* Reserved for future use */
};
  • 此時萬里長征已走一半,對比系統who命令,發現該結構體中並沒有直接給出用戶登錄時間的成員變量,而是內嵌了一個ut_tv的時間結構體,其中成員變量tv_sec才是我們需要的。

  • 如果耐心地看完該幫助文檔,可以在下面一段代碼中有新的發現:

  • 此處定義了一個ut_time的宏,指向的就是ut_tv.ut_sec,而我們需要用到的不是這樣一個以秒為單位的格林威治標准時間,所以才后面需用到ctime()函數

  • 在這兒用的就是剛剛說的linux的一個思想,一切事物都是文件。

Step2:其余頭文件的准備

  • time.h:將格林威治標准時間(GMT)長整形的數時間轉化為我們所熟悉的時間表示,運用ctime()函數
  • string.h:調整輸出格式,后面調試時會提到用處。
  • 其余頭文件:因為要打開文件嘛,所以有些頭文件必不可少,這個在我本周博客中有詳細介紹:博客地址

Step3:編程思想

  • 查看UTMP_FILE宏:grep -nr UTMP_FILE /usr/include

  • 讀取其結構體,將需要的變量提取出來並按照一定的格式輸出

  • 其中,時間輸出本來是一串格林威治標准時間(GMT)長整形的數,可以用ctime()將把日期和時間轉換為字符串

Step4:代碼實現

  • who代碼
  • 其中等下需要修改的代碼貼在此處:
void showtime(long timeval)
{  
    char* cp;  
    cp = ctime(&timeval);  
    printf("%s",cp+4);  //+4是因為*cp所指的一串字符前4個字符表示為“星期”,可以忽略此信息
}  

Step5:編譯執行

  • 執行結果如下:

  • 發現“(:0)”被換行了,但是程序中並沒有輸出換行字符。經過一番思索后,猜想ctime()函數的返回值*cp可能自動在最后補了一個字符\n

Step6:調試代碼

  • 那只要能回退一個字符就好了,比如輸出一個\b(退格字符)
  • 編譯再運行,發現輸出格式仍是原樣,最后經過了解\b確實是可以回退一個字符,但是並不能實現退到上行,也就是不能消除\n帶來的影響
  • 百度之后,說可以通過輸出\r\b來實現“退行”,但實踐后發現也不可取
  • 最后考慮到直接修改*cp字符串中最后一個字符為\0,使輸出達到與系統who命令一樣的效果
  • 在輸出語句前添加如下代碼(需用到string.h):
    • cp[strlen(cp)-1] = '\0'
  • 最后編譯執行效果,可以看出與who命令基本一致:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM