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