gapplicationimpl-dbus.c 29.4 KB
Newer Older
1 2 3 4 5 6
/*
 * Copyright © 2010 Codethink Limited
 *
 * 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
7
 * version 2.1 of the License, or (at your option) any later version.
8 9 10 11 12 13 14
 *
 * 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
15
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 17 18 19
 *
 * Authors: Ryan Lortie <desrt@desrt.ca>
 */

20 21
#include "config.h"

22 23
#include "gapplicationimpl.h"

24
#include "gactiongroup.h"
25
#include "gactiongroupexporter.h"
26
#include "gremoteactiongroup.h"
27
#include "gdbusactiongroup-private.h"
28 29 30 31 32
#include "gapplication.h"
#include "gfile.h"
#include "gdbusconnection.h"
#include "gdbusintrospection.h"
#include "gdbuserror.h"
Colin Walters's avatar
Colin Walters committed
33
#include "glib/gstdio.h"
34 35

#include <string.h>
36
#include <stdio.h>
37

38 39
#include "gapplicationcommandline.h"
#include "gdbusmethodinvocation.h"
40

41 42 43 44 45
#ifdef G_OS_UNIX
#include "gunixinputstream.h"
#include "gunixfdlist.h"
#endif

46
/* DBus Interface definition {{{1 */
47 48

/* For documentation of these interfaces, see
Michael Catanzaro's avatar
Michael Catanzaro committed
49
 * https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
50
 */
51 52
static const gchar org_gtk_Application_xml[] =
  "<node>"
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    "<interface name='org.gtk.Application'>"
      "<method name='Activate'>"
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
      "</method>"
      "<method name='Open'>"
        "<arg type='as' name='uris' direction='in'/>"
        "<arg type='s' name='hint' direction='in'/>"
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
      "</method>"
      "<method name='CommandLine'>"
        "<arg type='o' name='path' direction='in'/>"
        "<arg type='aay' name='arguments' direction='in'/>"
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
        "<arg type='i' name='exit-status' direction='out'/>"
      "</method>"
68
    "<property name='Busy' type='b' access='read'/>"
69
    "</interface>"
70 71 72 73
  "</node>";

static GDBusInterfaceInfo *org_gtk_Application;

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
static const gchar org_freedesktop_Application_xml[] =
  "<node>"
    "<interface name='org.freedesktop.Application'>"
      "<method name='Activate'>"
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
      "</method>"
      "<method name='Open'>"
        "<arg type='as' name='uris' direction='in'/>"
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
      "</method>"
      "<method name='ActivateAction'>"
        "<arg type='s' name='action-name' direction='in'/>"
        "<arg type='av' name='parameter' direction='in'/>"
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
      "</method>"
    "</interface>"
  "</node>";

static GDBusInterfaceInfo *org_freedesktop_Application;

94 95
static const gchar org_gtk_private_CommandLine_xml[] =
  "<node>"
96 97 98 99 100 101 102 103
    "<interface name='org.gtk.private.CommandLine'>"
      "<method name='Print'>"
        "<arg type='s' name='message' direction='in'/>"
      "</method>"
      "<method name='PrintError'>"
        "<arg type='s' name='message' direction='in'/>"
      "</method>"
    "</interface>"
104 105 106
  "</node>";

static GDBusInterfaceInfo *org_gtk_private_CommandLine;
107 108

/* GApplication implementation {{{1 */
109 110 111
struct _GApplicationImpl
{
  GDBusConnection *session_bus;
112
  GActionGroup    *exported_actions;
113
  const gchar     *bus_name;
114

115 116
  gchar           *object_path;
  guint            object_id;
117
  guint            fdo_object_id;
118
  guint            actions_id;
119 120

  gboolean         properties_live;
121
  gboolean         primary;
122
  gboolean         busy;
123
  gboolean         registered;
124
  GApplication    *app;
125 126
};

127 128 129 130

static GApplicationCommandLine *
g_dbus_command_line_new (GDBusMethodInvocation *invocation);

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
static GVariant *
g_application_impl_get_property (GDBusConnection *connection,
                                 const gchar  *sender,
                                 const gchar  *object_path,
                                 const gchar  *interface_name,
                                 const gchar  *property_name,
                                 GError      **error,
                                 gpointer      user_data)
{
  GApplicationImpl *impl = user_data;

  if (strcmp (property_name, "Busy") == 0)
    return g_variant_new_boolean (impl->busy);

  g_assert_not_reached ();

  return NULL;
}

