1/* 2 * Copyright (c) 2009 Mark Heily <mark@heily.com> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include <errno.h> 18#include <err.h> 19#include <fcntl.h> 20#include <pthread.h> 21#include <signal.h> 22#include <stdlib.h> 23#include <stdio.h> 24#include <sys/queue.h> 25#include <sys/socket.h> 26#include <sys/stat.h> 27#include <sys/types.h> 28#include <string.h> 29#include <unistd.h> 30 31#include <limits.h> 32#include <sys/inotify.h> 33#include <sys/epoll.h> 34 35#include "sys/event.h" 36#include "private.h" 37 38static char * 39inotify_mask_dump(uint32_t mask) 40{ 41 static char __thread buf[1024]; 42 43#define INEVT_MASK_DUMP(attrib) \ 44 if (mask & attrib) \ 45 strcat(buf, #attrib" "); 46 47 snprintf(buf, sizeof(buf), "mask = %d (", mask); 48 INEVT_MASK_DUMP(IN_ACCESS); 49 INEVT_MASK_DUMP(IN_MODIFY); 50 INEVT_MASK_DUMP(IN_ATTRIB); 51 INEVT_MASK_DUMP(IN_CLOSE_WRITE); 52 INEVT_MASK_DUMP(IN_CLOSE_NOWRITE); 53 INEVT_MASK_DUMP(IN_OPEN); 54 INEVT_MASK_DUMP(IN_MOVED_FROM); 55 INEVT_MASK_DUMP(IN_MOVED_TO); 56 INEVT_MASK_DUMP(IN_CREATE); 57 INEVT_MASK_DUMP(IN_DELETE); 58 INEVT_MASK_DUMP(IN_DELETE_SELF); 59 INEVT_MASK_DUMP(IN_MOVE_SELF); 60 buf[strlen(buf) - 1] = ')'; 61 62 return (buf); 63} 64 65static char * 66inotify_event_dump(struct inotify_event *evt) 67{ 68 static char __thread buf[1024]; 69 70 snprintf(buf, sizeof(buf), "wd=%d mask=%s", 71 evt->wd, 72 inotify_mask_dump(evt->mask)); 73 74 return (buf); 75} 76 77static int 78fd_to_path(char *buf, size_t bufsz, int fd) 79{ 80 char path[1024]; //TODO: Maxpathlen, etc. 81 82 if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0) 83 return (-1); 84 85 memset(buf, 0, bufsz); 86 return (readlink(path, buf, bufsz)); 87} 88 89 90/* TODO: USE this to get events with name field */ 91int 92get_one_event(struct inotify_event *dst, int pfd) 93{ 94 ssize_t n; 95 96 dbg_puts("reading one inotify event"); 97 for (;;) { 98 n = read(pfd, dst, sizeof(*dst)); 99 if (n < 0) { 100 if (errno == EINTR) 101 continue; 102 dbg_perror("read"); 103 return (-1); 104 } else { 105 break; 106 } 107 } 108 dbg_printf("read(2) from inotify wd: %zu bytes", n); 109 110 /* FIXME-TODO: if len > 0, read(len) */ 111 if (dst->len != 0) 112 abort(); 113 114 115 return (0); 116} 117 118static int 119add_watch(struct filter *filt, struct knote *kn) 120{ 121 char path[PATH_MAX]; 122 uint32_t mask; 123 124 /* Convert the fd to a pathname */ 125 if (fd_to_path(&path[0], sizeof(path), kn->kev.ident) < 0) 126 return (-1); 127 128 /* Convert the fflags to the inotify mask */ 129 mask = 0; 130 if (kn->kev.fflags & NOTE_DELETE) 131 mask |= IN_ATTRIB | IN_DELETE_SELF; 132 if (kn->kev.fflags & NOTE_WRITE) 133 mask |= IN_MODIFY | IN_ATTRIB; 134 if (kn->kev.fflags & NOTE_EXTEND) 135 mask |= IN_MODIFY | IN_ATTRIB; 136 if ((kn->kev.fflags & NOTE_ATTRIB) || 137 (kn->kev.fflags & NOTE_LINK)) 138 mask |= IN_ATTRIB; 139 if (kn->kev.fflags & NOTE_RENAME) 140 mask |= IN_MOVE_SELF; 141 if (kn->kev.flags & EV_ONESHOT) 142 mask |= IN_ONESHOT; 143 144 dbg_printf("inotify_add_watch(2); inofd=%d, %s, path=%s", 145 filt->kf_pfd, inotify_mask_dump(mask), path); 146 kn->kev.data = inotify_add_watch(filt->kf_pfd, path, mask); 147 if (kn->kev.data < 0) { 148 dbg_printf("inotify_add_watch(2): %s", strerror(errno)); 149 return (-1); 150 } 151 return (0); 152} 153 154static int 155delete_watch(struct filter *filt, struct knote *kn) 156{ 157 if (kn->kev.data < 0) 158 return (0); 159 if (inotify_rm_watch(filt->kf_pfd, kn->kev.data) < 0) { 160 dbg_printf("inotify_rm_watch(2): %s", strerror(errno)); 161 return (-1); 162 } 163 dbg_printf("wd %d removed", (int) kn->kev.data); 164 kn->kev.data = -1; 165 166 return (0); 167} 168 169int 170evfilt_vnode_init(struct filter *filt) 171{ 172 filt->kf_pfd = inotify_init(); 173 dbg_printf("inotify fd = %d", filt->kf_pfd); 174 if (filt->kf_pfd < 0) 175 return (-1); 176 177 return (0); 178} 179 180void 181evfilt_vnode_destroy(struct filter *filt) 182{ 183 close(filt->kf_pfd); 184} 185 186int 187evfilt_vnode_copyout(struct filter *filt, 188 struct kevent *dst, 189 int nevents) 190{ 191 struct inotify_event evt; 192 struct stat sb; 193 struct knote *kn; 194 195 if (get_one_event(&evt, filt->kf_pfd) < 0) 196 return (-1); 197 198 dbg_printf("inotify event: %s", inotify_event_dump(&evt)); 199 if (evt.mask & IN_IGNORED) { 200 /* TODO: possibly return error when fs is unmounted */ 201 return (0); 202 } 203 204 kn = knote_lookup_data(filt, evt.wd); 205 if (kn == NULL) { 206 dbg_printf("no match for wd # %d", evt.wd); 207 return (-1); 208 } 209 210 memcpy(dst, &kn->kev, sizeof(*dst)); 211 dst->data = 0; 212 213 /* No error checking because fstat(2) should rarely fail */ 214 //FIXME: EINTR 215 if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY) 216 && fstat(kn->kev.ident, &sb) == 0) { 217 if (sb.st_nlink == 0 && kn->kev.fflags & NOTE_DELETE) 218 dst->fflags |= NOTE_DELETE; 219 if (sb.st_nlink != kn->data.vnode.nlink && kn->kev.fflags & NOTE_LINK) 220 dst->fflags |= NOTE_LINK; 221#if HAVE_NOTE_TRUNCATE 222 if (sb.st_nsize == 0 && kn->kev.fflags & NOTE_TRUNCATE) 223 dst->fflags |= NOTE_TRUNCATE; 224#endif 225 if (sb.st_size > kn->data.vnode.size && kn->kev.fflags & NOTE_WRITE) 226 dst->fflags |= NOTE_EXTEND; 227 kn->data.vnode.nlink = sb.st_nlink; 228 kn->data.vnode.size = sb.st_size; 229 } 230 231 if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE) 232 dst->fflags |= NOTE_WRITE; 233 if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB) 234 dst->fflags |= NOTE_ATTRIB; 235 if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME) 236 dst->fflags |= NOTE_RENAME; 237 if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE) 238 dst->fflags |= NOTE_DELETE; 239 240 if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE) 241 dst->fflags |= NOTE_WRITE; 242 if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB) 243 dst->fflags |= NOTE_ATTRIB; 244 if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME) 245 dst->fflags |= NOTE_RENAME; 246 if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE) 247 dst->fflags |= NOTE_DELETE; 248 249 if (kn->kev.flags & EV_DISPATCH) { 250 delete_watch(filt, kn); /* TODO: error checking */ 251 KNOTE_DISABLE(kn); 252 } else if (kn->kev.flags & EV_ONESHOT) { 253 delete_watch(filt, kn); /* TODO: error checking */ 254 knote_free(filt, kn); 255 } 256 257 return (1); 258} 259 260int 261evfilt_vnode_knote_create(struct filter *filt, struct knote *kn) 262{ 263 struct stat sb; 264 265 if (fstat(kn->kev.ident, &sb) < 0) { 266 dbg_puts("fstat failed"); 267 return (-1); 268 } 269 kn->data.vnode.nlink = sb.st_nlink; 270 kn->data.vnode.size = sb.st_size; 271 kn->kev.data = -1; 272 273 return (add_watch(filt, kn)); 274} 275 276int 277evfilt_vnode_knote_modify(struct filter *filt, struct knote *kn, 278 const struct kevent *kev) 279{ 280 return (-1); /* FIXME - STUB */ 281} 282 283int 284evfilt_vnode_knote_delete(struct filter *filt, struct knote *kn) 285{ 286 return delete_watch(filt, kn); 287} 288 289int 290evfilt_vnode_knote_enable(struct filter *filt, struct knote *kn) 291{ 292 return add_watch(filt, kn); 293} 294 295int 296evfilt_vnode_knote_disable(struct filter *filt, struct knote *kn) 297{ 298 return delete_watch(filt, kn); 299} 300 301const struct filter evfilt_vnode = { 302 EVFILT_VNODE, 303 evfilt_vnode_init, 304 evfilt_vnode_destroy, 305 evfilt_vnode_copyout, 306 evfilt_vnode_knote_create, 307 evfilt_vnode_knote_modify, 308 evfilt_vnode_knote_delete, 309 evfilt_vnode_knote_enable, 310 evfilt_vnode_knote_disable, 311}; 312