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