static void
send_property_change (GApplicationImpl *impl)
{
  GVariantBuilder builder;

  g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
  g_variant_builder_add (&builder,
                         "{sv}",
                         "Busy", g_variant_new_boolean (impl->busy));

  g_dbus_connection_emit_signal (impl->session_bus,
                                 NULL,
                                 impl->object_path,
                                 "org.freedesktop.DBus.Properties",
                                 "PropertiesChanged",
                                 g_variant_new ("(sa{sv}as)",
                                                "org.gtk.Application",
                                                &builder,
                                                NULL),
                                 NULL);
}
171

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
static void
g_application_impl_method_call (GDBusConnection       *connection,
                                const gchar           *sender,
                                const gchar           *object_path,
                                const gchar           *interface_name,
                                const gchar           *method_name,
                                GVariant              *parameters,
                                GDBusMethodInvocation *invocation,
                                gpointer               user_data)
{
  GApplicationImpl *impl = user_data;
  GApplicationClass *class;

  class = G_APPLICATION_GET_CLASS (impl->app);

  if (strcmp (method_name, "Activate") == 0)
    {
      GVariant *platform_data;

191 192
      /* Completely the same for both freedesktop and gtk interfaces */

193
      g_variant_get (parameters, "(@a{sv})", &platform_data);
194

195 196 197 198
      class->before_emit (impl->app, platform_data);
      g_signal_emit_by_name (impl->app, "activate");
      class->after_emit (impl->app, platform_data);
      g_variant_unref (platform_data);
199 200

      g_dbus_method_invocation_return_value (invocation, NULL);
201 202 203 204
    }

  else if (strcmp (method_name, "Open") == 0)
    {
205
      GApplicationFlags flags;
206 207 208 209 210 211
      GVariant *platform_data;
      const gchar *hint;
      GVariant *array;
      GFile **files;
      gint n, i;

212 213 214 215 216 217 218
      flags = g_application_get_flags (impl->app);
      if ((flags & G_APPLICATION_HANDLES_OPEN) == 0)
        {
          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Application does not open files");
          return;
        }

219 220 221 222 223 224 225 226
      /* freedesktop interface has no hint parameter */
      if (g_str_equal (interface_name, "org.freedesktop.Application"))
        {
          g_variant_get (parameters, "(@as@a{sv})", &array, &platform_data);
          hint = "";
        }
      else
        g_variant_get (parameters, "(@as&s@a{sv})", &array, &hint, &platform_data);
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249

      n = g_variant_n_children (array);
      files = g_new (GFile *, n + 1);

      for (i = 0; i < n; i++)
        {
          const gchar *uri;

          g_variant_get_child (array, i, "&s", &uri);
          files[i] = g_file_new_for_uri (uri);
        }
      g_variant_unref (array);
      files[n] = NULL;

      class->before_emit (impl->app, platform_data);
      g_signal_emit_by_name (impl->app, "open", files, n, hint);
      class->after_emit (impl->app, platform_data);

      g_variant_unref (platform_data);

      for (i = 0; i < n; i++)
        g_object_unref (files[i]);
      g_free (files);
250 251

      g_dbus_method_invocation_return_value (invocation, NULL);
252 253
    }

254 255
  else if (strcmp (method_name, "CommandLine") == 0)
    {
256
      GApplicationFlags flags;
257 258 259 260
      GApplicationCommandLine *cmdline;
      GVariant *platform_data;
      int status;

261 262 263
      flags = g_application_get_flags (impl->app);
      if ((flags & G_APPLICATION_HANDLES_COMMAND_LINE) == 0)
        {
264 265
          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED,
                                                 "Application does not handle command line arguments");
266 267 268
          return;
        }

269 270
      /* Only on the GtkApplication interface */

271 272 273 274 275 276 277 278 279
      cmdline = g_dbus_command_line_new (invocation);
      platform_data = g_variant_get_child_value (parameters, 2);
      class->before_emit (impl->app, platform_data);
      g_signal_emit_by_name (impl->app, "command-line", cmdline, &status);
      g_application_command_line_set_exit_status (cmdline, status);
      class->after_emit (impl->app, platform_data);
      g_variant_unref (platform_data);
      g_object_unref (cmdline);
    }
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
  else if (g_str_equal (method_name, "ActivateAction"))
    {
      GVariant *parameter = NULL;
      GVariant *platform_data;
      GVariantIter *iter;
      const gchar *name;

      /* Only on the freedesktop interface */

      g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data);
      g_variant_iter_next (iter, "v", &parameter);
      g_variant_iter_free (iter);

      class->before_emit (impl->app, platform_data);
      g_action_group_activate_action (impl->exported_actions, name, parameter);
      class->after_emit (impl->app, platform_data);

      if (parameter)
        g_variant_unref (parameter);

      g_variant_unref (platform_data);
