프로그래밍/Tips [IA]

[IA] inotify in HP UNIX

TFTC 2025. 2. 25. 17:37
반응형

HP-UX에서 리눅스의 inotify와 유사한 기능을 제공하는 네이티브 C API는 존재하지 않습니다. HP-UX는 POSIX 표준을 따르는 유닉스 시스템이지만, inotify는 리눅스 커널에 특화된 기능으로, HP-UX에는 직접적인 대체재가 없습니다. 대신, HP-UX에서 파일 시스템 이벤트를 모니터링하려면 다음과 같은 대안을 사용할 수 있습니다:


  1. ** polling 방식 (주기적 파일 확인)**: 파일의 수정 시간을 주기적으로 확인하는 방식입니다. HP-UX에서 stat() 함수를 사용하여 파일의 타임스탬프를 체크할 수 있습니다.
  2. HP-UX audit 시스템: HP-UX는 파일 시스템 이벤트를 추적하기 위한 auditing 기능을 제공합니다. 하지만 이는 시스템 수준 설정이 필요하며, inotify처럼 실시간으로 응용 프로그램에 이벤트를 전달하지 않습니다.
  3. FAM (File Alteration Monitor): 과거 유닉스 시스템에서 사용되던 파일 모니터링 도구로, HP-UX에서도 일부 환경에서 지원될 수 있습니다. 그러나 최신 HP-UX 버전에서는 사용이 제한적일 수 있습니다
HP-UX에서 파일 수정 감지 및 내용 출력 예제 코드
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <signal.h>

#define MAX_PATH 1024
#define MAX_BUFFER 1024
#define INITIAL_CAPACITY 10

// 글로벌 변수 (시그널 처리와 파일 핸들러 관리용)
static volatile int keep_running = 1;
static FILE *log_file = NULL;

// 파일 정보를 저장할 구조체
typedef struct {
    char path[MAX_PATH];
    time_t mtime;  // 마지막 수정 시간
} FileInfo;

// 시그널 핸들러 (Ctrl+C 처리)
void signal_handler(int sig) {
    keep_running = 0;
}

// 로그 파일에 기록하는 함수
void log_to_file(const char *message) {
    if (log_file == NULL) return;
    time_t now = time(NULL);
    fprintf(log_file, "[%s] %s\n", ctime(&now), message);
    fflush(log_file);  // 즉시 기록 보장
}

// 파일 내용을 로그에 기록
void log_file_content(const char *filepath) {
    FILE *file = fopen(filepath, "r");
    if (file == NULL) {
        char msg[MAX_BUFFER];
        snprintf(msg, MAX_BUFFER, "파일 열기 실패: %s", filepath);
        log_to_file(msg);
        return;
    }

    char buffer[MAX_BUFFER];
    snprintf(buffer, MAX_BUFFER, "파일 내용 (%s):", filepath);
    log_to_file(buffer);

    while (fgets(buffer, MAX_BUFFER, file) != NULL) {
        buffer[strcspn(buffer, "\n")] = 0;  // 개행 문자 제거
        log_to_file(buffer);
    }
    fclose(file);
}

// 디렉토리 내 파일 목록을 재귀적으로 수집
void collect_files(const char *dirpath, FileInfo **files, int *file_count, int *capacity, const char *filter_ext) {
    DIR *dir = opendir(dirpath);
    if (dir == NULL) {
        char msg[MAX_BUFFER];
        snprintf(msg, MAX_BUFFER, "디렉토리 열기 실패: %s", dirpath);
        log_to_file(msg);
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char fullpath[MAX_PATH];
        snprintf(fullpath, MAX_PATH, "%s/%s", dirpath, entry->d_name);

        struct stat file_stat;
        if (stat(fullpath, &file_stat) == -1) {
            char msg[MAX_BUFFER];
            snprintf(msg, MAX_BUFFER, "stat 호출 실패: %s", fullpath);
            log_to_file(msg);
            continue;
        }

        if (S_ISDIR(file_stat.st_mode)) {
            // 디렉토리면 재귀 호출
            collect_files(fullpath, files, file_count, capacity, filter_ext);
        } else if (S_ISREG(file_stat.st_mode)) {
            // 필터링 적용
            if (filter_ext && strstr(entry->d_name, filter_ext) == NULL) {
                continue;
            }

            if (*file_count >= *capacity) {
                *capacity *= 2;
                *files = realloc(*files, *capacity * sizeof(FileInfo));
            }
            snprintf((*files)[*file_count].path, MAX_PATH, "%s", fullpath);
            (*files)[*file_count].mtime = file_stat.st_mtime;
            (*file_count)++;
        }
    }
    closedir(dir);
}

