#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
typedef struct monitor_elm {
const char *path;
int fd;
} *monitor_elm_t;
static void usage(int exit_code);
static void monitor_add(const char *path);
static void monitor_events(void);
static int VerboseOpt;
static int QuietOpt;
static int ExitOpt;
static int KQueueFd;
static int NumFiles;
static int MaxFiles;
static monitor_elm_t *Elms;
int
main(int ac, char **av)
{
int ch;
int i;
while ((ch = getopt(ac, av, "qvx")) != -1) {
switch (ch) {
case 'q':
if (VerboseOpt > 0)
--VerboseOpt;
else
++QuietOpt;
break;
case 'v':
if (QuietOpt > 0)
--QuietOpt;
else
++VerboseOpt;
break;
case 'x':
ExitOpt = 1;
break;
default:
usage(1);
}
}
ac -= optind;
av += optind;
if (ac < 1) {
usage(1);
}
if ((KQueueFd = kqueue()) < 0) {
perror("kqueue");
exit(1);
}
NumFiles = MaxFiles = 16;
Elms = calloc(MaxFiles, sizeof(monitor_elm_t));
for (i = 0; i < ac; ++i) {
monitor_add(av[i]);
}
fflush(stdout);
do {
monitor_events();
fflush(stdout);
} while (ExitOpt == 0);
exit(0);
}
static
void
monitor_add(const char *path)
{
monitor_elm_t elm;
struct kevent kev;
int n;
elm = malloc(sizeof(*elm));
bzero(elm, sizeof(*elm));
elm->path = path;
elm->fd = open(path, O_RDONLY);
if (elm->fd < 0) {
printf("%s\tnot found\n", path);
return;
}
EV_SET(&kev, elm->fd, EVFILT_VNODE, EV_ADD|EV_ENABLE|EV_CLEAR,
NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|
NOTE_LINK|NOTE_RENAME|NOTE_REVOKE,
0, NULL);
n = kevent(KQueueFd, &kev, 1, NULL, 0, NULL);
if (n < 0) {
perror("kqueue");
exit(1);
}
if (elm->fd >= NumFiles) {
MaxFiles = (elm->fd + 16) * 3 / 2;
Elms = realloc(Elms, MaxFiles * sizeof(elm));
bzero(&Elms[NumFiles], (MaxFiles - NumFiles) * sizeof(elm));
NumFiles = MaxFiles;
}
Elms[elm->fd] = elm;
}
static
void
monitor_events(void)
{
struct kevent kev_array[1];
struct kevent *kev;
monitor_elm_t elm;
struct stat st;
int bno;
int i;
int n;
n = kevent(KQueueFd, NULL, 0, kev_array, 1, NULL);
for (i = 0; i < n; ++i) {
kev = &kev_array[i];
elm = Elms[kev->ident];
printf("%-23s", elm->path);
if (VerboseOpt && fstat(kev->ident, &st) == 0 &&
S_ISREG(st.st_mode)) {
printf(" %10jd", (intmax_t)st.st_size);
}
while (QuietOpt == 0 && (bno = ffs(kev->fflags)) > 0) {
printf(" ");
--bno;
kev->fflags &= ~(1 << bno);
switch (1 << bno) {
case NOTE_DELETE:
printf("delete");
break;
case NOTE_WRITE:
printf("write");
break;
case NOTE_EXTEND:
printf("extend");
break;
case NOTE_ATTRIB:
printf("attrib");
break;
case NOTE_LINK:
printf("link");
break;
case NOTE_RENAME:
printf("rename");
break;
case NOTE_REVOKE:
printf("revoke");
break;
default:
printf("%08x", 1 << bno);
break;
}
}
printf("\n");
}
}
static
void
usage(int exit_code)
{
fprintf(stderr,
"monitor [-vx] files...\n"
" -v Be more verbose\n"
" -x Exit after first event reported\n"
);
exit(exit_code);
}