301 302

      g_dbus_method_invocation_return_value (invocation, NULL);
303
    }
304 305 306 307
  else
    g_assert_not_reached ();
}

308 309 310 311 312
static gchar *
application_path_from_appid (const gchar *appid)
{
  gchar *appid_path, *iter;

313 314 315 316
  if (appid == NULL)
    /* this is a private implementation detail */
    return g_strdup ("/org/gtk/Application/anonymous");

317 318 319 320 321
  appid_path = g_strconcat ("/", appid, NULL);
  for (iter = appid_path; *iter; iter++)
    {
      if (*iter == '.')
        *iter = '/';
322 323 324

      if (*iter == '-')
        *iter = '_';
325 326 327 328 329
    }

  return appid_path;
}

330 331 332 333 334 335 336 337 338 339 340 341 342
/* Attempt to become the primary instance.
 *
 * Returns %TRUE if everything went OK, regardless of if we became the
 * primary instance or not.  %FALSE is reserved for when something went
 * seriously wrong (and @error will be set too, in that case).
 *
 * After a %TRUE return, impl->primary will be TRUE if we were
 * successful.
 */
static gboolean
g_application_impl_attempt_primary (GApplicationImpl  *impl,
                                    GCancellable      *cancellable,
                                    GError           **error)
343
{
344 345
  const static GDBusInterfaceVTable vtable = {
    g_application_impl_method_call,
346 347
    g_application_impl_get_property,
    NULL /* set_property */
348
  };
349
  GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
350 351 352 353
  GVariant *reply;
  guint32 rval;

  if (org_gtk_Application == NULL)
354
    {
355 356 357 358 359 360 361 362 363 364
      GError *error = NULL;
      GDBusNodeInfo *info;

      info = g_dbus_node_info_new_for_xml (org_gtk_Application_xml, &error);
      if G_UNLIKELY (info == NULL)
        g_error ("%s", error->message);
      org_gtk_Application = g_dbus_node_info_lookup_interface (info, "org.gtk.Application");
      g_assert (org_gtk_Application != NULL);
      g_dbus_interface_info_ref (org_gtk_Application);
      g_dbus_node_info_unref (info);
365 366 367 368 369 370 371 372

      info = g_dbus_node_info_new_for_xml (org_freedesktop_Application_xml, &error);
      if G_UNLIKELY (info == NULL)
        g_error ("%s", error->message);
      org_freedesktop_Application = g_dbus_node_info_lookup_interface (info, "org.freedesktop.Application");
      g_assert (org_freedesktop_Application != NULL);
      g_dbus_interface_info_ref (org_freedesktop_Application);
      g_dbus_node_info_unref (info);
373
    }
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394

  /* We could possibly have been D-Bus activated as a result of incoming
   * requests on either the application or actiongroup interfaces.
   * Because of how GDBus dispatches messages, we need to ensure that
   * both of those things are registered before we attempt to request
   * our name.
   *
   * The action group need not be populated yet, as long as it happens
   * before we return to the mainloop.  The reason for that is because
   * GDBus does the check to make sure the object exists from the worker
   * thread but doesn't actually dispatch the action invocation until we
   * hit the mainloop in this thread.  There is also no danger of
   * receiving 'activate' or 'open' signals until after 'startup' runs,
   * for the same reason.
   */
  impl->object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
                                                       org_gtk_Application, &vtable, impl, NULL, error);

  if (impl->object_id == 0)
    return FALSE;

