Kjetil's Information Center: A Blog About My Projects

Monitoring File Open in a Directory

Here is part of an experiment I have been working on. I did not find any use for this (yet), but the method is worth considering. The following program uses the Linux kernel's "inotify" mechanism to print a message whenever a file is opened in a specific directory.

Have a look:

#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define WATCH_FILES_MAX 256

typedef struct watch_s {
  int wd;
  char file[PATH_MAX];
} watch_t;

static watch_t watch[WATCH_FILES_MAX];
static int watch_n = 0;
static int ifd;

static int watch_files(char *dir)
{
  struct dirent *entry;
  struct stat st;
  DIR *dh;
  char path[PATH_MAX];

  ifd = inotify_init();
  if (ifd == -1) {
    fprintf(stderr, "inotify_init() failed: %s\n", strerror(errno));
    return -1;
  }

  dh = opendir(dir);
  if (dh == NULL) {
    fprintf(stderr, "opendir(%s) failed: %s\n", dir, strerror(errno));
    return -1;
  }

  watch_n = 0;
  while ((entry = readdir(dh))) {
    if (entry->d_name[0] == '.')
      continue;

    snprintf(path, PATH_MAX, "%s/%s", dir, entry->d_name);

    if (stat(path, &st) == -1) {
      fprintf(stderr, "stat(%s) failed: %s\n", path, strerror(errno));
      closedir(dh);
      return -1;
    }

    if (S_ISREG(st.st_mode)) {
      watch[watch_n].wd = inotify_add_watch(ifd, path, IN_OPEN);
      if (watch[watch_n].wd == -1) {
        fprintf(stderr, "inotify_add_watch(%s) failed: %s\n", path, strerror(errno));
        closedir(dh);
        return -1;
      }

      strncpy(watch[watch_n].file, entry->d_name, PATH_MAX);
      fprintf(stderr, "Watching: %s\n", entry->d_name);

      watch_n++;
      if (watch_n >= WATCH_FILES_MAX) {
        fprintf(stderr, "Max files reached, skipping the rest!\n");
        break;
      }
    }
  }

  closedir(dh);
  return 0;
}

int main(int argc, char *argv[])
{
  struct inotify_event iev;
  time_t now;
  int i;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s <watch directory>\n", argv[0]);
    return 1;
  }

  if (watch_files(argv[1]) != 0) {
    return 1;
  }

  setlinebuf(stdout);

  while (1) {
    if (read(ifd, &iev, sizeof(struct inotify_event)) > 0) {
      for (i = 0; i < watch_n; i++) {
        if (iev.wd == watch[i].wd) {
          time(&now);
          fprintf(stdout, "%s @ %s", watch[i].file, ctime(&now));
        }
      }
    }
  }

  /* NOTE: No file descriptors explicitly closed. */
  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 09/01-2016, Article Link