Merge remote branch 'gvdb/master'
[platform/upstream/glib.git] / gio / fam / fam-helper.c
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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Alexander Larsson <alexl@redhat.com>
21  *          John McCutchan <john@johnmccutchan.com> 
22  *          Sebastian Dröge <slomo@circular-chaos.org>
23  */
24
25 #include "config.h"
26 #include <fam.h>
27 #include <gio/gfilemonitor.h>
28
29 #include "gfile.h"
30 #include "fam-helper.h"
31
32 static FAMConnection* fam_connection = NULL;
33 static gint fam_watch_id = 0;
34 G_LOCK_DEFINE_STATIC(fam_connection);
35
36 struct _fam_sub
37 {
38   gchar *pathname;
39   gboolean directory;
40   gpointer user_data;
41   gboolean cancelled;
42   FAMRequest request;
43 };
44
45 /* This uses int as the argument type because the
46    real type differs between implementations:
47    gamin has "typedef enum FAMCodes {....} FAMCodes;"
48    fam has "enum FAMCodes { ... }".
49 */
50 static GFileMonitorEvent  
51 fam_event_to_file_monitor_event (int code)
52 {
53   switch (code)
54     {
55     case FAMChanged:
56       return G_FILE_MONITOR_EVENT_CHANGED;
57       break;
58     case FAMDeleted:
59       return G_FILE_MONITOR_EVENT_DELETED;
60       break;
61     case FAMCreated:
62       return G_FILE_MONITOR_EVENT_CREATED;
63       break;
64     default:
65       return -1;
66       break;
67     }
68 }
69
70 static gboolean
71 fam_do_iter_unlocked (void)
72 {
73   while (fam_connection != NULL && FAMPending (fam_connection)) 
74     {
75       FAMEvent ev;
76       fam_sub* sub = NULL;
77       gboolean cancelled;
78     
79       if (FAMNextEvent (fam_connection, &ev) != 1) 
80         {
81           FAMClose (fam_connection);
82           g_free (fam_connection);
83           g_source_remove (fam_watch_id);
84           fam_watch_id = 0;
85           fam_connection = NULL;
86           return FALSE;
87         }
88     
89       sub = (fam_sub*)ev.userdata;
90       cancelled = sub->cancelled;
91       if (ev.code == FAMAcknowledge && cancelled)
92         {
93           _fam_sub_free (sub);
94           continue;
95         }
96     
97       if (cancelled)
98         continue;
99     
100       if (sub->directory)
101         {
102           GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
103           GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
104           gchar* path = NULL;
105           GFile *child, *parent;
106         
107           /* unsupported event */
108           if (eflags == -1)
109             continue;
110         
111           if (ev.filename[0] == '/')
112             path = g_strdup (ev.filename);
113           else
114             path = g_strdup_printf ("%s/%s", sub->pathname, ev.filename);
115
116           child = g_file_new_for_path (path);
117           parent = g_file_get_parent (child);
118           g_file_monitor_emit_event (monitor, child, NULL, eflags);
119           g_free (path);
120           g_object_unref (child);
121           g_object_unref (parent);
122         } 
123       else 
124         {
125           GFile *child;
126           GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
127           GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
128           gchar* path = NULL;
129       
130           if (eflags == -1)
131             continue;
132           path = g_strdup (ev.filename);
133           child = g_file_new_for_path (path);
134           g_file_monitor_emit_event (monitor, child, NULL, eflags);
135           g_free (path);
136           g_object_unref (child);
137         }
138     }
139   
140   return TRUE;
141 }
142
143 static gboolean
144 fam_callback (GIOChannel   *source,
145               GIOCondition  condition,
146               gpointer      data)
147 {
148   gboolean res;
149   G_LOCK (fam_connection);
150   
151   res = fam_do_iter_unlocked ();
152   
153   G_UNLOCK (fam_connection);
154   return res;
155 }
156
157 gboolean
158 _fam_sub_startup (void)
159 {
160   GIOChannel *ioc;
161   
162   G_LOCK (fam_connection);
163   
164   if (fam_connection == NULL) 
165     {
166       fam_connection = g_new0 (FAMConnection, 1);
167       if (FAMOpen2 (fam_connection, "gvfs user") != 0) 
168         {
169           g_warning ("FAMOpen failed, FAMErrno=%d\n", FAMErrno);
170           g_free (fam_connection);
171           fam_connection = NULL;
172           G_UNLOCK (fam_connection);
173           return FALSE;
174         }
175 #ifdef HAVE_FAM_NO_EXISTS
176     /* This is a gamin extension that avoids sending all the Exists event for dir monitors */
177       FAMNoExists (fam_connection);
178 #endif
179       ioc = g_io_channel_unix_new (FAMCONNECTION_GETFD(fam_connection));
180       fam_watch_id = g_io_add_watch (ioc,
181                                      G_IO_IN | G_IO_HUP | G_IO_ERR,
182                                      fam_callback, fam_connection);
183       g_io_channel_unref (ioc);
184     }
185   
186   G_UNLOCK (fam_connection);
187   
188   return TRUE;
189 }
190
191 void
192 _fam_sub_shutdown (void)
193 {
194   G_LOCK (fam_connection);
195
196   if (fam_connection != NULL) 
197     {
198       FAMClose (fam_connection);
199       g_free (fam_connection);
200       g_source_remove (fam_watch_id);
201       fam_watch_id = 0;
202       fam_connection = NULL;
203     }
204
205   G_UNLOCK (fam_connection);
206 }
207
208 fam_sub*
209 _fam_sub_add (const gchar *pathname,
210               gboolean     directory,
211               gpointer     user_data)
212 {
213   fam_sub *sub;
214
215   if (!_fam_sub_startup ())
216     return NULL;
217   
218   G_LOCK (fam_connection);
219   /* We need to queue up incoming messages to avoid blocking on write
220    *  if there are many monitors being canceled */
221   fam_do_iter_unlocked ();
222   
223   if (fam_connection == NULL) 
224     {
225       G_UNLOCK (fam_connection);
226       return NULL;
227     }
228
229   sub = g_new0 (fam_sub, 1);
230   sub->pathname = g_strdup (pathname);
231   sub->directory = directory;
232   sub->user_data = user_data;
233   
234   if (directory)
235     FAMMonitorDirectory (fam_connection, pathname, &sub->request, sub);
236   else
237     FAMMonitorFile (fam_connection, pathname, &sub->request, sub);
238   
239   G_UNLOCK (fam_connection);
240
241   return sub;
242 }
243
244 gboolean
245 _fam_sub_cancel (fam_sub* sub)
246 {
247   if (sub->cancelled)
248     return TRUE;
249   
250   sub->cancelled = TRUE;
251   
252   G_LOCK (fam_connection);
253   /* We need to queue up incoming messages to avoid blocking on write
254    *  if there are many monitors being canceled */
255   fam_do_iter_unlocked ();
256   
257   if (fam_connection == NULL) 
258     {
259       G_UNLOCK (fam_connection);
260       return FALSE;
261     }
262   
263   FAMCancelMonitor (fam_connection, &sub->request);
264   
265   G_UNLOCK (fam_connection);
266   
267   return TRUE;
268 }
269
270 void
271 _fam_sub_free (fam_sub* sub)
272 {
273   g_free (sub->pathname);
274   g_free (sub);
275 }
276