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 (at) redhat.com> 21 * John McCutchan <john (at) johnmccutchan.com> 22 * Sebastian Drge <slomo (at) 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 277