본문 바로가기
IT/programming

[C/C++] C언어 파일 생성/삭제 이벤트 모니터링 / inotify, event monitoring

by 어느해겨울 2022. 1. 20.

C언어 파일 생성/삭제 이벤트 확인 

inotify - monitoring filesystem events, inotify 를 이용하여 파일시스템 이벤트를 모니터링하는 프로그램을 만들어보자.

응용 또는 질문은 댓글에 남겨주면 성실하게 답장해주겠다.


이번에도 늘 그렇듯 완성된 동작의 소스코드를 동작한다.

이번 소스코드에서는 생성(IN_CREATE)와 삭제(IN_DELETE)만 사용하였지만 더 많은 event trigger가 존재한다.

 

inotify의 man page 일부 발췌

IN_ACCESS (+)
	File was accessed (e.g., read(2), execve(2)).

IN_ATTRIB (*)
	Metadata changed—for example, permissions (e.g., chmod(2)), timestamps (e.g., utimensat(2)), extended attributes
	(setxattr(2)), link count (since Linux 2.6.25;  e.g.,  for  the  target  of  link(2)  and  for  unlink(2)),  and
	user/group ID (e.g., chown(2)).

IN_CLOSE_WRITE (+)
	File opened for writing was closed.

IN_CLOSE_NOWRITE (*)
	File or directory not opened for writing was closed.

IN_CREATE (+)
	File/directory  created in watched directory (e.g., open(2) O_CREAT, mkdir(2), link(2), symlink(2), bind(2) on a
	UNIX domain socket).

IN_DELETE (+)
	File/directory deleted from watched directory.

IN_DELETE_SELF
	Watched file/directory was itself deleted.  (This event also occurs if an object is moved to another filesystem,
	since mv(1) in effect copies the file to the other filesystem and then deletes it from the original filesystem.)
	In addition, an IN_IGNORED event will subsequently be generated for the watch descriptor.

IN_MODIFY (+)
	File was modified (e.g., write(2), truncate(2)).

IN_MOVE_SELF
	Watched file/directory was itself moved.

IN_MOVED_FROM (+)
	Generated for the directory containing the old filename when a file is renamed.

IN_MOVED_TO (+)
	Generated for the directory containing the new filename when a file is renamed.

IN_OPEN (*)
	File or directory was opened.

IN_MOVE
	Equates to IN_MOVED_FROM | IN_MOVED_TO.

IN_CLOSE
	Equates to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE.

	The following further bits can be specified in mask when calling inotify_add_watch(2):

IN_DONT_FOLLOW (since Linux 2.6.15)
	Dont dereference pathname if it is a symbolic link.

IN_EXCL_UNLINK (since Linux 2.6.36)
	By  default,  when  watching events on the children of a directory, events are generated for children even after
	they have been unlinked from the directory.  This can result in large numbers of uninteresting events  for  some
	applications  (e.g., if watching /tmp, in which many applications create temporary files whose names are immedi‐
	ately unlinked).  Specifying IN_EXCL_UNLINK changes the default behavior, so that events are not  generated  for
	children after they have been unlinked from the watched directory.

IN_MASK_ADD
	If  a  watch instance already exists for the filesystem object corresponding to pathname, add (OR) the events in
	mask to the watch mask (instead of replacing the mask).

IN_ONESHOT
	Monitor the filesystem object corresponding to pathname for one event, then remove from watch list.

IN_ONLYDIR (since Linux 2.6.15)
	Only watch pathname if it is a directory.  Using this flag provides an  application  with  a  race-free  way  of
	ensuring that the monitored object is a directory.

	The following bits may be set in the mask field returned by read(2):

IN_IGNORED
	Watch was removed explicitly  (inotify_rm_watch(2))  or  automatically  (file  was deleted, or filesystem was unmounted).  See also BUGS.

IN_ISDIR
	Subject of this event is a directory.

IN_Q_OVERFLOW
	Event queue overflowed (wd is -1 for this event).

IN_UNMOUNT
	Filesystem containing watched object was unmounted.  In addition, an IN_IGNORED event will subsequently be  gen‐erated for the watch descriptor.

