1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
3 /*
4 * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 * Authors: Lin Ma <lin.ma@sun.com>
23 */
24
25 #include "config.h"
26 #include <glib.h>
27 #include "fen-data.h"
28 #include "fen-helper.h"
29 #include "fen-kernel.h"
30 #ifdef GIO_COMPILATION
31 #include "gfilemonitor.h"
32 #else
33 #include "gam_event.h"
34 #include "gam_server.h"
35 #include "gam_protocol.h"
36 #endif
37
38 #ifdef GIO_COMPILATION
39 #define FH_W if (fh_debug_enabled) g_warning
40 static gboolean fh_debug_enabled = FALSE;
41 #else
42 #include "gam_error.h"
43 #define FH_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
44 #endif
45
46 G_LOCK_EXTERN (fen_lock);
47
48 static void default_emit_event_cb (fdata *f, int events);
49 static void default_emit_once_event_cb (fdata *f, int events, gpointer sub);
50 static int default_event_converter (int event);
51
52 static void
scan_children_init(node_t * f,gpointer sub)53 scan_children_init (node_t *f, gpointer sub)
54 {
55 GDir *dir;
56 GError *err = NULL;
57 node_op_t op = {NULL, NULL, pre_del_cb, NULL};
58 fdata* pdata;
59
60 FH_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
61 pdata = node_get_data (f);
62
63 dir = g_dir_open (NODE_NAME(f), 0, &err);
64 if (dir) {
65 const char *basename;
66
67 while ((basename = g_dir_read_name (dir)))
68 {
69 node_t *childf = NULL;
70 fdata* data;
71 GList *idx;
72
73 childf = children_find (f, basename);
74 if (childf == NULL) {
75 gchar *filename;
76
77 filename = g_build_filename (NODE_NAME(f), basename, NULL);
78 childf = add_node (f, filename);
79 g_assert (childf);
80 g_free (filename);
81 }
82 if ((data = node_get_data (childf)) == NULL) {
83 data = fdata_new (childf, FALSE);
84 }
85
86 if (is_monitoring (data)) {
87 /* Ignored */
88 } else if (/* !is_ported (data) && */
89 port_add (&data->fobj, &data->len, data)) {
90 /* Emit created to all other subs */
91 fdata_emit_events (data, FN_EVENT_CREATED);
92 }
93 /* Emit created to the new sub */
94 #ifdef GIO_COMPILATION
95 /* fdata_emit_events_once (data, FN_EVENT_CREATED, sub); */
96 #else
97 gam_server_emit_one_event (NODE_NAME(childf),
98 gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1);
99 #endif
100 }
101 g_dir_close (dir);
102 } else {
103 FH_W (err->message);
104 g_error_free (err);
105 }
106 }
107
108 /**
109 * fen_add
110 *
111 * Won't hold a ref, we have a timout callback to clean unused fdata.
112 * If there is no value for a key, add it and return it; else return the old
113 * one.
114 */
115 void
fen_add(const gchar * filename,gpointer sub,gboolean is_mondir)116 fen_add (const gchar *filename, gpointer sub, gboolean is_mondir)
117 {
118 node_op_t op = {NULL, add_missing_cb, pre_del_cb, (gpointer)filename};
119 node_t* f;
120 fdata* data;
121
122 g_assert (filename);
123 g_assert (sub);
124
125 G_LOCK (fen_lock);
126 f = find_node_full (filename, &op);
127 FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename);
128 g_assert (f);
129 data = node_get_data (f);
130 if (data == NULL) {
131 data = fdata_new (f, is_mondir);
132 }
133
134 if (is_mondir) {
135 data->mon_dir_num ++;
136 }
137
138 /* Change to active */
139 #ifdef GIO_COMPILATION
140 if (port_add (&data->fobj, &data->len, data) ||
141 g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) {
142 if (is_mondir) {
143 scan_children_init (f, sub);
144 }
145 fdata_sub_add (data, sub);
146 } else {
147 fdata_sub_add (data, sub);
148 fdata_adjust_deleted (data);
149 }
150 #else
151 if (port_add (&data->fobj, &data->len, data) ||
152 g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) {
153 gam_server_emit_one_event (FN_NAME(data),
154 gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1);
155 if (is_mondir) {
156 scan_children_init (f, sub);
157 }
158 gam_server_emit_one_event (FN_NAME(data),
159 gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1);
160 fdata_sub_add (data, sub);
161 } else {
162 fdata_sub_add (data, sub);
163 gam_server_emit_one_event (FN_NAME(data),
164 gam_subscription_is_dir (sub), GAMIN_EVENT_DELETED, sub, 1);
165 fdata_adjust_deleted (data);
166 gam_server_emit_one_event (FN_NAME(data),
167 gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1);
168 }
169 #endif
170 G_UNLOCK (fen_lock);
171 }
172
173 void
fen_remove(const gchar * filename,gpointer sub,gboolean is_mondir)174 fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir)
175 {
176 node_op_t op = {NULL, add_missing_cb, pre_del_cb, (gpointer)filename};
177 node_t* f;
178 fdata* data;
179
180 g_assert (filename);
181 g_assert (sub);
182
183 G_LOCK (fen_lock);
184 f = find_node (filename);
185 FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename);
186
187 g_assert (f);
188 data = node_get_data (f);
189 g_assert (data);
190
191 if (is_mondir) {
192 data->mon_dir_num --;
193 }
194 fdata_sub_remove (data, sub);
195 if (FN_IS_PASSIVE(data)) {
196 #ifdef GIO_COMPILATION
197 pending_remove_node (f, &op);
198 #else
199 remove_node (f, &op);
200 #endif
201 }
202 G_UNLOCK (fen_lock);
203 }
204
205 static gboolean
fen_init_once_func(gpointer data)206 fen_init_once_func (gpointer data)
207 {
208 FH_W ("%s\n", __func__);
209 if (!node_class_init ()) {
210 FH_W ("node_class_init failed.");
211 return FALSE;
212 }
213 if (!fdata_class_init (default_emit_event_cb,
214 default_emit_once_event_cb,
215 default_event_converter)) {
216 FH_W ("fdata_class_init failed.");
217 return FALSE;
218 }
219 return TRUE;
220 }
221
222 gboolean
fen_init()223 fen_init ()
224 {
225 #ifdef GIO_COMPILATION
226 static GOnce fen_init_once = G_ONCE_INIT;
227 g_once (&fen_init_once, (GThreadFunc)fen_init_once_func, NULL);
228 return (gboolean)fen_init_once.retval;
229 #else
230 return fen_init_once_func (NULL);
231 #endif
232 }
233
234 static void
default_emit_once_event_cb(fdata * f,int events,gpointer sub)235 default_emit_once_event_cb (fdata *f, int events, gpointer sub)
236 {
237 #ifdef GIO_COMPILATION
238 GFile* child;
239 fen_sub* _sub = (fen_sub*)sub;
240 child = g_file_new_for_path (FN_NAME(f));
241 g_file_monitor_emit_event (G_FILE_MONITOR (_sub->user_data),
242 child, NULL, events);
243 g_object_unref (child);
244 #else
245 gam_server_emit_one_event (FN_NAME(f),
246 gam_subscription_is_dir (sub), events, sub, 1);
247 #endif
248 }
249
250 static void
default_emit_event_cb(fdata * f,int events)251 default_emit_event_cb (fdata *f, int events)
252 {
253 GList* i;
254 fdata* pdata;
255
256 #ifdef GIO_COMPILATION
257 GFile* child;
258 child = g_file_new_for_path (FN_NAME(f));
259 for (i = f->subs; i; i = i->next) {
260 fen_sub* sub = (fen_sub*)i->data;
261 gboolean file_is_dir = sub->is_mondir;
262 if ((events != G_FILE_MONITOR_EVENT_CHANGED &&
263 events != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) ||
264 !file_is_dir) {
265 g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
266 child, NULL, events);
267 }
268 }
269 if ((pdata = get_parent_data (f)) != NULL) {
270 for (i = pdata->subs; i; i = i->next) {
271 fen_sub* sub = (fen_sub*)i->data;
272 gboolean file_is_dir = sub->is_mondir;
273 g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
274 child, NULL, events);
275 }
276 }
277 g_object_unref (child);
278 #else
279 for (i = f->subs; i; i = i->next) {
280 gboolean file_is_dir = gam_subscription_is_dir (i->data);
281 if (events != GAMIN_EVENT_CHANGED || !file_is_dir) {
282 gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1);
283 }
284 }
285 if ((pdata = get_parent_data (f)) != NULL) {
286 for (i = pdata->subs; i; i = i->next) {
287 gboolean file_is_dir = gam_subscription_is_dir (i->data);
288 gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1);
289 }
290 }
291 #endif
292 }
293
294 static int
default_event_converter(int event)295 default_event_converter (int event)
296 {
297 #ifdef GIO_COMPILATION
298 switch (event) {
299 case FN_EVENT_CREATED:
300 return G_FILE_MONITOR_EVENT_CREATED;
301 case FILE_DELETE:
302 case FILE_RENAME_FROM:
303 return G_FILE_MONITOR_EVENT_DELETED;
304 case UNMOUNTED:
305 return G_FILE_MONITOR_EVENT_UNMOUNTED;
306 case FILE_ATTRIB:
307 return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
308 case MOUNTEDOVER:
309 case FILE_MODIFIED:
310 case FILE_RENAME_TO:
311 return G_FILE_MONITOR_EVENT_CHANGED;
312 default:
313 /* case FILE_ACCESS: */
314 g_assert_not_reached ();
315 return -1;
316 }
317 #else
318 switch (event) {
319 case FN_EVENT_CREATED:
320 return GAMIN_EVENT_CREATED;
321 case FILE_DELETE:
322 case FILE_RENAME_FROM:
323 return GAMIN_EVENT_DELETED;
324 case FILE_ATTRIB:
325 case MOUNTEDOVER:
326 case UNMOUNTED:
327 case FILE_MODIFIED:
328 case FILE_RENAME_TO:
329 return GAMIN_EVENT_CHANGED;
330 default:
331 /* case FILE_ACCESS: */
332 g_assert_not_reached ();
333 return -1;
334 }
335 #endif
336 }
337