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