gapplicationimpl-dbus.c 29.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * 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
 * 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
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
  GApplication    *app;
124 125
};

126 127 128 129

static GApplicationCommandLine *
g_dbus_command_line_new (GDBusMethodInvocation *invocation);

130 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
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);
}
170

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
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;

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

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

194 195 196 197
      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);
198 199

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

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

211 212 213 214 215 216 217
      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;
        }

218 219 220 221 222 223 224 225
      /* 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);
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

      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);
249 250

      g_dbus_method_invocation_return_value (invocation, NULL);
251 252
    }

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

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

268 269
      /* Only on the GtkApplication interface */

270 271 272 273 274 275 276 277 278
      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);
    }
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
  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);
300 301

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

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

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

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

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

  return appid_path;
}

329 330 331 332 333 334 335 336 337 338 339 340 341
/* 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)
342
{
343 344
  const static GDBusInterfaceVTable vtable = {
    g_application_impl_method_call,
345 346
    g_application_impl_get_property,
    NULL /* set_property */
347
  };
348
  GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
349 350 351 352
  GVariant *reply;
  guint32 rval;

  if (org_gtk_Application == NULL)
353
    {
354 355 356 357 358 359 360 361 362 363
      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);
364 365 366 367 368 369 370 371

      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);
372
    }
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

  /* 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;

394 395 396 397 398 399
  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;

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

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

406 407 408 409 410 411
  if (!app_class->dbus_register (impl->app,
                                 impl->session_bus,
                                 impl->object_path,
                                 error))
    return FALSE;

412
  if (impl->bus_name == NULL)
413 414 415 416 417 418 419 420 421 422 423 424 425 426
    {
      /* 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.
   */
427 428 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
  /* 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)
{
456 457 458 459 460 461
  GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);

  app_class->dbus_unregister (impl->app,
                              impl->session_bus,
                              impl->object_path);

462 463 464 465 466 467
  if (impl->object_id)
    {
      g_dbus_connection_unregister_object (impl->session_bus, impl->object_id);
      impl->object_id = 0;
    }

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

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

480
  if (impl->primary && impl->bus_name)
481
    {
482 483 484 485 486
      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;
487
    }
488 489
}

490 491 492 493 494 495 496 497 498 499 500
void
g_application_impl_set_busy_state (GApplicationImpl *impl,
                                   gboolean          busy)
{
  if (impl->busy != busy)
    {
      impl->busy = busy;
      send_property_change (impl);
    }
}

501 502 503 504 505 506 507 508 509
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);
510 511 512 513 514

  g_slice_free (GApplicationImpl, impl);
}

GApplicationImpl *
515 516 517
g_application_impl_register (GApplication        *application,
                             const gchar         *appid,
                             GApplicationFlags    flags,
518
                             GActionGroup        *exported_actions,
519 520 521
                             GRemoteActionGroup **remote_actions,
                             GCancellable        *cancellable,
                             GError             **error)
522
{
523
  GDBusActionGroup *actions;
524 525
  GApplicationImpl *impl;

526 527
  g_assert ((flags & G_APPLICATION_NON_UNIQUE) || appid != NULL);

528
  impl = g_slice_new0 (GApplicationImpl);
529 530

  impl->app = application;
531
  impl->exported_actions = exported_actions;
532 533 534 535

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

537
  impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL);
538 539 540

  if (impl->session_bus == NULL)
    {
541 542 543 544 545
      /* If we can't connect to the session bus, proceed as a normal
       * non-unique application.
       */
      *remote_actions = NULL;
      return impl;
546 547 548 549
    }

  impl->object_path = application_path_from_appid (appid);

550 551
  /* Only try to be the primary instance if
   * G_APPLICATION_IS_LAUNCHER was not specified.
552
   */
553
  if (~flags & G_APPLICATION_IS_LAUNCHER)
554
    {
555
      if (!g_application_impl_attempt_primary (impl, cancellable, error))
556
        {
557
          g_application_impl_destroy (impl);
558 559
          return NULL;
        }
560

561 562
      if (impl->primary)
        return impl;
563 564

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

567
      if (flags & G_APPLICATION_IS_SERVICE)
568 569
        {
          g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
570
                       "Unable to acquire bus name '%s'", appid);
571
          g_application_impl_destroy (impl);
572

573
          return NULL;
574 575 576
        }
    }

577 578 579 580
  /* 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).
   */
581 582
  actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path);
  if (!g_dbus_action_group_sync (actions, cancellable, error))
583 584
    {
      /* The primary appears not to exist.  Fail the registration. */
585
      g_application_impl_destroy (impl);
586
      g_object_unref (actions);
587 588 589 590

      return NULL;
    }

591
  *remote_actions = G_REMOTE_ACTION_GROUP (actions);
592

593 594 595 596 597 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
  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);
}

640 641 642 643 644 645 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
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;

679 680 681 682 683 684
#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

685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702

  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
703 704 705
g_application_impl_command_line (GApplicationImpl    *impl,
                                 const gchar * const *arguments,
                                 GVariant            *platform_data)
706 707 708 709 710 711 712 713 714 715 716 717 718
{
  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);

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
  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,
735 736 737 738
                                                 &vtable, &data, NULL, NULL);
  /* In theory we should try other paths... */
  g_assert (object_id != 0);

739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
#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);
756
    g_object_unref (fd_list);
757 758 759 760 761
  }
#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),
762
                          G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, NULL,
763
                          g_application_impl_cmdline_done, &data);
764
#endif
765 766 767 768 769 770 771 772 773 774

  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;
}

775 776 777
void
g_application_impl_flush (GApplicationImpl *impl)
{
778 779
  if (impl->session_bus)
    g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
780
}
781

782 783 784 785 786 787 788 789 790 791 792
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;
}
793

794
/* GDBusCommandLine implementation {{{1 */
795 796 797 798 799 800 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

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);
}

841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
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
860
        (void) g_close (fds[i], NULL);
861 862 863 864 865 866 867 868 869
      g_free (fds);
    }

  return result;
#else
  return NULL;
#endif
}

870
static void
871
g_dbus_command_line_finalize (GObject *object)
872
{
873 874
  GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) object;
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
  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;
900
  class->get_stdin = g_dbus_command_line_get_stdin;
901 902 903 904 905 906 907
}

static GApplicationCommandLine *
g_dbus_command_line_new (GDBusMethodInvocation *invocation)
{
  GDBusCommandLine *gdbcl;
  GVariant *args;
908
  GVariant *arguments, *platform_data;
909 910 911

  args = g_dbus_method_invocation_get_parameters (invocation);

912 913
  arguments = g_variant_get_child_value (args, 1);
  platform_data = g_variant_get_child_value (args, 2);
914
  gdbcl = g_object_new (g_dbus_command_line_get_type (),
915 916
                        "arguments", arguments,
                        "platform-data", platform_data,
917
                        NULL);
918 919 920
  g_variant_unref (arguments);
  g_variant_unref (platform_data);

921 922 923 924 925 926 927
  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);
}
928 929 930 931

/* Epilogue {{{1 */

/* vim:set foldmethod=marker: */