System Programming(who)

전창우·2024년 9월 29일

system programming

목록 보기
1/9

Who

리눅스 시스템 명령어 who를 구현해보자.
who는 호스트에 로그인한 사용자의 정보를 출력해주는 명령어로,
utmp 파일에서 각 사용자 별 로그인 정보를 utmp.h 내의 utmp struct 형태로 읽어옴으로써 구현된다

utmp(file)

현재 로그인한 사용자의 정보를 저장하고 있는 로그 파일로 var/run/utmp 경로에 위치해있다.

utmp.h

로그인한 사용자의 정보를 표현하기 위한 struct utmp 보유하고 있는 헤더파일로, who를 구현하기 위해, include 해주어야 한다.

utmp struct

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와 매핑된다.

file func

우리는 who를 구현하기위해 utmp로부터 위의 사용자 정보들을 읽어와야 한다는 것을 알았다. utmp 파일을 열고(open), 읽고나서(read) 닫기(close) 위한 c의 func을 알아보자.

file open

함수 원형 :

#include <sys/types.h> 
#include <fcntl.h>

int open(char* path,int flag)
//return : File Descriptor

argument

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

file read

함수 원형

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
//return: read한 byte, 파일의 끝(EOF)이면 0을 리턴, 읽기 실패한 경우 -1을 리턴

Argument

fd: File Descriptor
buf: 읽은 정보를 저장할 buf
nbytes: 읽을 크기

file close

함수 원형

 #include <unistd.h>
 int close(int fd);
 //return: 0 if success, -1 if error

이제 앞서 배운 정보를 바탕으로 who를 구현해보자.

utmplib.c

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);
    }
}




who.c

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");
}

0개의 댓글