Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2006-2007 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: Alexander Larsson <alexl@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include "gioenumtypes.h"
26 : : #include "glocalfilemonitor.h"
27 : : #include "giomodule-priv.h"
28 : : #include "gioerror.h"
29 : : #include "glibintl.h"
30 : : #include "glocalfile.h"
31 : : #include "glib-private.h"
32 : :
33 : : #include <string.h>
34 : :
35 : : #define DEFAULT_RATE_LIMIT 800 * G_TIME_SPAN_MILLISECOND
36 : : #define VIRTUAL_CHANGES_DONE_DELAY 2 * G_TIME_SPAN_SECOND
37 : :
38 : : /* GFileMonitorSource is a GSource responsible for emitting the changed
39 : : * signals in the owner-context of the GFileMonitor.
40 : : *
41 : : * It contains functionality for cross-thread queuing of events. It
42 : : * also handles merging of CHANGED events and emission of CHANGES_DONE
43 : : * events.
44 : : *
45 : : * We use the "priv" pointer in the external struct to store it.
46 : : */
47 : : struct _GFileMonitorSource {
48 : : GSource source;
49 : :
50 : : GMutex lock;
51 : : GWeakRef instance_ref;
52 : : GFileMonitorFlags flags;
53 : : gchar *dirname;
54 : : gchar *basename;
55 : : gchar *filename;
56 : : GSequence *pending_changes; /* sorted by ready time */
57 : : GHashTable *pending_changes_table;
58 : : GQueue event_queue;
59 : : gint64 rate_limit;
60 : : };
61 : :
62 : : /* PendingChange is a struct to keep track of a file that needs to have
63 : : * (at least) a CHANGES_DONE_HINT event sent for it in the near future.
64 : : *
65 : : * If 'dirty' is TRUE then a CHANGED event also needs to be sent.
66 : : *
67 : : * last_emission is the last time a CHANGED event was emitted. It is
68 : : * used to calculate the time to send the next event.
69 : : */
70 : : typedef struct {
71 : : gchar *child;
72 : : guint64 last_emission : 63;
73 : : guint64 dirty : 1;
74 : : } PendingChange;
75 : :
76 : : /* QueuedEvent is a signal that will be sent immediately, as soon as the
77 : : * source gets a chance to dispatch. The existence of any queued event
78 : : * implies that the source is ready now.
79 : : */
80 : : typedef struct
81 : : {
82 : : GFileMonitorEvent event_type;
83 : : GFile *child;
84 : : GFile *other;
85 : : } QueuedEvent;
86 : :
87 : : static gint64
88 : 114 : pending_change_get_ready_time (const PendingChange *change,
89 : : GFileMonitorSource *fms)
90 : : {
91 : 114 : if (change->dirty)
92 : 82 : return change->last_emission + fms->rate_limit;
93 : : else
94 : 32 : return change->last_emission + VIRTUAL_CHANGES_DONE_DELAY;
95 : : }
96 : :
97 : : static int
98 : 0 : pending_change_compare_ready_time (gconstpointer a_p,
99 : : gconstpointer b_p,
100 : : gpointer user_data)
101 : : {
102 : 0 : GFileMonitorSource *fms = user_data;
103 : 0 : const PendingChange *a = a_p;
104 : 0 : const PendingChange *b = b_p;
105 : : gint64 ready_time_a;
106 : : gint64 ready_time_b;
107 : :
108 : 0 : ready_time_a = pending_change_get_ready_time (a, fms);
109 : 0 : ready_time_b = pending_change_get_ready_time (b, fms);
110 : :
111 : 0 : if (ready_time_a < ready_time_b)
112 : 0 : return -1;
113 : : else
114 : 0 : return ready_time_a > ready_time_b;
115 : : }
116 : :
117 : : static void
118 : 87 : pending_change_free (gpointer data)
119 : : {
120 : 87 : PendingChange *change = data;
121 : :
122 : 87 : g_free (change->child);
123 : :
124 : 87 : g_slice_free (PendingChange, change);
125 : 87 : }
126 : :
127 : : static void
128 : 1057 : queued_event_free (QueuedEvent *event)
129 : : {
130 : 1057 : g_object_unref (event->child);
131 : 1057 : if (event->other)
132 : 4 : g_object_unref (event->other);
133 : :
134 : 1057 : g_slice_free (QueuedEvent, event);
135 : 1057 : }
136 : :
137 : : static gint64
138 : 2102 : g_file_monitor_source_get_ready_time (GFileMonitorSource *fms)
139 : : {
140 : : GSequenceIter *iter;
141 : :
142 : 2102 : if (fms->event_queue.length)
143 : 1097 : return 0;
144 : :
145 : 1005 : iter = g_sequence_get_begin_iter (fms->pending_changes);
146 : 1005 : if (g_sequence_iter_is_end (iter))
147 : 938 : return -1;
148 : :
149 : 67 : return pending_change_get_ready_time (g_sequence_get (iter), fms);
150 : : }
151 : :
152 : : static void
153 : 2102 : g_file_monitor_source_update_ready_time (GFileMonitorSource *fms)
154 : : {
155 : 2102 : g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms));
156 : 2102 : }
157 : :
158 : : static GSequenceIter *
159 : 1090 : g_file_monitor_source_find_pending_change (GFileMonitorSource *fms,
160 : : const gchar *child)
161 : : {
162 : 1090 : return g_hash_table_lookup (fms->pending_changes_table, child);
163 : : }
164 : :
165 : : static void
166 : 87 : g_file_monitor_source_add_pending_change (GFileMonitorSource *fms,
167 : : const gchar *child,
168 : : gint64 now)
169 : : {
170 : : PendingChange *change;
171 : : GSequenceIter *iter;
172 : :
173 : 87 : change = g_slice_new (PendingChange);
174 : 87 : change->child = g_strdup (child);
175 : 87 : change->last_emission = now;
176 : 87 : change->dirty = FALSE;
177 : :
178 : 87 : iter = g_sequence_insert_sorted (fms->pending_changes, change, pending_change_compare_ready_time, fms);
179 : 87 : g_hash_table_insert (fms->pending_changes_table, change->child, iter);
180 : 87 : }
181 : :
182 : : static gboolean
183 : 56 : g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms,
184 : : GSequenceIter *iter)
185 : : {
186 : : PendingChange *change;
187 : :
188 : 56 : change = g_sequence_get (iter);
189 : :
190 : : /* if it was already dirty then this change is 'uninteresting' */
191 : 56 : if (change->dirty)
192 : 3 : return FALSE;
193 : :
194 : 53 : change->dirty = TRUE;
195 : :
196 : 53 : g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
197 : :
198 : 53 : return TRUE;
199 : : }
200 : :
201 : : static gboolean
202 : 52 : g_file_monitor_source_get_pending_change_dirty (GFileMonitorSource *fms,
203 : : GSequenceIter *iter)
204 : : {
205 : : PendingChange *change;
206 : :
207 : 52 : change = g_sequence_get (iter);
208 : :
209 : 52 : return change->dirty;
210 : : }
211 : :
212 : : static void
213 : 52 : g_file_monitor_source_remove_pending_change (GFileMonitorSource *fms,
214 : : GSequenceIter *iter,
215 : : const gchar *child)
216 : : {
217 : : /* must remove the hash entry first -- its key is owned by the data
218 : : * which will be freed when removing the sequence iter
219 : : */
220 : 52 : g_hash_table_remove (fms->pending_changes_table, child);
221 : 52 : g_sequence_remove (iter);
222 : 52 : }
223 : :
224 : : static void
225 : 1057 : g_file_monitor_source_queue_event (GFileMonitorSource *fms,
226 : : GFileMonitorEvent event_type,
227 : : const gchar *child,
228 : : GFile *other)
229 : : {
230 : : QueuedEvent *event;
231 : :
232 : 1057 : event = g_slice_new (QueuedEvent);
233 : 1057 : event->event_type = event_type;
234 : 1057 : if (child != NULL && fms->dirname != NULL)
235 : 770 : event->child = g_local_file_new_from_dirname_and_basename (fms->dirname, child);
236 : 287 : else if (child != NULL)
237 : : {
238 : 2 : gchar *dirname = g_path_get_dirname (fms->filename);
239 : 2 : event->child = g_local_file_new_from_dirname_and_basename (dirname, child);
240 : 2 : g_free (dirname);
241 : : }
242 : 285 : else if (fms->dirname)
243 : 283 : event->child = _g_local_file_new (fms->dirname);
244 : 2 : else if (fms->filename)
245 : 2 : event->child = _g_local_file_new (fms->filename);
246 : 1057 : event->other = other;
247 : 1057 : if (other)
248 : 4 : g_object_ref (other);
249 : :
250 : 1057 : g_queue_push_tail (&fms->event_queue, event);
251 : 1057 : }
252 : :
253 : : static gboolean
254 : 67 : g_file_monitor_source_file_changed (GFileMonitorSource *fms,
255 : : const gchar *child,
256 : : gint64 now)
257 : : {
258 : : GSequenceIter *pending;
259 : : gboolean interesting;
260 : :
261 : 67 : pending = g_file_monitor_source_find_pending_change (fms, child);
262 : :
263 : : /* If there is no pending change, emit one and create a record,
264 : : * else: just mark the existing record as dirty.
265 : : */
266 : 67 : if (!pending)
267 : : {
268 : 11 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
269 : 11 : g_file_monitor_source_add_pending_change (fms, child, now);
270 : 11 : interesting = TRUE;
271 : : }
272 : : else
273 : 56 : interesting = g_file_monitor_source_set_pending_change_dirty (fms, pending);
274 : :
275 : 67 : g_file_monitor_source_update_ready_time (fms);
276 : :
277 : 67 : return interesting;
278 : : }
279 : :
280 : : static void
281 : 1023 : g_file_monitor_source_file_changes_done (GFileMonitorSource *fms,
282 : : const gchar *child)
283 : : {
284 : : GSequenceIter *pending;
285 : :
286 : 1023 : pending = g_file_monitor_source_find_pending_change (fms, child);
287 : 1023 : if (pending)
288 : : {
289 : : /* If it is dirty, make sure we push out the last CHANGED event */
290 : 52 : if (g_file_monitor_source_get_pending_change_dirty (fms, pending))
291 : 23 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
292 : :
293 : 52 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
294 : 52 : g_file_monitor_source_remove_pending_change (fms, pending, child);
295 : : }
296 : 1023 : }
297 : :
298 : : static void
299 : 76 : g_file_monitor_source_file_created (GFileMonitorSource *fms,
300 : : const gchar *child,
301 : : gint64 event_time)
302 : : {
303 : : /* Unlikely, but if we have pending changes for this filename, make
304 : : * sure we flush those out first, before creating the new ones.
305 : : */
306 : 76 : g_file_monitor_source_file_changes_done (fms, child);
307 : :
308 : : /* Emit CREATE and add a pending changes record */
309 : 76 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
310 : 76 : g_file_monitor_source_add_pending_change (fms, child, event_time);
311 : 76 : }
312 : :
313 : : static void
314 : 825 : g_file_monitor_source_send_event (GFileMonitorSource *fms,
315 : : GFileMonitorEvent event_type,
316 : : const gchar *child,
317 : : GFile *other)
318 : : {
319 : : /* always flush any pending changes before we queue a new event */
320 : 825 : g_file_monitor_source_file_changes_done (fms, child);
321 : 825 : g_file_monitor_source_queue_event (fms, event_type, child, other);
322 : 825 : }
323 : :
324 : : static void
325 : 34 : g_file_monitor_source_send_synthetic_created (GFileMonitorSource *fms,
326 : : const gchar *child)
327 : : {
328 : 34 : g_file_monitor_source_file_changes_done (fms, child);
329 : 34 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
330 : 34 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
331 : 34 : }
332 : :
333 : : #ifndef G_DISABLE_ASSERT
334 : : static gboolean
335 : 804 : is_basename (const gchar *name)
336 : : {
337 : 804 : if (name[0] == '.' && ((name[1] == '.' && name[2] == '\0') || name[1] == '\0'))
338 : 0 : return FALSE;
339 : :
340 : 804 : return !strchr (name, '/');
341 : : }
342 : : #endif /* !G_DISABLE_ASSERT */
343 : :
344 : : gboolean
345 : 1054 : g_file_monitor_source_handle_event (GFileMonitorSource *fms,
346 : : GFileMonitorEvent event_type,
347 : : const gchar *child,
348 : : const gchar *rename_to,
349 : : GFile *other,
350 : : gint64 event_time)
351 : : {
352 : 1054 : gboolean interesting = TRUE;
353 : :
354 : 1054 : g_assert (!child || is_basename (child));
355 : 1054 : g_assert (!rename_to || is_basename (rename_to));
356 : :
357 : 1054 : if (fms->basename && (!child || !g_str_equal (child, fms->basename))
358 : 14 : && (!rename_to || !g_str_equal (rename_to, fms->basename)))
359 : 0 : return TRUE;
360 : :
361 : 1054 : g_mutex_lock (&fms->lock);
362 : :
363 : : /* NOTE:
364 : : *
365 : : * We process events even if the file monitor has already been disposed.
366 : : * The reason is that we must not take a reference to the instance here as
367 : : * destroying it from the event handling thread will lead to a deadlock when
368 : : * taking the lock in _ih_sub_cancel.
369 : : *
370 : : * This results in seemingly-unbounded growth of the `event_queue` with the
371 : : * calls to `g_file_monitor_source_queue_event()`. However, each of those sets
372 : : * the ready time on the #GSource, which means that it will be dispatched in
373 : : * a subsequent iteration of the #GMainContext it’s attached to. At that
374 : : * point, `g_file_monitor_source_dispatch()` will return %FALSE, and this will
375 : : * trigger finalisation of the source. That will clear the `event_queue`.
376 : : *
377 : : * If the source is no longer attached, this will return early to prevent
378 : : * unbounded queueing.
379 : : */
380 : 1054 : if (g_source_is_destroyed ((GSource *) fms))
381 : : {
382 : 0 : g_mutex_unlock (&fms->lock);
383 : 0 : return TRUE;
384 : : }
385 : :
386 : 1054 : switch (event_type)
387 : : {
388 : 76 : case G_FILE_MONITOR_EVENT_CREATED:
389 : 76 : g_assert (!other && !rename_to);
390 : 76 : g_file_monitor_source_file_created (fms, child, event_time);
391 : 76 : break;
392 : :
393 : 67 : case G_FILE_MONITOR_EVENT_CHANGED:
394 : 67 : g_assert (!other && !rename_to);
395 : 67 : interesting = g_file_monitor_source_file_changed (fms, child, event_time);
396 : 67 : break;
397 : :
398 : 85 : case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
399 : 85 : g_assert (!other && !rename_to);
400 : 85 : g_file_monitor_source_file_changes_done (fms, child);
401 : 85 : break;
402 : :
403 : 2 : case G_FILE_MONITOR_EVENT_MOVED_IN:
404 : 2 : g_assert (!rename_to);
405 : 2 : if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
406 : 1 : g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_IN, child, other);
407 : : else
408 : 1 : g_file_monitor_source_send_synthetic_created (fms, child);
409 : 2 : break;
410 : :
411 : 2 : case G_FILE_MONITOR_EVENT_MOVED_OUT:
412 : 2 : g_assert (!rename_to);
413 : 2 : if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
414 : 2 : g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_OUT, child, other);
415 : 0 : else if (other && (fms->flags & G_FILE_MONITOR_SEND_MOVED))
416 : 0 : g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other);
417 : : else
418 : 0 : g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
419 : 2 : break;
420 : :
421 : 36 : case G_FILE_MONITOR_EVENT_RENAMED:
422 : 36 : g_assert (!other && rename_to);
423 : 36 : if (fms->flags & (G_FILE_MONITOR_WATCH_MOVES | G_FILE_MONITOR_SEND_MOVED))
424 : : {
425 : : GFile *other_file;
426 : : const gchar *dirname;
427 : 3 : gchar *allocated_dirname = NULL;
428 : : GFileMonitorEvent event;
429 : :
430 : 3 : event = (fms->flags & G_FILE_MONITOR_WATCH_MOVES) ? G_FILE_MONITOR_EVENT_RENAMED : G_FILE_MONITOR_EVENT_MOVED;
431 : :
432 : 3 : if (fms->dirname != NULL)
433 : 2 : dirname = fms->dirname;
434 : : else
435 : : {
436 : 1 : allocated_dirname = g_path_get_dirname (fms->filename);
437 : 1 : dirname = allocated_dirname;
438 : : }
439 : :
440 : 3 : other_file = g_local_file_new_from_dirname_and_basename (dirname, rename_to);
441 : 3 : g_file_monitor_source_file_changes_done (fms, rename_to);
442 : 3 : g_file_monitor_source_send_event (fms, event, child, other_file);
443 : :
444 : 3 : g_object_unref (other_file);
445 : 3 : g_free (allocated_dirname);
446 : : }
447 : : else
448 : : {
449 : 33 : g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
450 : 33 : g_file_monitor_source_send_synthetic_created (fms, rename_to);
451 : : }
452 : 36 : break;
453 : :
454 : 786 : case G_FILE_MONITOR_EVENT_DELETED:
455 : : case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
456 : : case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
457 : : case G_FILE_MONITOR_EVENT_UNMOUNTED:
458 : 786 : g_assert (!other && !rename_to);
459 : 786 : g_file_monitor_source_send_event (fms, event_type, child, NULL);
460 : 786 : break;
461 : :
462 : 0 : case G_FILE_MONITOR_EVENT_MOVED:
463 : : /* was never available in this API */
464 : : default:
465 : : g_assert_not_reached ();
466 : : }
467 : :
468 : 1054 : g_file_monitor_source_update_ready_time (fms);
469 : :
470 : 1054 : g_mutex_unlock (&fms->lock);
471 : :
472 : 1054 : return interesting;
473 : : }
474 : :
475 : : static gint64
476 : 0 : g_file_monitor_source_get_rate_limit (GFileMonitorSource *fms)
477 : : {
478 : : gint64 rate_limit;
479 : :
480 : 0 : g_mutex_lock (&fms->lock);
481 : 0 : rate_limit = fms->rate_limit;
482 : 0 : g_mutex_unlock (&fms->lock);
483 : :
484 : 0 : return rate_limit;
485 : : }
486 : :
487 : : static gboolean
488 : 12 : g_file_monitor_source_set_rate_limit (GFileMonitorSource *fms,
489 : : gint64 rate_limit)
490 : : {
491 : : gboolean changed;
492 : :
493 : 12 : g_mutex_lock (&fms->lock);
494 : :
495 : 12 : if (rate_limit != fms->rate_limit)
496 : : {
497 : 12 : fms->rate_limit = rate_limit;
498 : :
499 : 12 : g_sequence_sort (fms->pending_changes, pending_change_compare_ready_time, fms);
500 : 12 : g_file_monitor_source_update_ready_time (fms);
501 : :
502 : 12 : changed = TRUE;
503 : : }
504 : : else
505 : 0 : changed = FALSE;
506 : :
507 : 12 : g_mutex_unlock (&fms->lock);
508 : :
509 : 12 : return changed;
510 : : }
511 : :
512 : : static gboolean
513 : 486 : g_file_monitor_source_dispatch (GSource *source,
514 : : GSourceFunc callback,
515 : : gpointer user_data)
516 : : {
517 : 486 : GFileMonitorSource *fms = (GFileMonitorSource *) source;
518 : : QueuedEvent *event;
519 : : GQueue event_queue;
520 : : gint64 now;
521 : 486 : GFileMonitor *instance = NULL;
522 : :
523 : : /* make sure the monitor still exists */
524 : 486 : instance = g_weak_ref_get (&fms->instance_ref);
525 : 486 : if (instance == NULL)
526 : 1 : return FALSE;
527 : :
528 : 485 : now = g_source_get_time (source);
529 : :
530 : : /* Acquire the lock once and grab all events in one go, handling the
531 : : * queued events first. This avoids strange possibilities in cases of
532 : : * long delays, such as CHANGED events coming before CREATED events.
533 : : *
534 : : * We do this by converting the applicable pending changes into queued
535 : : * events (after the ones already queued) and then stealing the entire
536 : : * event queue in one go.
537 : : */
538 : 485 : g_mutex_lock (&fms->lock);
539 : :
540 : : /* Create events for any pending changes that are due to fire */
541 : 487 : while (!g_sequence_is_empty (fms->pending_changes))
542 : : {
543 : 47 : GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes);
544 : 47 : PendingChange *pending = g_sequence_get (iter);
545 : :
546 : : /* We've gotten to a pending change that's not ready. Stop. */
547 : 47 : if (pending_change_get_ready_time (pending, fms) > now)
548 : 45 : break;
549 : :
550 : 2 : if (pending->dirty)
551 : : {
552 : : /* It's time to send another CHANGED and update the record */
553 : 2 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL);
554 : 2 : pending->last_emission = now;
555 : 2 : pending->dirty = FALSE;
556 : :
557 : 2 : g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
558 : : }
559 : : else
560 : : {
561 : : /* It's time to send CHANGES_DONE and remove the pending record */
562 : 0 : g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL);
563 : 0 : g_file_monitor_source_remove_pending_change (fms, iter, pending->child);
564 : : }
565 : : }
566 : :
567 : : /* Steal the queue */
568 : 485 : memcpy (&event_queue, &fms->event_queue, sizeof event_queue);
569 : 485 : memset (&fms->event_queue, 0, sizeof fms->event_queue);
570 : :
571 : 485 : g_file_monitor_source_update_ready_time (fms);
572 : :
573 : 485 : g_mutex_unlock (&fms->lock);
574 : 485 : g_clear_object (&instance);
575 : :
576 : : /* We now have our list of events to deliver */
577 : 1456 : while ((event = g_queue_pop_head (&event_queue)))
578 : : {
579 : : /* an event handler could destroy 'instance', so check each time */
580 : 971 : instance = g_weak_ref_get (&fms->instance_ref);
581 : 971 : if (instance != NULL)
582 : 548 : g_file_monitor_emit_event (instance, event->child, event->other, event->event_type);
583 : :
584 : 971 : g_clear_object (&instance);
585 : 971 : queued_event_free (event);
586 : : }
587 : :
588 : 485 : return TRUE;
589 : : }
590 : :
591 : : static void
592 : 484 : g_file_monitor_source_dispose (GFileMonitorSource *fms)
593 : : {
594 : : GHashTableIter iter;
595 : : gpointer seqiter;
596 : : QueuedEvent *event;
597 : :
598 : 484 : g_mutex_lock (&fms->lock);
599 : :
600 : 484 : g_hash_table_iter_init (&iter, fms->pending_changes_table);
601 : 519 : while (g_hash_table_iter_next (&iter, NULL, &seqiter))
602 : : {
603 : 35 : g_hash_table_iter_remove (&iter);
604 : 35 : g_sequence_remove (seqiter);
605 : : }
606 : :
607 : 570 : while ((event = g_queue_pop_head (&fms->event_queue)))
608 : 86 : queued_event_free (event);
609 : :
610 : 484 : g_assert (g_sequence_is_empty (fms->pending_changes));
611 : 484 : g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
612 : 484 : g_assert (fms->event_queue.length == 0);
613 : 484 : g_weak_ref_set (&fms->instance_ref, NULL);
614 : :
615 : 484 : g_file_monitor_source_update_ready_time (fms);
616 : :
617 : 484 : g_source_destroy ((GSource *) fms);
618 : :
619 : 484 : g_mutex_unlock (&fms->lock);
620 : 484 : }
621 : :
622 : : static void
623 : 484 : g_file_monitor_source_finalize (GSource *source)
624 : : {
625 : 484 : GFileMonitorSource *fms = (GFileMonitorSource *) source;
626 : :
627 : : /* should already have been cleared in dispose of the monitor */
628 : 484 : g_assert (g_weak_ref_get (&fms->instance_ref) == NULL);
629 : 484 : g_weak_ref_clear (&fms->instance_ref);
630 : :
631 : 484 : g_assert (g_sequence_is_empty (fms->pending_changes));
632 : 484 : g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
633 : 484 : g_assert (fms->event_queue.length == 0);
634 : :
635 : 484 : g_hash_table_unref (fms->pending_changes_table);
636 : 484 : g_sequence_free (fms->pending_changes);
637 : :
638 : 484 : g_free (fms->dirname);
639 : 484 : g_free (fms->basename);
640 : 484 : g_free (fms->filename);
641 : :
642 : 484 : g_mutex_clear (&fms->lock);
643 : 484 : }
644 : :
645 : : static guint
646 : 1229 : str_hash0 (gconstpointer str)
647 : : {
648 : 1229 : return str ? g_str_hash (str) : 0;
649 : : }
650 : :
651 : : static gboolean
652 : 160 : str_equal0 (gconstpointer a,
653 : : gconstpointer b)
654 : : {
655 : 160 : return g_strcmp0 (a, b) == 0;
656 : : }
657 : :
658 : : static GFileMonitorSource *
659 : 676 : g_file_monitor_source_new (gpointer instance,
660 : : const gchar *filename,
661 : : gboolean is_directory,
662 : : GFileMonitorFlags flags)
663 : : {
664 : : static GSourceFuncs source_funcs = {
665 : : NULL, NULL,
666 : : g_file_monitor_source_dispatch,
667 : : g_file_monitor_source_finalize,
668 : : NULL, NULL
669 : : };
670 : : GFileMonitorSource *fms;
671 : : GSource *source;
672 : :
673 : 676 : source = g_source_new (&source_funcs, sizeof (GFileMonitorSource));
674 : 676 : fms = (GFileMonitorSource *) source;
675 : :
676 : 676 : g_source_set_static_name (source, "GFileMonitorSource");
677 : :
678 : 676 : g_mutex_init (&fms->lock);
679 : 676 : g_weak_ref_init (&fms->instance_ref, instance);
680 : 676 : fms->pending_changes = g_sequence_new (pending_change_free);
681 : 676 : fms->pending_changes_table = g_hash_table_new (str_hash0, str_equal0);
682 : 676 : fms->rate_limit = DEFAULT_RATE_LIMIT;
683 : 676 : fms->flags = flags;
684 : :
685 : 676 : if (is_directory)
686 : : {
687 : 560 : fms->dirname = g_strdup (filename);
688 : 560 : fms->basename = NULL;
689 : 560 : fms->filename = NULL;
690 : : }
691 : 116 : else if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS)
692 : : {
693 : 1 : fms->dirname = NULL;
694 : 1 : fms->basename = NULL;
695 : 1 : fms->filename = g_strdup (filename);
696 : : }
697 : : else
698 : : {
699 : 115 : fms->dirname = g_path_get_dirname (filename);
700 : 115 : fms->basename = g_path_get_basename (filename);
701 : 115 : fms->filename = NULL;
702 : : }
703 : :
704 : 676 : return fms;
705 : : }
706 : :
707 : 2258 : G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR)
708 : :
709 : : enum {
710 : : PROP_0,
711 : : PROP_RATE_LIMIT,
712 : : };
713 : :
714 : : static void
715 : 0 : g_local_file_monitor_get_property (GObject *object, guint prop_id,
716 : : GValue *value, GParamSpec *pspec)
717 : : {
718 : 0 : GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
719 : : gint64 rate_limit;
720 : :
721 : 0 : g_assert (prop_id == PROP_RATE_LIMIT);
722 : :
723 : 0 : rate_limit = g_file_monitor_source_get_rate_limit (monitor->source);
724 : 0 : rate_limit /= G_TIME_SPAN_MILLISECOND;
725 : :
726 : 0 : g_value_set_int (value, rate_limit);
727 : 0 : }
728 : :
729 : : static void
730 : 12 : g_local_file_monitor_set_property (GObject *object, guint prop_id,
731 : : const GValue *value, GParamSpec *pspec)
732 : : {
733 : 12 : GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
734 : : gint64 rate_limit;
735 : :
736 : 12 : g_assert (prop_id == PROP_RATE_LIMIT);
737 : :
738 : 12 : rate_limit = g_value_get_int (value);
739 : 12 : rate_limit *= G_TIME_SPAN_MILLISECOND;
740 : :
741 : 12 : if (g_file_monitor_source_set_rate_limit (monitor->source, rate_limit))
742 : 12 : g_object_notify (object, "rate-limit");
743 : 12 : }
744 : :
745 : : #ifndef G_OS_WIN32
746 : : static void
747 : 0 : g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor,
748 : : gpointer user_data)
749 : : {
750 : 0 : GLocalFileMonitor *local_monitor = user_data;
751 : : GUnixMountEntry *mount;
752 : : gboolean is_mounted;
753 : : GFile *file;
754 : :
755 : : /* Emulate unmount detection */
756 : 0 : mount = g_unix_mount_entry_at (local_monitor->source->dirname, NULL);
757 : :
758 : 0 : is_mounted = mount != NULL;
759 : :
760 : 0 : if (mount)
761 : 0 : g_unix_mount_entry_free (mount);
762 : :
763 : 0 : if (local_monitor->was_mounted != is_mounted)
764 : : {
765 : 0 : if (local_monitor->was_mounted && !is_mounted)
766 : : {
767 : 0 : file = g_file_new_for_path (local_monitor->source->dirname);
768 : 0 : g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED);
769 : 0 : g_object_unref (file);
770 : : }
771 : 0 : local_monitor->was_mounted = is_mounted;
772 : : }
773 : 0 : }
774 : : #endif
775 : :
776 : : static void
777 : 676 : g_local_file_monitor_start (GLocalFileMonitor *local_monitor,
778 : : const gchar *filename,
779 : : gboolean is_directory,
780 : : GFileMonitorFlags flags,
781 : : GMainContext *context)
782 : : {
783 : 676 : GLocalFileMonitorClass *class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor);
784 : : GFileMonitorSource *source;
785 : :
786 : 676 : g_return_if_fail (G_IS_LOCAL_FILE_MONITOR (local_monitor));
787 : :
788 : 676 : g_assert (!local_monitor->source);
789 : :
790 : 676 : source = g_file_monitor_source_new (local_monitor, filename, is_directory, flags);
791 : 676 : local_monitor->source = source; /* owns the ref */
792 : :
793 : 676 : if (is_directory && !class->mount_notify && (flags & G_FILE_MONITOR_WATCH_MOUNTS))
794 : : {
795 : : #ifdef G_OS_WIN32
796 : : /*claim everything was mounted */
797 : : local_monitor->was_mounted = TRUE;
798 : : #else
799 : : GUnixMountEntry *mount;
800 : :
801 : : /* Emulate unmount detection */
802 : :
803 : 0 : mount = g_unix_mount_entry_at (local_monitor->source->dirname, NULL);
804 : :
805 : 0 : local_monitor->was_mounted = mount != NULL;
806 : :
807 : 0 : if (mount)
808 : 0 : g_unix_mount_entry_free (mount);
809 : :
810 : 0 : local_monitor->mount_monitor = g_unix_mount_monitor_get ();
811 : 0 : g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed",
812 : : G_CALLBACK (g_local_file_monitor_mounts_changed), local_monitor,
813 : : G_CONNECT_DEFAULT);
814 : : #endif
815 : : }
816 : :
817 : 676 : g_source_attach ((GSource *) source, context);
818 : :
819 : 676 : G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor)->start (local_monitor,
820 : 676 : source->dirname, source->basename, source->filename,
821 : : source);
822 : : }
823 : :
824 : : static void
825 : 484 : g_local_file_monitor_dispose (GObject *object)
826 : : {
827 : 484 : GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
828 : :
829 : 484 : g_file_monitor_source_dispose (local_monitor->source);
830 : :
831 : 484 : G_OBJECT_CLASS (g_local_file_monitor_parent_class)->dispose (object);
832 : 484 : }
833 : :
834 : : static void
835 : 484 : g_local_file_monitor_finalize (GObject *object)
836 : : {
837 : 484 : GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
838 : :
839 : 484 : g_source_unref ((GSource *) local_monitor->source);
840 : :
841 : 484 : G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize (object);
842 : 484 : }
843 : :
844 : : static void
845 : 676 : g_local_file_monitor_init (GLocalFileMonitor* local_monitor)
846 : : {
847 : 676 : }
848 : :
849 : 57 : static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class)
850 : : {
851 : 57 : GObjectClass *gobject_class = G_OBJECT_CLASS (class);
852 : :
853 : 57 : gobject_class->get_property = g_local_file_monitor_get_property;
854 : 57 : gobject_class->set_property = g_local_file_monitor_set_property;
855 : 57 : gobject_class->dispose = g_local_file_monitor_dispose;
856 : 57 : gobject_class->finalize = g_local_file_monitor_finalize;
857 : :
858 : 57 : g_object_class_override_property (gobject_class, PROP_RATE_LIMIT, "rate-limit");
859 : 57 : }
860 : :
861 : : static GLocalFileMonitor *
862 : 676 : g_local_file_monitor_new (gboolean is_remote_fs,
863 : : gboolean is_directory,
864 : : GError **error)
865 : : {
866 : 676 : GType type = G_TYPE_INVALID;
867 : :
868 : 676 : if (is_remote_fs)
869 : 0 : type = _g_io_module_get_default_type (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
870 : : "GIO_USE_FILE_MONITOR",
871 : : G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
872 : :
873 : : /* Fallback rather to poll file monitor for remote files, see gfile.c. */
874 : 676 : if (type == G_TYPE_INVALID && (!is_remote_fs || is_directory))
875 : 676 : type = _g_io_module_get_default_type (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
876 : : "GIO_USE_FILE_MONITOR",
877 : : G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
878 : :
879 : 676 : if (type == G_TYPE_INVALID)
880 : : {
881 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
882 : : _("Unable to find default local file monitor type"));
883 : 0 : return NULL;
884 : : }
885 : :
886 : 676 : return g_object_new (type, NULL);
887 : : }
888 : :
889 : : GFileMonitor *
890 : 126 : g_local_file_monitor_new_for_path (const gchar *pathname,
891 : : gboolean is_directory,
892 : : GFileMonitorFlags flags,
893 : : GError **error)
894 : : {
895 : : GLocalFileMonitor *monitor;
896 : : gboolean is_remote_fs;
897 : :
898 : 126 : is_remote_fs = g_local_file_is_nfs_home (pathname);
899 : :
900 : 126 : monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
901 : :
902 : 126 : if (monitor)
903 : 126 : g_local_file_monitor_start (monitor, pathname, is_directory, flags, g_main_context_get_thread_default ());
904 : :
905 : 126 : return G_FILE_MONITOR (monitor);
906 : : }
907 : :
908 : : GFileMonitor *
909 : 550 : g_local_file_monitor_new_in_worker (const gchar *pathname,
910 : : gboolean is_directory,
911 : : GFileMonitorFlags flags,
912 : : GFileMonitorCallback callback,
913 : : gpointer user_data,
914 : : GClosureNotify destroy_user_data,
915 : : GError **error)
916 : : {
917 : : GLocalFileMonitor *monitor;
918 : : gboolean is_remote_fs;
919 : :
920 : 550 : is_remote_fs = g_local_file_is_nfs_home (pathname);
921 : :
922 : 550 : monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
923 : :
924 : 550 : if (monitor)
925 : : {
926 : 550 : if (callback)
927 : 550 : g_signal_connect_data (monitor, "changed", G_CALLBACK (callback),
928 : : user_data, destroy_user_data, G_CONNECT_DEFAULT);
929 : :
930 : 550 : g_local_file_monitor_start (monitor, pathname, is_directory, flags, GLIB_PRIVATE_CALL(g_get_worker_context) ());
931 : : }
932 : :
933 : 550 : return G_FILE_MONITOR (monitor);
934 : : }
|