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., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23 #include <config.h>
24
25 #include <strings.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <fcntl.h>
32
33 #include <glib.h>
34 #include <glib-object.h>
35 #include <glib/gi18n.h>
36 #include <gvfsmonitor.h>
37 #include <gvfsdaemonprotocol.h>
38 #include <gvfsdaemonutils.h>
39 #include <gvfsjobcloseread.h>
40 #include <gvfsjobclosewrite.h>
41 #include <gvfsdbus.h>
42
43 #define OBJ_PATH_PREFIX "/org/gtk/vfs/daemon/dirmonitor/"
44
45
46 typedef struct {
47 GDBusConnection *connection;
48 char *id;
49 char *object_path;
50 GVfsMonitor *monitor;
51 } Subscriber;
52
53 struct _GVfsMonitorPrivate
54 {
55 GVfsDaemon *daemon;
56 GVfsBackend *backend; /* weak ref */
57 GMountSpec *mount_spec;
58 char *object_path;
59 GList *subscribers;
60 };
61
62 /* atomic */
63 static volatile gint path_counter = 1;
64
65 G_DEFINE_TYPE_WITH_PRIVATE (GVfsMonitor, g_vfs_monitor, G_TYPE_OBJECT)
66
67 static void unsubscribe (Subscriber *subscriber);
68
69 static void
backend_died(GVfsMonitor * monitor,GObject * old_backend)70 backend_died (GVfsMonitor *monitor,
71 GObject *old_backend)
72 {
73 Subscriber *subscriber;
74
75 /*
76 * Take an extra ref on the monitor because
77 * unsubscribing may lead to the last ref
78 * being released.
79 */
80 g_object_ref (G_OBJECT (monitor));
81
82 monitor->priv->backend = NULL;
83
84 while (monitor->priv->subscribers != NULL)
85 {
86 subscriber = monitor->priv->subscribers->data;
87 unsubscribe (subscriber);
88 }
89
90 g_object_unref (G_OBJECT (monitor));
91 }
92
93 static void
g_vfs_monitor_finalize(GObject * object)94 g_vfs_monitor_finalize (GObject *object)
95 {
96 GVfsMonitor *monitor;
97
98 monitor = G_VFS_MONITOR (object);
99
100 if (monitor->priv->backend)
101 g_object_weak_unref (G_OBJECT (monitor->priv->backend),
102 (GWeakNotify)backend_died,
103 monitor);
104
105 g_vfs_daemon_unregister_path (monitor->priv->daemon, monitor->priv->object_path);
106 g_object_unref (monitor->priv->daemon);
107
108 g_mount_spec_unref (monitor->priv->mount_spec);
109
110 g_free (monitor->priv->object_path);
111
112 if (G_OBJECT_CLASS (g_vfs_monitor_parent_class)->finalize)
113 (*G_OBJECT_CLASS (g_vfs_monitor_parent_class)->finalize) (object);
114 }
115
116 static void
g_vfs_monitor_class_init(GVfsMonitorClass * klass)117 g_vfs_monitor_class_init (GVfsMonitorClass *klass)
118 {
119 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
120
121 gobject_class->finalize = g_vfs_monitor_finalize;
122 }
123
124 static void
g_vfs_monitor_init(GVfsMonitor * monitor)125 g_vfs_monitor_init (GVfsMonitor *monitor)
126 {
127 gint id;
128
129 monitor->priv = g_vfs_monitor_get_instance_private (monitor);
130
131 id = g_atomic_int_add (&path_counter, 1);
132 monitor->priv->object_path = g_strdup_printf (OBJ_PATH_PREFIX"%d", id);
133 }
134
135 static gboolean
matches_subscriber(Subscriber * subscriber,GDBusConnection * connection,const char * object_path,const char * dbus_id)136 matches_subscriber (Subscriber *subscriber,
137 GDBusConnection *connection,
138 const char *object_path,
139 const char *dbus_id)
140 {
141 return (subscriber->connection == connection &&
142 strcmp (subscriber->object_path, object_path) == 0 &&
143 ((dbus_id == NULL && subscriber->id == NULL) ||
144 (dbus_id != NULL && subscriber->id != NULL &&
145 strcmp (subscriber->id, dbus_id) == 0)));
146 }
147
148 static void
unsubscribe(Subscriber * subscriber)149 unsubscribe (Subscriber *subscriber)
150 {
151 subscriber->monitor->priv->subscribers = g_list_remove (subscriber->monitor->priv->subscribers, subscriber);
152
153 g_signal_handlers_disconnect_by_data (subscriber->connection, subscriber);
154 g_object_unref (subscriber->connection);
155 g_free (subscriber->id);
156 g_free (subscriber->object_path);
157 g_object_unref (subscriber->monitor);
158 g_free (subscriber);
159 }
160
161 static void
subscriber_connection_closed(GDBusConnection * connection,gboolean remote_peer_vanished,GError * error,Subscriber * subscriber)162 subscriber_connection_closed (GDBusConnection *connection,
163 gboolean remote_peer_vanished,
164 GError *error,
165 Subscriber *subscriber)
166 {
167 unsubscribe (subscriber);
168 }
169
170 static gboolean
handle_subscribe(GVfsDBusMonitor * object,GDBusMethodInvocation * invocation,const gchar * arg_object_path,GVfsMonitor * monitor)171 handle_subscribe (GVfsDBusMonitor *object,
172 GDBusMethodInvocation *invocation,
173 const gchar *arg_object_path,
174 GVfsMonitor *monitor)
175 {
176 Subscriber *subscriber;
177
178 subscriber = g_new0 (Subscriber, 1);
179 subscriber->connection = g_object_ref (g_dbus_method_invocation_get_connection (invocation));
180 subscriber->id = g_strdup (g_dbus_method_invocation_get_sender (invocation));
181 subscriber->object_path = g_strdup (arg_object_path);
182 subscriber->monitor = g_object_ref (monitor);
183
184 g_signal_connect (subscriber->connection, "closed", G_CALLBACK (subscriber_connection_closed), subscriber);
185
186 monitor->priv->subscribers = g_list_prepend (monitor->priv->subscribers, subscriber);
187
188 gvfs_dbus_monitor_complete_subscribe (object, invocation);
189
190 return TRUE;
191 }
192
193 static gboolean
handle_unsubscribe(GVfsDBusMonitor * object,GDBusMethodInvocation * invocation,const gchar * arg_object_path,GVfsMonitor * monitor)194 handle_unsubscribe (GVfsDBusMonitor *object,
195 GDBusMethodInvocation *invocation,
196 const gchar *arg_object_path,
197 GVfsMonitor *monitor)
198 {
199 Subscriber *subscriber;
200 GList *l;
201
202 g_object_ref (monitor); /* Keep alive during possible last remove */
203 for (l = monitor->priv->subscribers; l != NULL; l = l->next)
204 {
205 subscriber = l->data;
206
207 if (matches_subscriber (subscriber,
208 g_dbus_method_invocation_get_connection (invocation),
209 arg_object_path,
210 g_dbus_method_invocation_get_sender (invocation)))
211 {
212 unsubscribe (subscriber);
213 break;
214 }
215 }
216 g_object_unref (monitor);
217
218 gvfs_dbus_monitor_complete_unsubscribe (object, invocation);
219
220 return TRUE;
221 }
222
223 static GDBusInterfaceSkeleton *
register_path_cb(GDBusConnection * conn,const char * obj_path,gpointer data)224 register_path_cb (GDBusConnection *conn,
225 const char *obj_path,
226 gpointer data)
227 {
228 GError *error;
229 GVfsDBusMonitor *skeleton;
230
231 skeleton = gvfs_dbus_monitor_skeleton_new ();
232 g_signal_connect (skeleton, "handle-subscribe", G_CALLBACK (handle_subscribe), data);
233 g_signal_connect (skeleton, "handle-unsubscribe", G_CALLBACK (handle_unsubscribe), data);
234
235 error = NULL;
236 if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
237 conn,
238 obj_path,
239 &error))
240 {
241 g_warning ("Error registering path: %s (%s, %d)\n",
242 error->message, g_quark_to_string (error->domain), error->code);
243 g_error_free (error);
244 }
245
246 return G_DBUS_INTERFACE_SKELETON (skeleton);
247 }
248
249 GVfsMonitor *
g_vfs_monitor_new(GVfsBackend * backend)250 g_vfs_monitor_new (GVfsBackend *backend)
251 {
252 GVfsMonitor *monitor;
253
254 monitor = g_object_new (G_TYPE_VFS_MONITOR, NULL);
255
256 monitor->priv->backend = backend;
257
258 g_object_weak_ref (G_OBJECT (backend),
259 (GWeakNotify)backend_died,
260 monitor);
261
262 monitor->priv->daemon = g_object_ref (g_vfs_backend_get_daemon (backend));
263 monitor->priv->mount_spec = g_mount_spec_ref (g_vfs_backend_get_mount_spec (backend));
264
265 g_vfs_daemon_register_path (monitor->priv->daemon,
266 monitor->priv->object_path,
267 register_path_cb,
268 monitor);
269
270 return monitor;
271 }
272
273 const char *
g_vfs_monitor_get_object_path(GVfsMonitor * monitor)274 g_vfs_monitor_get_object_path (GVfsMonitor *monitor)
275 {
276 return monitor->priv->object_path;
277 }
278
279
280 typedef struct {
281 GVfsMonitor *monitor;
282 GFileMonitorEvent event_type;
283 gchar *file_path;
284 gchar *other_file_path;
285 } EmitEventData;
286
287 static void
emit_event_data_free(EmitEventData * data)288 emit_event_data_free (EmitEventData *data)
289 {
290 g_object_unref (data->monitor);
291 g_free (data->file_path);
292 g_free (data->other_file_path);
293 g_free (data);
294 }
295
296 static void
changed_cb(GVfsDBusMonitorClient * proxy,GAsyncResult * res,EmitEventData * data)297 changed_cb (GVfsDBusMonitorClient *proxy,
298 GAsyncResult *res,
299 EmitEventData *data)
300 {
301 GError *error = NULL;
302
303 if (! gvfs_dbus_monitor_client_call_changed_finish (proxy, res, &error))
304 {
305 g_dbus_error_strip_remote_error (error);
306 g_printerr ("Error calling org.gtk.vfs.MonitorClient.Changed(): %s (%s, %d)\n",
307 error->message, g_quark_to_string (error->domain), error->code);
308 g_error_free (error);
309 }
310
311 emit_event_data_free (data);
312 }
313
314 static void
got_proxy_cb(GObject * source_object,GAsyncResult * res,EmitEventData * data)315 got_proxy_cb (GObject *source_object,
316 GAsyncResult *res,
317 EmitEventData *data)
318 {
319 GError *error = NULL;
320 GVfsDBusMonitorClient *proxy;
321
322 proxy = gvfs_dbus_monitor_client_proxy_new_finish (res, &error);
323 if (proxy == NULL)
324 {
325 g_printerr ("Error creating proxy: %s (%s, %d)\n",
326 error->message, g_quark_to_string (error->domain), error->code);
327 g_error_free (error);
328 emit_event_data_free (data);
329 return;
330 }
331
332 gvfs_dbus_monitor_client_call_changed (proxy,
333 data->event_type,
334 g_mount_spec_to_dbus (data->monitor->priv->mount_spec),
335 data->file_path,
336 g_mount_spec_to_dbus (data->monitor->priv->mount_spec),
337 data->other_file_path ? data->other_file_path : "",
338 NULL,
339 (GAsyncReadyCallback) changed_cb,
340 data);
341 g_object_unref (proxy);
342 }
343
344 void
g_vfs_monitor_emit_event(GVfsMonitor * monitor,GFileMonitorEvent event_type,const char * file_path,const char * other_file_path)345 g_vfs_monitor_emit_event (GVfsMonitor *monitor,
346 GFileMonitorEvent event_type,
347 const char *file_path,
348 const char *other_file_path)
349 {
350 GList *l;
351 Subscriber *subscriber;
352
353 for (l = monitor->priv->subscribers; l != NULL; l = l->next)
354 {
355 EmitEventData *data;
356
357 subscriber = l->data;
358
359 data = g_new0 (EmitEventData, 1);
360 data->monitor = g_object_ref (monitor);
361 data->event_type = event_type;
362 data->file_path = g_strdup (file_path);
363 data->other_file_path = g_strdup (other_file_path);
364
365 gvfs_dbus_monitor_client_proxy_new (subscriber->connection,
366 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
367 subscriber->id,
368 subscriber->object_path,
369 NULL,
370 (GAsyncReadyCallback) got_proxy_cb,
371 data);
372 }
373 }
374