static void process_inotify_file_defer(struct watch_info *wi, char *path, struct inotify_event *ie) { struct deferred_file *f; struct timeval tv = { 10, 0 }; if (!(ie->mask & IN_CREATE)) { process_inotify_file(wi, path, ie); return; } DPRINTF(E_INFO, L_SCAN, "Deferring scan of newly created file %s\n", path); ie->mask = IN_CLOSE_WRITE; f = calloc(1, sizeof(struct deferred_file)); f->wi = *wi; f->wi.path = strdup(wi->path); f->ie = *ie; strcpy(f->path, path); f->next = filestack; filestack = f; event_add(deferred_inoev, &tv); }
/* Since FreeBSD doesn't really have inotify we only get a IN_CREATE. That is * a bit too soon to start scanning the file, so we defer it for 10 seconds. */ static void inotify_deferred_cb(int fd, short what, void *arg) { struct deferred_file *f; struct deferred_file *next; for (f = filestack; f; f = next) { next = f->next; DPRINTF(E_DBG, L_SCAN, "Processing deferred file %s\n", f->path); process_inotify_file(&f->wi, f->path, &f->ie); free(f->wi.path); free(f); } filestack = NULL; }
/* Thread: scan */ static void inotify_cb(int fd, short event, void *arg) { struct inotify_event *buf; struct inotify_event *ie; struct watch_info wi; char path[PATH_MAX]; int qsize; int namelen; int ret; /* Determine the size of the inotify queue */ ret = ioctl(fd, FIONREAD, &qsize); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Could not determine inotify queue size: %s\n", strerror(errno)); return; } buf = (struct inotify_event *)malloc(qsize); if (!buf) { DPRINTF(E_LOG, L_SCAN, "Could not allocate %d bytes for inotify events\n", qsize); return; } ret = read(fd, buf, qsize); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "inotify read failed: %s\n", strerror(errno)); free(buf); return; } /* ioctl(FIONREAD) returns the number of bytes, now we need the number of elements */ qsize /= sizeof(struct inotify_event); /* Loop through all the events we got */ for (ie = buf; (ie - buf) < qsize; ie += (1 + (ie->len / sizeof(struct inotify_event)))) { memset(&wi, 0, sizeof(struct watch_info)); /* ie[0] contains the inotify event information * the memory space for ie[1+] contains the name of the file * see the inotify documentation */ wi.wd = ie->wd; ret = db_watch_get_bywd(&wi); if (ret < 0) { if (!(ie->mask & IN_IGNORED)) DPRINTF(E_LOG, L_SCAN, "No matching watch found, ignoring event (0x%x)\n", ie->mask); continue; } if (ie->mask & IN_IGNORED) { DPRINTF(E_DBG, L_SCAN, "%s deleted or backing filesystem unmounted!\n", wi.path); db_watch_delete_bywd(ie->wd); free(wi.path); continue; } path[0] = '\0'; ret = snprintf(path, PATH_MAX, "%s", wi.path); if ((ret < 0) || (ret >= PATH_MAX)) { DPRINTF(E_LOG, L_SCAN, "Skipping event under %s, PATH_MAX exceeded\n", wi.path); free(wi.path); continue; } if (ie->len > 0) { namelen = PATH_MAX - ret; ret = snprintf(path + ret, namelen, "/%s", ie->name); if ((ret < 0) || (ret >= namelen)) { DPRINTF(E_LOG, L_SCAN, "Skipping %s/%s, PATH_MAX exceeded\n", wi.path, ie->name); free(wi.path); continue; } } /* ie->len == 0 catches events on the subject of the watch itself. * As we only watch directories, this catches directories. * General watch events like IN_UNMOUNT and IN_IGNORED do not come * with the IN_ISDIR flag set. */ if ((ie->mask & IN_ISDIR) || (ie->len == 0)) process_inotify_dir(&wi, path, ie); else process_inotify_file(&wi, path, ie); free(wi.path); } free(buf); event_add(&inoev, NULL); }
/* Thread: scan */ static void inotify_cb(int fd, short event, void *arg) { struct inotify_event *ie; struct watch_info wi; uint8_t *buf; uint8_t *ptr; char path[PATH_MAX]; int size; int namelen; int ret; /* Determine the amount of bytes to read from inotify */ ret = ioctl(fd, FIONREAD, &size); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Could not determine inotify queue size: %s\n", strerror(errno)); return; } buf = malloc(size); if (!buf) { DPRINTF(E_LOG, L_SCAN, "Could not allocate %d bytes for inotify events\n", size); return; } ret = read(fd, buf, size); if (ret < 0 || ret != size) { DPRINTF(E_LOG, L_SCAN, "inotify read failed: %s (ret was %d, size %d)\n", strerror(errno), ret, size); free(buf); return; } for (ptr = buf; ptr < buf + size; ptr += ie->len + sizeof(struct inotify_event)) { ie = (struct inotify_event *)ptr; memset(&wi, 0, sizeof(struct watch_info)); /* ie[0] contains the inotify event information * the memory space for ie[1+] contains the name of the file * see the inotify documentation */ wi.wd = ie->wd; ret = db_watch_get_bywd(&wi); if (ret < 0) { if (!(ie->mask & IN_IGNORED)) DPRINTF(E_LOG, L_SCAN, "No matching watch found, ignoring event (0x%x)\n", ie->mask); continue; } if (ie->mask & IN_IGNORED) { DPRINTF(E_DBG, L_SCAN, "%s deleted or backing filesystem unmounted!\n", wi.path); db_watch_delete_bywd(ie->wd); free(wi.path); continue; } path[0] = '\0'; ret = snprintf(path, PATH_MAX, "%s", wi.path); if ((ret < 0) || (ret >= PATH_MAX)) { DPRINTF(E_LOG, L_SCAN, "Skipping event under %s, PATH_MAX exceeded\n", wi.path); free(wi.path); continue; } if (ie->len > 0) { namelen = PATH_MAX - ret; ret = snprintf(path + ret, namelen, "/%s", ie->name); if ((ret < 0) || (ret >= namelen)) { DPRINTF(E_LOG, L_SCAN, "Skipping %s/%s, PATH_MAX exceeded\n", wi.path, ie->name); free(wi.path); continue; } } /* ie->len == 0 catches events on the subject of the watch itself. * As we only watch directories, this catches directories. * General watch events like IN_UNMOUNT and IN_IGNORED do not come * with the IN_ISDIR flag set. */ if ((ie->mask & IN_ISDIR) || (ie->len == 0)) process_inotify_dir(&wi, path, ie); else #if defined(__linux__) process_inotify_file(&wi, path, ie); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) process_inotify_file_defer(&wi, path, ie); #endif free(wi.path); } free(buf); event_add(inoev, NULL); }