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