8570d032e78294e0d750d7e641727be316e57f31
[idea/community.git] / native / fsNotifier / linux / inotify.c
1 /*
2  * Copyright 2000-2012 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "fsnotifier.h"
18
19 #include <dirent.h>
20 #include <errno.h>
21 #include <linux/limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/inotify.h>
26 #include <sys/stat.h>
27 #include <syslog.h>
28 #include <unistd.h>
29
30
31 #define WATCH_COUNT_NAME "/proc/sys/fs/inotify/max_user_watches"
32
33 #define DEFAULT_SUBDIR_COUNT 5
34
35 typedef struct __watch_node {
36   char* name;
37   int wd;
38   struct __watch_node* parent;
39   array* kids;
40 } watch_node;
41
42 static int inotify_fd = -1;
43 static int watch_count = 0;
44 static table* watches;
45 static bool limit_reached = false;
46 static void (* callback)(char*, int) = NULL;
47
48 #define EVENT_SIZE (sizeof(struct inotify_event))
49 #define EVENT_BUF_LEN (2048 * (EVENT_SIZE + 16))
50 static char event_buf[EVENT_BUF_LEN];
51
52 static void read_watch_descriptors_count();
53 static void watch_limit_reached();
54
55
56 bool init_inotify() {
57   inotify_fd = inotify_init();
58   if (inotify_fd < 0) {
59     int e = errno;
60     userlog(LOG_ERR, "inotify_init: %s", strerror(e));
61     if (e == EMFILE) {
62       message(MSG_INSTANCE_LIMIT);
63     }
64     return false;
65   }
66   userlog(LOG_DEBUG, "inotify fd: %d", get_inotify_fd());
67
68   read_watch_descriptors_count();
69   if (watch_count <= 0) {
70     close(inotify_fd);
71     inotify_fd = -1;
72     return false;
73   }
74   userlog(LOG_INFO, "inotify watch descriptors: %d", watch_count);
75
76   watches = table_create(watch_count);
77   if (watches == NULL) {
78     userlog(LOG_ERR, "out of memory");
79     close(inotify_fd);
80     inotify_fd = -1;
81     return false;
82   }
83
84   return true;
85 }
86
87 static void read_watch_descriptors_count() {
88   FILE* f = fopen(WATCH_COUNT_NAME, "r");
89   if (f == NULL) {
90     userlog(LOG_ERR, "can't open %s: %s", WATCH_COUNT_NAME, strerror(errno));
91     return;
92   }
93
94   char* str = read_line(f);
95   if (str == NULL) {
96     userlog(LOG_ERR, "can't read from %s", WATCH_COUNT_NAME);
97   }
98   else {
99     watch_count = atoi(str);
100   }
101
102   fclose(f);
103 }
104
105
106 inline void set_inotify_callback(void (* _callback)(char*, int)) {
107   callback = _callback;
108 }
109
110
111 inline int get_inotify_fd() {
112   return inotify_fd;
113 }
114
115
116 #define EVENT_MASK IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF
117
118 static int add_watch(const char* path, watch_node* parent) {
119   int wd = inotify_add_watch(inotify_fd, path, EVENT_MASK);
120   if (wd < 0) {
121     if (errno == EACCES || errno == ENOENT) {
122       userlog(LOG_DEBUG, "inotify_add_watch(%s): %s", path, strerror(errno));
123       return ERR_IGNORE;
124     }
125     else if (errno == ENOSPC) {
126       userlog(LOG_WARNING, "inotify_add_watch(%s): %s", path, strerror(errno));
127       watch_limit_reached();
128       return ERR_CONTINUE;
129     }
130     else {
131       userlog(LOG_ERR, "inotify_add_watch(%s): %s", path, strerror(errno));
132       return ERR_ABORT;
133     }
134   }
135   else {
136     userlog(LOG_DEBUG, "watching %s: %d", path, wd);
137   }
138
139   watch_node* node = table_get(watches, wd);
140   if (node != NULL) {
141     if (node->wd != wd) {
142       userlog(LOG_ERR, "table error: corruption at %d:%s / %d:%s)", wd, path, node->wd, node->name);
143       return ERR_ABORT;
144     }
145     else if (strcmp(node->name, path) != 0) {
146       char buf1[PATH_MAX], buf2[PATH_MAX];
147       const char* normalized1 = realpath(node->name, buf1);
148       const char* normalized2 = realpath(path, buf2);
149       if (normalized1 == NULL || normalized2 == NULL || strcmp(normalized1, normalized2) != 0) {
150         userlog(LOG_ERR, "table error: collision at %d (new %s, existing %s)", wd, path, node->name);
151         return ERR_ABORT;
152       }
153       else {
154         userlog(LOG_INFO, "intersection at %d: (new %s, existing %s, real %s)", wd, path, node->name, normalized1);
155         return ERR_IGNORE;
156       }
157     }
158
159     return wd;
160   }
161
162   node = malloc(sizeof(watch_node));
163
164   CHECK_NULL(node, ERR_ABORT);
165   node->name = strdup(path);
166   CHECK_NULL(node->name, ERR_ABORT);
167   node->wd = wd;
168   node->parent = parent;
169   node->kids = NULL;
170
171   if (parent != NULL) {
172     if (parent->kids == NULL) {
173       parent->kids = array_create(DEFAULT_SUBDIR_COUNT);
174       CHECK_NULL(parent->kids, ERR_ABORT);
175     }
176     CHECK_NULL(array_push(parent->kids, node), ERR_ABORT);
177   }
178
179   if (table_put(watches, wd, node) == NULL) {
180     userlog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path);
181     return ERR_ABORT;
182   }
183
184   return wd;
185 }
186
187 static void watch_limit_reached() {
188   if (!limit_reached) {
189     limit_reached = true;
190     message(MSG_WATCH_LIMIT);
191   }
192 }
193
194 static void rm_watch(int wd, bool update_parent) {
195   watch_node* node = table_get(watches, wd);
196   if (node == NULL) {
197     return;
198   }
199
200   userlog(LOG_DEBUG, "unwatching %s: %d (%p)", node->name, node->wd, node);
201
202   if (inotify_rm_watch(inotify_fd, node->wd) < 0) {
203     userlog(LOG_DEBUG, "inotify_rm_watch(%d:%s): %s", node->wd, node->name, strerror(errno));
204   }
205
206   for (int i=0; i<array_size(node->kids); i++) {
207     watch_node* kid = array_get(node->kids, i);
208     if (kid != NULL) {
209       rm_watch(kid->wd, false);
210     }
211   }
212
213   if (update_parent && node->parent != NULL) {
214     for (int i=0; i<array_size(node->parent->kids); i++) {
215       if (array_get(node->parent->kids, i) == node) {
216         array_put(node->parent->kids, i, NULL);
217         break;
218       }
219     }
220   }
221
222   free(node->name);
223   array_delete(node->kids);
224   free(node);
225   table_put(watches, wd, NULL);
226 }
227
228
229 static int walk_tree(const char* path, watch_node* parent, bool recursive, array* mounts) {
230   for (int j=0; j<array_size(mounts); j++) {
231     char* mount = array_get(mounts, j);
232     if (strncmp(path, mount, strlen(mount)) == 0) {
233       userlog(LOG_DEBUG, "watch path '%s' crossed mount point '%s' - skipping", path, mount);
234       return ERR_IGNORE;
235     }
236   }
237
238   DIR* dir = NULL;
239   if (recursive) {
240     if ((dir = opendir(path)) == NULL) {
241       if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) {
242         userlog(LOG_DEBUG, "opendir(%s): %d", path, errno);
243         return ERR_IGNORE;
244       }
245       else {
246         userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno));
247         return ERR_CONTINUE;
248       }
249     }
250   }
251
252   int id = add_watch(path, parent);
253
254   if (dir == NULL) {
255     return id;
256   }
257   else if (id < 0) {
258     closedir(dir);
259     return id;
260   }
261
262   char subdir[PATH_MAX];
263   strcpy(subdir, path);
264   if (subdir[strlen(subdir) - 1] != '/') {
265     strcat(subdir, "/");
266   }
267   char* p = subdir + strlen(subdir);
268
269   struct dirent* entry;
270   while ((entry = readdir(dir)) != NULL) {
271     if (entry->d_type != DT_DIR ||
272         strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
273       continue;
274     }
275
276     strcpy(p, entry->d_name);
277
278     int subdir_id = walk_tree(subdir, table_get(watches, id), recursive, mounts);
279     if (subdir_id < 0 && subdir_id != ERR_IGNORE) {
280       rm_watch(id, true);
281       id = subdir_id;
282       break;
283     }
284   }
285
286   closedir(dir);
287   return id;
288 }
289
290
291 int watch(const char* root, array* mounts) {
292   bool recursive = true;
293   if (root[0] == '|') {
294     root++;
295     recursive = false;
296   }
297
298   struct stat st;
299   if (stat(root, &st) != 0) {
300     if (errno == EACCES) {
301       return ERR_IGNORE;
302     }
303     else if (errno == ENOENT) {
304       return ERR_CONTINUE;
305     }
306     userlog(LOG_ERR, "stat(%s): %s", root, strerror(errno));
307     return ERR_ABORT;
308   }
309
310   if (S_ISREG(st.st_mode)) {
311     recursive = false;
312   }
313   else if (!S_ISDIR(st.st_mode)) {
314     userlog(LOG_WARNING, "unexpected node type: %s, %d", root, st.st_mode);
315     return ERR_IGNORE;
316   }
317
318   return walk_tree(root, NULL, recursive, mounts);
319 }
320
321
322 void unwatch(int id) {
323   rm_watch(id, true);
324 }
325
326
327 static bool process_inotify_event(struct inotify_event* event) {
328   watch_node* node = table_get(watches, event->wd);
329   if (node == NULL) {
330     return true;
331   }
332
333   bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR;
334   userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), is_dir, node->name);
335
336   char path[PATH_MAX];
337   strcpy(path, node->name);
338   if (event->len > 0) {
339     if (path[strlen(path) - 1] != '/') {
340       strcat(path, "/");
341     }
342     strcat(path, event->name);
343   }
344
345   if (is_dir && ((event->mask & IN_CREATE) == IN_CREATE || (event->mask & IN_MOVED_TO) == IN_MOVED_TO)) {
346     int result = walk_tree(path, node, true, NULL);
347     if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) {
348       return false;
349     }
350   }
351
352   if (is_dir && ((event->mask & IN_DELETE) == IN_DELETE || (event->mask & IN_MOVED_FROM) == IN_MOVED_FROM)) {
353     for (int i=0; i<array_size(node->kids); i++) {
354       watch_node* kid = array_get(node->kids, i);
355       if (kid != NULL && strcmp(kid->name, path) == 0) {
356         rm_watch(kid->wd, false);
357         array_put(node->kids, i, NULL);
358         break;
359       }
360     }
361   }
362
363   if (callback != NULL) {
364     (*callback)(path, event->mask);
365   }
366   return true;
367 }
368
369
370 bool process_inotify_input() {
371   ssize_t len = read(inotify_fd, event_buf, EVENT_BUF_LEN);
372   if (len < 0) {
373     userlog(LOG_ERR, "read: %s", strerror(errno));
374     return false;
375   }
376
377   int i = 0;
378   while (i < len) {
379     struct inotify_event* event = (struct inotify_event*) &event_buf[i];
380     i += EVENT_SIZE + event->len;
381
382     if (event->mask & IN_IGNORED) {
383       continue;
384     }
385     if (event->mask & IN_Q_OVERFLOW) {
386       userlog(LOG_ERR, "event queue overflow");
387       continue;
388     }
389
390     if (!process_inotify_event(event)) {
391       return false;
392     }
393   }
394
395   return true;
396 }
397
398
399 void close_inotify() {
400   if (watches != NULL) {
401     table_delete(watches);
402   }
403
404   if (inotify_fd >= 0) {
405     close(inotify_fd);
406   }
407 }