리눅스 시스템 명령어 who를 구현해보자.
who는 호스트에 로그인한 사용자의 정보를 출력해주는 명령어로,
utmp 파일에서 각 사용자 별 로그인 정보를 utmp.h 내의 utmp struct 형태로 읽어옴으로써 구현된다
현재 로그인한 사용자의 정보를 저장하고 있는 로그 파일로 var/run/utmp 경로에 위치해있다.
로그인한 사용자의 정보를 표현하기 위한 struct utmp 보유하고 있는 헤더파일로, who를 구현하기 위해, include 해주어야 한다.
struct utmp
{
short int ut_type; /* Type of login. */
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE] __attribute_nonstring__; /* Devicename. */
char ut_id[4]__attribute_nonstring__; /* Inittab ID. */
char ut_user[UT_NAMESIZE]
__attribute_nonstring__; /* Username. */
char ut_host[UT_HOSTSIZE]
__attribute_nonstring__; /* Hostname for remote login. */
struct exit_status ut_exit; /* Exit status of a process marked
as DEAD_PROCESS. */
/* 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_TIME64_COMPAT32
int32_t ut_session; /* Session ID, used for windowing. */
struct
{
int32_t tv_sec; /* Seconds. */
int32_t tv_usec; /* Microseconds. */
} ut_tv; /* Time entry was made. */
#else
long int ut_session; /* Session ID, used for windowing. */
struct timeval ut_tv; /* Time entry was made. */
#endif
int32_t ut_addr_v6[4]; /* Internet address of remote host. */
char __glibc_reserved[20]; /* Reserved for future use. */
};
위 utmp struct에는 다양한 멤버 변수가 있지만, who를 구현하기 위해서 utmp struct의 모든 멤버가 필요한 것은 아니다. 아래는 who 명령어의 실행 결과이다.

첫번째 필드인 'changwoo'는 사용자 이름을 뜻하고, utmp struct의 ut_user와 매핑.
두번째 필드인 'tty2'는 사용자가 로그인한 터미널을 뜻하고,utmp struct의 ut_line과 매핑.
세번째 필드인 '2024~'는 사용자가 로그인한 시간을 뜻하고,utmp struct의 ut_tv.tv_sec와 매핑.
네번째 필드는 원격으로 접속한 사용자의 ip 정보를 뜻하며, utmp struct의 ut_host와 매핑된다.
우리는 who를 구현하기위해 utmp로부터 위의 사용자 정보들을 읽어와야 한다는 것을 알았다. utmp 파일을 열고(open), 읽고나서(read) 닫기(close) 위한 c의 func을 알아보자.
함수 원형 :
#include <sys/types.h>
#include <fcntl.h>
int open(char* path,int flag)
//return : File Descriptor
path: 파일 경로
flag: file open mode
- about file open Mode
O_CREAT: file create
O_TRUNC: truncate size to 0
O_APPEND: append on each write
O_RDONLY: open for reading only
O_WRONLY: open for writing only
O_RDWR: open for reading and write
함수 원형
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
//return: read한 byte, 파일의 끝(EOF)이면 0을 리턴, 읽기 실패한 경우 -1을 리턴
fd: File Descriptor
buf: 읽은 정보를 저장할 buf
nbytes: 읽을 크기
함수 원형
#include <unistd.h>
int close(int fd);
//return: 0 if success, -1 if error
이제 앞서 배운 정보를 바탕으로 who를 구현해보자.
main 함수를 구현하기 이전에 utmp 파일을 열고, 읽고, 닫을 코드를 먼저 구현해보자.
int utmp_open(char* file name)
work: utmp 파일을 염
return: file descriptor
int utmp_reload()
work: utmp 파일에서 num_recs개의 사용자 정보를 읽어옴
return: 읽어온 사용자 정보의 개수
struct utmp utmp_next()
work: buf에 더 이상 처리할 사용자 정보가 없다면, utmp_reload() 호출 및 buf로부터 사용자 정보를 하나씩 읽어옴
return: 현재 처리 중인 utmp struct를 리턴
int utmp_close()
work: 파일 닫기
return: 0 if success, -1 if error
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <utmp.h>
#include <unistd.h>
#include <stdlib.h>
#define NRECS 16
#define NULLUT ((struct utmp *)NULL)
#define UTSIZE (sizeof(struct utmp))
static struct utmp utmpbuf[NRECS]; // struct utmp 배열로 변경
static int num_recs; //utmp 구조체 배열의 길이
static int cur_rec; //현재 처리 중인 위치
static int fd_utmp = -1; //File descriptor
int utmp_open(char* filename)
{
fd_utmp=open(filename, O_RDONLY);
if(fd_utmp==--1)
{
fprintf(stderr,"Error: can not open utmp file";
exit(-1);
}
cur_rec=0;
num_recs=0;
return fd_utmp;
}
int utmp_reload()
{
int amt_read = read(fd_utmp, utmpbuf, NRECS * UTSIZE);
if(amt_read == -1)
{
fprintf(stderr,"Error: can not read from the utmp file");
perror("");
exit(-1);
}
num_recs = amt_read/UTSIZE; //읽은 byte 수로 utmp 정보의 수를 계산
cur_rec =0;
return num_recs;
}
struct utmp *utmp_next()
{
struct utmp *recp = NULLUT;
if (fd_utmp == -1)
{
return recp;
}
// buffer is empty, time to reload records
// check whether we have reached EOF at the same time
// and if we got any records, cur_rec is reset to 0
if ((cur_rec == num_recs) && (utmp_reload() == 0))
{
return recp;
}
recp =&utmpbuf[cur_rec];
cur_rec += 1; // increment the pointer by one
return recp;
}
void utmp_close()
{
if (fd_utmp != -1)
{
close(fd_utmp);
}
}
int show_time(int seconds)
work: utmp struct에 저장된 ut_tv.tv_sec의 정보를 전처리
void show_info(struct utmp *ut_buf_p)
work: utmp struct에서 who에 필요한 멤버 변수들만을 선택하여 출력
#include <stdio.h>
#include <utmp.h>
#include <time.h>
#include <stdlib.h>
extern int utmp_open(char* filename);
extern struct utmp *utmp_next();
extern void utmp_close();
void show_info(struct utmp *);
void showtime(time_t);
int main()
{
struct utmp *utbufp = NULL;
if (utmp_open("/var/run/utmp") == -1)
{
perror("/var/run/utmp");
exit(-1);
}
while ((utbufp = utmp_next()) != NULL)
show_info(utbufp);
utmp_close();
return 0;
}
void show_time(int seconds)
{
time_t sec = (time_t)seconds;
char *cp = ctime(&sec);
printf("%12.12s",cp+4);
}
void show_info(struct utmp *ut_buf_p)
{
if(ut_buf_p->ut_type != USER_PROCESS) //blank record 제거
return;
printf("%-8.8s", ut_buf_p->ut_user);
printf(" ");
printf("%-8.8s", ut_buf_p->ut_line);
printf(" ");
show_time(ut_buf_p->ut_tv.tv_sec);
if (ut_buf_p->ut_host[0] != '\0')
printf("(%s)", ut_buf_p->ut_host);
printf("\n");
}