Branch data Line data Source code
1 : : /*
2 : : Copyright (C) 2005 John McCutchan
3 : : Copyright © 2015 Canonical Limited
4 : :
5 : : This library is free software; you can redistribute it and/or
6 : : modify it under the terms of the GNU Lesser General Public
7 : : License as published by the Free Software Foundation; either
8 : : version 2.1 of the License, or (at your option) any later version.
9 : :
10 : : This library is distributed in the hope that it will be useful,
11 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : Lesser General Public License for more details.
14 : :
15 : : You should have received a copy of the GNU Lesser General Public License
16 : : along with this library; if not, see <http://www.gnu.org/licenses/>.
17 : :
18 : : Authors:
19 : : Ryan Lortie <desrt@desrt.ca>
20 : : John McCutchan <john@johnmccutchan.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <stdio.h>
26 : : #include <sys/ioctl.h>
27 : : #include <unistd.h>
28 : : #include <errno.h>
29 : : #include <string.h>
30 : : #include <glib.h>
31 : : #include "inotify-kernel.h"
32 : : #include <sys/inotify.h>
33 : : #ifdef HAVE_SYS_FILIO_H
34 : : #include <sys/filio.h>
35 : : #endif
36 : : #include <glib/glib-unix.h>
37 : :
38 : : #include "glib-private.h"
39 : :
40 : : /* From inotify(7) */
41 : : #define MAX_EVENT_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1)
42 : :
43 : : /* Amount of time to sleep on receipt of uninteresting events */
44 : : #define BOREDOM_SLEEP_TIME (100 * G_TIME_SPAN_MILLISECOND)
45 : :
46 : : /* Define limits on the maximum amount of time and maximum amount of
47 : : * interceding events between FROM/TO that can be merged.
48 : : */
49 : : #define MOVE_PAIR_DELAY (10 * G_TIME_SPAN_MILLISECOND)
50 : : #define MOVE_PAIR_DISTANCE (100)
51 : :
52 : : /* We use the lock from inotify-helper.c
53 : : *
54 : : * We only have to take it on our read callback.
55 : : *
56 : : * The rest of locking is taken care of in inotify-helper.c
57 : : */
58 : : G_LOCK_EXTERN (inotify_lock);
59 : :
60 : : static ik_event_t *
61 : 935 : ik_event_new (struct inotify_event *kevent,
62 : : gint64 now)
63 : : {
64 : 935 : ik_event_t *event = g_new0 (ik_event_t, 1);
65 : :
66 : 935 : event->wd = kevent->wd;
67 : 935 : event->mask = kevent->mask;
68 : 935 : event->cookie = kevent->cookie;
69 : 935 : event->len = kevent->len;
70 : 935 : event->timestamp = now;
71 [ + + ]: 935 : if (event->len)
72 : 1262 : event->name = g_strdup (kevent->name);
73 : : else
74 : 304 : event->name = NULL;
75 : :
76 : 935 : return event;
77 : : }
78 : :
79 : : void
80 : 935 : _ik_event_free (ik_event_t *event)
81 : : {
82 [ + + ]: 935 : if (event->pair)
83 : : {
84 : 29 : event->pair->pair = NULL;
85 : 29 : _ik_event_free (event->pair);
86 : : }
87 : :
88 : 935 : g_free (event->name);
89 : 935 : g_free (event);
90 : 935 : }
91 : :
92 : : typedef struct
93 : : {
94 : : GSource source;
95 : :
96 : : GQueue queue;
97 : : gpointer fd_tag;
98 : : gint fd;
99 : :
100 : : GHashTable *unmatched_moves;
101 : : gboolean is_bored;
102 : : } InotifyKernelSource;
103 : :
104 : : static InotifyKernelSource *inotify_source;
105 : :
106 : : static gint64
107 : 1678 : ik_source_get_dispatch_time (InotifyKernelSource *iks)
108 : : {
109 : : ik_event_t *head;
110 : :
111 : 1678 : head = g_queue_peek_head (&iks->queue);
112 : :
113 : : /* nothing in the queue: not ready */
114 [ + + ]: 1678 : if (!head)
115 : 764 : return -1;
116 : :
117 : : /* if it's not an unpaired move, it is ready now */
118 [ + + + + ]: 914 : if (~head->mask & IN_MOVED_FROM || head->pair)
119 : 905 : return 0;
120 : :
121 : : /* if the queue is too long then it's ready now */
122 [ - + ]: 9 : if (iks->queue.length > MOVE_PAIR_DISTANCE)
123 : 0 : return 0;
124 : :
125 : : /* otherwise, it's ready after the delay */
126 : 9 : return head->timestamp + MOVE_PAIR_DELAY;
127 : : }
128 : :
129 : : static gboolean
130 : 1292 : ik_source_can_dispatch_now (InotifyKernelSource *iks,
131 : : gint64 now)
132 : : {
133 : : gint64 dispatch_time;
134 : :
135 : 1292 : dispatch_time = ik_source_get_dispatch_time (iks);
136 : :
137 [ + + + + ]: 1292 : return 0 <= dispatch_time && dispatch_time <= now;
138 : : }
139 : :
140 : : static gsize
141 : 386 : ik_source_read_some_events (InotifyKernelSource *iks,
142 : : gchar *buffer,
143 : : gsize buffer_len)
144 : : {
145 : : gssize result;
146 : : int errsv;
147 : :
148 : 386 : again:
149 : 386 : result = read (iks->fd, buffer, buffer_len);
150 : 386 : errsv = errno;
151 : :
152 [ + + ]: 386 : if (result < 0)
153 : : {
154 [ - + ]: 3 : if (errsv == EINTR)
155 : 0 : goto again;
156 : :
157 [ + - ]: 3 : if (errsv == EAGAIN)
158 : 3 : return 0;
159 : :
160 : 0 : g_error ("inotify read(): %s", g_strerror (errsv));
161 : : }
162 [ - + ]: 383 : else if (result == 0)
163 : 0 : g_error ("inotify unexpectedly hit eof");
164 : :
165 : 383 : return result;
166 : : }
167 : :
168 : : static gchar *
169 : 385 : ik_source_read_all_the_events (InotifyKernelSource *iks,
170 : : gchar *buffer,
171 : : gsize buffer_len,
172 : : gsize *length_out)
173 : : {
174 : : gsize n_read;
175 : :
176 : 385 : n_read = ik_source_read_some_events (iks, buffer, buffer_len);
177 : :
178 : : /* Check if we might have gotten another event if we had passed in a
179 : : * bigger buffer...
180 : : */
181 [ + + ]: 385 : if (n_read + MAX_EVENT_SIZE > buffer_len)
182 : : {
183 : : gchar *new_buffer;
184 : : guint n_readable;
185 : : gint result;
186 : : int errsv;
187 : :
188 : : /* figure out how many more bytes there are to read */
189 : 1 : result = ioctl (iks->fd, FIONREAD, &n_readable);
190 : 1 : errsv = errno;
191 [ - + ]: 1 : if (result != 0)
192 : 0 : g_error ("inotify ioctl(FIONREAD): %s", g_strerror (errsv));
193 : :
194 [ + - ]: 1 : if (n_readable != 0)
195 : : {
196 : : /* there is in fact more data. allocate a new buffer, copy
197 : : * the existing data, and then append the remaining.
198 : : */
199 : 1 : new_buffer = g_malloc (n_read + n_readable);
200 : 1 : memcpy (new_buffer, buffer, n_read);
201 : 1 : n_read += ik_source_read_some_events (iks, new_buffer + n_read, n_readable);
202 : :
203 : 1 : buffer = new_buffer;
204 : :
205 : : /* There may be new events in the buffer that were added after
206 : : * the FIONREAD was performed, but we can't risk getting into
207 : : * a loop. We'll get them next time.
208 : : */
209 : : }
210 : : }
211 : :
212 : 385 : *length_out = n_read;
213 : :
214 : 385 : return buffer;
215 : : }
216 : :
217 : : static gboolean
218 : 386 : ik_source_dispatch (GSource *source,
219 : : GSourceFunc func,
220 : : gpointer user_data)
221 : : {
222 : 386 : InotifyKernelSource *iks = (InotifyKernelSource *) source;
223 : 386 : gboolean (*user_callback) (ik_event_t *event) = (void *) func;
224 : 386 : gboolean interesting = FALSE;
225 : : gint64 now;
226 : :
227 : 386 : now = g_source_get_time (source);
228 : :
229 [ + + + + ]: 386 : if (iks->is_bored || g_source_query_unix_fd (source, iks->fd_tag))
230 : : {
231 : : gchar stack_buffer[4096];
232 : : gsize buffer_len;
233 : : gchar *buffer;
234 : : gsize offset;
235 : :
236 : : /* We want to read all of the available events.
237 : : *
238 : : * We need to do it in a finite number of steps so that we don't
239 : : * get caught in a loop of read() with another process
240 : : * continuously adding events each time we drain them.
241 : : *
242 : : * In the normal case we will have only a few events in the queue,
243 : : * so start out by reading into a small stack-allocated buffer.
244 : : * Even though we're on a fresh stack frame, there is no need to
245 : : * pointlessly blow up with the size of the worker thread stack
246 : : * with a huge buffer here.
247 : : *
248 : : * If the result is large enough to cause us to suspect that
249 : : * another event may be pending then we allocate a buffer on the
250 : : * heap that can hold all of the events and read (once!) into that
251 : : * buffer.
252 : : */
253 : 385 : buffer = ik_source_read_all_the_events (iks, stack_buffer, sizeof stack_buffer, &buffer_len);
254 : :
255 : 385 : offset = 0;
256 : :
257 [ + + ]: 1320 : while (offset < buffer_len)
258 : : {
259 : 935 : struct inotify_event *kevent = (struct inotify_event *) (buffer + offset);
260 : : ik_event_t *event;
261 : :
262 : 935 : event = ik_event_new (kevent, now);
263 : :
264 : 935 : offset += sizeof (struct inotify_event) + event->len;
265 : :
266 [ + + ]: 935 : if (event->mask & IN_MOVED_TO)
267 : : {
268 : : ik_event_t *pair;
269 : :
270 : 30 : pair = g_hash_table_lookup (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
271 [ + + ]: 30 : if (pair != NULL)
272 : : {
273 : 29 : g_assert (!pair->pair);
274 : :
275 : 29 : g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
276 : 29 : event->is_second_in_pair = TRUE;
277 : 29 : event->pair = pair;
278 : 29 : pair->pair = event;
279 : 29 : continue;
280 : : }
281 : :
282 : 1 : interesting = TRUE;
283 : : }
284 : :
285 [ + + ]: 905 : else if (event->mask & IN_MOVED_FROM)
286 : : {
287 : : gboolean new;
288 : :
289 : 30 : new = g_hash_table_insert (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), event);
290 [ - + ]: 30 : if G_UNLIKELY (!new)
291 : 0 : g_warning ("inotify: got IN_MOVED_FROM event with already-pending cookie %#x", event->cookie);
292 : :
293 : 30 : interesting = TRUE;
294 : : }
295 : :
296 : 906 : g_queue_push_tail (&iks->queue, event);
297 : : }
298 : :
299 [ + + ]: 385 : if (buffer_len == 0)
300 : : {
301 : : /* We can end up reading nothing if we arrived here due to a
302 : : * boredom timer but the stream of events stopped meanwhile.
303 : : *
304 : : * In that case, we need to switch back to polling the file
305 : : * descriptor in the usual way.
306 : : */
307 : 3 : g_assert (iks->is_bored);
308 : 3 : interesting = TRUE;
309 : : }
310 : :
311 [ + + ]: 385 : if (buffer != stack_buffer)
312 : 1 : g_free (buffer);
313 : : }
314 : :
315 [ + + ]: 1292 : while (ik_source_can_dispatch_now (iks, now))
316 : : {
317 : : ik_event_t *event;
318 : :
319 : : /* callback will free the event */
320 : 906 : event = g_queue_pop_head (&iks->queue);
321 : :
322 [ + + + + ]: 906 : if (event->mask & IN_MOVED_FROM && !event->pair)
323 : 1 : g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
324 : :
325 : 906 : G_LOCK (inotify_lock);
326 : :
327 : 906 : interesting |= (* user_callback) (event);
328 : :
329 : 906 : G_UNLOCK (inotify_lock);
330 : : }
331 : :
332 : : /* The queue gets blocked iff we have unmatched moves */
333 : 386 : g_assert ((iks->queue.length > 0) == (g_hash_table_size (iks->unmatched_moves) > 0));
334 : :
335 : : /* Here's where we decide what will wake us up next.
336 : : *
337 : : * If the last event was interesting then we will wake up on the fd or
338 : : * when the timeout is reached on an unpaired move (if any).
339 : : *
340 : : * If the last event was uninteresting then we will wake up after the
341 : : * shorter of the boredom sleep or any timeout for an unpaired move.
342 : : */
343 [ + + ]: 386 : if (interesting)
344 : : {
345 [ + + ]: 377 : if (iks->is_bored)
346 : : {
347 : 9 : g_source_modify_unix_fd (source, iks->fd_tag, G_IO_IN);
348 : 9 : iks->is_bored = FALSE;
349 : : }
350 : :
351 : 377 : g_source_set_ready_time (source, ik_source_get_dispatch_time (iks));
352 : : }
353 : : else
354 : : {
355 : 9 : guint64 dispatch_time = ik_source_get_dispatch_time (iks);
356 : 9 : guint64 boredom_time = now + BOREDOM_SLEEP_TIME;
357 : :
358 [ + - ]: 9 : if (!iks->is_bored)
359 : : {
360 : 9 : g_source_modify_unix_fd (source, iks->fd_tag, 0);
361 : 9 : iks->is_bored = TRUE;
362 : : }
363 : :
364 : 9 : g_source_set_ready_time (source, MIN (dispatch_time, boredom_time));
365 : : }
366 : :
367 : 386 : return TRUE;
368 : : }
369 : :
370 : : static InotifyKernelSource *
371 : 57 : ik_source_new (gboolean (* callback) (ik_event_t *event))
372 : : {
373 : : static GSourceFuncs source_funcs = {
374 : : NULL, NULL,
375 : : ik_source_dispatch,
376 : : NULL, NULL, NULL
377 : : };
378 : : InotifyKernelSource *iks;
379 : : GSource *source;
380 : 57 : gboolean should_set_nonblock = FALSE;
381 : :
382 : 57 : source = g_source_new (&source_funcs, sizeof (InotifyKernelSource));
383 : 57 : iks = (InotifyKernelSource *) source;
384 : :
385 : 57 : g_source_set_static_name (source, "inotify kernel source");
386 : :
387 : 57 : iks->unmatched_moves = g_hash_table_new (NULL, NULL);
388 : 57 : iks->fd = inotify_init1 (IN_CLOEXEC | IN_NONBLOCK);
389 : :
390 [ - + ]: 57 : if (iks->fd < 0)
391 : : {
392 : 0 : should_set_nonblock = TRUE;
393 : 0 : iks->fd = inotify_init ();
394 : : }
395 : :
396 [ + - ]: 57 : if (iks->fd >= 0)
397 : : {
398 : 57 : GError *error = NULL;
399 : :
400 [ - + ]: 57 : if (should_set_nonblock)
401 : : {
402 : 0 : g_unix_set_fd_nonblocking (iks->fd, TRUE, &error);
403 : 0 : g_assert_no_error (error);
404 : : }
405 : :
406 : 57 : iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN);
407 : : }
408 : :
409 : 57 : g_source_set_callback (source, (GSourceFunc) callback, NULL, NULL);
410 : :
411 : 57 : g_source_attach (source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
412 : :
413 : 57 : return iks;
414 : : }
415 : :
416 : : gboolean
417 : 57 : _ik_startup (gboolean (*cb)(ik_event_t *event))
418 : : {
419 [ + - + - : 57 : if (g_once_init_enter_pointer (&inotify_source))
+ - ]
420 : 57 : g_once_init_leave_pointer (&inotify_source, ik_source_new (cb));
421 : :
422 : 57 : return inotify_source->fd >= 0;
423 : : }
424 : :
425 : : gint32
426 : 389 : _ik_watch (const char *path,
427 : : guint32 mask,
428 : : int *err)
429 : : {
430 : 389 : gint32 wd = -1;
431 : :
432 : 389 : g_assert (path != NULL);
433 : 389 : g_assert (inotify_source && inotify_source->fd >= 0);
434 : :
435 : 389 : wd = inotify_add_watch (inotify_source->fd, path, mask);
436 : :
437 [ + + ]: 389 : if (wd < 0)
438 : : {
439 : 3 : int e = errno;
440 : : /* FIXME: debug msg failed to add watch */
441 [ + - ]: 3 : if (err)
442 : 3 : *err = e;
443 : 3 : return wd;
444 : : }
445 : :
446 : 386 : g_assert (wd >= 0);
447 : 386 : return wd;
448 : : }
449 : :
450 : : int
451 : 189 : _ik_ignore (const char *path,
452 : : gint32 wd)
453 : : {
454 : 189 : g_assert (wd >= 0);
455 : 189 : g_assert (inotify_source && inotify_source->fd >= 0);
456 : :
457 [ + + ]: 189 : if (inotify_rm_watch (inotify_source->fd, wd) < 0)
458 : : {
459 : : /* int e = errno; */
460 : : /* failed to rm watch */
461 : 10 : return -1;
462 : : }
463 : :
464 : 179 : return 0;
465 : : }
|