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 }