395 396 397 398 399 400
  impl->fdo_object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
                                                           org_freedesktop_Application, &vtable, impl, NULL, error);

  if (impl->fdo_object_id == 0)
    return FALSE;

401 402
  impl->actions_id = g_dbus_connection_export_action_group (impl->session_bus, impl->object_path,
                                                            impl->exported_actions, error);
403

404
  if (impl->actions_id == 0)
405 406
    return FALSE;

407
  impl->registered = TRUE;
408 409 410 411 412 413
  if (!app_class->dbus_register (impl->app,
                                 impl->session_bus,
                                 impl->object_path,
                                 error))
    return FALSE;

414
  if (impl->bus_name == NULL)
415 416 417 418 419 420 421 422 423 424 425 426 427 428
    {
      /* If this is a non-unique application then it is sufficient to
       * have our object paths registered. We can return now.
       *
       * Note: non-unique applications always act as primary-instance.
       */
      impl->primary = TRUE;
      return TRUE;
    }

  /* If this is a unique application then we need to attempt to own
   * the well-known name and fall back to remote mode (!is_primary)
   * in the case that we can't do that.
   */
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
  /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
  reply = g_dbus_connection_call_sync (impl->session_bus, "org.freedesktop.DBus", "/org/freedesktop/DBus",
                                       "org.freedesktop.DBus", "RequestName",
                                       g_variant_new ("(su)", impl->bus_name, 0x4), G_VARIANT_TYPE ("(u)"),
                                       0, -1, cancellable, error);

  if (reply == NULL)
    return FALSE;

  g_variant_get (reply, "(u)", &rval);
  g_variant_unref (reply);

  /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
  impl->primary = (rval != 3);

  return TRUE;
}

/* Stop doing the things that the primary instance does.
 *
 * This should be called if attempting to become the primary instance
 * failed (in order to clean up any partial success) and should also
 * be called when freeing the GApplication.
 *
 * It is safe to call this multiple times.
 */
static void
g_application_impl_stop_primary (GApplicationImpl *impl)
{
458 459
  GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);

460 461 462 463 464 465 466
  if (impl->registered)
    {
      app_class->dbus_unregister (impl->app,
                                  impl->session_bus,
                                  impl->object_path);
      impl->registered = FALSE;
    }
467

468 469 470 471 472 473
  if (impl->object_id)
    {
      g_dbus_connection_unregister_object (impl->session_bus, impl->object_id);
      impl->object_id = 0;
    }

474 475 476 477 478 479
  if (impl->fdo_object_id)
    {
      g_dbus_connection_unregister_object (impl->session_bus, impl->fdo_object_id);
      impl->fdo_object_id = 0;
    }

480
  if (impl->actions_id)
481
    {
482 483
      g_dbus_connection_unexport_action_group (impl->session_bus, impl->actions_id);
      impl->actions_id = 0;
484 485
    }

486
  if (impl->primary && impl->bus_name)
487
    {
488 489 490 491 492
      g_dbus_connection_call (impl->session_bus, "org.freedesktop.DBus",
                              "/org/freedesktop/DBus", "org.freedesktop.DBus",
                              "ReleaseName", g_variant_new ("(s)", impl->bus_name),
                              NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
      impl->primary = FALSE;
493
    }
494 495
}

496 497 498 499 500 501 502 503 504 505 506
void
g_application_impl_set_busy_state (GApplicationImpl *impl,
                                   gboolean          busy)
{
  if (impl->busy != busy)
    {
      impl->busy = busy;
      send_property_change (impl);
    }
}

507 508 509 510 511 512 513 514 515
void
g_application_impl_destroy (GApplicationImpl *impl)
{
  g_application_impl_stop_primary (impl);

  if (impl->session_bus)
    g_object_unref (impl->session_bus);

  g_free (impl->object_path);
516 517 518 519 520

  g_slice_free (GApplicationImpl, impl);
}

GApplicationImpl *
521 522 523
g_application_impl_register (GApplication        *application,
                             const gchar         *appid,
                             GApplicationFlags    flags,
524
                             GActionGroup        *exported_actions,
525 526 527
                             GRemoteActionGroup **remote_actions,
                             GCancellable        *cancellable,
                             GError             **error)
