Exemplo n.º 1
0
	int
	main(int argc, char *argv[])
{
	int inotifyFd, wd, j;
	char buf[BUF_LEN];
	ssize_t numRead;
	char *p;
	struct inotify_event *event;
	if (argc < 2 || strcmp(argv[1], "--help") == 0)
		usageErr("%s pathname... \n", argv[0]);
	q inotifyFd = inotify_init(); /* Create inotify instance */
	if (inotifyFd == -1)
		errExit("inotify_init");
	for (j = 1; j < argc; j++) {
		w wd = inotify_add_watch(inotifyFd, argv[j], IN_ALL_EVENTS);
		if (wd == -1)
			errExit("inotify_add_watch");
		printf("Watching %s using wd %d\n", argv[j], wd);
	}
	for (;;) { /* Read events forever */
		e numRead = read(inotifyFd, buf, BUF_LEN);
		if (numRead == 0)
			fatal("read() from inotify fd returned 0!");
		if (numRead == -1)
			errExit("read");
		printf("Read %ld bytes from inotify fd\n", (long) numRead);
		/* Process all of the events in buffer returned by read() */
		for (p = buf; p < buf + numRead; ) {
			event = (struct inotify_event *) p;
			r displayInotifyEvent(event);
			p += sizeof(struct inotify_event) + event->len;
		}
	}
	exit(EXIT_SUCCESS);
}
Exemplo n.º 2
0
int main(int argc,char *argv[])
{
    int inotifyFd,wd,j;
    char buf[BUF_LEN];
    ssize_t numRead;
    char *p;
    struct inotify_event *event;
    //Setp1 用户输入参数处理
    if(argc < 2 || strcmp(argv[1],"--help") == 0) {
        fprintf(stderr,"%s pathname....\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    //Setp2 初始化inotify Fd
    inotifyFd = inotify_init();
    if(inotifyFd == -1) {
        perror("init inotifyFd");
        exit(EXIT_FAILURE);
    }
    //把所有的参数中指定的文件添加到监控列表中
    for(j = 1;j < argc;++j) {
        wd = inotify_add_watch(inotifyFd,argv[j],IN_ALL_EVENTS);
        if(wd == -1) {
            perror("inotify_add_watch");
            exit(EXIT_FAILURE);
        }
        printf("Watching %s using wd %d\n",argv[j],wd);
    }


    //循环读取fd产生的事件
    for(;;) {
        numRead = read(inotifyFd,buf,BUF_LEN);

        if(numRead == 0) {
            fprintf(stderr,"read() from inotify fd returned 0!");
            exit(EXIT_FAILURE);
        }

        if(numRead == -1) {
            fprintf(stderr,"read");
            exit(EXIT_FAILURE);
        }

        printf("Read %ld bytes from inotify fd \n",(long)numRead);

        //显示读取到的事件,每次buf可能包含多个inotify_event,因此需要移动指针
        for(p = buf; p < buf + numRead;) {
            event = (struct inotify_event*)p;
            displayInotifyEvent(event);
            p += sizeof(struct inotify_event) + event->len;
        }
    }

    exit(EXIT_SUCCESS);
}
Exemplo n.º 3
0
static size_t
processNextInotifyEvent(int *inotifyFd, char *buf, int bufSize, int firstTry)
{
    char fullPath[PATH_MAX];
    struct inotify_event *ev;
    size_t evLen;
    int evCacheSlot;

    ev = (struct inotify_event *) buf;

    displayInotifyEvent(ev);

    if (ev->wd != -1 && !(ev->mask & IN_IGNORED)) {

                /* IN_Q_OVERFLOW has (ev->wd == -1) */
                /* Skip IN_IGNORED, since it will come after an event
                   that has already zapped the corresponding cache entry */

        /* Cache consistency check; see the discussion
           of "intra-tree" rename() events */

        evCacheSlot = findWatchChecked(ev->wd);
        if (evCacheSlot == -1) {

           /* Cache reached an inconsistent state */

           *inotifyFd = reinitialize(*inotifyFd);

           /* Discard all remaining events in current read() buffer */

           return INOTIFY_READ_BUF_LEN;
        }
    }

    evLen = sizeof(struct inotify_event) + ev->len;

    if ((ev->mask & IN_ISDIR) &&
            (ev->mask & (IN_CREATE | IN_MOVED_TO))) {

        /* A new subdirectory was created, or a subdirectory was
           renamed into the tree; create watches for it, and all
           of its subdirectories. */

        snprintf(fullPath, sizeof(fullPath), "%s/%s",
                 wlCache[evCacheSlot].path, ev->name);

        logMessage(VB_BASIC, "Directory creation on wd %d: %s\n",
                ev->wd, fullPath);

        /* We only watch the new subtree if it has not already been cached.

           This deals with a race condition:
           * On the one hand, the following steps might occur:
               1. The "child" directory is created.
               2. The "grandchild" directory is created
               3. We receive an IN_CREATE event for the creation of the
                  "child" and create a watch and a cache entry for it.
               4. To handle the possibility that step 2 came before
                  step 3, we recursively walk through the descendants of
                  the "child" directory, adding any subdirectories to
                  the cache.
           * On the other hand, the following steps might occur:
               1. The "child" directory is created.
               3. We receive an IN_CREATE event for the creation of the
                  "child" and create a watch and a cache entry for it.
               3. The "grandchild" directory is created
               4. During the recursive walk through the descendants of
                  the "child" directory, we cache the "grandchild" and
                  add a watch for it.
               5. We receive the IN_CREATE event for the creation of
                  the "grandchild". At this point, we should NOT create
                  a cache entry and watch for the "grandchild" because
                  they already exist. (Creating the watch for the
                  second time is harmless, but adding a second cache
                  for the grandchild would leave the cache in a confused
                  state.) */

        if (!pathnameInCache(fullPath))
            watchSubtree(*inotifyFd, fullPath);

    } else if (ev->mask & IN_DELETE_SELF) {

        /* A directory was deleted. Remove the corresponding item from
           the cache. */

        logMessage(VB_BASIC, "Clearing watchlist item %d (%s)\n",
                   ev->wd, wlCache[evCacheSlot].path);

        if (isRootDirPath(wlCache[evCacheSlot].path))
            zapRootDirPath(wlCache[evCacheSlot].path);

        markCacheSlotEmpty(evCacheSlot);
            /* No need to remove the watch; that happens automatically */

    } else if ((ev->mask & (IN_MOVED_FROM | IN_ISDIR)) ==
               (IN_MOVED_FROM | IN_ISDIR)) {

        /* We have a "moved from" event. To know how to deal with it, we
           need to determine whether there is a following "moved to"
           event with a matching cookie value (i.e., an "intra-tree"
           rename() where the source and destination are inside our
           monitored trees).  If there is not, then we are dealing
           with a rename() out of our monitored tree(s).

           We assume that if this is an "intra-tree" rename() event, then
           the "moved to" event is the next event in the buffer returned
           by the current read(). (If we are already at the last event in
           this buffer, then we ask our caller to read a bit more, in
           the hope of getting the following IN_MOVED_TO event in the
           next read().)

           In most cases, the assumption holds. However, where multiple
           processes are manipulating the tree, we can can get event
           sequences such as the following:

                 IN_MOVED_FROM (rename(x) by process A)
                         IN_MOVED_FROM (rename(y) by process B)
                         IN_MOVED_TO   (rename(y) by process B)
                 IN_MOVED_TO   (rename(x) by process A)

           In principle, there may be arbitrarily complex variations on
           the above theme. Our assumption that related IN_MOVED_FROM
           and IN_MOVED_TO events are consecutive is broken by such
           scenarios.

           We could try to resolve this issue by extending the window
           we use to search for IN_MOVED_TO events beyond the next item
           in the queue. But this must be done heuristically (e.g.,
           limiting the window to N events or to events read within
           X milliseconds), because sometimes we will have an unmatched
           IN_MOVED_FROM events that result from out-of-tree renames.
           The heuristic approach is therefore unavoidably racy: there
           is always a chance that we will fail to match up an
           IN_MOVED_FROM+IN_MOVED_TO event pair.

           So, this program takes the simple approach of assuming
           that an IN_MOVED_FROM+IN_MOVED_TO pair occupy consecutive
           events in the buffer returned by read().

           When that assumption is wrong (and we therefore fail
           to recognize an intra-tree rename() event), then
           the rename will be treated as separate "moved from" and
           "moved to" events, with the result that some watch items
           and cache entries are zapped and re-created. This causes
           the watch descriptors in our cache to become inconsistent
           with the watch descriptors in as yet unread events,
           because the watches are re-created with different watch
           descriptor numbers.

           Once such an inconsistency occurs, then, at some later point,
           we will do a lookup for a watch descriptor returned by
           inotify, and find that it is not in our cache. When that
           happens, we reinitialize our cache with a fresh set of watch
           descriptors and re-create the inotify file descriptor, in
           order to bring our cache back into consistency with the
           filesystem. An alternative would be to cache the cookies of
           the (recent) IN_MOVED_FROM events for which which we did not
           find a matching IN_MOVED_TO event, and rebuild our watch
           cache when we find an IN_MOVED_TO event whose cookie matches
           one of the cached cookies. Yet another approach when we
           detect an out-of-tree rename would be to reinitialize the
           cache and create a new inotify file descriptor.
           (TODO: consider the fact that for a rename event, there
           won't be other events for the object between IN_MOVED_FROM
           and IN_MOVED_TO.)

           Rebuilding the watch cache is expensive if the monitored
           tree is large. So, there is a trade-off between how much
           effort we want to go to to avoid cache rebuilds versus
           how much effort we want to devote to matching up
           IN_MOVED_FROM+IN_MOVED_TO event pairs. At the one extreme
           we would do no search ahead for IN_MOVED_TO, with the result
           that every rename() potentially could trigger a cache
           rebuild. Limiting the search window to just the following
           event is a compromise that catches the vast majority of
           intra-tree renames and triggers relatively few cache rebuilds.
         */

        struct inotify_event *nextEv;

        nextEv = (struct inotify_event *) (buf + evLen);

        if (((char *) nextEv < buf + bufSize) &&
                (nextEv->mask & IN_MOVED_TO) &&
                (nextEv->cookie == ev->cookie)) {

            int nextEvCacheSlot;

            /* We have a rename() event. We need to fix up the
               cached pathnames for the corresponding directory
               and all of its subdirectories. */

            nextEvCacheSlot = findWatchChecked(nextEv->wd);

            if (nextEvCacheSlot == -1) {

                /* Cache reached an inconsistent state */

                *inotifyFd = reinitialize(*inotifyFd);

                /* Discard all remaining events in current read() buffer */

                return INOTIFY_READ_BUF_LEN;
            }

            rewriteCachedPaths(wlCache[evCacheSlot].path, ev->name,
                               wlCache[nextEvCacheSlot].path, nextEv->name);

            /* We have also processed the next (IN_MOVED_TO) event,
               so skip over it */

            evLen += sizeof(struct inotify_event) + nextEv->len;

        } else if (((char *) nextEv < buf + bufSize) || !firstTry) {

            /* We got a "moved from" event without an accompanying
               "moved to" event. The directory has been moved
               outside the tree we are monitoring. We need to
               remove the watches and zap the cache entries for
               the moved directory and all of its subdirectories. */

            logMessage(VB_NOISY, "MOVED_OUT: %p %p\n",
                    wlCache[evCacheSlot].path, ev->name);
            logMessage(VB_NOISY, "firstTry = %d; remaining bytes = %d\n",
                    firstTry, buf + bufSize - (char *) nextEv);
            snprintf(fullPath, sizeof(fullPath), "%s/%s",
                     wlCache[evCacheSlot].path, ev->name);

            if (zapSubtree(*inotifyFd, fullPath) == -1) {

                /* Cache reached an inconsistent state */

                *inotifyFd = reinitialize(*inotifyFd);

                /* Discard all remaining events in current read() buffer */

                return INOTIFY_READ_BUF_LEN;
            }

        } else {
            logMessage(VB_NOISY, "HANGING IN_MOVED_FROM\n");

            return -1;  /* Tell our caller to do another read() */
        }

    } else if (ev->mask & IN_Q_OVERFLOW) {

        static int overflowCnt = 0;

        overflowCnt++;

        logMessage(0, "Queue overflow (%d) (inotifyReadCnt = %d)\n",
                    overflowCnt, inotifyReadCnt);

        /* When the queue overflows, some events are lost, at which
           point we've lost any chance of keeping our cache consistent
           with the state of the filesystem. So, discard this inotify
           file descriptor and create a new one, and zap and rebuild
           the cache. */

        *inotifyFd = reinitialize(*inotifyFd);

        /* Discard all remaining events in current read() buffer */

        evLen = INOTIFY_READ_BUF_LEN;

    } else if (ev->mask & IN_UNMOUNT) {

        /* When a filesystem is unmounted, each of the watches on the
           is dropped, and an unmount and an ignore event are generated.
           There's nothing left for us to monitor, so we just zap the
           corresponding cache entry. */

        logMessage(0, "Filesystem unmounted: %s\n",
                wlCache[evCacheSlot].path);

        markCacheSlotEmpty(evCacheSlot);
            /* No need to remove the watch; that happens automatically */

    } else if (ev->mask & IN_MOVE_SELF &&
            isRootDirPath(wlCache[evCacheSlot].path)) {

        /* If the root path moves to a new location in the same
           filesystem, then all cached pathnames become invalid, and we
           have no direct way of knowing the new name of the root path.
           We could in theory find the new name by caching the i-node of
           the root path on start-up and then trying to find a pathname
           that corresponds to that i-node. Instead, we'll keep things
           simple, and just cease monitoring it. */

        logMessage(0, "Root path moved: %s\n",
                    wlCache[evCacheSlot].path);

        zapRootDirPath(wlCache[evCacheSlot].path);

        if (zapSubtree(*inotifyFd, wlCache[evCacheSlot].path) == -1) {

            /* Cache reached an inconsistent state */

            *inotifyFd = reinitialize(*inotifyFd);

            /* Discard all remaining events in current read() buffer */

            return INOTIFY_READ_BUF_LEN;
        }
    }

    if (checkCache)
        checkCacheConsistency();

    if (dumpCache)
        dumpCacheToLog();

    return evLen;
}
Exemplo n.º 4
0
int
main(int argc, char *argv[])
{
    int inotifyFd, childFd, maxFd, wd, j, rst;
    fd_set      fds;
    struct timeval timeout;
    (void)j;
    char buf[BUF_LEN] __attribute__ ((aligned(8)));
    ssize_t numRead;
    char *p;
    struct inotify_event *event;

    if (argc < 3 || strcmp(argv[1], "--help") == 0)
        error_exit("%s pathname childname\n", argv[0]);

    inotifyFd = inotify_init();                 /* Create inotify instance */
    if (inotifyFd == -1)
        error_exit("inotify_init");

    childFd = inotify_init();
    if (childFd == -1)
        error_exit("inotify_init");
    maxFd = inotifyFd > childFd ? inotifyFd : childFd;
    printf("Parent:%d, childFd:%d, maxFd:%d\n", 
            inotifyFd, childFd, maxFd);

    /*for (j = 1; j < argc; j++) {*/
        /*wd = inotify_add_watch(inotifyFd, argv[j], IN_ALL_EVENTS);*/
        /*if (wd == -1)*/
            /*error_exit("inotify_add_watch");*/

        /*printf("Watching %s using wd %d\n", argv[j], wd);*/
    /*}*/
    wd = inotify_add_watch(inotifyFd, argv[1], PARENT_EVENTS);
    if (wd == -1) {                             /* parent inotify */
        error_exit("Parent directory(inotify_add_watch) failed");
    }
    wd = inotify_add_watch(childFd, argv[2], CHILD_EVENTS);
    if (wd == -1) {                             /* children inotify */
        error_exit("Children directory(inotify_add_watch) failed");
    }

    for (;;) {                                  /* Read events forever */
        FD_ZERO(&fds);               
        FD_SET(inotifyFd, &fds);        
        FD_SET(childFd, &fds);        
        timeout.tv_sec = 2; 
        timeout.tv_usec = 0;

        rst = select(maxFd+1, &fds, NULL, NULL, &timeout);
        if (rst <= 0) {
            continue;
        }

        if (FD_ISSET(childFd, &fds)) {
            numRead = read(childFd, buf, BUF_LEN);
            if (numRead == 0)
                error_exit("read() from inotify fd returned 0!");

            if (numRead == -1)
                error_exit("read");
            printf("Read %ld bytes from children inotify fd\n", 
                    (long) numRead);
        } else {
            numRead = read(inotifyFd, buf, BUF_LEN);
            if (numRead == 0)
                error_exit("read() from inotify fd returned 0!");

            if (numRead == -1)
                error_exit("read");
            printf("Read %ld bytes from parent inotify fd\n", 
                    (long) numRead);
        }

        /* Process all of the events in buffer returned by read() */

        for (p = buf; p < buf + numRead; ) {
            event = (struct inotify_event *) p;
            displayInotifyEvent(event);

            p += sizeof(struct inotify_event) + event->len;
        }
    }

    exit(EXIT_SUCCESS);
}