Bug 544140 - fam-helper 64-bit issue?
[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     FAMEvent ev;
75     fam_sub* sub = NULL;
76     gboolean cancelled;
77     
78     if (FAMNextEvent (fam_connection, &ev) != 1) {
79       FAMClose (fam_connection);
80       g_free (fam_connection);
81       g_source_remove (fam_watch_id);
82       fam_watch_id = 0;
83       fam_connection = NULL;
84       return FALSE;
85     }
86     
87     sub = (fam_sub*)ev.userdata;
88     cancelled = sub->cancelled;
89     if (ev.code == FAMAcknowledge && cancelled)
90       {
91         _fam_sub_free (sub);
92         continue;
93       }
94     
95     if (cancelled)
96       continue;
97     
98     if (sub->directory)
99       {
100         GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
101         GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
102         gchar* path = NULL;
103         GFile *child, *parent;
104         
105         /* unsupported event */
106         if (eflags == -1)
107           continue;
108         
109         if (ev.filename[0] == '/')
110           path = g_strdup (ev.filename);
111         else
112           path = g_strdup_printf ("%s/%s", sub->pathname, ev.filename);
113
114         child = g_file_new_for_path (path);
115         parent = g_file_get_parent (child);
116         g_file_monitor_emit_event (monitor, child, NULL, eflags);
117         g_free (path);
118         g_object_unref (child);
119         g_object_unref (parent);
120       } else {
121         GFile *child;
122         GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
123         GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
124         gchar* path = NULL;
125       
126         if (eflags == -1)
127           continue;
128         path = g_strdup (ev.filename);
129         child = g_file_new_for_path (path);
130         g_file_monitor_emit_event (monitor, child, NULL, eflags);
131         g_free (path);
132         g_object_unref (child);
133       }
134   }
135   
136   return TRUE;
137 }
138
139 static gboolean
140 fam_callback (GIOChannel *source,
141               GIOCondition condition,
142               gpointer data)
143 {
144   gboolean res;
145   G_LOCK (fam_connection);
146   
147   res = fam_do_iter_unlocked ();
148   
149   G_UNLOCK (fam_connection);
150   return res;
151 }
152
153 gboolean
154 _fam_sub_startup (void)
155 {
156   GIOChannel *ioc;
157   
158   G_LOCK (fam_connection);
159   
160   if (fam_connection == NULL) {
161     fam_connection = g_new0 (FAMConnection, 1);
162     if (FAMOpen2 (fam_connection, "gvfs user") != 0) {
163       g_warning ("FAMOpen failed, FAMErrno=%d\n", FAMErrno);
164       g_free (fam_connection);
165       fam_connection = NULL;
166       G_UNLOCK (fam_connection);
167       return FALSE;
168     }
169 #ifdef HAVE_FAM_NO_EXISTS
170     /* This is a gamin extension that avoids sending all the Exists event for dir monitors */
171     FAMNoExists (fam_connection);
172 #endif
173     ioc = g_io_channel_unix_new (FAMCONNECTION_GETFD(fam_connection));
174     fam_watch_id = g_io_add_watch (ioc,
175                                    G_IO_IN | G_IO_HUP | G_IO_ERR,
176                                    fam_callback, fam_connection);
177     g_io_channel_unref (ioc);
178   }
179   
180   G_UNLOCK (fam_connection);
181   
182   return TRUE;
183 }
184
185 void
186 _fam_sub_shutdown (void)
187 {
188   G_LOCK (fam_connection);
189
190   if (fam_connection != NULL) {
191     FAMClose (fam_connection);
192     g_free (fam_connection);
193     g_source_remove (fam_watch_id);
194     fam_watch_id = 0;
195     fam_connection = NULL;
196   }
197
198   G_UNLOCK (fam_connection);
199 }
200
201 fam_sub*
202 _fam_sub_add (const gchar* pathname,
203               gboolean directory,
204               gpointer user_data)
205 {
206   fam_sub *sub;
207
208   if (!_fam_sub_startup ())
209     return NULL;
210   
211   sub = g_new0 (fam_sub, 1);
212   sub->pathname = g_strdup (pathname);
213   sub->directory = directory;
214   sub->user_data = user_data;
215   
216   G_LOCK (fam_connection);
217   /* We need to queue up incoming messages to avoid blocking on write
218    *  if there are many monitors being canceled */
219   fam_do_iter_unlocked ();
220   
221   if (fam_connection == NULL) {
222     G_UNLOCK (fam_connection);
223     return NULL;
224   }
225   
226   if (directory)
227     FAMMonitorDirectory (fam_connection, pathname, &sub->request, sub);
228   else
229     FAMMonitorFile (fam_connection, pathname, &sub->request, sub);
230   
231   G_UNLOCK (fam_connection);
232   return sub;
233 }
234
235 gboolean
236 _fam_sub_cancel (fam_sub* sub)
237 {
238   if (sub->cancelled)
239     return TRUE;
240   
241   sub->cancelled = TRUE;
242   
243   G_LOCK (fam_connection);
244   /* We need to queue up incoming messages to avoid blocking on write
245    *  if there are many monitors being canceled */
246   fam_do_iter_unlocked ();
247   
248   if (fam_connection == NULL) {
249     G_UNLOCK (fam_connection);
250     return FALSE;
251   }
252   
253   FAMCancelMonitor (fam_connection, &sub->request);
254   
255   G_UNLOCK (fam_connection);
256   
257   return TRUE;
258 }
259
260 void
261 _fam_sub_free (fam_sub* sub)
262 {
263   g_free (sub->pathname);
264   g_free (sub);
265 }
266