// 파일 목록 갱신 및 변경 감지
int update_file_list(const char *dirpath, FileInfo **files, int *file_count, int *capacity, const char *filter_ext) {
    int new_count = 0;
    FileInfo *new_files = malloc(*capacity * sizeof(FileInfo));

    // 새 파일 목록 수집
    collect_files(dirpath, &new_files, &new_count, capacity, filter_ext);

    // 변경 감지
    for (int i = 0; i < *file_count; i++) {
        int found = 0;
        for (int j = 0; j < new_count; j++) {
            if (strcmp((*files)[i].path, new_files[j].path) == 0) {
                found = 1;
                if ((*files)[i].mtime != new_files[j].mtime) {
                    char msg[MAX_BUFFER];
                    snprintf(msg, MAX_BUFFER, "파일 수정됨: %s (수정 시간: %s)", new_files[j].path, ctime(&new_files[j].mtime));
                    log_to_file(msg);
                    log_file_content(new_files[j].path);
                }
                break;
            }
        }
        if (!found) {
            char msg[MAX_BUFFER];
            snprintf(msg, MAX_BUFFER, "파일 삭제됨: %s", (*files)[i].path);
            log_to_file(msg);
        }
    }

    for (int j = 0; j < new_count; j++) {
        int found = 0;
        for (int i = 0; i < *file_count; i++) {
            if (strcmp((*files)[i].path, new_files[j].path) == 0) {
                found = 1;
                break;
            }
        }
        if (!found) {
            char msg[MAX_BUFFER];
            snprintf(msg, MAX_BUFFER, "파일 추가됨: %s (생성/수정 시간: %s)", new_files[j].path, ctime(&new_files[j].mtime));
            log_to_file(msg);
            log_file_content(new_files[j].path);
        }
    }

    // 기존 목록 교체
    free(*files);
    *files = new_files;
    *file_count = new_count;
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc < 2 || argc > 3) {
        fprintf(stderr, "사용법: %s <디렉토리경로> [polling_interval_sec]\n");
        return 1;
    }

    const char *dirpath = argv[1];
    int polling_interval = (argc == 3) ? atoi(argv[2]) : 1;
    if (polling_interval <= 0) polling_interval = 1;

    // 로그 파일 열기
    log_file = fopen("file_monitor.log", "a");
    if (log_file == NULL) {
        perror("로그 파일 열기 실패");
        return 1;
    }

    // 시그널 핸들러 등록
    signal(SIGINT, signal_handler);

    FileInfo *files = NULL;
    int file_count = 0;
    int capacity = INITIAL_CAPACITY;
    const char *filter_ext = ".txt";  // 감시할 파일 확장자 (NULL로 모든 파일 감시)

    // 초기 파일 목록
    if (update_file_list(dirpath, &files, &file_count, &capacity, filter_ext) == -1) {
        fclose(log_file);
        return 1;
    }

    char msg[MAX_BUFFER];
    snprintf(msg, MAX_BUFFER, "디렉토리 모니터링 시작: %s (%d개 파일 감지됨, polling 간격: %d초)", dirpath, file_count, polling_interval);
    log_to_file(msg);

    // 모니터링 루프
    while (keep_running) {
        update_file_list(dirpath, &files, &file_count, &capacity, filter_ext);
        sleep(polling_interval);
    }

    // 종료 처리
    snprintf(msg, MAX_BUFFER, "모니터링 종료: %s", dirpath);
    log_to_file(msg);
    free(files);
    fclose(log_file);
    return 0;
}

기능 설명


  1. 중첩 디렉토리 감시:
    • collect_files() 함수에서 재귀적으로 디렉토리를 탐색하여 하위 디렉토리의 파일까지 감시합니다.
    • S_ISDIR로 디렉토리를 확인하고 재귀 호출합니다.
  2. 시그널 처리:
    • signal(SIGINT, signal_handler)로 Ctrl+C를 감지하여 keep_running 플래그를 변경합니다.
    • 종료 시 메모리와 로그 파일을 안전하게 정리합니다.
  3. 로그 파일 출력:
    • file_monitor.log라는 파일에 모든 이벤트를 기록합니다.
    • log_to_file()log_file_content()로 메시지와 파일 내용을 로그에 저장합니다.
  4. 기존 기능 유지:
    • 파일 수정/추가/삭제 감지, polling 간격 조정, 확장자 필터링 등 이전 개선 사항을 모두 포함합니다.

한계

  • 실시간성: 여전히 polling 방식의 한계로 inotify 수준의 즉각적인 감지는 불가능합니다.
  • 성능: 중첩 디렉토리와 파일 수가 많아질수록 I/O 부하가 증가할 수 있습니다.
반응형