• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
ik_event_new(struct inotify_event * kevent,gint64 now)61 ik_event_new (struct inotify_event *kevent,
62               gint64                now)
63 {
64   ik_event_t *event = g_new0 (ik_event_t, 1);
65 
66   event->wd = kevent->wd;
67   event->mask = kevent->mask;
68   event->cookie = kevent->cookie;
69   event->len = kevent->len;
70   event->timestamp = now;
71   if (event->len)
72     event->name = g_strdup (kevent->name);
73   else
74     event->name = NULL;
75 
76   return event;
77 }
78 
79 void
_ik_event_free(ik_event_t * event)80 _ik_event_free (ik_event_t *event)
81 {
82   if (event->pair)
83     {
84       event->pair->pair = NULL;
85       _ik_event_free (event->pair);
86     }
87 
88   g_free (event->name);
89   g_free (event);
90 }
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
ik_source_get_dispatch_time(InotifyKernelSource * iks)107 ik_source_get_dispatch_time (InotifyKernelSource *iks)
108 {
109   ik_event_t *head;
110 
111   head = g_queue_peek_head (&iks->queue);
112 
113   /* nothing in the queue: not ready */
114   if (!head)
115     return -1;
116 
117   /* if it's not an unpaired move, it is ready now */
118   if (~head->mask & IN_MOVED_FROM || head->pair)
119     return 0;
120 
121   /* if the queue is too long then it's ready now */
122   if (iks->queue.length > MOVE_PAIR_DISTANCE)
123     return 0;
124 
125   /* otherwise, it's ready after the delay */
126   return head->timestamp + MOVE_PAIR_DELAY;
127 }
128 
129 static gboolean
ik_source_can_dispatch_now(InotifyKernelSource * iks,gint64 now)130 ik_source_can_dispatch_now (InotifyKernelSource *iks,
131                             gint64               now)
132 {
133   gint64 dispatch_time;
134 
135   dispatch_time = ik_source_get_dispatch_time (iks);
136 
137   return 0 <= dispatch_time && dispatch_time <= now;
138 }
139 
140 static gsize
ik_source_read_some_events(InotifyKernelSource * iks,gchar * buffer,gsize buffer_len)141 ik_source_read_some_events (InotifyKernelSource *iks,
142                             gchar               *buffer,
143                             gsize                buffer_len)
144 {
145   gssize result;
146   int errsv;
147 
148 again:
149   result = read (iks->fd, buffer, buffer_len);
150   errsv = errno;
151 
152   if (result < 0)
153     {
154       if (errsv == EINTR)
155         goto again;
156 
157       if (errsv == EAGAIN)
158         return 0;
159 
160       g_error ("inotify read(): %s", g_strerror (errsv));
161     }
162   else if (result == 0)
163     g_error ("inotify unexpectedly hit eof");
164 
165   return result;
166 }
167 
168 static gchar *
ik_source_read_all_the_events(InotifyKernelSource * iks,gchar * buffer,gsize buffer_len,gsize * length_out)169 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   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   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       result = ioctl (iks->fd, FIONREAD, &n_readable);
190       errsv = errno;
191       if (result != 0)
192         g_error ("inotify ioctl(FIONREAD): %s", g_strerror (errsv));
193 
194       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           new_buffer = g_malloc (n_read + n_readable);
200           memcpy (new_buffer, buffer, n_read);
201           n_read += ik_source_read_some_events (iks, new_buffer + n_read, n_readable);
202 
203           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   *length_out = n_read;
213 
214   return buffer;
215 }
216 
217 static gboolean
ik_source_dispatch(GSource * source,GSourceFunc func,gpointer user_data)218 ik_source_dispatch (GSource     *source,
219                     GSourceFunc  func,
220                     gpointer     user_data)
221 {
222   InotifyKernelSource *iks = (InotifyKernelSource *) source;
223   gboolean (*user_callback) (ik_event_t *event) = (void *) func;
224   gboolean interesting = FALSE;
225   gint64 now;
226 
227   now = g_source_get_time (source);
228 
229   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       buffer = ik_source_read_all_the_events (iks, stack_buffer, sizeof stack_buffer, &buffer_len);
254 
255       offset = 0;
256 
257       while (offset < buffer_len)
258         {
259           struct inotify_event *kevent = (struct inotify_event *) (buffer + offset);
260           ik_event_t *event;
261 
262           event = ik_event_new (kevent, now);
263 
264           offset += sizeof (struct inotify_event) + event->len;
265 
266           if (event->mask & IN_MOVED_TO)
267             {
268               ik_event_t *pair;
269 
270               pair = g_hash_table_lookup (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
271               if (pair != NULL)
272                 {
273                   g_assert (!pair->pair);
274 
275                   g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
276                   event->is_second_in_pair = TRUE;
277                   event->pair = pair;
278                   pair->pair = event;
279                   continue;
280                 }
281 
282               interesting = TRUE;
283             }
284 
285           else if (event->mask & IN_MOVED_FROM)
286             {
287               gboolean new;
288 
289               new = g_hash_table_insert (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), event);
290               if G_UNLIKELY (!new)
291                 g_warning ("inotify: got IN_MOVED_FROM event with already-pending cookie %#x", event->cookie);
292 
293               interesting = TRUE;
294             }
295 
296           g_queue_push_tail (&iks->queue, event);
297         }
298 
299       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           g_assert (iks->is_bored);
308           interesting = TRUE;
309         }
310 
311       if (buffer != stack_buffer)
312         g_free (buffer);
313     }
314 
315   while (ik_source_can_dispatch_now (iks, now))
316     {
317       ik_event_t *event;
318 
319       /* callback will free the event */
320       event = g_queue_pop_head (&iks->queue);
321 
322       if (event->mask & IN_MOVED_FROM && !event->pair)
323         g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
324 
325       G_LOCK (inotify_lock);
326 
327       interesting |= (* user_callback) (event);
328 
329       G_UNLOCK (inotify_lock);
330     }
331 
332   /* The queue gets blocked iff we have unmatched moves */
333   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   if (interesting)
344     {
345       if (iks->is_bored)
346         {
347           g_source_modify_unix_fd (source, iks->fd_tag, G_IO_IN);
348           iks->is_bored = FALSE;
349         }
350 
351       g_source_set_ready_time (source, ik_source_get_dispatch_time (iks));
352     }
353   else
354     {
355       guint64 dispatch_time = ik_source_get_dispatch_time (iks);
356       guint64 boredom_time = now + BOREDOM_SLEEP_TIME;
357 
358       if (!iks->is_bored)
359         {
360           g_source_modify_unix_fd (source, iks->fd_tag, 0);
361           iks->is_bored = TRUE;
362         }
363 
364       g_source_set_ready_time (source, MIN (dispatch_time, boredom_time));
365     }
366 
367   return TRUE;
368 }
369 
370 static InotifyKernelSource *
ik_source_new(gboolean (* callback)(ik_event_t * event))371 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 
381   source = g_source_new (&source_funcs, sizeof (InotifyKernelSource));
382   iks = (InotifyKernelSource *) source;
383 
384   g_source_set_name (source, "inotify kernel source");
385 
386   iks->unmatched_moves = g_hash_table_new (NULL, NULL);
387   iks->fd = inotify_init1 (IN_CLOEXEC);
388 
389   if (iks->fd < 0)
390     iks->fd = inotify_init ();
391 
392   if (iks->fd >= 0)
393     {
394       GError *error = NULL;
395 
396       g_unix_set_fd_nonblocking (iks->fd, TRUE, &error);
397       g_assert_no_error (error);
398 
399       iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN);
400     }
401 
402   g_source_set_callback (source, (GSourceFunc) callback, NULL, NULL);
403 
404   g_source_attach (source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
405 
406   return iks;
407 }
408 
409 gboolean
_ik_startup(gboolean (* cb)(ik_event_t * event))410 _ik_startup (gboolean (*cb)(ik_event_t *event))
411 {
412   if (g_once_init_enter (&inotify_source))
413     g_once_init_leave (&inotify_source, ik_source_new (cb));
414 
415   return inotify_source->fd >= 0;
416 }
417 
418 gint32
_ik_watch(const char * path,guint32 mask,int * err)419 _ik_watch (const char *path,
420            guint32     mask,
421            int        *err)
422 {
423   gint32 wd = -1;
424 
425   g_assert (path != NULL);
426   g_assert (inotify_source && inotify_source->fd >= 0);
427 
428   wd = inotify_add_watch (inotify_source->fd, path, mask);
429 
430   if (wd < 0)
431     {
432       int e = errno;
433       /* FIXME: debug msg failed to add watch */
434       if (err)
435         *err = e;
436       return wd;
437     }
438 
439   g_assert (wd >= 0);
440   return wd;
441 }
442 
443 int
_ik_ignore(const char * path,gint32 wd)444 _ik_ignore (const char *path,
445             gint32      wd)
446 {
447   g_assert (wd >= 0);
448   g_assert (inotify_source && inotify_source->fd >= 0);
449 
450   if (inotify_rm_watch (inotify_source->fd, wd) < 0)
451     {
452       /* int e = errno; */
453       /* failed to rm watch */
454       return -1;
455     }
456 
457   return 0;
458 }
459