linux.c

Go to the documentation of this file.
00001 /*              $Id: $          */
00002 
00003 /*
00004  * Copyright (c) 2007 Mark Heily <devel@heily.com>
00005  *
00006  * Permission to use, copy, modify, and distribute this software for any
00007  * purpose with or without fee is hereby granted, provided that the above
00008  * copyright notice and this permission notice appear in all copies.
00009  *
00010  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00011  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00012  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00013  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00014  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00015  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00016  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00017  */
00018 
00019 #include "config.h"
00020 #include "pnotify.h"
00021 #include "pnotify-internal.h"
00022 
00029 #if HAVE_INOTIFY
00030 
00031 #include <sys/epoll.h>
00032 #include <sys/inotify.h>
00033 
00034 static int INOTIFY_FD = -1;
00035 static int EPOLL_FD = -1;
00036 
00037 int _get_inotify_event(struct pnotify_event *evt, struct pnotify_ctx *ctx);
00038 
00039 void linux_dump_inotify_event(struct inotify_event *iev);
00040 
00041 
00042 void *
00043 linux_inotify_loop(void * unused)
00044 {
00045         struct pn_watch *watch;
00046         struct pnotify_event *evt;
00047         struct inotify_event *iev, *endp;
00048         ssize_t         bytes;
00049         char            buf[4096];
00050 
00051         /* Avoid a compiler warning */
00052         watch = unused;
00053 
00054         /* Create an inotify descriptor */
00055         if ((INOTIFY_FD = inotify_init()) < 0)
00056                 err(1, "inotify_init(2)");
00057 
00058         /* Loop forever waiting for events */
00059         for (;;) {
00060 
00061                 /* 
00062                  * Wait for an event and read it into a buffer.
00063                  * This may block, so release the mutex.
00064                  */
00065                 bytes = read(INOTIFY_FD, &buf, sizeof(buf));
00066                 if (bytes <= 0) 
00067                         err(1, "read(2)");
00068 
00069                 /* Compute the beginning and end of the event list */
00070                 iev = (struct inotify_event *) & buf;
00071                 endp = iev + bytes;
00072 
00073                 /* Process each pending event */
00074                 while (iev < endp) {
00075 
00076                         if (iev->wd == 0)
00077                                 break;
00078 
00079                         /* We don't care about IN_IGNORED events */
00080                         if (iev->mask & IN_IGNORED)
00081                                 goto next_event;
00082                                 
00083                         /* Find the matching watch structure */
00084                         /* FIXME: This may be a normal occurrance
00085                          * when a watch is removed when there are
00086                          * still events pending.. ?
00087                          */
00088                         if ((watch = pn_get_watch_by_id(iev->wd)) == NULL) {
00089                                 warnx("watch # %d not found\n", iev->wd);       
00090                                 linux_dump_inotify_event(iev);
00091                                 continue;
00092                         }
00093 
00094                         /* Construct a pnotify_event structure */
00095                         if ((evt = calloc(1, sizeof(*evt))) == NULL) 
00096                                 err(1, "malloc failed");
00097 
00098                         evt->watch = watch;
00099                         (void) strncpy(evt->name, iev->name, iev->len);
00100                         if (iev->mask & IN_ATTRIB)
00101                                 evt->mask |= PN_ATTRIB;
00102                         if (iev->mask & IN_MODIFY)
00103                                 evt->mask |= PN_MODIFY;
00104                         if (iev->mask & IN_CREATE)
00105                                 evt->mask |= PN_CREATE;
00106                         if (iev->mask & IN_DELETE)
00107                                 evt->mask |= PN_DELETE;
00108                         if (iev->mask & IN_DELETE_SELF) {
00109                                 evt->mask |= PN_DELETE;
00110                                 (void) strncpy(evt->name, "", 0);
00111                         }
00112 
00113                         /* Add the event to the list of pending events */
00114                         pn_event_add(watch->ctx, evt);
00115 
00116 next_event:
00117                         /* Go to the next event */
00118                         iev += sizeof(*iev) + iev->len;
00119                 }
00120         }
00121 
00122         close(INOTIFY_FD);
00123         return NULL;
00124 }
00125 
00126 
00127 void *
00128 linux_epoll_loop(void * unused)
00129 {
00130         static const int maxevents = 100;
00131         struct pnotify_event *evt;
00132         struct pn_watch *watch;
00133         struct epoll_event events[maxevents];
00134         int i, numevents;
00135 
00136         /* Avoid a compiler warning */
00137         watch = unused;
00138 
00139         /* Create an epoll descriptor */
00140         if ((EPOLL_FD = epoll_create(1000)) < 0)
00141                 err(1, "epoll_create(2)");
00142 
00143         /* Loop forever waiting for events */
00144         for (;;) {
00145 
00146                 /* Wait for an event */
00147                 numevents = epoll_wait(EPOLL_FD, 
00148                                 (struct epoll_event *) &events, maxevents, -1);
00149                 if (numevents < 0) {
00150                         if (errno == EINTR)
00151                                 continue;
00152                         err(1, "epoll_wait(2)");
00153                 }
00154 
00155                 /* Convert each epoll event into a pnotify event */
00156                 for (i = 0; i < numevents; i++) {
00157 
00158                         watch = (struct pn_watch *) events[i].data.ptr; 
00159 
00160 #if DEAD
00161                         /* Get the delivery context, or ignore the signal */
00162                         pthread_mutex_lock(&SIGNAL_CTX_MUTEX);
00163                         ctx = SIGNAL_CTX[signum];
00164                         pthread_mutex_unlock(&SIGNAL_CTX_MUTEX);
00165                         if (!ctx) {
00166                                 default_signal_handler(signum);
00167                                 continue;
00168                         }
00169 #endif
00170 
00171                         /* Create a new event structure */
00172                         if ((evt = calloc(1, sizeof(*evt))) == NULL)
00173                                 err(1, "calloc(3)");
00174                         evt->watch = watch;
00175                         evt->mask = 0;
00176                         if (events[i].events & EPOLLIN)
00177                                 evt->mask |= PN_READ;
00178                         if (events[i].events & EPOLLOUT)
00179                                 evt->mask |= PN_WRITE;
00180                         if (events[i].events & EPOLLHUP)
00181                                 evt->mask |= PN_CLOSE;
00182                         if (events[i].events & EPOLLERR)
00183                                 evt->mask |= PN_ERROR;
00184 
00185                         /* Add the event to an event queue */
00186                         pn_event_add(watch->ctx, evt);
00187                 }
00188         }
00189 
00190         close(EPOLL_FD);
00191         return NULL;
00192 }
00193 
00194 
00195 void
00196 linux_init_once(void)
00197 {
00198         pthread_t tid;
00199 
00200         /* Create a dedicated epoll thread */
00201         if (pthread_create( &tid, NULL, linux_epoll_loop, NULL ) != 0)
00202                 errx(1, "pthread_create(3) failed");
00203 
00204         /* Create a dedicated inotify thread */
00205         if (pthread_create( &tid, NULL, linux_inotify_loop, NULL ) != 0)
00206                 errx(1, "pthread_create(3) failed");
00207 
00208         /* TODO: push cleanup function */
00209 }
00210 
00211 
00212 void
00213 linux_cleanup(void)
00214 {
00215         (void) close(INOTIFY_FD);
00216 }
00217 
00218 
00219 int
00220 linux_add_watch(struct pn_watch *watch)
00221 {
00222         struct epoll_event *ev = &watch->epoll_evt;
00223         int mask = watch->mask;
00224         uint32_t        imask = 0;
00225 
00226         switch (watch->type) {
00227 
00228                 case WATCH_FD:
00229                         /* Generate the epoll_event structure */
00230                         ev->events = EPOLLET;
00231                         if (mask & PN_READ)
00232                                 ev->events |= EPOLLIN;
00233                         if (mask & PN_WRITE)
00234                                 ev->events |= EPOLLOUT;
00235                         ev->data.ptr = watch;
00236 
00237                         /* Add the epoll_event structure to the kernel queue */
00238                         if (epoll_ctl(EPOLL_FD, EPOLL_CTL_ADD, watch->ident.fd, ev) < 0) {
00239                                 warn("epoll_ctl(2) failed");
00240                                 return -1;
00241                         }
00242                         break;
00243 
00244                 case WATCH_VNODE:
00245                         /* Generate the mask */
00246                         if (mask & PN_ATTRIB)
00247                                 imask |= IN_ATTRIB;
00248                         if (mask & PN_CREATE)
00249                                 imask |= IN_CREATE;
00250                         if (mask & PN_DELETE)
00251                                 imask |= IN_DELETE | IN_DELETE_SELF;
00252                         if (mask & PN_MODIFY)
00253                                 imask |= IN_MODIFY;
00254                         if (mask & PN_ONESHOT)
00255                                 imask |= IN_ONESHOT;
00256 
00257                         /* Add the event to the kernel event queue */
00258                         /* XXX-FIXME this overwrites watch->wd as assigned earlier! */
00259                         watch->wd = inotify_add_watch(INOTIFY_FD, watch->ident.path, imask);
00260                         if (watch->wd < 0) {
00261                                 perror("inotify_add_watch(2) failed");
00262                                 return -1;
00263                         }
00264                         break;
00265 
00266                 default:
00267                         /* The default action is to do nothing. */
00268                         break;
00269         }
00270 
00271         return 0;
00272 }
00273 
00274 int
00275 linux_rm_watch(struct pn_watch *watch)
00276 {
00277         if (inotify_rm_watch(INOTIFY_FD, watch->wd) < 0) {
00278                 perror("inotify_rm_watch(2)");
00279                 return -1;
00280         }
00281 
00282         return 0;
00283 }
00284 
00285 int
00286 _get_inotify_event(struct pnotify_event *evt, struct pnotify_ctx *ctx)
00287 {
00288 
00289 
00290         return 0;
00291 }
00292 
00293 int
00294 linux_trap_signal(struct pnotify_ctx *ctx, int signum)
00295 {
00296         /* Linux does not have a kernel mechanism for
00297          * converting signals to events but if it did,
00298          * this would be the place to activate it.
00299          */
00300         assert(ctx && signum);
00301         return 0;
00302 }
00303 
00304 void
00305 linux_dump_inotify_event(struct inotify_event *iev)
00306 {
00307         static const char *nam[] = {
00308                 "IN_ACCESS", "IN_MODIFY", "IN_ATTRIB", "IN_CLOSE_WRITE",
00309                 "IN_CLOSE_NOWRITE", "IN_OPEN", "IN_MOVED_FROM",
00310                 "IN_MOVED_TO", "IN_CREATE", "IN_DELETE", "IN_DELETE_SELF",
00311                 "IN_MOVE_SELF", "IN_UNMOUNT", "IN_Q_OVERFLOW", "IN_IGNORED",
00312                 "IN_ONLYDIR", "IN_DONT_FOLLOW", "IN_MASK_ADD", "IN_ISDIR",
00313                 "IN_ONESHOT", NULL };
00314         static const int val[] = {
00315                 IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE,
00316                 IN_CLOSE_NOWRITE, IN_OPEN, IN_MOVED_FROM,
00317                 IN_MOVED_TO, IN_CREATE, IN_DELETE, IN_DELETE_SELF,
00318                 IN_MOVE_SELF, IN_UNMOUNT, IN_Q_OVERFLOW, IN_IGNORED,
00319                 IN_ONLYDIR, IN_DONT_FOLLOW, IN_MASK_ADD, IN_ISDIR,
00320                 IN_ONESHOT, 0 };
00321         int i;
00322 
00323         fprintf(stderr, "inotify event: wd=%d mask=", iev->wd);
00324         for (i = 0; val[i] != 0; i++) {
00325                 if (iev->mask & val[i])
00326                         fprintf(stderr, "%s ", nam[i]);
00327         }
00328         fprintf(stderr, "\n");
00329 }
00330 
00331 const struct pnotify_vtable LINUX_VTABLE = {
00332         .init_once = linux_init_once,
00333         .add_watch = linux_add_watch,
00334         .rm_watch = linux_rm_watch,
00335         .cleanup = linux_cleanup,
00336 };
00337 
00338 #endif
00339 

Generated on Wed Aug 22 23:15:42 2007 for pnotify by  doxygen 1.5.1