528
{
529
  GDBusActionGroup *actions;
530 531
  GApplicationImpl *impl;

532 533
  g_assert ((flags & G_APPLICATION_NON_UNIQUE) || appid != NULL);

534
  impl = g_slice_new0 (GApplicationImpl);
535 536

  impl->app = application;
537
  impl->exported_actions = exported_actions;
538 539 540 541

  /* non-unique applications do not attempt to acquire a bus name */
  if (~flags & G_APPLICATION_NON_UNIQUE)
    impl->bus_name = appid;
542

543
  impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL);
544 545 546

  if (impl->session_bus == NULL)
    {
547 548 549 550 551
      /* If we can't connect to the session bus, proceed as a normal
       * non-unique application.
       */
      *remote_actions = NULL;
      return impl;
552 553 554 555
    }

  impl->object_path = application_path_from_appid (appid);

556 557
  /* Only try to be the primary instance if
   * G_APPLICATION_IS_LAUNCHER was not specified.
558
   */
559
  if (~flags & G_APPLICATION_IS_LAUNCHER)
560
    {
561
      if (!g_application_impl_attempt_primary (impl, cancellable, error))
562
        {
563
          g_application_impl_destroy (impl);
564 565
          return NULL;
        }
566

567 568
      if (impl->primary)
        return impl;
569 570

      /* We didn't make it.  Drop our service-side stuff. */
571
      g_application_impl_stop_primary (impl);
572

573
      if (flags & G_APPLICATION_IS_SERVICE)
574 575
        {
          g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
576
                       "Unable to acquire bus name '%s'", appid);
577
          g_application_impl_destroy (impl);
578

579
          return NULL;
580 581 582
        }
    }

583 584 585 586
  /* We are non-primary.  Try to get the primary's list of actions.
   * This also serves as a mechanism to ensure that the primary exists
   * (ie: DBus service files installed correctly, etc).
   */
587 588
  actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path);
  if (!g_dbus_action_group_sync (actions, cancellable, error))
589 590
    {
      /* The primary appears not to exist.  Fail the registration. */
591
      g_application_impl_destroy (impl);
592
      g_object_unref (actions);
593 594 595 596

      return NULL;
    }

597
  *remote_actions = G_REMOTE_ACTION_GROUP (actions);
598

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
  return impl;
}

void
g_application_impl_activate (GApplicationImpl *impl,
                             GVariant         *platform_data)
{
  g_dbus_connection_call (impl->session_bus,
                          impl->bus_name,
                          impl->object_path,
                          "org.gtk.Application",
                          "Activate",
                          g_variant_new ("(@a{sv})", platform_data),
                          NULL, 0, -1, NULL, NULL, NULL);
}

void
g_application_impl_open (GApplicationImpl  *impl,
                         GFile            **files,
                         gint               n_files,
                         const gchar       *hint,
                         GVariant          *platform_data)
{
  GVariantBuilder builder;
  gint i;

  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})"));
  g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
  for (i = 0; i < n_files; i++)
    {
      gchar *uri = g_file_get_uri (files[i]);
      g_variant_builder_add (&builder, "s", uri);
      g_free (uri);
    }
  g_variant_builder_close (&builder);
  g_variant_builder_add (&builder, "s", hint);
  g_variant_builder_add_value (&builder, platform_data);

  g_dbus_connection_call (impl->session_bus,
                          impl->bus_name,
                          impl->object_path,
                          "org.gtk.Application",
                          "Open",
                          g_variant_builder_end (&builder),
                          NULL, 0, -1, NULL, NULL, NULL);
}

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
static void
g_application_impl_cmdline_method_call (GDBusConnection       *connection,
                                        const gchar           *sender,
                                        const gchar           *object_path,
                                        const gchar           *interface_name,
                                        const gchar           *method_name,
                                        GVariant              *parameters,
                                        GDBusMethodInvocation *invocation,
                                        gpointer               user_data)
{
  const gchar *message;

  g_variant_get_child (parameters, 0, "&s", &message);

  if (strcmp (method_name, "Print") == 0)
    g_print ("%s", message);
  else if (strcmp (method_name, "PrintError") == 0)
    g_printerr ("%s", message);
  else
    g_assert_not_reached ();

  g_dbus_method_invocation_return_value (invocation, NULL);
}

typedef struct
{
  GMainLoop *loop;
  int status;
} CommandLineData;

static void
g_application_impl_cmdline_done (GObject      *source,
                                 GAsyncResult *result,
                                 gpointer      user_data)
{
  CommandLineData *data = user_data;
  GError *error = NULL;
  GVariant *reply;

685 686 687 688 689 690
#ifdef G_OS_UNIX
  reply = g_dbus_connection_call_with_unix_fd_list_finish (G_DBUS_CONNECTION (source), NULL, result, &error);
#else
  reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
#endif

691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708

  if (reply != NULL)
    {
      g_variant_get (reply, "(i)", &data->status);
      g_variant_unref (reply);
    }

  else
    {
      g_printerr ("%s\n", error->message);
      g_error_free (error);
      data->status = 1;
    }

  g_main_loop_quit (data->loop);
}

int
709 710 711
g_application_impl_command_line (GApplicationImpl    *impl,
                                 const gchar * const *arguments,
                                 GVariant            *platform_data)
712 713 714 715 716 717 718 719 720 721 722 723 724
{
  const static GDBusInterfaceVTable vtable = {
    g_application_impl_cmdline_method_call
  };
  const gchar *object_path = "/org/gtk/Application/CommandLine";
  GMainContext *context;
  CommandLineData data;
  guint object_id;

  context = g_main_context_new ();
  data.loop = g_main_loop_new (context, FALSE);
  g_main_context_push_thread_default (context);

725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
  if (org_gtk_private_CommandLine == NULL)
    {
      GError *error = NULL;
      GDBusNodeInfo *info;

      info = g_dbus_node_info_new_for_xml (org_gtk_private_CommandLine_xml, &error);
      if G_UNLIKELY (info == NULL)
        g_error ("%s", error->message);
      org_gtk_private_CommandLine = g_dbus_node_info_lookup_interface (info, "org.gtk.private.CommandLine");
      g_assert (org_gtk_private_CommandLine != NULL);
      g_dbus_interface_info_ref (org_gtk_private_CommandLine);
      g_dbus_node_info_unref (info);
    }

  object_id = g_dbus_connection_register_object (impl->session_bus, object_path,
                                                 org_gtk_private_CommandLine,
741 742 743 744
                                                 &vtable, &data, NULL, NULL);
  /* In theory we should try other paths... */
  g_assert (object_id != 0);

745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
#ifdef G_OS_UNIX
  {
    GError *error = NULL;
    GUnixFDList *fd_list;

    /* send along the stdin in case
     * g_application_command_line_get_stdin_data() is called
     */
    fd_list = g_unix_fd_list_new ();
    g_unix_fd_list_append (fd_list, 0, &error);
    g_assert_no_error (error);

    g_dbus_connection_call_with_unix_fd_list (impl->session_bus, impl->bus_name, impl->object_path,
                                              "org.gtk.Application", "CommandLine",
                                              g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data),
                                              G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, fd_list, NULL,
                                              g_application_impl_cmdline_done, &data);
762
    g_object_unref (fd_list);
763 764 765 766 767
  }
#else
  g_dbus_connection_call (impl->session_bus, impl->bus_name, impl->object_path,
                          "org.gtk.Application", "CommandLine",
                          g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data),
768
                          G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, NULL,
769
                          g_application_impl_cmdline_done, &data);
770
#endif
771 772 773 774 775 776 777 778 779 780

  g_main_loop_run (data.loop);

  g_main_context_pop_thread_default (context);
  g_main_context_unref (context);
  g_main_loop_unref (data.loop);

  return data.status;
}

781 782 783
void
g_application_impl_flush (GApplicationImpl *impl)
{
784 785
  if (impl->session_bus)
    g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
786
}
787

788 789 790 791 792 793 794 795 796 797 798
GDBusConnection *
g_application_impl_get_dbus_connection (GApplicationImpl *impl)
{
  return impl->session_bus;
}

const gchar *
g_application_impl_get_dbus_object_path (GApplicationImpl *impl)
{
  return impl->object_path;
}
799

800
/* GDBusCommandLine implementation {{{1 */
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846

typedef GApplicationCommandLineClass GDBusCommandLineClass;
static GType g_dbus_command_line_get_type (void);
typedef struct
{
  GApplicationCommandLine  parent_instance;
  GDBusMethodInvocation   *invocation;

  GDBusConnection *connection;
  const gchar     *bus_name;
  const gchar     *object_path;
} GDBusCommandLine;


G_DEFINE_TYPE (GDBusCommandLine,
               g_dbus_command_line,
               G_TYPE_APPLICATION_COMMAND_LINE)

static void
g_dbus_command_line_print_literal (GApplicationCommandLine *cmdline,
                                   const gchar             *message)
{
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;

  g_dbus_connection_call (gdbcl->connection,
                          gdbcl->bus_name,
                          gdbcl->object_path,
                          "org.gtk.private.CommandLine", "Print",
                          g_variant_new ("(s)", message),
                          NULL, 0, -1, NULL, NULL, NULL);
}

static void
g_dbus_command_line_printerr_literal (GApplicationCommandLine *cmdline,
                                      const gchar             *message)
{
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;

  g_dbus_connection_call (gdbcl->connection,
                          gdbcl->bus_name,
                          gdbcl->object_path,
                          "org.gtk.private.CommandLine", "PrintError",
                          g_variant_new ("(s)", message),
                          NULL, 0, -1, NULL, NULL, NULL);
}

847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
static GInputStream *
g_dbus_command_line_get_stdin (GApplicationCommandLine *cmdline)
{
#ifdef G_OS_UNIX
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
  GInputStream *result = NULL;
  GDBusMessage *message;
  GUnixFDList *fd_list;

  message = g_dbus_method_invocation_get_message (gdbcl->invocation);
  fd_list = g_dbus_message_get_unix_fd_list (message);

  if (fd_list && g_unix_fd_list_get_length (fd_list))
    {
      gint *fds, n_fds, i;

      fds = g_unix_fd_list_steal_fds (fd_list, &n_fds);
      result = g_unix_input_stream_new (fds[0], TRUE);
      for (i = 1; i < n_fds; i++)
Colin Walters's avatar
Colin Walters committed
866
        (void) g_close (fds[i], NULL);
867 868 869 870 871 872 873 874 875
      g_free (fds);
    }

  return result;
#else
  return NULL;
#endif
}

876
static void
877
g_dbus_command_line_finalize (GObject *object)
878
{
879 880
  GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) object;
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
  gint status;

  status = g_application_command_line_get_exit_status (cmdline);

  g_dbus_method_invocation_return_value (gdbcl->invocation,
                                         g_variant_new ("(i)", status));
  g_object_unref (gdbcl->invocation);

  G_OBJECT_CLASS (g_dbus_command_line_parent_class)
    ->finalize (object);
}

static void
g_dbus_command_line_init (GDBusCommandLine *gdbcl)
{
}

static void
g_dbus_command_line_class_init (GApplicationCommandLineClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);

  object_class->finalize = g_dbus_command_line_finalize;
  class->printerr_literal = g_dbus_command_line_printerr_literal;
  class->print_literal = g_dbus_command_line_print_literal;
906
  class->get_stdin = g_dbus_command_line_get_stdin;
907 908 909 910 911 912 913
}

static GApplicationCommandLine *
g_dbus_command_line_new (GDBusMethodInvocation *invocation)
{
  GDBusCommandLine *gdbcl;
  GVariant *args;
914
  GVariant *arguments, *platform_data;
915 916 917

  args = g_dbus_method_invocation_get_parameters (invocation);

918 919
  arguments = g_variant_get_child_value (args, 1);
  platform_data = g_variant_get_child_value (args, 2);
920
  gdbcl = g_object_new (g_dbus_command_line_get_type (),
921 922
                        "arguments", arguments,
                        "platform-data", platform_data,
923
                        NULL);
924 925 926
  g_variant_unref (arguments);
  g_variant_unref (platform_data);

927 928 929 930 931 932 933
  gdbcl->connection = g_dbus_method_invocation_get_connection (invocation);
  gdbcl->bus_name = g_dbus_method_invocation_get_sender (invocation);
  g_variant_get_child (args, 0, "&o", &gdbcl->object_path);
  gdbcl->invocation = g_object_ref (invocation);

  return G_APPLICATION_COMMAND_LINE (gdbcl);
}
934 935 936 937

/* Epilogue {{{1 */

/* vim:set foldmethod=marker: */