genmarshal Only wrap body prototypes in C++ guards
[glib.git] / gio / inotify / inotify-helper.c
blobdce57e507e0bbae308105467542d0b3492e91e58
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/>.
20 Authors:
21 John McCutchan <john@johnmccutchan.com>
24 #include "config.h"
25 #include <errno.h>
26 #include <time.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/stat.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,
42 inotify_sub *sub,
43 gboolean file_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
52 * list.
54 * We take the lock in all public functions
56 G_LOCK_DEFINE (inotify_lock);
58 static GFileMonitorEvent ih_mask_to_EventFlags (guint32 mask);
60 /**
61 * _ih_startup:
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
68 gboolean
69 _ih_startup (void)
71 static gboolean initialized = FALSE;
72 static gboolean result = FALSE;
74 G_LOCK (inotify_lock);
76 if (initialized == TRUE)
78 G_UNLOCK (inotify_lock);
79 return result;
82 result = _ip_startup (ih_event_callback);
83 if (!result)
85 G_UNLOCK (inotify_lock);
86 return FALSE;
88 _im_startup (ih_not_missing_callback);
90 IH_W ("started gvfs inotify backend\n");
92 initialized = TRUE;
94 G_UNLOCK (inotify_lock);
96 return TRUE;
100 * Adds a subscription to be monitored.
102 gboolean
103 _ih_sub_add (inotify_sub *sub)
105 G_LOCK (inotify_lock);
107 if (!_ip_start_watching (sub))
108 _im_add (sub);
110 G_UNLOCK (inotify_lock);
112 return TRUE;
116 * Cancels a subscription which was being monitored.
118 gboolean
119 _ih_sub_cancel (inotify_sub *sub)
121 G_LOCK (inotify_lock);
123 if (!sub->cancelled)
125 IH_W ("cancelling %s\n", sub->dirname);
126 sub->cancelled = TRUE;
127 _im_rm (sub);
128 _ip_stop_watching (sub);
131 G_UNLOCK (inotify_lock);
133 return TRUE;
136 static char *
137 _ih_fullpath_from_event (ik_event_t *event,
138 const char *dirname,
139 const char *filename)
141 char *fullpath;
143 if (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);
147 else
148 fullpath = g_strdup_printf ("%s/", dirname);
150 return fullpath;
153 static gboolean
154 ih_event_callback (ik_event_t *event,
155 inotify_sub *sub,
156 gboolean file_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);
173 else
175 GFile *other;
177 if (event->pair)
179 const char *parent_dir;
180 gchar *fullpath;
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);
185 g_free (fullpath);
187 else
188 other = NULL;
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);
194 if (other)
195 g_object_unref (other);
198 else
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;
206 gchar *fullname;
207 struct stat buf;
208 gint s;
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);
227 g_free (fullname);
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);
235 return interesting;
238 static void
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)
253 mask &= ~IN_ISDIR;
254 switch (mask)
256 case IN_MODIFY:
257 return G_FILE_MONITOR_EVENT_CHANGED;
258 case IN_CLOSE_WRITE:
259 return G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT;
260 case IN_ATTRIB:
261 return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
262 case IN_MOVE_SELF:
263 case IN_DELETE:
264 case IN_DELETE_SELF:
265 return G_FILE_MONITOR_EVENT_DELETED;
266 case IN_CREATE:
267 return G_FILE_MONITOR_EVENT_CREATED;
268 case IN_MOVED_FROM:
269 return G_FILE_MONITOR_EVENT_MOVED_OUT;
270 case IN_MOVED_TO:
271 return G_FILE_MONITOR_EVENT_MOVED_IN;
272 case IN_UNMOUNT:
273 return G_FILE_MONITOR_EVENT_UNMOUNTED;
274 case IN_Q_OVERFLOW:
275 case IN_OPEN:
276 case IN_CLOSE_NOWRITE:
277 case IN_ACCESS:
278 case IN_IGNORED:
279 default:
280 return -1;