• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
fam_event_to_file_monitor_event(int code)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
fam_do_iter_unlocked(void)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
fam_callback(GIOChannel * source,GIOCondition condition,gpointer data)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
_fam_sub_startup(void)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
_fam_sub_shutdown(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*
_fam_sub_add(const gchar * pathname,gboolean directory,gpointer user_data)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
_fam_sub_cancel(fam_sub * sub)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
_fam_sub_free(fam_sub * sub)271 _fam_sub_free (fam_sub* sub)
272 {
273   g_free (sub->pathname);
274   g_free (sub);
275 }
276 
277