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 <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/errno.h>
28 #include <errno.h>
29 #include <sys/un.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdlib.h>
33
34 #include <glib.h>
35 #include <glib/gi18n.h>
36 #include <glib/gstdio.h>
37 #include <glib-object.h>
38 #include <gvfsdaemon.h>
39 #include <gvfsdaemonprotocol.h>
40 #include <gvfsdaemonutils.h>
41 #include <gvfsutils.h>
42 #include <gvfsjobmount.h>
43 #include <gvfsjobopenforread.h>
44 #include <gvfsjobopenforwrite.h>
45 #include <gvfsjobunmount.h>
46 #include <gvfsmonitorimpl.h>
47
48 enum {
49 PROP_0
50 };
51
52 enum {
53 SHUTDOWN,
54 LAST_SIGNAL
55 };
56
57 typedef struct {
58 char *obj_path;
59 GVfsRegisterPathCallback callback;
60 gpointer data;
61 GDBusInterfaceSkeleton *session_skeleton;
62 GHashTable *client_skeletons;
63 } RegisteredPath;
64
65 struct _GVfsDaemon
66 {
67 GObject parent_instance;
68
69 GMutex lock;
70 gboolean main_daemon;
71
72 GThreadPool *thread_pool;
73 GHashTable *registered_paths;
74 GHashTable *client_connections;
75 GList *jobs;
76 GList *job_sources;
77
78 guint exit_tag;
79
80 gint mount_counter;
81
82 GDBusAuthObserver *auth_observer;
83 GDBusConnection *conn;
84 GVfsDBusDaemon *daemon_skeleton;
85 GVfsDBusMountable *mountable_skeleton;
86 guint name_watcher;
87 gboolean lost_main_daemon;
88 };
89
90 typedef struct {
91 GVfsDaemon *daemon;
92 char *socket_dir;
93 GDBusServer *server;
94
95 GDBusConnection *conn;
96 } NewConnectionData;
97
98 static guint signals[LAST_SIGNAL] = { 0 };
99
100 static void g_vfs_daemon_get_property (GObject *object,
101 guint prop_id,
102 GValue *value,
103 GParamSpec *pspec);
104 static void g_vfs_daemon_set_property (GObject *object,
105 guint prop_id,
106 const GValue *value,
107 GParamSpec *pspec);
108
109 static gboolean handle_get_connection (GVfsDBusDaemon *object,
110 GDBusMethodInvocation *invocation,
111 gpointer user_data);
112 static gboolean handle_cancel (GVfsDBusDaemon *object,
113 GDBusMethodInvocation *invocation,
114 guint arg_serial,
115 gpointer user_data);
116 static gboolean handle_list_monitor_implementations (GVfsDBusDaemon *object,
117 GDBusMethodInvocation *invocation,
118 gpointer user_data);
119 static gboolean daemon_handle_mount (GVfsDBusMountable *object,
120 GDBusMethodInvocation *invocation,
121 GVariant *arg_mount_spec,
122 gboolean arg_automount,
123 GVariant *arg_mount_source,
124 gpointer user_data);
125 static void g_vfs_daemon_re_register_job_sources (GVfsDaemon *daemon);
126
127
128
129
130
131
G_DEFINE_TYPE(GVfsDaemon,g_vfs_daemon,G_TYPE_OBJECT)132 G_DEFINE_TYPE (GVfsDaemon, g_vfs_daemon, G_TYPE_OBJECT)
133
134 static void
135 registered_path_free (RegisteredPath *data)
136 {
137 g_free (data->obj_path);
138 if (data->session_skeleton)
139 {
140 /* Unexport the interface skeleton on session bus */
141 g_dbus_interface_skeleton_unexport (data->session_skeleton);
142 g_object_unref (data->session_skeleton);
143 }
144 g_hash_table_destroy (data->client_skeletons);
145
146 g_free (data);
147 }
148
149 static void
g_vfs_daemon_finalize(GObject * object)150 g_vfs_daemon_finalize (GObject *object)
151 {
152 GVfsDaemon *daemon;
153
154 daemon = G_VFS_DAEMON (object);
155
156 /* There may be some jobs outstanding if we've been force unmounted. */
157 if (daemon->jobs)
158 g_warning ("daemon->jobs != NULL when finalizing daemon!");
159
160 if (daemon->name_watcher)
161 g_bus_unwatch_name (daemon->name_watcher);
162
163 if (daemon->daemon_skeleton != NULL)
164 {
165 g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (daemon->daemon_skeleton));
166 g_object_unref (daemon->daemon_skeleton);
167 }
168 if (daemon->mountable_skeleton != NULL)
169 {
170 g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (daemon->mountable_skeleton));
171 g_object_unref (daemon->mountable_skeleton);
172 }
173 if (daemon->conn != NULL)
174 g_object_unref (daemon->conn);
175 if (daemon->auth_observer != NULL)
176 g_object_unref (daemon->auth_observer);
177
178 g_hash_table_destroy (daemon->registered_paths);
179 g_hash_table_destroy (daemon->client_connections);
180 g_mutex_clear (&daemon->lock);
181
182 if (G_OBJECT_CLASS (g_vfs_daemon_parent_class)->finalize)
183 (*G_OBJECT_CLASS (g_vfs_daemon_parent_class)->finalize) (object);
184 }
185
186 static void
g_vfs_daemon_class_init(GVfsDaemonClass * klass)187 g_vfs_daemon_class_init (GVfsDaemonClass *klass)
188 {
189 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
190
191 gobject_class->finalize = g_vfs_daemon_finalize;
192 gobject_class->set_property = g_vfs_daemon_set_property;
193 gobject_class->get_property = g_vfs_daemon_get_property;
194
195 signals[SHUTDOWN] =
196 g_signal_new ("shutdown",
197 G_TYPE_FROM_CLASS (gobject_class),
198 G_SIGNAL_RUN_LAST,
199 G_STRUCT_OFFSET (GVfsDaemonClass, shutdown),
200 NULL, NULL,
201 g_cclosure_marshal_VOID__VOID,
202 G_TYPE_NONE, 0);
203 }
204
205 static void
job_handler_callback(gpointer data,gpointer user_data)206 job_handler_callback (gpointer data,
207 gpointer user_data)
208 {
209 GVfsJob *job = G_VFS_JOB (data);
210
211 g_vfs_job_run (job);
212 }
213
214 static void
name_appeared_handler(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)215 name_appeared_handler (GDBusConnection *connection,
216 const gchar *name,
217 const gchar *name_owner,
218 gpointer user_data)
219 {
220 GVfsDaemon *daemon = G_VFS_DAEMON (user_data);
221
222 if (strcmp (name, G_VFS_DBUS_DAEMON_NAME) == 0 &&
223 *name_owner != 0 &&
224 daemon->lost_main_daemon)
225 {
226 /* There is a new owner. Register mounts with it */
227 g_vfs_daemon_re_register_job_sources (daemon);
228 }
229 }
230
231 static void
name_vanished_handler(GDBusConnection * connection,const gchar * name,gpointer user_data)232 name_vanished_handler (GDBusConnection *connection,
233 const gchar *name,
234 gpointer user_data)
235 {
236 GVfsDaemon *daemon = G_VFS_DAEMON (user_data);
237
238 /* Ensure we react only to really lost daemon */
239 daemon->lost_main_daemon = TRUE;
240 }
241
242 /*
243 * Authentication observer signal handler that rejects all authentication
244 * mechanisms except for EXTERNAL (credentials-passing), which is the
245 * recommended authentication mechanism for AF_UNIX sockets.
246 */
247 static gboolean
allow_mechanism_cb(GDBusAuthObserver * observer,const gchar * mechanism,G_GNUC_UNUSED gpointer user_data)248 allow_mechanism_cb (GDBusAuthObserver *observer,
249 const gchar *mechanism,
250 G_GNUC_UNUSED gpointer user_data)
251 {
252 if (g_strcmp0 (mechanism, "EXTERNAL") == 0)
253 return TRUE;
254
255 return FALSE;
256 }
257
258 /*
259 * Authentication observer signal handler that authorizes connections
260 * from the same uid as this process. This matches the behaviour of a
261 * libdbus DBusServer/DBusConnection when no DBusAllowUnixUserFunction
262 * has been set, but is not the default in GDBus.
263 */
264 static gboolean
authorize_authenticated_peer_cb(GDBusAuthObserver * observer,G_GNUC_UNUSED GIOStream * stream,GCredentials * credentials,G_GNUC_UNUSED gpointer user_data)265 authorize_authenticated_peer_cb (GDBusAuthObserver *observer,
266 G_GNUC_UNUSED GIOStream *stream,
267 GCredentials *credentials,
268 G_GNUC_UNUSED gpointer user_data)
269 {
270 gboolean authorized = FALSE;
271
272 if (credentials != NULL)
273 {
274 GCredentials *own_credentials;
275
276 own_credentials = g_credentials_new ();
277
278 if (g_credentials_is_same_user (credentials, own_credentials, NULL))
279 authorized = TRUE;
280
281 g_object_unref (own_credentials);
282 }
283
284 return authorized;
285 }
286
287 static void
g_vfs_daemon_init(GVfsDaemon * daemon)288 g_vfs_daemon_init (GVfsDaemon *daemon)
289 {
290 GError *error;
291 gint max_threads = 1; /* TODO: handle max threads */
292
293 daemon->thread_pool = g_thread_pool_new (job_handler_callback,
294 daemon,
295 max_threads,
296 FALSE, NULL);
297 /* TODO: verify thread_pool != NULL in a nicer way */
298 g_assert (daemon->thread_pool != NULL);
299
300 g_mutex_init (&daemon->lock);
301
302 daemon->mount_counter = 0;
303
304 daemon->jobs = NULL;
305 daemon->registered_paths =
306 g_hash_table_new_full (g_str_hash, g_str_equal,
307 g_free, (GDestroyNotify)registered_path_free);
308
309 /* This is where we store active client connections so when a new filter is registered,
310 * we re-register them on all active connections */
311 daemon->client_connections =
312 g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
313
314 daemon->conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
315 g_assert (daemon->conn != NULL);
316 daemon->auth_observer = g_dbus_auth_observer_new ();
317 g_signal_connect (daemon->auth_observer, "allow-mechanism", G_CALLBACK (allow_mechanism_cb), NULL);
318 g_signal_connect (daemon->auth_observer, "authorize-authenticated-peer", G_CALLBACK (authorize_authenticated_peer_cb), NULL);
319
320 daemon->daemon_skeleton = gvfs_dbus_daemon_skeleton_new ();
321 g_signal_connect (daemon->daemon_skeleton, "handle-get-connection", G_CALLBACK (handle_get_connection), daemon);
322 g_signal_connect (daemon->daemon_skeleton, "handle-cancel", G_CALLBACK (handle_cancel), daemon);
323 g_signal_connect (daemon->daemon_skeleton, "handle-list-monitor-implementations", G_CALLBACK (handle_list_monitor_implementations), daemon);
324
325 error = NULL;
326 if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon->daemon_skeleton),
327 daemon->conn,
328 G_VFS_DBUS_DAEMON_PATH,
329 &error))
330 {
331 g_warning ("Error exporting daemon interface: %s (%s, %d)\n",
332 error->message, g_quark_to_string (error->domain), error->code);
333 g_error_free (error);
334 }
335
336 daemon->mountable_skeleton = gvfs_dbus_mountable_skeleton_new ();
337 g_signal_connect (daemon->mountable_skeleton, "handle-mount", G_CALLBACK (daemon_handle_mount), daemon);
338
339 error = NULL;
340 if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon->mountable_skeleton),
341 daemon->conn,
342 G_VFS_DBUS_MOUNTABLE_PATH,
343 &error))
344 {
345 g_warning ("Error exporting mountable interface: %s (%s, %d)\n",
346 error->message, g_quark_to_string (error->domain), error->code);
347 g_error_free (error);
348 }
349 }
350
351 static void
g_vfs_daemon_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)352 g_vfs_daemon_set_property (GObject *object,
353 guint prop_id,
354 const GValue *value,
355 GParamSpec *pspec)
356 {
357 switch (prop_id)
358 {
359 default:
360 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
361 break;
362 }
363 }
364
365 static void
g_vfs_daemon_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)366 g_vfs_daemon_get_property (GObject *object,
367 guint prop_id,
368 GValue *value,
369 GParamSpec *pspec)
370 {
371 switch (prop_id)
372 {
373 default:
374 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
375 break;
376 }
377 }
378
379 GVfsDaemon *
g_vfs_daemon_new(gboolean main_daemon,gboolean replace)380 g_vfs_daemon_new (gboolean main_daemon, gboolean replace)
381 {
382 GVfsDaemon *daemon;
383 GDBusConnection *conn;
384 GError *error;
385
386 error = NULL;
387 conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
388 if (!conn)
389 {
390 g_printerr ("Failed to connect to the D-BUS daemon: %s (%s, %d)\n",
391 error->message, g_quark_to_string (error->domain), error->code);
392 g_error_free (error);
393 return NULL;
394 }
395
396 daemon = g_object_new (G_VFS_TYPE_DAEMON, NULL);
397 daemon->main_daemon = main_daemon;
398
399 if (! main_daemon)
400 {
401 daemon->name_watcher = g_bus_watch_name_on_connection (conn,
402 G_VFS_DBUS_DAEMON_NAME,
403 G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
404 name_appeared_handler,
405 name_vanished_handler,
406 daemon,
407 NULL);
408 }
409
410 g_object_unref (conn);
411
412 return daemon;
413 }
414
415 void
g_vfs_daemon_set_max_threads(GVfsDaemon * daemon,gint max_threads)416 g_vfs_daemon_set_max_threads (GVfsDaemon *daemon,
417 gint max_threads)
418 {
419 g_thread_pool_set_max_threads (daemon->thread_pool, max_threads, NULL);
420 }
421
422 static gboolean
exit_at_idle(GVfsDaemon * daemon)423 exit_at_idle (GVfsDaemon *daemon)
424 {
425 g_signal_emit (daemon, signals[SHUTDOWN], 0);
426 return FALSE;
427 }
428
429 static void
daemon_unschedule_exit(GVfsDaemon * daemon)430 daemon_unschedule_exit (GVfsDaemon *daemon)
431 {
432 if (daemon->exit_tag != 0)
433 {
434 g_source_remove (daemon->exit_tag);
435 daemon->exit_tag = 0;
436 }
437 }
438
439 static void
daemon_schedule_exit(GVfsDaemon * daemon)440 daemon_schedule_exit (GVfsDaemon *daemon)
441 {
442 if (daemon->exit_tag == 0)
443 daemon->exit_tag = g_timeout_add_seconds (1, (GSourceFunc)exit_at_idle, daemon);
444 }
445
446 static void
job_source_new_job_callback(GVfsJobSource * job_source,GVfsJob * job,GVfsDaemon * daemon)447 job_source_new_job_callback (GVfsJobSource *job_source,
448 GVfsJob *job,
449 GVfsDaemon *daemon)
450 {
451 g_vfs_daemon_queue_job (daemon, job);
452 }
453
454 static void
job_source_closed_callback(GVfsJobSource * job_source,GVfsDaemon * daemon)455 job_source_closed_callback (GVfsJobSource *job_source,
456 GVfsDaemon *daemon)
457 {
458 g_mutex_lock (&daemon->lock);
459
460 daemon->job_sources = g_list_remove (daemon->job_sources,
461 job_source);
462
463 g_signal_handlers_disconnect_by_func (job_source,
464 (GCallback)job_source_new_job_callback,
465 daemon);
466 g_signal_handlers_disconnect_by_func (job_source,
467 (GCallback)job_source_closed_callback,
468 daemon);
469
470 g_object_unref (job_source);
471
472 if (daemon->job_sources == NULL)
473 daemon_schedule_exit (daemon);
474
475 g_mutex_unlock (&daemon->lock);
476 }
477
478 static void
re_register_jobs_cb(GVfsBackend * backend,GAsyncResult * res,gpointer user_data)479 re_register_jobs_cb (GVfsBackend *backend,
480 GAsyncResult *res,
481 gpointer user_data)
482 {
483 GError *error = NULL;
484
485 g_vfs_backend_register_mount_finish (backend, res, &error);
486 g_debug ("re_register_jobs_cb, error: %p\n", error);
487 g_clear_error (&error);
488 }
489
490 static void
g_vfs_daemon_re_register_job_sources(GVfsDaemon * daemon)491 g_vfs_daemon_re_register_job_sources (GVfsDaemon *daemon)
492 {
493 GList *l;
494
495 g_mutex_lock (&daemon->lock);
496
497 for (l = daemon->job_sources; l != NULL; l = l->next)
498 {
499 if (G_VFS_IS_BACKEND (l->data))
500 {
501 GVfsBackend *backend = G_VFS_BACKEND (l->data);
502
503 /* Only re-register if we registered before, not e.g
504 if we're currently mounting. */
505 if (g_vfs_backend_is_mounted (backend))
506 g_vfs_backend_register_mount (backend, (GAsyncReadyCallback) re_register_jobs_cb, NULL);
507 }
508 }
509
510 g_mutex_unlock (&daemon->lock);
511 }
512
513 void
g_vfs_daemon_add_job_source(GVfsDaemon * daemon,GVfsJobSource * job_source)514 g_vfs_daemon_add_job_source (GVfsDaemon *daemon,
515 GVfsJobSource *job_source)
516 {
517 g_debug ("Added new job source %p (%s)\n", job_source, g_type_name_from_instance ((gpointer)job_source));
518
519 g_mutex_lock (&daemon->lock);
520
521 daemon_unschedule_exit (daemon);
522
523 g_object_ref (job_source);
524 daemon->job_sources = g_list_append (daemon->job_sources,
525 job_source);
526 g_signal_connect (job_source, "new_job",
527 (GCallback)job_source_new_job_callback, daemon);
528 g_signal_connect (job_source, "closed",
529 (GCallback)job_source_closed_callback, daemon);
530
531 g_mutex_unlock (&daemon->lock);
532 }
533
534 static void
unref_skeleton(gpointer object)535 unref_skeleton (gpointer object)
536 {
537 GDBusInterfaceSkeleton *skeleton = object;
538
539 g_dbus_interface_skeleton_unexport (skeleton);
540 g_object_unref (skeleton);
541 }
542
543 static void
peer_register_skeleton(const gchar * obj_path,RegisteredPath * reg_path,GDBusConnection * dbus_conn)544 peer_register_skeleton (const gchar *obj_path,
545 RegisteredPath *reg_path,
546 GDBusConnection *dbus_conn)
547 {
548 GDBusInterfaceSkeleton *skeleton;
549
550 if (! g_hash_table_contains (reg_path->client_skeletons, dbus_conn))
551 {
552 skeleton = reg_path->callback (dbus_conn, obj_path, reg_path->data);
553 g_hash_table_insert (reg_path->client_skeletons, dbus_conn, skeleton);
554 }
555 else
556 {
557 /* Interface skeleton has been already registered on the connection, skipping */
558 }
559 }
560
561 static void
client_conn_register_skeleton(GDBusConnection * dbus_conn,gpointer value,RegisteredPath * reg_path)562 client_conn_register_skeleton (GDBusConnection *dbus_conn,
563 gpointer value,
564 RegisteredPath *reg_path)
565 {
566 peer_register_skeleton (reg_path->obj_path, reg_path, dbus_conn);
567 }
568
569 /* This registers a dbus interface skeleton on *all* connections, client and session bus */
570 /* The object path needs to be unique globally. */
571 void
g_vfs_daemon_register_path(GVfsDaemon * daemon,const char * obj_path,GVfsRegisterPathCallback callback,gpointer user_data)572 g_vfs_daemon_register_path (GVfsDaemon *daemon,
573 const char *obj_path,
574 GVfsRegisterPathCallback callback,
575 gpointer user_data)
576 {
577 RegisteredPath *data;
578
579 data = g_new0 (RegisteredPath, 1);
580 data->obj_path = g_strdup (obj_path);
581 data->callback = callback;
582 data->data = user_data;
583 data->client_skeletons = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)unref_skeleton);
584
585 g_hash_table_insert (daemon->registered_paths, g_strdup (obj_path), data);
586
587 /* Export the newly registered interface skeleton on session bus */
588 /* TODO: change the way we export skeletons on connections once
589 * https://bugzilla.gnome.org/show_bug.cgi?id=662718 is in place.
590 */
591 data->session_skeleton = callback (daemon->conn, obj_path, user_data);
592
593 /* Export this newly registered path to all active client connections */
594 g_hash_table_foreach (daemon->client_connections, (GHFunc) client_conn_register_skeleton, data);
595 }
596
597 void
g_vfs_daemon_unregister_path(GVfsDaemon * daemon,const char * obj_path)598 g_vfs_daemon_unregister_path (GVfsDaemon *daemon,
599 const char *obj_path)
600 {
601 g_hash_table_remove (daemon->registered_paths, obj_path);
602 }
603
604 /* NOTE: Might be emitted on a thread */
605 static void
job_new_source_callback(GVfsJob * job,GVfsJobSource * job_source,GVfsDaemon * daemon)606 job_new_source_callback (GVfsJob *job,
607 GVfsJobSource *job_source,
608 GVfsDaemon *daemon)
609 {
610 g_vfs_daemon_add_job_source (daemon, job_source);
611 }
612
613 /* NOTE: Might be emitted on a thread */
614 static void
job_finished_callback(GVfsJob * job,GVfsDaemon * daemon)615 job_finished_callback (GVfsJob *job,
616 GVfsDaemon *daemon)
617 {
618
619 g_signal_handlers_disconnect_by_func (job,
620 (GCallback)job_new_source_callback,
621 daemon);
622 g_signal_handlers_disconnect_by_func (job,
623 (GCallback)job_finished_callback,
624 daemon);
625
626 g_mutex_lock (&daemon->lock);
627 daemon->jobs = g_list_remove (daemon->jobs, job);
628 g_mutex_unlock (&daemon->lock);
629
630 g_object_unref (job);
631 }
632
633 void
g_vfs_daemon_queue_job(GVfsDaemon * daemon,GVfsJob * job)634 g_vfs_daemon_queue_job (GVfsDaemon *daemon,
635 GVfsJob *job)
636 {
637 g_debug ("Queued new job %p (%s)\n", job, g_type_name_from_instance ((gpointer)job));
638
639 g_object_ref (job);
640 g_signal_connect (job, "finished", (GCallback)job_finished_callback, daemon);
641 g_signal_connect (job, "new_source", (GCallback)job_new_source_callback, daemon);
642
643 g_mutex_lock (&daemon->lock);
644 daemon->jobs = g_list_prepend (daemon->jobs, job);
645 g_mutex_unlock (&daemon->lock);
646
647 /* Can we start the job immediately / async */
648 if (!g_vfs_job_try (job))
649 {
650 /* Couldn't finish / run async, queue worker thread */
651 g_thread_pool_push (daemon->thread_pool, job, NULL); /* TODO: Check error */
652 }
653 }
654
655 static void
new_connection_data_free(void * memory)656 new_connection_data_free (void *memory)
657 {
658 NewConnectionData *data = memory;
659 gchar *socket;
660
661 /* Remove the socket and dir after connected */
662 if (data->socket_dir)
663 {
664 socket = g_strdup_printf ("%s/socket", data->socket_dir);
665 g_unlink (socket);
666 g_free (socket);
667 rmdir (data->socket_dir);
668 g_free (data->socket_dir);
669 }
670
671 g_free (data);
672 }
673
674 static void
peer_unregister_skeleton(const gchar * obj_path,RegisteredPath * reg_path,GDBusConnection * dbus_conn)675 peer_unregister_skeleton (const gchar *obj_path,
676 RegisteredPath *reg_path,
677 GDBusConnection *dbus_conn)
678 {
679 g_hash_table_remove (reg_path->client_skeletons, dbus_conn);
680 }
681
682 static void
peer_connection_closed(GDBusConnection * connection,gboolean remote_peer_vanished,GError * error,gpointer user_data)683 peer_connection_closed (GDBusConnection *connection,
684 gboolean remote_peer_vanished,
685 GError *error,
686 gpointer user_data)
687 {
688 GVfsDaemon *daemon = G_VFS_DAEMON (user_data);
689 GList *l;
690 GVfsDBusDaemon *daemon_skeleton;
691 GVfsJob *job_to_cancel;
692
693 do
694 {
695 job_to_cancel = NULL;
696
697 g_mutex_lock (&daemon->lock);
698 for (l = daemon->jobs; l != NULL; l = l->next)
699 {
700 GVfsJob *job = G_VFS_JOB (l->data);
701
702 if (G_VFS_IS_JOB_DBUS (job) &&
703 !g_vfs_job_is_cancelled (job) &&
704 G_VFS_JOB_DBUS (job)->invocation &&
705 g_dbus_method_invocation_get_connection (G_VFS_JOB_DBUS (job)->invocation) == connection)
706 {
707 job_to_cancel = g_object_ref (job);
708 break;
709 }
710 }
711 g_mutex_unlock (&daemon->lock);
712
713 if (job_to_cancel)
714 {
715 g_vfs_job_cancel (job_to_cancel);
716 g_object_unref (job_to_cancel);
717 }
718 }
719 while (job_to_cancel != NULL);
720
721 daemon_skeleton = g_object_get_data (G_OBJECT (connection), "daemon_skeleton");
722 /* daemon_skeleton should be always valid in this case */
723 g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (daemon_skeleton));
724
725 g_hash_table_remove (daemon->client_connections, connection);
726
727 /* Unexport the registered interface skeletons */
728 g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_unregister_skeleton, connection);
729
730 /* The peer-to-peer connection was disconnected */
731 g_signal_handlers_disconnect_by_data (connection, user_data);
732 g_object_unref (connection);
733 }
734
735 static void
daemon_peer_connection_setup(GVfsDaemon * daemon,GDBusConnection * dbus_conn,NewConnectionData * data)736 daemon_peer_connection_setup (GVfsDaemon *daemon,
737 GDBusConnection *dbus_conn,
738 NewConnectionData *data)
739 {
740 GVfsDBusDaemon *daemon_skeleton;
741 GError *error;
742
743 daemon_skeleton = gvfs_dbus_daemon_skeleton_new ();
744 g_signal_connect (daemon_skeleton, "handle-cancel", G_CALLBACK (handle_cancel), daemon);
745
746 error = NULL;
747 if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon_skeleton),
748 dbus_conn,
749 G_VFS_DBUS_DAEMON_PATH,
750 &error))
751 {
752 g_warning ("Failed to accept client: %s, %s (%s, %d)", "object registration failed",
753 error->message, g_quark_to_string (error->domain), error->code);
754 g_error_free (error);
755 g_object_unref (data->conn);
756 goto error_out;
757 }
758 g_object_set_data_full (G_OBJECT (data->conn), "daemon_skeleton", daemon_skeleton, (GDestroyNotify) g_object_unref);
759
760 /* Export registered interface skeletons on this new connection */
761 g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_register_skeleton, dbus_conn);
762
763 g_hash_table_insert (daemon->client_connections, g_object_ref (dbus_conn), NULL);
764
765 g_signal_connect (data->conn, "closed", G_CALLBACK (peer_connection_closed), data->daemon);
766
767 error_out:
768 new_connection_data_free (data);
769 }
770
771 #ifdef __linux__
772 #define USE_ABSTRACT_SOCKETS
773 #endif
774
775 #ifndef USE_ABSTRACT_SOCKETS
776 static gboolean
test_safe_socket_dir(const char * dirname)777 test_safe_socket_dir (const char *dirname)
778 {
779 struct stat statbuf;
780
781 if (g_stat (dirname, &statbuf) != 0)
782 return FALSE;
783
784 #ifndef G_PLATFORM_WIN32
785 if (statbuf.st_uid != getuid ())
786 return FALSE;
787
788 if ((statbuf.st_mode & (S_IRWXG|S_IRWXO)) ||
789 !S_ISDIR (statbuf.st_mode))
790 return FALSE;
791 #endif
792
793 return TRUE;
794 }
795
796
797 static char *
create_socket_dir(void)798 create_socket_dir (void)
799 {
800 char *dirname;
801 long iteration = 0;
802 char *safe_dir;
803 gchar tmp[9];
804 int i;
805
806 safe_dir = NULL;
807 do
808 {
809 g_free (safe_dir);
810
811 gvfs_randomize_string (tmp, 8);
812 tmp[8] = '\0';
813
814 dirname = g_strdup_printf ("gvfs-%s-%s",
815 g_get_user_name (), tmp);
816 safe_dir = g_build_filename (g_get_tmp_dir (), dirname, NULL);
817 g_free (dirname);
818
819 if (g_mkdir (safe_dir, 0700) < 0)
820 {
821 switch (errno)
822 {
823 case EACCES:
824 g_error ("I can't write to '%s', daemon init failed",
825 safe_dir);
826 break;
827
828 case ENAMETOOLONG:
829 g_error ("Name '%s' too long your system is broken",
830 safe_dir);
831 break;
832
833 case ENOMEM:
834 #ifdef ELOOP
835 case ELOOP:
836 #endif
837 case ENOSPC:
838 case ENOTDIR:
839 case ENOENT:
840 g_error ("Resource problem creating '%s'", safe_dir);
841 break;
842
843 default: /* carry on going */
844 break;
845 }
846 }
847 /* Possible race - so we re-scan. */
848
849 if (iteration++ == 1000)
850 g_error ("Cannot find a safe socket path in '%s'", g_get_tmp_dir ());
851 }
852 while (!test_safe_socket_dir (safe_dir));
853
854 return safe_dir;
855 }
856 #endif
857
858 static void
generate_address(char ** address,char ** folder)859 generate_address (char **address,
860 char **folder)
861 {
862 *address = NULL;
863 *folder = NULL;
864
865 #ifdef USE_ABSTRACT_SOCKETS
866 {
867 gchar tmp[9];
868
869 gvfs_randomize_string (tmp, 8);
870 tmp[8] = '\0';
871 *address = g_strdup_printf ("unix:abstract=/dbus-vfs-daemon/socket-%s", tmp);
872 }
873 #else
874 {
875 char *dir;
876
877 dir = create_socket_dir ();
878 *address = g_strdup_printf ("unix:path=%s/socket", dir);
879 *folder = dir;
880 }
881 #endif
882 }
883
884 static gboolean
daemon_new_connection_func(GDBusServer * server,GDBusConnection * connection,gpointer user_data)885 daemon_new_connection_func (GDBusServer *server,
886 GDBusConnection *connection,
887 gpointer user_data)
888 {
889 NewConnectionData *data;
890
891 data = user_data;
892
893 /* Take ownership */
894 data->conn = g_object_ref (connection);
895
896 daemon_peer_connection_setup (data->daemon, data->conn, data);
897
898 /* Kill the server, no more need for it */
899 g_dbus_server_stop (server);
900 g_object_unref (server);
901
902 return TRUE;
903 }
904
905 static gboolean
handle_get_connection(GVfsDBusDaemon * object,GDBusMethodInvocation * invocation,gpointer user_data)906 handle_get_connection (GVfsDBusDaemon *object,
907 GDBusMethodInvocation *invocation,
908 gpointer user_data)
909 {
910 GVfsDaemon *daemon = G_VFS_DAEMON (user_data);
911 GDBusServer *server;
912 GError *error;
913 gchar *address1;
914 NewConnectionData *data;
915 char *socket_dir;
916 gchar *guid;
917
918 generate_address (&address1, &socket_dir);
919
920 data = g_new (NewConnectionData, 1);
921 data->daemon = daemon;
922 data->socket_dir = socket_dir;
923 data->conn = NULL;
924
925 guid = g_dbus_generate_guid ();
926 error = NULL;
927 server = g_dbus_server_new_sync (address1,
928 G_DBUS_SERVER_FLAGS_NONE,
929 guid,
930 daemon->auth_observer,
931 NULL, /* GCancellable */
932 &error);
933 g_free (guid);
934
935 if (server == NULL)
936 {
937 g_dbus_method_invocation_return_gerror (invocation, error);
938 g_printerr ("daemon: Error creating server at address %s: %s\n", address1, error->message);
939 g_error_free (error);
940 goto error_out;
941 }
942
943 g_dbus_server_start (server);
944 data->server = server;
945
946 g_signal_connect (server, "new-connection", G_CALLBACK (daemon_new_connection_func), data);
947
948 gvfs_dbus_daemon_complete_get_connection (object,
949 invocation,
950 address1,
951 "");
952
953 g_free (address1);
954 return TRUE;
955
956 error_out:
957 new_connection_data_free (data);
958 g_free (address1);
959 return TRUE;
960 }
961
962 static gboolean
handle_cancel(GVfsDBusDaemon * object,GDBusMethodInvocation * invocation,guint arg_serial,gpointer user_data)963 handle_cancel (GVfsDBusDaemon *object,
964 GDBusMethodInvocation *invocation,
965 guint arg_serial,
966 gpointer user_data)
967 {
968 GVfsDaemon *daemon = G_VFS_DAEMON (user_data);
969 GList *l;
970 GVfsJob *job_to_cancel = NULL;
971
972 g_mutex_lock (&daemon->lock);
973 for (l = daemon->jobs; l != NULL; l = l->next)
974 {
975 GVfsJob *job = G_VFS_JOB (l->data);
976
977 if (G_VFS_IS_JOB_DBUS (job) &&
978 g_vfs_job_dbus_is_serial (G_VFS_JOB_DBUS (job),
979 g_dbus_method_invocation_get_connection (invocation),
980 arg_serial))
981 {
982 job_to_cancel = g_object_ref (job);
983 break;
984 }
985 }
986 g_mutex_unlock (&daemon->lock);
987
988 if (job_to_cancel)
989 {
990 g_vfs_job_cancel (job_to_cancel);
991 g_object_unref (job_to_cancel);
992 }
993
994 gvfs_dbus_daemon_complete_cancel (object, invocation);
995
996 return TRUE;
997 }
998
999 static gboolean
handle_list_monitor_implementations(GVfsDBusDaemon * object,GDBusMethodInvocation * invocation,gpointer user_data)1000 handle_list_monitor_implementations (GVfsDBusDaemon *object,
1001 GDBusMethodInvocation *invocation,
1002 gpointer user_data)
1003 {
1004 GList *impls, *l;
1005 GVariantBuilder builder;
1006
1007 impls = g_vfs_list_monitor_implementations ();
1008
1009 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssbia{sv})"));
1010
1011 for (l = impls; l != NULL; l = l->next)
1012 {
1013 GVfsMonitorImplementation *impl = l->data;
1014
1015 g_variant_builder_add_value (&builder, g_vfs_monitor_implementation_to_dbus (impl));
1016 }
1017
1018 g_list_free_full (impls, (GDestroyNotify)g_vfs_monitor_implementation_free);
1019
1020 gvfs_dbus_daemon_complete_list_monitor_implementations (object,
1021 invocation,
1022 g_variant_builder_end (&builder));
1023
1024 return TRUE;
1025 }
1026
1027 static gboolean
daemon_handle_mount(GVfsDBusMountable * object,GDBusMethodInvocation * invocation,GVariant * arg_mount_spec,gboolean arg_automount,GVariant * arg_mount_source,gpointer user_data)1028 daemon_handle_mount (GVfsDBusMountable *object,
1029 GDBusMethodInvocation *invocation,
1030 GVariant *arg_mount_spec,
1031 gboolean arg_automount,
1032 GVariant *arg_mount_source,
1033 gpointer user_data)
1034 {
1035 GVfsDaemon *daemon = G_VFS_DAEMON (user_data);
1036 GMountSpec *mount_spec;
1037 GMountSource *mount_source;
1038
1039 mount_spec = g_mount_spec_from_dbus (arg_mount_spec);
1040 if (mount_spec == NULL)
1041 g_dbus_method_invocation_return_error_literal (invocation,
1042 G_IO_ERROR,
1043 G_IO_ERROR_INVALID_ARGUMENT,
1044 "Error in mount spec");
1045 else
1046 {
1047 mount_source = g_mount_source_from_dbus (arg_mount_source);
1048 g_vfs_daemon_initiate_mount (daemon, mount_spec, mount_source, arg_automount,
1049 object, invocation);
1050 g_object_unref (mount_source);
1051 g_mount_spec_unref (mount_spec);
1052 }
1053
1054 return TRUE;
1055 }
1056
1057 void
g_vfs_daemon_initiate_mount(GVfsDaemon * daemon,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount,GVfsDBusMountable * object,GDBusMethodInvocation * invocation)1058 g_vfs_daemon_initiate_mount (GVfsDaemon *daemon,
1059 GMountSpec *mount_spec,
1060 GMountSource *mount_source,
1061 gboolean is_automount,
1062 GVfsDBusMountable *object,
1063 GDBusMethodInvocation *invocation)
1064 {
1065 const char *type;
1066 GType backend_type;
1067 char *obj_path;
1068 GVfsJob *job;
1069 GVfsBackend *backend;
1070
1071 type = g_mount_spec_get_type (mount_spec);
1072
1073 backend_type = G_TYPE_INVALID;
1074 if (type)
1075 backend_type = g_vfs_lookup_backend (type);
1076
1077 if (backend_type == G_TYPE_INVALID)
1078 {
1079 if (invocation)
1080 g_dbus_method_invocation_return_error_literal (invocation,
1081 G_IO_ERROR,
1082 G_IO_ERROR_FAILED,
1083 "Invalid backend type");
1084 else
1085 g_warning ("Error mounting: invalid backend type\n");
1086 return;
1087 }
1088
1089 obj_path = g_strdup_printf ("/org/gtk/vfs/mount/%d", ++daemon->mount_counter);
1090 backend = g_object_new (backend_type,
1091 "daemon", daemon,
1092 "object-path", obj_path,
1093 NULL);
1094 g_free (obj_path);
1095
1096 g_vfs_daemon_add_job_source (daemon, G_VFS_JOB_SOURCE (backend));
1097 g_object_unref (backend);
1098
1099 job = g_vfs_job_mount_new (mount_spec, mount_source, is_automount, object, invocation, backend);
1100 g_vfs_daemon_queue_job (daemon, job);
1101 g_object_unref (job);
1102 }
1103
1104 /**
1105 * g_vfs_daemon_get_blocking_processes:
1106 * @daemon: A #GVfsDaemon.
1107 *
1108 * Gets all processes that blocks unmounting, e.g. processes with open
1109 * file handles. Returned array could be empty in spite of
1110 * g_vfs_daemon_has_blocking_processes returns TRUE, because for jobs without
1111 * channel we can't get #GPid.
1112 *
1113 * Returns: An array of #GPid. Free with g_array_unref().
1114 */
1115 GArray *
g_vfs_daemon_get_blocking_processes(GVfsDaemon * daemon)1116 g_vfs_daemon_get_blocking_processes (GVfsDaemon *daemon)
1117 {
1118 GArray *processes;
1119 GList *l;
1120
1121 g_mutex_lock (&daemon->lock);
1122
1123 processes = g_array_new (FALSE, FALSE, sizeof (GPid));
1124 for (l = daemon->job_sources; l != NULL; l = l->next)
1125 {
1126 if (G_VFS_IS_CHANNEL (l->data))
1127 {
1128 GPid pid;
1129 pid = g_vfs_channel_get_actual_consumer (G_VFS_CHANNEL (l->data));
1130 g_array_append_val (processes, pid);
1131 }
1132 }
1133
1134 g_mutex_unlock (&daemon->lock);
1135
1136 return processes;
1137 }
1138
1139 /**
1140 * g_vfs_daemon_has_blocking_processes:
1141 * @daemon: A #GVfsDaemon.
1142 *
1143 * Determines if there are any jobs blocking unmounting, all jobs excepting
1144 * unmount job.
1145 *
1146 * Returns: TRUE if there are any blocking processes, or FALSE otherwise.
1147 */
1148 gboolean
g_vfs_daemon_has_blocking_processes(GVfsDaemon * daemon)1149 g_vfs_daemon_has_blocking_processes (GVfsDaemon *daemon)
1150 {
1151 GList *l;
1152
1153 g_mutex_lock (&daemon->lock);
1154 for (l = daemon->jobs; l != NULL; l = l->next)
1155 {
1156 if (!G_VFS_IS_JOB_UNMOUNT (l->data))
1157 {
1158 g_debug ("blocking job: %p\n", l->data);
1159 g_mutex_unlock (&daemon->lock);
1160 return TRUE;
1161 }
1162 }
1163 g_mutex_unlock (&daemon->lock);
1164
1165 return FALSE;
1166 }
1167
1168 void
g_vfs_daemon_run_job_in_thread(GVfsDaemon * daemon,GVfsJob * job)1169 g_vfs_daemon_run_job_in_thread (GVfsDaemon *daemon,
1170 GVfsJob *job)
1171 {
1172 g_thread_pool_push (daemon->thread_pool, job, NULL); /* TODO: Check error */
1173 }
1174
1175 void
g_vfs_daemon_close_active_channels(GVfsDaemon * daemon,GVfsBackend * backend)1176 g_vfs_daemon_close_active_channels (GVfsDaemon *daemon,
1177 GVfsBackend *backend)
1178 {
1179 GList *l;
1180 GVfsChannel *channel_to_close;
1181
1182 do
1183 {
1184 channel_to_close = NULL;
1185
1186 g_mutex_lock (&daemon->lock);
1187 for (l = daemon->job_sources; l != NULL; l = l->next)
1188 {
1189 if (G_VFS_IS_CHANNEL (l->data) &&
1190 g_vfs_channel_get_backend (G_VFS_CHANNEL (l->data)) == backend)
1191 {
1192 channel_to_close = g_object_ref (G_VFS_CHANNEL (l->data));
1193 break;
1194 }
1195 }
1196 g_mutex_unlock (&daemon->lock);
1197
1198 if (channel_to_close)
1199 {
1200 g_vfs_channel_force_close (channel_to_close);
1201 g_object_unref (channel_to_close);
1202 }
1203 }
1204 while (channel_to_close != NULL);
1205 }
1206