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