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
;
160 g_assert (!file_event
); /* XXX hardlink support */
162 if (event
->mask
& IN_MOVE
)
164 /* We either have a rename (in the same directory) or a move
165 * (between different directories).
167 if (event
->pair
&& event
->pair
->wd
== event
->wd
)
169 /* this is a rename */
170 interesting
= g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_RENAMED
,
171 event
->name
, event
->pair
->name
, NULL
, event
->timestamp
);
179 const char *parent_dir
;
182 parent_dir
= _ip_get_path_for_wd (event
->pair
->wd
);
183 fullpath
= _ih_fullpath_from_event (event
->pair
, parent_dir
, NULL
);
184 other
= g_file_new_for_path (fullpath
);
190 /* this is either an incoming or outgoing move */
191 interesting
= g_file_monitor_source_handle_event (sub
->user_data
, ih_mask_to_EventFlags (event
->mask
),
192 event
->name
, NULL
, other
, event
->timestamp
);
195 g_object_unref (other
);
199 /* unpaired event -- no 'other' field */
200 interesting
= g_file_monitor_source_handle_event (sub
->user_data
, ih_mask_to_EventFlags (event
->mask
),
201 event
->name
, NULL
, NULL
, event
->timestamp
);
203 if (event
->mask
& IN_CREATE
)
205 const gchar
*parent_dir
;
210 /* The kernel reports IN_CREATE for two types of events:
212 * - creat(), in which case IN_CLOSE_WRITE will come soon; or
213 * - link(), mkdir(), mknod(), etc., in which case it won't
215 * We can attempt to detect the second case and send the
216 * CHANGES_DONE immediately so that the user isn't left waiting.
218 * The detection for link() is not 100% reliable since the link
219 * count could be 1 if the original link was deleted or if
220 * O_TMPFILE was being used, but in that case the virtual
221 * CHANGES_DONE will be emitted to close the loop.
224 parent_dir
= _ip_get_path_for_wd (event
->wd
);
225 fullname
= _ih_fullpath_from_event (event
, parent_dir
, NULL
);
226 s
= stat (fullname
, &buf
);
229 /* if it doesn't look like the result of creat()... */
230 if (s
!= 0 || !S_ISREG (buf
.st_mode
) || buf
.st_nlink
!= 1)
231 g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
,
232 event
->name
, NULL
, NULL
, event
->timestamp
);
239 ih_not_missing_callback (inotify_sub
*sub
)
241 gint now
= g_get_monotonic_time ();
243 g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_CREATED
,
244 sub
->filename
, NULL
, NULL
, now
);
245 g_file_monitor_source_handle_event (sub
->user_data
, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
,
246 sub
->filename
, NULL
, NULL
, now
);
249 /* Transforms a inotify event to a GVFS event. */
250 static GFileMonitorEvent
251 ih_mask_to_EventFlags (guint32 mask
)
257 return G_FILE_MONITOR_EVENT_CHANGED
;
259 return G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
;
261 return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED
;
265 return G_FILE_MONITOR_EVENT_DELETED
;
267 return G_FILE_MONITOR_EVENT_CREATED
;
269 return G_FILE_MONITOR_EVENT_MOVED_OUT
;
271 return G_FILE_MONITOR_EVENT_MOVED_IN
;
273 return G_FILE_MONITOR_EVENT_UNMOUNTED
;
276 case IN_CLOSE_NOWRITE
: