1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
3 /* inotify-helper.c - GVFS Monitor based on inotify.
5 Copyright (C) 2007 John McCutchan
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.
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.
17 You should have received a copy of the GNU Lesser General Public License
18 along with this library; if not, see <http://www.gnu.org/licenses/>.
21 John McCutchan <john@johnmccutchan.com>
28 #include <sys/ioctl.h>
30 /* Just include the local header to stop all the pain */
31 #include <sys/inotify.h>
32 #include <gio/glocalfilemonitor.h>
33 #include <gio/gfile.h>
34 #include "inotify-helper.h"
35 #include "inotify-missing.h"
36 #include "inotify-path.h"
38 static gboolean ih_debug_enabled
= FALSE
;
39 #define IH_W if (ih_debug_enabled) g_warning
41 static gboolean
ih_event_callback (ik_event_t
*event
,
44 static void ih_not_missing_callback (inotify_sub
*sub
);
46 /* We share this lock with inotify-kernel.c and inotify-missing.c
48 * inotify-kernel.c takes the lock when it reads events from
49 * the kernel and when it processes those events
51 * inotify-missing.c takes the lock when it is scanning the missing
54 * We take the lock in all public functions
56 G_LOCK_DEFINE (inotify_lock
);
58 static GFileMonitorEvent
ih_mask_to_EventFlags (guint32 mask
);
63 * Initializes the inotify backend. This must be called before
64 * any other functions in this module.
66 * Returns: #TRUE if initialization succeeded, #FALSE otherwise
71 static gboolean initialized
= FALSE
;
72 static gboolean result
= FALSE
;
74 G_LOCK (inotify_lock
);
76 if (initialized
== TRUE
)
78 G_UNLOCK (inotify_lock
);
82 result
= _ip_startup (ih_event_callback
);
85 G_UNLOCK (inotify_lock
);
88 _im_startup (ih_not_missing_callback
);
90 IH_W ("started gvfs inotify backend\n");
94 G_UNLOCK (inotify_lock
);
100 * Adds a subscription to be monitored.
103 _ih_sub_add (inotify_sub
*sub
)
105 G_LOCK (inotify_lock
);
107 if (!_ip_start_watching (sub
))
110 G_UNLOCK (inotify_lock
);
116 * Cancels a subscription which was being monitored.
119 _ih_sub_cancel (inotify_sub
*sub
)
121 G_LOCK (inotify_lock
);
125 IH_W ("cancelling %s\n", sub
->dirname
);
126 sub
->cancelled
= TRUE
;
128 _ip_stop_watching (sub
);
131 G_UNLOCK (inotify_lock
);
137 _ih_fullpath_from_event (ik_event_t
*event
,
139 const char *filename
)
144 fullpath
= g_strdup_printf ("%s/%s", dirname
, filename
);
145 else if (event
->name
)
146 fullpath
= g_strdup_printf ("%s/%s", dirname
, event
->name
);
148 fullpath
= g_strdup_printf ("%s/", dirname
);
154 ih_event_callback (ik_event_t
*event
,
158 gboolean interesting
;
159 GFileMonitorEvent event_flags
;
161 g_assert (!file_event
); /* XXX hardlink support */
163 event_flags
= ih_mask_to_EventFlags (event
->mask
);
165 if (event
->mask
& IN_MOVE
)
167 /* We either have a rename (in the same directory) or a move
168 * (between different directories).
170 if (event
->pair
&& event
->pair
->wd
== event
->wd
)
172 /* this is a rename */
173 interesting
= g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_RENAMED
,
174 event
->name
, event
->pair
->name
, NULL
, event
->timestamp
);
182 const char *parent_dir
;
185 parent_dir
= _ip_get_path_for_wd (event
->pair
->wd
);
186 fullpath
= _ih_fullpath_from_event (event
->pair
, parent_dir
, NULL
);
187 other
= g_file_new_for_path (fullpath
);
193 /* This is either an incoming or outgoing move. Since we checked the
194 * event->mask above, it should have converted to a #GFileMonitorEvent
195 * properly. If not, the assumption we have made about event->mask
196 * only ever having a single bit set (apart from IN_ISDIR) is false.
197 * The kernel documentation is lacking here. */
198 g_assert (event_flags
!= -1);
199 interesting
= g_file_monitor_source_handle_event (sub
->user_data
, event_flags
,
200 event
->name
, NULL
, other
, event
->timestamp
);
203 g_object_unref (other
);
206 else if (event_flags
!= -1)
207 /* unpaired event -- no 'other' field */
208 interesting
= g_file_monitor_source_handle_event (sub
->user_data
, event_flags
,
209 event
->name
, NULL
, NULL
, event
->timestamp
);
211 if (event
->mask
& IN_CREATE
)
213 const gchar
*parent_dir
;
218 /* The kernel reports IN_CREATE for two types of events:
220 * - creat(), in which case IN_CLOSE_WRITE will come soon; or
221 * - link(), mkdir(), mknod(), etc., in which case it won't
223 * We can attempt to detect the second case and send the
224 * CHANGES_DONE immediately so that the user isn't left waiting.
226 * The detection for link() is not 100% reliable since the link
227 * count could be 1 if the original link was deleted or if
228 * O_TMPFILE was being used, but in that case the virtual
229 * CHANGES_DONE will be emitted to close the loop.
232 parent_dir
= _ip_get_path_for_wd (event
->wd
);
233 fullname
= _ih_fullpath_from_event (event
, parent_dir
, NULL
);
234 s
= stat (fullname
, &buf
);
237 /* if it doesn't look like the result of creat()... */
238 if (s
!= 0 || !S_ISREG (buf
.st_mode
) || buf
.st_nlink
!= 1)
239 g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
,
240 event
->name
, NULL
, NULL
, event
->timestamp
);
247 ih_not_missing_callback (inotify_sub
*sub
)
249 gint now
= g_get_monotonic_time ();
251 g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_CREATED
,
252 sub
->filename
, NULL
, NULL
, now
);
253 g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
,
254 sub
->filename
, NULL
, NULL
, now
);
257 /* Transforms a inotify event to a GVFS event. */
258 static GFileMonitorEvent
259 ih_mask_to_EventFlags (guint32 mask
)
265 return G_FILE_MONITOR_EVENT_CHANGED
;
267 return G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
;
269 return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED
;
273 return G_FILE_MONITOR_EVENT_DELETED
;
275 return G_FILE_MONITOR_EVENT_CREATED
;
277 return G_FILE_MONITOR_EVENT_MOVED_OUT
;
279 return G_FILE_MONITOR_EVENT_MOVED_IN
;
281 return G_FILE_MONITOR_EVENT_UNMOUNTED
;
284 case IN_CLOSE_NOWRITE
: