gvfsdaemondbus.c 18.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* GIO - GLib Input, Output and Streaming Library
 * 
 * Copyright (C) 2006-2007 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
Felix Möller's avatar
Felix Möller committed
17 18
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
19 20 21 22
 *
 * Author: Alexander Larsson <alexl@redhat.com>
 */

23 24
#include <config.h>

Alexander Larsson's avatar
Alexander Larsson committed
25 26 27
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
28 29 30
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
31 32 33
#include <errno.h>

#include <glib/gi18n-lib.h>
Alexander Larsson's avatar
Alexander Larsson committed
34

35
#include <gio/gio.h>
Alexander Larsson's avatar
Alexander Larsson committed
36
#include "gvfsdaemondbus.h"
37
#include <gvfsdaemonprotocol.h>
38
#include <gdaemonvfs.h>
39
#include <gvfsdbus.h>
40

41
/* Extra vfs-specific data for GDBusConnections */
42
typedef struct {
43
  char *async_dbus_id;
44 45
} VfsConnectionData;

46 47 48 49
typedef struct _ThreadLocalConnections ThreadLocalConnections;
static void free_local_connections (ThreadLocalConnections *local);

static GPrivate local_connections = G_PRIVATE_INIT((GDestroyNotify)free_local_connections);
50

51
/* dbus id -> async connection */
52 53
static GHashTable *async_map = NULL;
G_LOCK_DEFINE_STATIC(async_map);
54

55 56 57 58 59 60

GQuark
_g_vfs_error_quark (void)
{
  return g_quark_from_static_string ("g-vfs-error-quark");
}
61

62 63 64 65
static void
connection_data_free (gpointer p)
{
  VfsConnectionData *data = p;
66 67

  g_free (data->async_dbus_id);
68 69 70
  g_free (data);
}

71
static void
72 73 74 75
vfs_connection_closed (GDBusConnection *connection,
                       gboolean remote_peer_vanished,
                       GError *error,
                       gpointer user_data)
76 77
{
  VfsConnectionData *connection_data;
78

79 80
  connection_data = g_object_get_data (G_OBJECT (connection), "connection_data");
  g_assert (connection_data != NULL);
81

82
  if (connection_data->async_dbus_id)
83
    {
84
      _g_daemon_vfs_invalidate (connection_data->async_dbus_id, NULL);
85 86 87
      G_LOCK (async_map);
      g_hash_table_remove (async_map, connection_data->async_dbus_id);
      G_UNLOCK (async_map);
88 89 90 91
    }
}

static void
92 93
vfs_connection_setup (GDBusConnection *connection,
		      gboolean async)
94
{
95
  VfsConnectionData *connection_data;
96

97
  connection_data = g_new0 (VfsConnectionData, 1);
98
  
99
  g_object_set_data_full (G_OBJECT (connection), "connection_data", connection_data, connection_data_free);
100

101
  g_signal_connect (connection, "closed", G_CALLBACK (vfs_connection_closed), NULL);
102 103
}

104
/*******************************************************************
Alexander Larsson's avatar
Alexander Larsson committed
105
 *                Caching of async connections                     *
106 107
 *******************************************************************/

108

109
static GDBusConnection *
110
get_connection_for_async (const char *dbus_id)
111
{
112
  GDBusConnection *connection;
113 114

  connection = NULL;
115 116 117
  G_LOCK (async_map);
  if (async_map != NULL)
    connection = g_hash_table_lookup (async_map, dbus_id);
118
  if (connection)
119
    g_object_ref (connection);
120
  G_UNLOCK (async_map);
121 122 123 124
  
  return connection;
}

Alexander Larsson's avatar
Alexander Larsson committed
125 126 127
static void
close_and_unref_connection (void *data)
{
128
  GDBusConnection *connection = G_DBUS_CONNECTION (data);
129 130 131
  
  /* TODO: watch for the need to manually call g_dbus_connection_close_sync () */
  g_object_unref (connection);
Alexander Larsson's avatar
Alexander Larsson committed
132 133
}

134
static void
135
set_connection_for_async (GDBusConnection *connection, const char *dbus_id)
136
{
137 138
  VfsConnectionData *data;
  
139
  G_LOCK (async_map);
140 141
  data = g_object_get_data (G_OBJECT (connection), "connection_data");
  g_assert (data != NULL);
142 143
  data->async_dbus_id = g_strdup (dbus_id);

144
  if (async_map == NULL)
Alexander Larsson's avatar
Alexander Larsson committed
145
    async_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, close_and_unref_connection);
146
      
147
  g_hash_table_insert (async_map, g_strdup (dbus_id), connection);
148
  g_object_ref (connection);
149
  G_UNLOCK (async_map);
150
}
151

152 153 154
/**************************************************************************
 *                 Asynchronous daemon calls                              *
 *************************************************************************/
155

156
typedef struct {
William Jon McCann's avatar
William Jon McCann committed
157
  char *dbus_id;
158

159
  GDBusConnection *connection;
160 161 162 163 164 165 166 167 168 169
  GCancellable *cancellable;

  GVfsAsyncDBusCallback callback;
  gpointer callback_data;
  
  GError *io_error;
  gulong cancelled_tag;
} AsyncDBusCall;

static void
170
async_call_finish (AsyncDBusCall *async_call)
171
{
Alexander Larsson's avatar
Alexander Larsson committed
172
  if (async_call->callback)
173
    async_call->callback (async_call->io_error ? NULL : async_call->connection,
Alexander Larsson's avatar
Alexander Larsson committed
174 175
			  async_call->io_error, 
			  async_call->callback_data);
176

177 178
  g_clear_object (&async_call->connection);
  g_clear_object (&async_call->cancellable);
179
  g_clear_error (&async_call->io_error);
William Jon McCann's avatar
William Jon McCann committed
180
  g_free (async_call->dbus_id);
181 182 183 184
  g_free (async_call);
}

static void
185 186 187
async_got_private_connection_cb (GObject *source_object,
                                 GAsyncResult *res,
                                 gpointer user_data)
188
{
189 190 191
  AsyncDBusCall *async_call = user_data;
  GDBusConnection *connection, *existing_connection;
  GError *error = NULL;
192
  
193 194
  connection = g_dbus_connection_new_for_address_finish (res, &error);
  if (!connection)
195 196
    {
      async_call->io_error = g_error_copy (error);
197
      g_error_free (error);
198
      async_call_finish (async_call);
199 200 201
      return;
    }

202
  vfs_connection_setup (connection, TRUE);
203
  
204
  /* Maybe we already had a connection? This happens if we requested
205
   * the same owner several times in parallel.
206 207 208
   * If so, just drop this connection and use that.
   */
  
209
  existing_connection = get_connection_for_async (async_call->dbus_id);
210 211 212
  if (existing_connection != NULL)
    {
      async_call->connection = existing_connection;
213 214
      /* TODO: watch for the need to manually call g_dbus_connection_close_sync () */
      g_object_unref (connection);
215 216 217
    }
  else
    {  
218
      set_connection_for_async (connection, async_call->dbus_id);
219 220
      async_call->connection = connection;
    }
221 222 223

  /* Maybe we were canceled while setting up connection, then
   * avoid doing the operation */
224
  if (g_cancellable_set_error_if_cancelled (async_call->cancellable, &async_call->io_error))
225
    {
226
      async_call_finish (async_call);
227 228 229
      return;
    }

230
  async_call_finish (async_call);
231 232 233
}

static void
234 235 236
async_get_connection_response (GVfsDBusDaemon *proxy,
                               GAsyncResult *res,
                               gpointer user_data)
237
{
238 239
  AsyncDBusCall *async_call = user_data;
  GError *error = NULL;
William Jon McCann's avatar
William Jon McCann committed
240
  gchar *address1 = NULL;
241

242
  if (! gvfs_dbus_daemon_call_get_connection_finish (proxy,
243
                                                     &address1, NULL,
244 245 246 247 248
                                                     res,
                                                     &error))
    {
      async_call->io_error = g_error_copy (error);
      g_error_free (error);
William Jon McCann's avatar
William Jon McCann committed
249
      g_free (address1);
250 251 252 253 254 255 256 257 258 259
      async_call_finish (async_call);
      return;
    }
  
  g_dbus_connection_new_for_address (address1,
                                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
                                     NULL, /* GDBusAuthObserver */
                                     async_call->cancellable,
                                     async_got_private_connection_cb,
                                     async_call);
William Jon McCann's avatar
William Jon McCann committed
260
  g_free (address1);
261
}
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
static void
open_connection_async_cb (GObject *source_object,
                          GAsyncResult *res,
                          gpointer user_data)
{
  GVfsDBusDaemon *proxy;
  AsyncDBusCall *async_call = user_data;
  GError *error = NULL;
 
  proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error);
  if (proxy == NULL)
    {
      async_call->io_error = g_error_copy (error);
      g_error_free (error);
      async_call_finish (async_call);
      return;
    }
  
  g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_TIMEOUT_MSECS);
282
  
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
  gvfs_dbus_daemon_call_get_connection (proxy,
                                        async_call->cancellable,
                                        (GAsyncReadyCallback) async_get_connection_response,
                                        async_call);
  
  g_object_unref (proxy);
}

static void
open_connection_async (AsyncDBusCall *async_call)
{
  gvfs_dbus_daemon_proxy_new (_g_daemon_vfs_get_async_bus (),
                              G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
                              async_call->dbus_id,
                              G_VFS_DBUS_DAEMON_PATH,
                              async_call->cancellable,
                              open_connection_async_cb,
                              async_call);
301 302 303
}

void
304 305 306 307
_g_dbus_connection_get_for_async (const char *dbus_id,
                                  GVfsAsyncDBusCallback callback,
                                  gpointer callback_data,
                                  GCancellable *cancellable)
308 309 310 311
{
  AsyncDBusCall *async_call;

  async_call = g_new0 (AsyncDBusCall, 1);
312
  async_call->dbus_id = g_strdup (dbus_id);
313 314 315 316
  if (cancellable)
    async_call->cancellable = g_object_ref (cancellable);
  async_call->callback = callback;
  async_call->callback_data = callback_data;
317

318 319 320
  async_call->connection = get_connection_for_async (async_call->dbus_id);
  if (async_call->connection == NULL)
    open_connection_async (async_call);
321
  else
322
    {
323
      async_call_finish (async_call);
324
    }
325
}
326

327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367

typedef struct {
  GDBusConnection *connection;
  guint32 serial;
} AsyncCallCancelData;

static void
async_call_cancel_data_free (gpointer _data)
{
  AsyncCallCancelData *data = _data;

  g_object_unref (data->connection);
  g_free (data);
}

static void
cancelled_got_proxy (GObject *source_object,
                     GAsyncResult *res,
                     gpointer user_data)
{
  guint32 serial = GPOINTER_TO_UINT (user_data);
  GVfsDBusDaemon *proxy;
  GError *error = NULL;

  proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error);
  if (! proxy)
    {
      g_printerr ("Failed to construct daemon proxy for cancellation: %s (%s, %d)\n",
                  error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
      return;
    }

  gvfs_dbus_daemon_call_cancel (proxy,
                                serial,
                                NULL,
                                NULL,  /* we don't need any reply */
                                NULL);
  g_object_unref (proxy);
}

368 369
static gboolean
async_call_cancelled_cb_on_idle (gpointer _data)
370 371 372 373 374
{
  AsyncCallCancelData *data = _data;

  /* TODO: use shared daemon proxy on private connection if possible */
  gvfs_dbus_daemon_proxy_new (data->connection,
375
                              G_DBUS_PROXY_FLAGS_NONE,
376 377 378 379 380
                              NULL,
                              G_VFS_DBUS_DAEMON_PATH,
                              NULL,
                              cancelled_got_proxy,
                              GUINT_TO_POINTER (data->serial));  /* not passing "data" in as long it may not exist anymore between async calls */
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

  return FALSE;
}

/* Might be called on another thread */
static void
async_call_cancelled_cb (GCancellable *cancellable,
                         gpointer _data)
{
  AsyncCallCancelData *data = _data;
  AsyncCallCancelData *idle_data;

  idle_data = g_new0 (AsyncCallCancelData, 1);
  idle_data->connection = g_object_ref (data->connection);
  idle_data->serial = data->serial;

  /* Call on idle to not block g_cancellable_disconnect() as it causes deadlocks
   * in gdbus codes, see: https://gitlab.gnome.org/GNOME/glib/issues/1023.
   */
  g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
                   async_call_cancelled_cb_on_idle,
                   idle_data,
                   async_call_cancel_data_free);
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
}

gulong
_g_dbus_async_subscribe_cancellable (GDBusConnection *connection, GCancellable *cancellable)
{
  AsyncCallCancelData *cancel_data;
  gulong cancelled_tag = 0;

  if (cancellable)
    {
      cancel_data = g_new0 (AsyncCallCancelData, 1);
      cancel_data->connection = g_object_ref (connection);
      /* make sure we get the serial *after* the message has been sent, otherwise
       * it will be 0
       */
      cancel_data->serial = g_dbus_connection_get_last_serial (connection);
      cancelled_tag =
        g_signal_connect_data (cancellable, "cancelled",
422
                               G_CALLBACK (async_call_cancelled_cb),
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
                               cancel_data,
                               (GClosureNotify)async_call_cancel_data_free,
                               0);
    }

  return cancelled_tag;
}

void
_g_dbus_async_unsubscribe_cancellable (GCancellable *cancellable, gulong cancelled_tag)
{
  if (cancelled_tag)
    {
      g_assert (cancellable != NULL);
      g_signal_handler_disconnect (cancellable, cancelled_tag);
    }
}

void
442 443
_g_dbus_send_cancelled_with_serial_sync (GDBusConnection *connection,
                                         guint32 serial)
