I need to monitor the creation of a specific file type (with a known extension) in a specific folder in linux. I understand that inotify will only watch existing files. Do I understand correctly?
Is there an alternative to inotify (or similar package) that would allow me to monitor file creation by file type?
EDIT: what I need is to monitor file creation by mask. I need to monitor a path for *.json, while disregarding other file types.
This sounds like a good use case of inotify. The man page has a pretty good example that is easily transferred to your problem.
Here is a little program that can be used like
$ myprog /tmp '*.o' '*.a'
to watch the directory /tmp for the creation of *.o and *.a files. Note that the patterns are quoted to prevent expansion by the shell. The program runs forever until interrupted by SIGINT (press Ctrl + C).
I am using fnmatch to match the names of the created files against the patterns and install a signal handler for SIGINT that sets a global flag.
#include <assert.h>
#include <errno.h>
#include <fnmatch.h>
#include <limits.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
I define a small preprocessor macro to align the buffer using GCC's __attribute__ extension. More on this later when we actually use this macro.
#ifdef __GNUC__
# define ALIGNAS(TYPE) __attribute__ ((aligned(__alignof__(TYPE))))
#else
# define ALIGNAS(TYPE) /* empty */
#endif
This is a global flag that we'll set in the signal handler to indicate that the program should quit gracefully.
static volatile int interrupted = 0;
And this is the signal handler itself.
static void
interruption_handler(const int s)
{
if (s == SIGINT)
interrupted = 1;
}
This function is the working-horse of the program.
static int
monitor_directory(const char *const directory,
const char *const *const patterns,
const size_t pattern_count)
{
int notifyfd = -1;
int watchfd = -1;
int ret = 0;
const char * errmsg = "unknown error";
First, we initialize inotify. inotify_init will return a file descriptor that we can read() notifications from. I use blocking I/O so the read() will block until an event occurs.
notifyfd = inotify_init();
if (notifyfd < 0)
{
errmsg = "inotify_init";
goto catch;
}
Now we register files to watch. In our case, we want to watch a single directory (directory) for the creation of new files (IN_CREATE). The returned file descriptor could be used to tell (if an event occurs) to what watched file it belongs. But since we are watching only a single file (that happens to be a directory) anyway, we don't really need this info.
watchfd = inotify_add_watch(notifyfd, directory, IN_CREATE);
if (watchfd < 0)
{
errmsg = "inotify_add_watch";
goto catch;
}
Now everything is set up properly and we can start read()ing from the notification file descriptor.
while (1)
{
It is not known in advance how much a call to read from the inotify descriptor will read so we have to read into a char buffer. We wish to align it properly, tough. See the man page for more comments on this.
char buffer[sizeof(struct inotify_event) + NAME_MAX + 1] ALIGNAS(struct inotify_event);
const struct inotify_event * event_ptr;
read() from the file descriptor. If we get interrupted, the read() will unblock and return –1 as it will if an error occurs.
ssize_t count = read(notifyfd, buffer, sizeof(buffer));
if (count < 0)
{
if (interrupted)
goto finally;
errmsg = "read";
goto catch;
}
We have a new event, handle it.
event_ptr = (const struct inotify_event *) buffer;
assert(event_ptr->wd == watchfd);
assert(event_ptr->mask & IN_CREATE);
if (event_ptr->len)
{
size_t i;
Try matching the file name against each of our patterns.
for (i = 0; i < pattern_count; ++i)
{
switch (fnmatch(patterns[i], event_ptr->name, FNM_PATHNAME))
{
case 0:
/* Your application logic here... */
if (printf("%s\n", event_ptr->name) < 0)
{
errmsg = "printf";
goto catch;
}
break;
case FNM_NOMATCH:
break;
default:
errmsg = "fnmatch";
goto catch;
}
}
}
}
finally, we have to do some cleanup. close()ing the file descriptors created by inotify will cause it to release any associated resources.
finally:
if (watchfd >= 0)
{
int status = close(watchfd);
watchfd = -1;
if (status < 0)
{
errmsg = "close(watchfd)";
goto catch;
}
}
if (notifyfd >= 0)
{
int status = close(notifyfd);
notifyfd = -1;
if (status < 0)
{
errmsg = "close(notifyfd)";
goto catch;
}
}
return ret;
catch:
if (errmsg && errno)
perror(errmsg);
ret = -1;
goto finally;
}
And this is how we hook everything together and run the program.
int
main(const int argc, const char *const *const argv)
{
if (argc < 3)
{
fprintf(stderr, "usage: %s DIRECTORY PATTERN...\n", argv[0]);
return EXIT_FAILURE;
}
{
struct sigaction sa;
sa.sa_handler = interruption_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
}
if (monitor_directory(argv[1], argv + 2, argc - 2) < 0)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With