2 Copyright (C) 2005 John McCutchan
3 Copyright © 2015 Canonical Limited
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.
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.
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/>.
19 Ryan Lortie <desrt@desrt.ca>
20 John McCutchan <john@johnmccutchan.com>
26 #include <sys/ioctl.h>
31 #include "inotify-kernel.h"
32 #include <sys/inotify.h>
33 #include <glib/glib-unix.h>
35 #include "glib-private.h"
38 #define MAX_EVENT_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1)
40 /* Amount of time to sleep on receipt of uninteresting events */
41 #define BOREDOM_SLEEP_TIME (100 * G_TIME_SPAN_MILLISECOND)
43 /* Define limits on the maximum amount of time and maximum amount of
44 * interceding events between FROM/TO that can be merged.
46 #define MOVE_PAIR_DELAY (10 * G_TIME_SPAN_MILLISECOND)
47 #define MOVE_PAIR_DISTANCE (100)
49 /* We use the lock from inotify-helper.c
51 * We only have to take it on our read callback.
53 * The rest of locking is taken care of in inotify-helper.c
55 G_LOCK_EXTERN (inotify_lock
);
58 ik_event_new (struct inotify_event
*kevent
,
61 ik_event_t
*event
= g_new0 (ik_event_t
, 1);
63 event
->wd
= kevent
->wd
;
64 event
->mask
= kevent
->mask
;
65 event
->cookie
= kevent
->cookie
;
66 event
->len
= kevent
->len
;
67 event
->timestamp
= now
;
69 event
->name
= g_strdup (kevent
->name
);
77 _ik_event_free (ik_event_t
*event
)
81 event
->pair
->pair
= NULL
;
82 _ik_event_free (event
->pair
);
97 GHashTable
*unmatched_moves
;
99 } InotifyKernelSource
;
101 static InotifyKernelSource
*inotify_source
;
104 ik_source_get_dispatch_time (InotifyKernelSource
*iks
)
108 head
= g_queue_peek_head (&iks
->queue
);
110 /* nothing in the queue: not ready */
114 /* if it's not an unpaired move, it is ready now */
115 if (~head
->mask
& IN_MOVED_FROM
|| head
->pair
)
118 /* if the queue is too long then it's ready now */
119 if (iks
->queue
.length
> MOVE_PAIR_DISTANCE
)
122 /* otherwise, it's ready after the delay */
123 return head
->timestamp
+ MOVE_PAIR_DELAY
;
127 ik_source_can_dispatch_now (InotifyKernelSource
*iks
,
130 gint64 dispatch_time
;
132 dispatch_time
= ik_source_get_dispatch_time (iks
);
134 return 0 <= dispatch_time
&& dispatch_time
<= now
;
138 ik_source_read_some_events (InotifyKernelSource
*iks
,
146 result
= read (iks
->fd
, buffer
, buffer_len
);
157 g_error ("inotify read(): %s", g_strerror (errsv
));
159 else if (result
== 0)
160 g_error ("inotify unexpectedly hit eof");
166 ik_source_read_all_the_events (InotifyKernelSource
*iks
,
173 n_read
= ik_source_read_some_events (iks
, buffer
, buffer_len
);
175 /* Check if we might have gotten another event if we had passed in a
178 if (n_read
+ MAX_EVENT_SIZE
> buffer_len
)
185 /* figure out how many more bytes there are to read */
186 result
= ioctl (iks
->fd
, FIONREAD
, &n_readable
);
189 g_error ("inotify ioctl(FIONREAD): %s", g_strerror (errsv
));
193 /* there is in fact more data. allocate a new buffer, copy
194 * the existing data, and then append the remaining.
196 new_buffer
= g_malloc (n_read
+ n_readable
);
197 memcpy (new_buffer
, buffer
, n_read
);
198 n_read
+= ik_source_read_some_events (iks
, new_buffer
+ n_read
, n_readable
);
202 /* There may be new events in the buffer that were added after
203 * the FIONREAD was performed, but we can't risk getting into
204 * a loop. We'll get them next time.
209 *length_out
= n_read
;
215 ik_source_dispatch (GSource
*source
,
219 InotifyKernelSource
*iks
= (InotifyKernelSource
*) source
;
220 gboolean (*user_callback
) (ik_event_t
*event
) = (void *) func
;
221 gboolean interesting
= FALSE
;
224 now
= g_source_get_time (source
);
226 if (iks
->is_bored
|| g_source_query_unix_fd (source
, iks
->fd_tag
))
228 gchar stack_buffer
[4096];
233 /* We want to read all of the available events.
235 * We need to do it in a finite number of steps so that we don't
236 * get caught in a loop of read() with another process
237 * continuously adding events each time we drain them.
239 * In the normal case we will have only a few events in the queue,
240 * so start out by reading into a small stack-allocated buffer.
241 * Even though we're on a fresh stack frame, there is no need to
242 * pointlessly blow up with the size of the worker thread stack
243 * with a huge buffer here.
245 * If the result is large enough to cause us to suspect that
246 * another event may be pending then we allocate a buffer on the
247 * heap that can hold all of the events and read (once!) into that
250 buffer
= ik_source_read_all_the_events (iks
, stack_buffer
, sizeof stack_buffer
, &buffer_len
);
254 while (offset
< buffer_len
)
256 struct inotify_event
*kevent
= (struct inotify_event
*) (buffer
+ offset
);
259 event
= ik_event_new (kevent
, now
);
261 offset
+= sizeof (struct inotify_event
) + event
->len
;
263 if (event
->mask
& IN_MOVED_TO
)
267 pair
= g_hash_table_lookup (iks
->unmatched_moves
, GUINT_TO_POINTER (event
->cookie
));
270 g_assert (!pair
->pair
);
272 g_hash_table_remove (iks
->unmatched_moves
, GUINT_TO_POINTER (event
->cookie
));
273 event
->is_second_in_pair
= TRUE
;
282 else if (event
->mask
& IN_MOVED_FROM
)
286 new = g_hash_table_insert (iks
->unmatched_moves
, GUINT_TO_POINTER (event
->cookie
), event
);
288 g_warning ("inotify: got IN_MOVED_FROM event with already-pending cookie %#x", event
->cookie
);
293 g_queue_push_tail (&iks
->queue
, event
);
298 /* We can end up reading nothing if we arrived here due to a
299 * boredom timer but the stream of events stopped meanwhile.
301 * In that case, we need to switch back to polling the file
302 * descriptor in the usual way.
304 g_assert (iks
->is_bored
);
308 if (buffer
!= stack_buffer
)
312 while (ik_source_can_dispatch_now (iks
, now
))
316 /* callback will free the event */
317 event
= g_queue_pop_head (&iks
->queue
);
319 if (event
->mask
& IN_MOVED_FROM
&& !event
->pair
)
320 g_hash_table_remove (iks
->unmatched_moves
, GUINT_TO_POINTER (event
->cookie
));
322 G_LOCK (inotify_lock
);
324 interesting
|= (* user_callback
) (event
);
326 G_UNLOCK (inotify_lock
);
329 /* The queue gets blocked iff we have unmatched moves */
330 g_assert ((iks
->queue
.length
> 0) == (g_hash_table_size (iks
->unmatched_moves
) > 0));
332 /* Here's where we decide what will wake us up next.
334 * If the last event was interesting then we will wake up on the fd or
335 * when the timeout is reached on an unpaired move (if any).
337 * If the last event was uninteresting then we will wake up after the
338 * shorter of the boredom sleep or any timeout for a unpaired move.
344 g_source_modify_unix_fd (source
, iks
->fd_tag
, G_IO_IN
);
345 iks
->is_bored
= FALSE
;
348 g_source_set_ready_time (source
, ik_source_get_dispatch_time (iks
));
352 guint64 dispatch_time
= ik_source_get_dispatch_time (iks
);
353 guint64 boredom_time
= now
+ BOREDOM_SLEEP_TIME
;
357 g_source_modify_unix_fd (source
, iks
->fd_tag
, 0);
358 iks
->is_bored
= TRUE
;
361 g_source_set_ready_time (source
, MIN (dispatch_time
, boredom_time
));
367 static InotifyKernelSource
*
368 ik_source_new (gboolean (* callback
) (ik_event_t
*event
))
370 static GSourceFuncs source_funcs
= {
373 /* should have a finalize, but it will never happen */
375 InotifyKernelSource
*iks
;
378 source
= g_source_new (&source_funcs
, sizeof (InotifyKernelSource
));
379 iks
= (InotifyKernelSource
*) source
;
381 g_source_set_name (source
, "inotify kernel source");
383 iks
->unmatched_moves
= g_hash_table_new (NULL
, NULL
);
384 iks
->fd
= inotify_init1 (IN_CLOEXEC
);
387 iks
->fd
= inotify_init ();
391 GError
*error
= NULL
;
393 g_unix_set_fd_nonblocking (iks
->fd
, TRUE
, &error
);
394 g_assert_no_error (error
);
396 iks
->fd_tag
= g_source_add_unix_fd (source
, iks
->fd
, G_IO_IN
);
399 g_source_set_callback (source
, (GSourceFunc
) callback
, NULL
, NULL
);
401 g_source_attach (source
, GLIB_PRIVATE_CALL (g_get_worker_context
) ());
407 _ik_startup (gboolean (*cb
)(ik_event_t
*event
))
409 if (g_once_init_enter (&inotify_source
))
410 g_once_init_leave (&inotify_source
, ik_source_new (cb
));
412 return inotify_source
->fd
>= 0;
416 _ik_watch (const char *path
,
422 g_assert (path
!= NULL
);
423 g_assert (inotify_source
&& inotify_source
->fd
>= 0);
425 wd
= inotify_add_watch (inotify_source
->fd
, path
, mask
);
430 /* FIXME: debug msg failed to add watch */
441 _ik_ignore (const char *path
,
445 g_assert (inotify_source
&& inotify_source
->fd
>= 0);
447 if (inotify_rm_watch (inotify_source
->fd
, wd
) < 0)
450 /* failed to rm watch */