1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
  14  */
  15 
  16 #include <sys/inotify.h>
  17 #include <sys/stat.h>
  18 #include <unistd.h>
  19 #include <errno.h>
  20 #include <fcntl.h>
  21 #include <strings.h>
  22 #include <dirent.h>
  23 
  24 int
  25 inotify_init()
  26 {
  27         return (open("/dev/inotify", O_RDWR));
  28 }
  29 
  30 int
  31 inotify_init1(int flags)
  32 {
  33         int oflags = O_RDWR;
  34 
  35         if (flags & IN_NONBLOCK)
  36                 oflags |= O_NONBLOCK;
  37 
  38         if (flags & IN_CLOEXEC)
  39                 oflags |= O_CLOEXEC;
  40 
  41         return (open("/dev/inotify", oflags));
  42 }
  43 
  44 int
  45 inotify_add_watch(int fd, const char *pathname, uint32_t mask)
  46 {
  47         inotify_addwatch_t ioc;
  48         inotify_addchild_t cioc;
  49         struct stat buf;
  50         int dirfd, wd;
  51         DIR *dir;
  52         struct dirent *dp;
  53         int oflags = O_RDONLY;
  54 
  55         if (mask & IN_DONT_FOLLOW)
  56                 oflags |= O_NOFOLLOW;
  57 
  58         if ((dirfd = open(pathname, oflags)) < 0)
  59                 return (-1);
  60 
  61         if (fstat(dirfd, &buf) != 0) {
  62                 (void) close(dirfd);
  63                 return (-1);
  64         }
  65 
  66         if ((mask & IN_ONLYDIR) && !(buf.st_mode & S_IFDIR)) {
  67                 (void) close(dirfd);
  68                 errno = ENOTDIR;
  69                 return (-1);
  70         }
  71 
  72         bzero(&ioc, sizeof (ioc));
  73         ioc.inaw_fd = dirfd;
  74         ioc.inaw_mask = mask;
  75 
  76         if ((wd = ioctl(fd, INOTIFYIOC_ADD_WATCH, &ioc)) < 0) {
  77                 (void) close(dirfd);
  78                 return (-1);
  79         }
  80 
  81         if (!(buf.st_mode & S_IFDIR) || !(mask & IN_CHILD_EVENTS)) {
  82                 (void) close(dirfd);
  83                 (void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd);
  84                 return (wd);
  85         }
  86 
  87         /*
  88          * If we have a directory and we have a mask that denotes child events,
  89          * we need to manually add a child watch to every directory entry.
  90          * (Because our watch is in place, it will automatically be added to
  91          * files that are newly created after this point.)
  92          */
  93         if ((dir = fdopendir(dirfd)) == NULL) {
  94                 (void) inotify_rm_watch(fd, wd);
  95                 (void) close(dirfd);
  96                 return (-1);
  97         }
  98 
  99         bzero(&cioc, sizeof (cioc));
 100         cioc.inac_fd = dirfd;
 101 
 102         while ((dp = readdir(dir)) != NULL) {
 103                 if (strcmp(dp->d_name, ".") == 0)
 104                         continue;
 105 
 106                 if (strcmp(dp->d_name, "..") == 0)
 107                         continue;
 108 
 109                 cioc.inac_name = dp->d_name;
 110 
 111                 if (ioctl(fd, INOTIFYIOC_ADD_CHILD, &cioc) != 0) {
 112                         /*
 113                          * If we get an error that indicates clear internal
 114                          * malfunctioning, we propagate the error.  Otherwise
 115                          * we eat it:  this could be a file that no longer
 116                          * exists or a symlink or something else that we
 117                          * can't lookup.
 118                          */
 119                         switch (errno) {
 120                         case ENXIO:
 121                         case EFAULT:
 122                         case EBADF:
 123                                 (void) closedir(dir);
 124                                 (void) inotify_rm_watch(fd, wd);
 125                                 return (-1);
 126                         default:
 127                                 break;
 128                         }
 129                 }
 130         }
 131 
 132         (void) closedir(dir);
 133         (void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd);
 134 
 135         return (wd);
 136 }
 137 
 138 int
 139 inotify_rm_watch(int fd, int wd)
 140 {
 141         return (ioctl(fd, INOTIFYIOC_RM_WATCH, wd));
 142 }