(*)는 파일 및 디렉토리까지 적용되는 플래그이고, (+)디렉토리 내에 있는 파일을 대상으로하는 플래그다.

그 외에는 additional mask flag인데 딱히 써본적이 없어서 모르겠다.

 

 

소스코드

- 이벤트 모니터링 대상을 변경하고 싶으면 PATH_TARGET_DIR 부분을 변경하면 된다.

define 하지 않고 getopt를 통해 인자로 받아도 되고 자유롭게 수정해서 사용하자.

- inotify_add_watch() 3번째 인자에 | 를 통해 event mask 를 추가하면 더욱 다양한 옵션을 사용 가능하다.

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/inotify.h>


#define INTY_MAX_EVENTS     1024                                                    /* Max. number of events to process at one go */
#define INTY_LEN_NAME       16                                                      /* Assuming that the length of the filename won't exceed 16 bytes */
#define INTY_EVENT_SIZE     (sizeof (struct inotify_event))                         /* size of one event */
#define INTY_BUF_SIZE       (INTY_MAX_EVENTS * (INTY_EVENT_SIZE + INTY_LEN_NAME))   /* buffer to store the data of events */

#define PATH_TARGET_DIR     "/tmp"


int main(int _argc, char *_argv[]) {
    int     inty_fd, inty_wd;

    int     inty_length, inty_idx = 0;
    char    inty_buffer[INTY_BUF_SIZE];
    bool    is_noty_evt_status = false;


    if( (inty_fd = inotify_init()) < 0 ) {
        printf("inotify_init() failed : [%02d] %s\n", errno, strerror(errno));
        return -1;
    }

    if( (inty_wd = inotify_add_watch(inty_fd, PATH_TARGET_DIR, IN_CREATE | IN_DELETE)) == -1 ) {
        printf("inotify_add_watch() failed [%s] : [%02d] %s \n", PATH_TARGET_DIR, errno, strerror(errno));
        return -1;
    }
    printf("inotify_add_watch() watching : [%s]\n", PATH_TARGET_DIR);

    while( true ) {
        inty_idx = 0;
        if( (inty_length = read(inty_fd, inty_buffer, INTY_BUF_SIZE)) < 0 ) {
            printf("read() failed : [%02d] %s\n", errno, strerror(errno));
        }

        while( inty_idx < inty_length ) {
            struct inotify_event *event = (struct inotify_event *)&inty_buffer[inty_idx];

            if( event->len ) {
                if( event->mask & IN_CREATE || event->mask & IN_DELETE ) {
                    printf("inotify event status: [%s], file: [%s]\n", (event->mask == 256 ? "create" : "remove"), event->name);
                    is_noty_evt_status = true;
                }
            }

            inty_idx += INTY_EVENT_SIZE + event->len;
        }
    }


    return 0;
}

 

컴파일 및 결과

- /tmp 를 타겟으로 이벤트 모니터링 를 실행하였다.

- system에서 생성하고 삭제하는 파일들과 본인이 touch /tmp/a 하여 생성한 파일과 rm /tmp/a하여 삭제한 파일의 이벤트가 catch되는 것을 확인할 수 있다.

muabow@muabow-WorkSpace:~/dev/inotify$ gcc inotify_test.c -o inotify_test ; ./inotify_test
inotify_add_watch() watching : [/tmp]
inotify event status: [create], file: [tmpf5EaiiE]
inotify event status: [remove], file: [tmpf5EaiiE]
inotify event status: [create], file: [sh-thd-1196864159]
inotify event status: [remove], file: [sh-thd-1196864159]
inotify event status: [create], file: [sh-thd-2339961226]
inotify event status: [remove], file: [sh-thd-2339961226]
inotify event status: [create], file: [a]
inotify event status: [remove], file: [a]
inotify event status: [create], file: [tmpfxaiGkH]
inotify event status: [remove], file: [tmpfxaiGkH]
inotify event status: [create], file: [sh-thd-4583039949]
inotify event status: [remove], file: [sh-thd-4583039949]
inotify event status: [create], file: [sh-thd-9513632609]
inotify event status: [remove], file: [sh-thd-9513632609]
inotify event status: [create], file: [aa]
inotify event status: [remove], file: [aa]

 

끝.

 

 

댓글