00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00025 #include "config.h"
00026 #include "pnotify.h"
00027 #include "pnotify-internal.h"
00028
00029
00030 #if HAVE_KQUEUE
00031
00032 #include <fcntl.h>
00033 #include <sys/event.h>
00034
00035
00036 static int kq_directory_event_handler(struct kevent kev, struct pn_watch * watch);
00037 static int directory_open(struct pn_watch * watch);
00038 static int directory_scan(struct pn_watch * watch);
00039
00041 static int KQUEUE_FD = -1;
00042
00043 void *
00044 bsd_kqueue_loop(void * unused)
00045 {
00046 const int nkev = 100;
00047 struct pn_watch *watch;
00048 struct kevent _kev[nkev];
00049 struct kevent *kev;
00050 struct pnotify_event *evt;
00051 int i, rc;
00052
00053
00054 watch = unused;
00055
00056
00057 if ((KQUEUE_FD = kqueue()) < 0)
00058 err(1, "kqueue(2)");
00059
00060
00061 LOOP:
00062
00063
00064 dprintf("waiting for kernel event..\n");
00065 rc = kevent(KQUEUE_FD, NULL, 0, (struct kevent *) &_kev, nkev, NULL);
00066 if (rc < 0)
00067 err(1, "kevent(2) failed");
00068
00069
00070 for (i = 0; i < rc; i++) {
00071
00072 kev = &_kev[i];
00073
00074
00075 watch = (struct pn_watch *) kev->udata;
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 if (watch->parent_wd && kev->fflags & NOTE_DELETE) {
00086 dprintf("ignoring NOTE_DELETE on a watched file\n");
00087 continue;
00088 }
00089
00090
00091 if (!watch->is_dir) {
00092
00093
00094 if ((evt = calloc(1, sizeof(*evt))) == NULL)
00095 err(1, "malloc failed");
00096
00097 if (kev->fflags & NOTE_WRITE)
00098 evt->mask |= PN_MODIFY;
00099 if (kev->fflags & NOTE_TRUNCATE)
00100 evt->mask |= PN_MODIFY;
00101 if (kev->flags & NOTE_EXTEND)
00102 evt->mask |= PN_MODIFY;
00103 if (kev->fflags & NOTE_ATTRIB)
00104 evt->mask |= PN_ATTRIB;
00105 if (kev->fflags & NOTE_DELETE)
00106 evt->mask |= PN_DELETE;
00107
00108
00109
00110
00111 if (watch->parent_wd) {
00112
00113
00114 char *fn = strrchr(watch->path, '/') ;
00115 if (!fn) {
00116 fn = watch->path;
00117 } else {
00118 fn++;
00119 }
00120
00121 evt->watch = watch;
00122
00123 (void) strncpy(evt->name, fn, strlen(fn));
00124 }
00125
00126
00127 pn_event_add(watch->ctx, evt);
00128
00129 dprint_event(evt);
00130
00131
00132 } else {
00133
00134
00135 if (kev->fflags & NOTE_WRITE) {
00136 if (kq_directory_event_handler(*kev, watch) < 0) {
00137 warn("error processing diretory");
00138 return NULL;
00139 }
00140 }
00141
00142 else if (kev->fflags & NOTE_DELETE) {
00143 warn("unimplemented - TODO");
00144 return NULL;
00145 } else {
00146 warn("unknown event recieved");
00147 return NULL;
00148 }
00149
00150 }
00151 }
00152
00153 goto LOOP;
00154
00155 close(KQUEUE_FD);
00156 return NULL;
00157 }
00158
00159
00160 void
00161 bsd_init_once(void)
00162 {
00163 pthread_t tid;
00164
00165
00166 if (pthread_create( &tid, NULL, bsd_kqueue_loop, NULL ) != 0)
00167 errx(1, "pthread_create(3) failed");
00168
00169
00170 }
00171
00172
00173 void
00174 bsd_cleanup(void)
00175 {
00176 (void) close(KQUEUE_FD);
00177 }
00178
00179
00180 #if DEAD
00181
00182
00183 int
00184 bsd_add_watch(struct pnotify_ctx *ctx, struct pn_watch *watch)
00185 {
00186 struct epoll_event *ev = &watch->epoll_evt;
00187 int mask = watch->mask;
00188 uint32_t imask = 0;
00189
00190 switch (watch->type) {
00191
00192 case WATCH_FD:
00193
00194 ev->events = EPOLLET;
00195 if (mask & PN_READ)
00196 ev->events |= EPOLLIN;
00197 if (mask & PN_WRITE)
00198 ev->events |= EPOLLOUT;
00199 ev->data.ptr = watch;
00200
00201
00202 if (epoll_ctl(EPOLL_FD, EPOLL_CTL_ADD, watch->ident.fd, ev) < 0) {
00203 warn("epoll_ctl(2) failed");
00204 return -1;
00205 }
00206 break;
00207
00208 case WATCH_VNODE:
00209
00210 if (mask & PN_ATTRIB)
00211 imask |= IN_ATTRIB;
00212 if (mask & PN_CREATE)
00213 imask |= IN_CREATE;
00214 if (mask & PN_DELETE)
00215 imask |= IN_DELETE | IN_DELETE_SELF;
00216 if (mask & PN_MODIFY)
00217 imask |= IN_MODIFY;
00218 if (mask & PN_ONESHOT)
00219 imask |= IN_ONESHOT;
00220
00221
00222 watch->wd = inotify_add_watch(INOTIFY_FD, watch->ident.path, imask);
00223 if (watch->wd < 0) {
00224 perror("inotify_add_watch(2) failed");
00225 return -1;
00226 }
00227 break;
00228
00229 default:
00230
00231 break;
00232 }
00233
00234 return 0;
00235 }
00236
00237 void
00238 bsd_dump_inotify_event(struct inotify_event *iev)
00239 {
00240 static const char *nam[] = {
00241 "IN_ACCESS", "IN_MODIFY", "IN_ATTRIB", "IN_CLOSE_WRITE",
00242 "IN_CLOSE_NOWRITE", "IN_OPEN", "IN_MOVED_FROM",
00243 "IN_MOVED_TO", "IN_CREATE", "IN_DELETE", "IN_DELETE_SELF",
00244 "IN_MOVE_SELF", "IN_UNMOUNT", "IN_Q_OVERFLOW", "IN_IGNORED",
00245 "IN_ONLYDIR", "IN_DONT_FOLLOW", "IN_MASK_ADD", "IN_ISDIR",
00246 "IN_ONESHOT", NULL };
00247 static const int val[] = {
00248 IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE,
00249 IN_CLOSE_NOWRITE, IN_OPEN, IN_MOVED_FROM,
00250 IN_MOVED_TO, IN_CREATE, IN_DELETE, IN_DELETE_SELF,
00251 IN_MOVE_SELF, IN_UNMOUNT, IN_Q_OVERFLOW, IN_IGNORED,
00252 IN_ONLYDIR, IN_DONT_FOLLOW, IN_MASK_ADD, IN_ISDIR,
00253 IN_ONESHOT, 0 };
00254 int i;
00255
00256 fprintf(stderr, "inotify event: wd=%d mask=", iev->wd);
00257 for (i = 0; val[i] != 0; i++) {
00258 if (iev->mask & val[i])
00259 fprintf(stderr, "%s ", nam[i]);
00260 }
00261 fprintf(stderr, "\n");
00262 }
00263
00264 #endif
00265
00266
00267 int
00268 bsd_add_watch(struct pn_watch *watch)
00269 {
00270 struct stat st;
00271 struct kevent *kev = &watch->kev;
00272 int mask = watch->mask;
00273
00274
00275 if ((watch->fd = open(watch->path, O_RDONLY)) < 0) {
00276 warn("opening path `%s' failed", watch->path);
00277 return -1;
00278 }
00279
00280
00281 if (fstat(watch->fd, &st) < 0) {
00282 warn("fstat(2) failed");
00283 return -1;
00284 }
00285 watch->is_dir = S_ISDIR(st.st_mode);
00286
00287
00288 if (watch->is_dir)
00289 directory_open(watch);
00290
00291
00292 EV_SET(kev, watch->fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, 0, 0, watch);
00293 if (mask & PN_ONESHOT)
00294 kev->flags |= EV_ONESHOT;
00295 switch (watch->type) {
00296
00297 case WATCH_FD:
00298 if (mask & PN_READ)
00299 kev->filter = EVFILT_READ;
00300 if (mask & PN_WRITE)
00301 kev->filter = EVFILT_WRITE;
00302 break;
00303
00304 case WATCH_VNODE:
00305 kev->filter = EVFILT_VNODE;
00306 if (mask & PN_ATTRIB)
00307 kev->fflags |= NOTE_ATTRIB;
00308 if (mask & PN_CREATE)
00309 kev->fflags |= NOTE_WRITE;
00310 if (mask & PN_DELETE)
00311 kev->fflags |= NOTE_DELETE | NOTE_WRITE;
00312 if (mask & PN_MODIFY)
00313 kev->fflags |= NOTE_WRITE;
00314 default:
00315 break;
00316 }
00317
00318
00319 if (kevent(KQUEUE_FD, kev, 1, NULL, 0, NULL) < 0) {
00320 perror("kevent(2)");
00321 return -1;
00322 }
00323
00324 return 0;
00325 }
00326
00327
00328 int
00329 bsd_rm_watch(struct pn_watch *watch)
00330 {
00331
00332
00333
00334
00335 if (close(watch->fd) < 0) {
00336 perror("unable to close watch fd");
00337 return -1;
00338 }
00339
00340
00341 if (watch->dir.dirp != NULL) {
00342 if (closedir(watch->dir.dirp) != 0) {
00343 perror("closedir(3)");
00344 return -1;
00345 }
00346 }
00347
00348 return 0;
00349 }
00350
00354 static int
00355 directory_open(struct pn_watch * watch)
00356 {
00357 struct directory * dir;
00358
00359 dir = &watch->dir;
00360
00361
00362 LIST_INIT(&dir->all);
00363 if ((dir->dirp = opendir(watch->path)) == NULL) {
00364 perror("opendir(2)");
00365 return -1;
00366 }
00367
00368
00369 dir->path_len = strlen(watch->path);
00370 if ((dir->path_len >= PATH_MAX) ||
00371 ((dir->path = malloc(dir->path_len + 1)) == NULL)) {
00372 perror("malloc(3)");
00373 return -1;
00374 }
00375 strncpy(dir->path, watch->path, dir->path_len);
00376
00377
00378 if (directory_scan(watch) < 0) {
00379 warn("directory_scan failed");
00380 return -1;
00381 }
00382
00383 return 0;
00384 }
00385
00386
00390 static int
00391 directory_scan(struct pn_watch * watch)
00392 {
00393 struct pn_watch *wtmp;
00394 struct directory *dir;
00395 struct dirent ent, *entp;
00396 struct dentry *dptr;
00397 bool found;
00398 char path[PATH_MAX + 1];
00399 char *cp;
00400 struct stat st;
00401
00402 assert(watch != NULL);
00403
00404 dir = &watch->dir;
00405
00406 dprintf("scanning directory\n");
00407
00408
00409 (void) snprintf((char *) &path, PATH_MAX, "%s/", dir->path);
00410 cp = path + dir->path_len + 1;
00411
00412
00413
00414
00415
00416 LIST_FOREACH(dptr, &dir->all, entries) {
00417 dptr->mask = PN_DELETE;
00418 }
00419
00420
00421
00422 rewinddir(dir->dirp);
00423 if (readdir_r(dir->dirp, &ent, &entp) != 0) {
00424 perror("readdir_r(3)");
00425 return -1;
00426 }
00427 if (strcmp(ent.d_name, ".") == 0) {
00428 if (readdir_r(dir->dirp, &ent, &entp) != 0) {
00429 perror("readdir_r(3)");
00430 return -1;
00431 }
00432 }
00433
00434
00435 for (;;) {
00436
00437
00438 if (readdir_r(dir->dirp, &ent, &entp) != 0) {
00439 perror("readdir_r(3)");
00440 return -1;
00441 }
00442
00443
00444 if (entp == NULL)
00445 break;
00446
00447
00448 found = false;
00449 LIST_FOREACH(dptr, &dir->all, entries) {
00450
00451
00452
00453
00454
00455
00456
00457 if (dptr->ent.d_fileno != ent.d_fileno)
00458 continue;
00459
00460 dprintf("old entry: %s\n", ent.d_name);
00461 dptr->mask = 0;
00462
00463 found = true;
00464 break;
00465 }
00466
00467
00468 if (!found) {
00469 dprintf("new entry: %s\n", ent.d_name);
00470
00471
00472 if ((dptr = malloc(sizeof(*dptr))) == NULL) {
00473 perror("malloc(3)");
00474 return -1;
00475 }
00476
00477
00478 memcpy(&dptr->ent, &ent, sizeof(ent));
00479 dptr->mask = PN_CREATE;
00480
00481
00482
00483 strncpy(cp, ent.d_name, NAME_MAX);
00484
00485
00486 if (stat((char *) &path, &st) < 0) {
00487 warn("stat(2) of `%s' failed", (char *) &path);
00488 return -1;
00489 }
00490
00491
00492 if (S_ISREG(st.st_mode)) {
00493 int wd;
00494
00495 wd = pnotify_watch_vnode(path, watch->mask, NULL, NULL);
00496 if (wd < 0)
00497 return -1;
00498 wtmp = pn_get_watch_by_id(wd);
00499 wtmp->parent_wd = watch->wd;
00500 }
00501
00502 LIST_INSERT_HEAD(&dir->all, dptr, entries);
00503 }
00504 }
00505
00506 return 0;
00507 }
00508
00509
00510
00514 static int
00515 kq_directory_event_handler(struct kevent kev, struct pn_watch * watch)
00516 {
00517 struct pnotify_ctx * ctx;
00518 struct pnotify_event *evt;
00519 struct dentry *dptr, *dtmp;
00520
00521 assert(watch);
00522
00523 ctx = watch->ctx;
00524
00525
00526 if (directory_scan(watch) < 0) {
00527 warn("directory_scan failed");
00528 return -1;
00529 }
00530
00531
00532 LIST_FOREACH_SAFE(dptr, &watch->dir.all, entries, dtmp) {
00533
00534
00535 if (dptr->mask == 0)
00536 continue;
00537
00538
00539 if ((evt = calloc(1, sizeof(*evt))) == NULL) {
00540 warn("malloc failed");
00541 return -1;
00542 }
00543 evt->watch = watch;
00544 evt->mask = dptr->mask;
00545 (void) strlcpy(evt->name, dptr->ent.d_name, sizeof(evt->name));
00546 dprint_event(ev);
00547
00548
00549 pn_event_add(watch->ctx, evt);
00550
00551
00552 if (dptr->mask & PN_DELETE) {
00553 LIST_REMOVE(dptr, entries);
00554 free(dptr);
00555 }
00556 }
00557
00558 return 0;
00559 }
00560
00561
00562 const struct pnotify_vtable BSD_VTABLE = {
00563 .init_once = bsd_init_once,
00564 .add_watch = bsd_add_watch,
00565 .rm_watch = bsd_rm_watch,
00566 .cleanup = bsd_cleanup,
00567 };
00568
00569 #endif