who

次のプログラムはwhoコマンド。whoはログインセッションを表示する。
whoはutmpファイル(OS Xの場合はutmpx)を呼んでいる。 utmpファイルはutmpエントリの連続になっている。utmp.hにutmpエントリは定義されている。

ファイルから構造体を読み出すにはどうすればよいか?
ファイルから文字や行を読み出すときはgetc、fgetsを使う。生の構造体を読み出すにはopen,read,closeシステムコールを使う。

open
int fd = open(char *name, int how);

openシステムコールはプロセスとファイルの間の接続を作る。この接続はファイルディスクリプタと呼ばれ、プロセスからカーネルにつながる。
ファイルをオープンするにはファイル名と必要な接続のタイプを指定する(読み出し用、書き込み用、読み書き両用)。ファイルのオープンはカーネルのサービスであり、openシステムコールはプログラムからカーネルに対する要求である。カーネルはエラーを検出すると-1という値を返してくる。

read
ssize_t numread = read(int fd, void *buf, size_t qty);

readシステムコールはファイルディスクリプタからプロセスにデータを読み出してくることができる。ファイルディスクリプタfdから呼び出し元プロセスのメモリ空間内の配列bufにqtyバイトのデータを転送するようにカーネルに要求する。要求が失敗した場合は−1を返し、成功した場合は転送したバイト数を返す。

close
int result = close(int fd);

データの読み書きが終了したら、ファイルディスクリプタをクローズする。

ではwhoのプログラムを作ってみる。
who1.c

#include <stdio.h>
#include <stdlib.h>
#include <utmpx.h>
#include <fcntl.h>
#include <unistd.h>

#define SHOWHOST                /* remote machine name to output */

void show_info(struct utmpx *utbufp);

int main()
{
    struct utmpx current_record; /* read information from here */
    int utmpxfd;                 /* read from this descriptor */
    int reclen = sizeof(current_record);

    if ((utmpxfd = open(UTMPX_FILE, O_RDONLY)) == -1) {
        perror(UTMPX_FILE);
        exit(1);
    }

    while (read(utmpxfd, &current_record, reclen) == reclen)
        show_info(&current_record);
    close(utmpxfd);
    return 0;
}

void show_info(struct utmpx *utbufp)
{
    printf("%-8.8s", utbufp->ut_user); /* login name */
    printf(" ");
    printf("-8.8s", utbufp->ut_line); /* tty */
    printf(" ");
    printf("101d", utbufp->ut_tv.tv_sec); /* login time */
    printf(" ");
#ifdef SHOWHOST
    printf("(%s)", utbufp->ut_host); /* host */
#endif
    printf("\n");

}

<

このコマンドで表示されるログイン時刻は人間が理解できる形式で表示されない為、変換する必要がある。
Unixグリニッジ標準時1970年1月1日0時からの秒数という形式で時刻を格納している。time_t型は秒数を格納する。この秒数を人間が理解できるように変換するにはctimeを使う

who02.c

#include <stdio.h>
#include <stdlib.h>
#include <utmpx.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

#define SHOWHOST                /* remote machine name to output */

void showtime(long);
void show_info(struct utmpx *utbufp);

int main()
{
    struct utmpx utbuf; /* read information from here */
    int utmpxfd;                 /* read from this descriptor */

    if ((utmpxfd = open(UTMPX_FILE, O_RDONLY)) == -1) {
        perror(UTMPX_FILE);
        exit(1);
    }

    while (read(utmpxfd, &utbuf, sizeof(utbuf)) == sizeof(utbuf))
        show_info(&utbuf);
    close(utmpxfd);
    return 0;
}

void show_info(struct utmpx *utbufp)
{
    if (utbufp->ut_type != USER_PROCESS)
        return;
    printf("%-8.8s", utbufp->ut_user); /* login name */
    printf(" ");
    printf("-8.8s", utbufp->ut_line); /* tty */
    printf(" ");
    showtime(utbufp->ut_tv.tv_sec);
#ifdef SHOWHOST
    if (utbufp->ut_host[0] != '\0')
        printf("(%s)", utbufp->ut_host); /* host */
#endif
    printf("\n");
}

void showtime(long timeval)
{
    char *cp;
    cp = ctime(&timeval);

    printf("%12.12s", cp+4);
}