ab605424d66c9e504fc5244e8161de74d1d8a8e4
[idea/community.git] / native / fsNotifier / linux / inotify.c
1 /*
2  * Copyright 2000-2011 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 #define CHECK_NULL(p) if (p == NULL)  { userlog(LOG_ERR, "out of memory"); return ERR_ABORT; }
36
37 typedef struct __watch_node {
38   char* name;
39   int wd;
40   struct __watch_node* parent;
41   array* kids;
42 } watch_node;
43
44 static int inotify_fd = -1;
45 static int watch_count = 0;
46 static table* watches;
47 static bool limit_reached = false;
48 static void (* callback)(char*, int) = NULL;
49
50 #define EVENT_SIZE (sizeof(struct inotify_event))
51 #define EVENT_BUF_LEN (2048 * (EVENT_SIZE + 16))
52 static char event_buf[EVENT_BUF_LEN];
53
54
55 static void read_watch_descriptors_count() {
56   FILE* f = fopen(WATCH_COUNT_NAME, "r");
57   if (f == NULL) {
58     userlog(LOG_ERR, "can't open %s: %s", WATCH_COUNT_NAME, strerror(errno));
59     return;
60   }
61
62   char* str = read_line(f);
63   if (str == NULL) {
64     userlog(LOG_ERR, "can't read from %s", WATCH_COUNT_NAME);
65   }
66   else {
67     watch_count = atoi(str);
68   }
69
70   fclose(f);
71 }
72
73
74 bool init_inotify() {
75   inotify_fd = inotify_init();
76   if (inotify_fd < 0) {
77     userlog(LOG_ERR, "inotify_init: %s", strerror(errno));
78     return false;
79   }
80   userlog(LOG_DEBUG, "inotify fd: %d", get_inotify_fd());
81
82   read_watch_descriptors_count();
83   if (watch_count <= 0) {
84     close(inotify_fd);
85     inotify_fd = -1;
86     return false;
87   }
88   userlog(LOG_INFO, "inotify watch descriptors: %d", watch_count);
89
90   watches = table_create(watch_count);
91   if (watches == NULL) {
92     userlog(LOG_ERR, "out of memory");
93     close(inotify_fd);
94     inotify_fd = -1;
95     return false;
96   }
97
98   return true;
99 }
100
101
102 inline void set_inotify_callback(void (* _callback)(char*, int)) {
103   callback = _callback;
104 }
105
106
107 inline int get_inotify_fd() {
108   return inotify_fd;
109 }
110
111
112 inline int get_watch_count() {
113   return watch_count;
114 }
115
116
117 inline bool watch_limit_reached() {
118   return limit_reached;
119 }
120
121
122 static int add_watch(const char* path, watch_node* parent) {
123   int wd = inotify_add_watch(inotify_fd, path, IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF);
124   if (wd < 0) {
125     if (errno == ENOSPC) {
126       limit_reached = true;
127     }
128     userlog(LOG_ERR, "inotify_add_watch(%s): %s", path, strerror(errno));
129     return ERR_CONTINUE;
130   }
131   else {
132     userlog(LOG_DEBUG, "watching %s: %d", path, wd);
133   }
134
135   watch_node* node = table_get(watches, wd);
136   if (node != NULL) {
137     if (node->wd != wd || strcmp(node->name, path) != 0) {
138       char buf1[PATH_MAX], buf2[PATH_MAX];
139       const char* normalized1 = realpath(node->name, buf1);
140       const char* normalized2 = realpath(path, buf2);
141       if (normalized1 == NULL || normalized2 == NULL || strcmp(normalized1, normalized2) != 0) {
142         userlog(LOG_ERR, "table error: collision at %d (new %s, existing %s)", wd, path, node->name);
143         return ERR_ABORT;
144       }
145       else {
146         userlog(LOG_WARNING, "intersection at %d: (new %s, existing %s, real %s)", wd, path, node->name, normalized1);
147         return ERR_IGNORE;
148       }
149     }
150
151     return wd;
152   }
153
154   node = malloc(sizeof(watch_node));
155
156   CHECK_NULL(node);
157   node->name = strdup(path);
158   CHECK_NULL(node->name);
159   node->wd = wd;
160   node->parent = parent;
161   node->kids = NULL;
162
163   if (parent != NULL) {
164     if (parent->kids == NULL) {
165       parent->kids = array_create(DEFAULT_SUBDIR_COUNT);
166       CHECK_NULL(parent->kids);
167     }
168     CHECK_NULL(array_push(parent->kids, node));
169   }
170
171   if (table_put(watches, wd, node) == NULL) {
172     userlog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path);
173     return ERR_ABORT;
174   }
175
176   return wd;
177 }
178
179
180 static void rm_watch(int wd, bool update_parent) {
181   watch_node* node = table_get(watches, wd);
182   if (node == NULL) {
183     return;
184   }
185
186   userlog(LOG_DEBUG, "unwatching %s: %d (%p)", node->name, node->wd, node);
187
188   if (inotify_rm_watch(inotify_fd, node->wd) < 0) {
189     userlog(LOG_DEBUG, "inotify_rm_watch(%d:%s): %s", node->wd, node->name, strerror(errno));
190   }
191
192   for (int i=0; i<array_size(node->kids); i++) {
193     watch_node* kid = array_get(node->kids, i);
194     if (kid != NULL) {
195       rm_watch(kid->wd, false);
196     }
197   }
198
199   if (update_parent && node->parent != NULL) {
200     for (int i=0; i<array_size(node->parent->kids); i++) {
201       if (array_get(node->parent->kids, i) == node) {
202         array_put(node->parent->kids, i, NULL);
203         break;
204       }
205     }
206   }
207
208   free(node->name);
209   array_delete(node->kids);
210   free(node);
211   table_put(watches, wd, NULL);
212 }
213
214
215 static bool is_directory(struct dirent* entry, const char* path) {
216   if (entry->d_type == DT_DIR) {
217     return true;
218   }
219   else if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) {
220     struct stat st;
221     return (stat(path, &st) == 0 && S_ISDIR(st.st_mode));
222   }
223   return false;
224 }
225
226 static bool is_ignored(const char* path, array* ignores) {
227   if (ignores != NULL) {
228     int pl = strlen(path);
229     for (int i=0; i<array_size(ignores); i++) {
230       const char* ignore = array_get(ignores, i);
231       int il = strlen(ignore);
232       if (pl >= il && strncmp(path, ignore, il) == 0) {
233         userlog(LOG_DEBUG, "path %s is under unwatchable %s - ignoring", path, ignore);
234         return true;
235       }
236     }
237   }
238   return false;
239 }
240
241 static int walk_tree(const char* path, watch_node* parent, array* ignores) {
242   if (is_ignored(path, ignores)) {
243     return ERR_IGNORE;
244   }
245
246   DIR* dir = opendir(path);
247   if (dir == NULL) {
248     if (errno == EACCES) {
249       return ERR_IGNORE;
250     }
251     else if (errno == ENOTDIR) {  // flat root
252       return add_watch(path, parent);
253     }
254     userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno));
255     return ERR_CONTINUE;
256   }
257
258   int id = add_watch(path, parent);
259   if (id < 0) {
260     closedir(dir);
261     return id;
262   }
263
264   struct dirent* entry;
265   char subdir[PATH_MAX];
266   strcpy(subdir, path);
267   if (subdir[strlen(subdir) - 1] != '/') {
268     strcat(subdir, "/");
269   }
270   char* p = subdir + strlen(subdir);
271
272   while ((entry = readdir(dir)) != NULL) {
273     if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
274       continue;
275     }
276
277     strcpy(p, entry->d_name);
278     if (!is_directory(entry, subdir)) {
279       continue;
280     }
281
282     int subdir_id = walk_tree(subdir, table_get(watches, id), ignores);
283     if (subdir_id < 0 && subdir_id != ERR_IGNORE) {
284       rm_watch(id, true);
285       id = subdir_id;
286       break;
287     }
288   }
289
290   closedir(dir);
291   return id;
292 }
293
294
295 int watch(const char* root, array* ignores) {
296   char buf[PATH_MAX];
297   const char* normalized = realpath(root, buf);
298   return walk_tree((normalized != NULL ? normalized : root), NULL, ignores);
299 }
300
301
302 void unwatch(int id) {
303   rm_watch(id, true);
304 }
305
306
307 static bool process_inotify_event(struct inotify_event* event) {
308   watch_node* node = table_get(watches, event->wd);
309   if (node == NULL) {
310     return true;
311   }
312
313   userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s",
314       event->wd, event->mask & (~IN_ISDIR), (event->mask & IN_ISDIR) != 0, node->name);
315
316   char path[PATH_MAX];
317   strcpy(path, node->name);
318   if (event->len > 0) {
319     if (path[strlen(path) - 1] != '/') {
320       strcat(path, "/");
321     }
322     strcat(path, event->name);
323   }
324
325   if ((event->mask & IN_CREATE || event->mask & IN_MOVED_TO) && event->mask & IN_ISDIR) {
326     int result = walk_tree(path, node, NULL);
327     if (result < 0 && result != ERR_IGNORE) {
328       return false;
329     }
330   }
331
332   if ((event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) && event->mask & IN_ISDIR) {
333     for (int i=0; i<array_size(node->kids); i++) {
334       watch_node* kid = array_get(node->kids, i);
335       if (kid != NULL && strcmp(kid->name, path) == 0) {
336         rm_watch(kid->wd, false);
337         array_put(node->kids, i, NULL);
338         break;
339       }
340     }
341   }
342
343   if (callback != NULL) {
344     (*callback)(path, event->mask);
345   }
346   return true;
347 }
348
349
350 bool process_inotify_input() {
351   ssize_t len = read(inotify_fd, event_buf, EVENT_BUF_LEN);
352   if (len < 0) {
353     userlog(LOG_ERR, "read: %s", strerror(errno));
354     return false;
355   }
356
357   int i = 0;
358   while (i < len) {
359     struct inotify_event* event = (struct inotify_event*) &event_buf[i];
360     i += EVENT_SIZE + event->len;
361
362     if (event->mask & IN_IGNORED) {
363       continue;
364     }
365     if (event->mask & IN_Q_OVERFLOW) {
366       userlog(LOG_ERR, "event queue overflow");
367       continue;
368     }
369
370     if (!process_inotify_event(event)) {
371       return false;
372     }
373   }
374
375   return true;
376 }
377
378
379 void close_inotify() {
380   if (watches != NULL) {
381     table_delete(watches);
382   }
383
384   if (inotify_fd >= 0) {
385     close(inotify_fd);
386   }
387 }