/* Thread: scan */ static void process_directories(char *root, int flags) { char *path; process_directory(root, flags); if (scan_exit) return; while ((path = pop_dir(&dirstack))) { process_directory(path, flags); free(path); if (scan_exit) return; } }
static void process_directories(char *root, int parent_id, int flags) { struct stacked_dir *dir; process_directory(root, parent_id, flags); if (scan_exit) return; while ((dir = pop_dir(&dirstack))) { process_directory(dir->path, dir->parent_id, flags); free(dir->path); free(dir); if (scan_exit) return; } }
/* Thread: scan */ static void kqueue_cb(int fd, short event, void *arg) { struct kevent kev; struct timespec ts; struct watch_info wi; struct watch_enum we; struct stacked_dir *rescan; struct stacked_dir *d; struct stacked_dir *dprev; char *path; uint32_t wd; int d_len; int w_len; int need_rescan; int ret; ts.tv_sec = 0; ts.tv_nsec = 0; we.cookie = 0; rescan = NULL; DPRINTF(E_DBG, L_SCAN, "Library changed!\n"); /* We can only monitor directories with kqueue; to monitor files, we'd need * to have an open fd on every file in the library, which is totally insane. * Unfortunately, that means we only know when directories get renamed, * deleted or changed. We don't get directory/file names when directories/files * are created/deleted/renamed in the directory, so we have to rescan. */ while (kevent(fd, NULL, 0, &kev, 1, &ts) > 0) { /* This should not happen, and if it does, we'll end up in * an infinite loop. */ if (kev.filter != EVFILT_VNODE) continue; wi.wd = kev.ident; ret = db_watch_get_bywd(&wi); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Found no matching watch for kevent, killing this event\n"); close(kev.ident); continue; } /* Whatever the type of event that happened, disable matching watches and * files before we trigger an eventual rescan. */ we.match = wi.path; ret = db_watch_enum_start(&we); if (ret < 0) { free(wi.path); continue; } while ((db_watch_enum_fetchwd(&we, &wd) == 0) && (wd)) { close(wd); } db_watch_enum_end(&we); db_watch_delete_bymatch(wi.path); close(wi.wd); db_watch_delete_bywd(wi.wd); /* Disable files */ db_file_disable_bymatch(wi.path, "", 0); db_pl_disable_bymatch(wi.path, "", 0); if (kev.flags & EV_ERROR) { DPRINTF(E_LOG, L_SCAN, "kevent reports EV_ERROR (%s): %s\n", wi.path, strerror(kev.data)); ret = access(wi.path, F_OK); if (ret != 0) { free(wi.path); continue; } /* The directory still exists, so try to add it back to the library */ kev.fflags |= NOTE_WRITE; } /* No further action on NOTE_DELETE & NOTE_RENAME; NOTE_WRITE on the * parent directory will trigger a rescan in both cases and the * renamed directory will be picked up then. */ if (kev.fflags & NOTE_WRITE) { DPRINTF(E_DBG, L_SCAN, "Got NOTE_WRITE (%s)\n", wi.path); need_rescan = 1; w_len = strlen(wi.path); /* Abusing stacked_dir a little bit here */ dprev = NULL; d = rescan; while (d) { d_len = strlen(d->path); if (d_len > w_len) { /* Stacked dir child of watch dir? */ if ((d->path[w_len] == '/') && (strncmp(d->path, wi.path, w_len) == 0)) { DPRINTF(E_DBG, L_SCAN, "Watched directory is a parent\n"); if (dprev) dprev->next = d->next; else rescan = d->next; free(d->path); free(d); if (dprev) d = dprev->next; else d = rescan; continue; } } else if (w_len > d_len) { /* Watch dir child of stacked dir? */ if ((wi.path[d_len] == '/') && (strncmp(wi.path, d->path, d_len) == 0)) { DPRINTF(E_DBG, L_SCAN, "Watched directory is a child\n"); need_rescan = 0; break; } } else if (strcmp(wi.path, d->path) == 0) { DPRINTF(E_DBG, L_SCAN, "Watched directory already listed\n"); need_rescan = 0; break; } dprev = d; d = d->next; } if (need_rescan) push_dir(&rescan, wi.path); } free(wi.path); } while ((path = pop_dir(&rescan))) { process_directories(path, 0); free(path); if (rescan) DPRINTF(E_LOG, L_SCAN, "WARNING: unhandled leftover directories\n"); } event_add(&inoev, NULL); }