444 445 446 447 448
{
  GVfsDBusDaemon *proxy;
  GError *error = NULL;

  proxy = gvfs_dbus_daemon_proxy_new_sync (connection,
449
                                           G_DBUS_PROXY_FLAGS_NONE,
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
                                           NULL,
                                           G_VFS_DBUS_DAEMON_PATH,
                                           NULL,
                                           &error);
  if (! proxy)
    {
      g_printerr ("Failed to construct daemon proxy for cancellation: %s (%s, %d)\n",
                  error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
      return;
    }

  gvfs_dbus_daemon_call_cancel (proxy,
                                serial,
                                NULL,
                                NULL,  /* we don't need any reply */
                                NULL);
  g_object_unref (proxy);
}

470 471 472 473 474 475 476
void
_g_dbus_send_cancelled_sync (GDBusConnection *connection)
{
  _g_dbus_send_cancelled_with_serial_sync (connection,
                                           g_dbus_connection_get_last_serial (connection));
}

477

478 479 480 481
/*************************************************************************
 *               get per-thread synchronous dbus connections             *
 *************************************************************************/

482
struct _ThreadLocalConnections {
483
  GHashTable *connections;
484
  GDBusConnection *session_bus;
485
};
486 487

static void
488
free_local_connections (ThreadLocalConnections *local)
489
{
490
  g_hash_table_destroy (local->connections);
491
  g_clear_object (&local->session_bus);
492
  g_free (local);
493
}
494

495 496 497 498 499 500
static void
invalidate_local_connection (const char *dbus_id,
			     GError **error)
{
  ThreadLocalConnections *local;
  
501
  _g_daemon_vfs_invalidate (dbus_id, NULL);
502

503
  local = g_private_get (&local_connections);
504 505 506
  if (local)
    g_hash_table_remove (local->connections, dbus_id);
  
507 508 509 510
  g_set_error_literal (error,
		       G_VFS_ERROR,
		       G_VFS_ERROR_RETRY,
		       "Cache invalid, retry (internally handled)");
511 512
}

513
GDBusConnection *
514
_g_dbus_connection_get_sync (const char *dbus_id,
515
                             GCancellable *cancellable,
516
			     GError **error)
517
{
518
  GDBusConnection *bus;
519
  ThreadLocalConnections *local;
520
  GError *local_error;
521
  GDBusConnection *connection;
522
  gchar *address1;
523 524
  GVfsDBusDaemon *daemon_proxy;
  gboolean res;
Alexander Larsson's avatar
Alexander Larsson committed
525

526 527
  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    return NULL;
528

529
  local = g_private_get (&local_connections);
530 531
  if (local == NULL)
    {
Alexander Larsson's avatar
Alexander Larsson committed
532
      local = g_new0 (ThreadLocalConnections, 1);
533
      local->connections = g_hash_table_new_full (g_str_hash, g_str_equal,
534
						  g_free, (GDestroyNotify)g_object_unref);
535
      g_private_set (&local_connections, local);
536
    }
537

538
  if (dbus_id == NULL)
539 540 541 542 543
    {
      /* Session bus */
      
      if (local->session_bus)
	{
544
	  if (! g_dbus_connection_is_closed (local->session_bus))
545 546 547
	    return local->session_bus;

	  /* Session bus was disconnected, re-connect */
548
	  g_object_unref (local->session_bus);
549
	  local->session_bus = NULL;
550 551
	}
    }
552
  else
553
    {
554 555 556 557
      /* Mount daemon connection */
      
      connection = g_hash_table_lookup (local->connections, dbus_id);
      if (connection != NULL)
558
	{
559
	  if (g_dbus_connection_is_closed (connection))
560 561 562 563 564 565 566 567 568 569
	    {
	      /* The mount for this connection died, we invalidate
	       * the caches, and then caller needs to retry.
	       */

	      invalidate_local_connection (dbus_id, error);
	      return NULL;
	    }
	  
	  return connection;
570 571
	}
    }
572

573
  if (local->session_bus == NULL)
574
    {
575
      bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, error);
576
      if (bus == NULL)
577
        return NULL;
578 579 580 581
      
      local->session_bus = bus;

      if (dbus_id == NULL)
582
	return bus; /* We actually wanted the session bus, so done */
583
    }
584

585 586 587 588 589 590 591 592 593
  daemon_proxy = gvfs_dbus_daemon_proxy_new_sync (local->session_bus,
                                                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
                                                  dbus_id,
                                                  G_VFS_DBUS_DAEMON_PATH,
                                                  cancellable,
                                                  error);
  if (daemon_proxy == NULL)
    return NULL;

William Jon McCann's avatar
William Jon McCann committed
594
  address1 = NULL;
595 596
  res = gvfs_dbus_daemon_call_get_connection_sync (daemon_proxy,
                                                   &address1,
597
                                                   NULL,
598 599 600 601 602
                                                   cancellable,
                                                   error);
  g_object_unref (daemon_proxy);

  if (!res)
William Jon McCann's avatar
William Jon McCann committed
603 604 605 606 607
    {
      g_free (address1);
      return NULL;
    }

608

609
  local_error = NULL;
610 611 612 613 614
  connection = g_dbus_connection_new_for_address_sync (address1,
                                                       G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
                                                       NULL, /* GDBusAuthObserver */
                                                       cancellable,
                                                       &local_error);
William Jon McCann's avatar
William Jon McCann committed
615 616
  g_free (address1);

617 618
  if (!connection)
    {
619
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
620
		   "Error while getting peer-to-peer dbus connection: %s",
621 622
		   local_error->message);
      g_error_free (local_error);
623 624 625
      return NULL;
    }

626
  vfs_connection_setup (connection, FALSE);
Alexander Larsson's avatar
Alexander Larsson committed
627

628
  g_hash_table_insert (local->connections, g_strdup (dbus_id), connection);
629 630 631

  return connection;
}
632

633 634 635 636 637 638 639
void
_g_propagate_error_stripped (GError **dest, GError *src)
{
  g_propagate_error (dest, src);
  if (dest && *dest)
    g_dbus_error_strip_remote_error (*dest);
}