#include #include #include #include #include #include #include #include #include #include #include #include "linux/fanotify.h" int watch_inode(int fan_fd, const char *path, uint32_t mask) { struct fanotify_so_inode_mark mark; memset(&mark, 0, sizeof(mark)); mark.fd = open(path, 0); if (mark.fd == -1) return -1; mark.mask = mask; if (setsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_SET_MARK, &mark, sizeof(mark)) != 0) return -1; close(mark.fd); return 0; } void synopsis(const char *progname, int status) { FILE *file = status ? stderr : stdout; fprintf(file, "USAGE: %s [-cg] [-o {open,close,access,modify}] file ...\n", progname); exit(status); } int main(int argc, char *argv[]) { int opt; int fan_fd; uint32_t fan_mask = FAN_OPEN | FAN_CLOSE | FAN_ACCESS | FAN_MODIFY; bool opt_child = false, opt_global = false; ssize_t len; struct fanotify_addr addr; char buf[4096]; #ifdef WITH_PID pid_t pid; #endif while ((opt = getopt(argc, argv, "o:cgh")) != -1) { switch(opt) { case 'o': { char *str, *tok; fan_mask = 0; str = optarg; while ((tok = strtok(str, ",")) != NULL) { str = NULL; if (strcmp(tok, "open") == 0) fan_mask |= FAN_OPEN; else if (strcmp(tok, "close") == 0) fan_mask |= FAN_CLOSE; else if (strcmp(tok, "access") == 0) fan_mask |= FAN_ACCESS; else if (strcmp(tok, "modify") == 0) fan_mask |= FAN_MODIFY; else synopsis(argv[0], 1); } break; } case 'c': opt_child = true; break; case 'g': opt_global = true; break; case 'h': synopsis(argv[0], 0); default: /* '?' */ synopsis(argv[0], 1); } } if (optind == argc && !opt_global) synopsis(argv[0], 1); if (opt_child) fan_mask |= FAN_EVENT_ON_CHILD; memset(&addr, 0, sizeof(addr)); addr.family = AF_FANOTIFY; addr.priority = 32768; addr.mask = opt_global ? fan_mask : 0; fan_fd = socket(PF_FANOTIFY, SOCK_RAW, 0); if (fan_fd == -1) goto fail; if (bind(fan_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) goto fail; for (; optind < argc; optind++) if (watch_inode(fan_fd, argv[optind], fan_mask) != 0) goto fail; #if WITH_PID pid = getpid(); #endif while ((len = recv(fan_fd, buf, sizeof(buf), 0)) > 0) { struct fanotify_event_metadata *metadata; metadata = (void *)buf; while(FAN_EVENT_OK(metadata, len)) { struct stat st; #if WITH_PID if (metadata->pid == pid) goto skip; #endif if (metadata->fd >= 0) { char path[PATH_MAX]; sprintf(path, "/proc/self/fd/%d", metadata->fd); if (readlink(path, path, sizeof(path)) == -1) goto fail; printf("%s:", path); } else printf("?:"); #if WITH_PID if (metadata->pid >= 0) printf(" pid=%ld", metadata->pid); #endif if (metadata->mask & FAN_ACCESS) printf(" access"); if (metadata->mask & FAN_OPEN) printf(" open"); if (metadata->mask & FAN_MODIFY) printf(" modify"); if (metadata->mask & FAN_CLOSE) { if (metadata->mask & FAN_CLOSE_WRITE) printf(" close(writable)"); else printf(" close"); } printf("\n"); skip: if (metadata->fd >= 0 && close(metadata->fd) != 0) goto fail; metadata = FAN_EVENT_NEXT(metadata, len); } } if (len < 0) goto fail; return 0; fail: fprintf(stderr, "%s\n", strerror(errno)); return 1; }