Coverage Report

Created: 2025-06-13 06:55

/src/glib/gio/gapplicationimpl-dbus.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2010 Codethink Limited
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 *
19
 * Authors: Ryan Lortie <desrt@desrt.ca>
20
 */
21
22
#include "config.h"
23
24
#include "gapplicationimpl.h"
25
26
#include "gactiongroup.h"
27
#include "gactiongroupexporter.h"
28
#include "gremoteactiongroup.h"
29
#include "gdbusactiongroup-private.h"
30
#include "gapplication.h"
31
#include "gfile.h"
32
#include "gdbusconnection.h"
33
#include "gdbusintrospection.h"
34
#include "gdbuserror.h"
35
#include "glib/gstdio.h"
36
37
#include <string.h>
38
#include <stdio.h>
39
40
#include "gapplicationcommandline.h"
41
#include "gdbusmethodinvocation.h"
42
43
#ifdef G_OS_UNIX
44
#include "gunixinputstream.h"
45
#include "gunixfdlist.h"
46
#endif
47
48
/* D-Bus Interface definition {{{1 */
49
50
/* For documentation of these interfaces, see
51
 * https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
52
 */
53
static const gchar org_gtk_Application_xml[] =
54
  "<node>"
55
    "<interface name='org.gtk.Application'>"
56
      "<method name='Activate'>"
57
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
58
      "</method>"
59
      "<method name='Open'>"
60
        "<arg type='as' name='uris' direction='in'/>"
61
        "<arg type='s' name='hint' direction='in'/>"
62
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
63
      "</method>"
64
      "<method name='CommandLine'>"
65
        "<arg type='o' name='path' direction='in'/>"
66
        "<arg type='aay' name='arguments' direction='in'/>"
67
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
68
        "<arg type='i' name='exit-status' direction='out'/>"
69
      "</method>"
70
    "<property name='Busy' type='b' access='read'/>"
71
    "</interface>"
72
  "</node>";
73
74
static GDBusInterfaceInfo *org_gtk_Application;
75
76
static const gchar org_freedesktop_Application_xml[] =
77
  "<node>"
78
    "<interface name='org.freedesktop.Application'>"
79
      "<method name='Activate'>"
80
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
81
      "</method>"
82
      "<method name='Open'>"
83
        "<arg type='as' name='uris' direction='in'/>"
84
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
85
      "</method>"
86
      "<method name='ActivateAction'>"
87
        "<arg type='s' name='action-name' direction='in'/>"
88
        "<arg type='av' name='parameter' direction='in'/>"
89
        "<arg type='a{sv}' name='platform-data' direction='in'/>"
90
      "</method>"
91
    "</interface>"
92
  "</node>";
93
94
static GDBusInterfaceInfo *org_freedesktop_Application;
95
96
static const gchar org_gtk_private_CommandLine_xml[] =
97
  "<node>"
98
    "<interface name='org.gtk.private.CommandLine'>"
99
      "<method name='Print'>"
100
        "<arg type='s' name='message' direction='in'/>"
101
      "</method>"
102
      "<method name='PrintError'>"
103
        "<arg type='s' name='message' direction='in'/>"
104
      "</method>"
105
    "</interface>"
106
  "</node>";
107
108
static GDBusInterfaceInfo *org_gtk_private_CommandLine;
109
110
/* GApplication implementation {{{1 */
111
struct _GApplicationImpl
112
{
113
  GDBusConnection *session_bus;
114
  GActionGroup    *exported_actions;
115
  const gchar     *bus_name;
116
  guint            name_lost_signal;
117
118
  gchar           *object_path;
119
  guint            object_id;
120
  guint            fdo_object_id;
121
  guint            actions_id;
122
123
  gboolean         properties_live;
124
  gboolean         primary;
125
  gboolean         busy;
126
  gboolean         registered;
127
  GApplication    *app;
128
};
129
130
131
static GApplicationCommandLine *
132
g_dbus_command_line_new (GDBusMethodInvocation *invocation);
133
134
static GVariant *
135
g_application_impl_get_property (GDBusConnection *connection,
136
                                 const gchar  *sender,
137
                                 const gchar  *object_path,
138
                                 const gchar  *interface_name,
139
                                 const gchar  *property_name,
140
                                 GError      **error,
141
                                 gpointer      user_data)
142
0
{
143
0
  GApplicationImpl *impl = user_data;
144
145
0
  if (strcmp (property_name, "Busy") == 0)
146
0
    return g_variant_new_boolean (impl->busy);
147
148
0
  g_assert_not_reached ();
149
150
0
  return NULL;
151
0
}
152
153
static void
154
send_property_change (GApplicationImpl *impl)
155
0
{
156
0
  GVariantBuilder builder;
157
158
0
  g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
159
0
  g_variant_builder_add (&builder,
160
0
                         "{sv}",
161
0
                         "Busy", g_variant_new_boolean (impl->busy));
162
163
0
  g_dbus_connection_emit_signal (impl->session_bus,
164
0
                                 NULL,
165
0
                                 impl->object_path,
166
0
                                 "org.freedesktop.DBus.Properties",
167
0
                                 "PropertiesChanged",
168
0
                                 g_variant_new ("(sa{sv}as)",
169
0
                                                "org.gtk.Application",
170
0
                                                &builder,
171
0
                                                NULL),
172
0
                                 NULL);
173
0
}
174
175
static void
176
g_application_impl_method_call (GDBusConnection       *connection,
177
                                const gchar           *sender,
178
                                const gchar           *object_path,
179
                                const gchar           *interface_name,
180
                                const gchar           *method_name,
181
                                GVariant              *parameters,
182
                                GDBusMethodInvocation *invocation,
183
                                gpointer               user_data)
184
0
{
185
0
  GApplicationImpl *impl = user_data;
186
0
  GApplicationClass *class;
187
188
0
  class = G_APPLICATION_GET_CLASS (impl->app);
189
190
0
  if (strcmp (method_name, "Activate") == 0)
191
0
    {
192
0
      GVariant *platform_data;
193
194
      /* Completely the same for both freedesktop and gtk interfaces */
195
196
0
      g_variant_get (parameters, "(@a{sv})", &platform_data);
197
198
0
      class->before_emit (impl->app, platform_data);
199
0
      g_signal_emit_by_name (impl->app, "activate");
200
0
      class->after_emit (impl->app, platform_data);
201
0
      g_variant_unref (platform_data);
202
203
0
      g_dbus_method_invocation_return_value (invocation, NULL);
204
0
    }
205
206
0
  else if (strcmp (method_name, "Open") == 0)
207
0
    {
208
0
      GApplicationFlags flags;
209
0
      GVariant *platform_data;
210
0
      const gchar *hint;
211
0
      GVariant *array;
212
0
      GFile **files;
213
0
      gint n, i;
214
215
0
      flags = g_application_get_flags (impl->app);
216
0
      if ((flags & G_APPLICATION_HANDLES_OPEN) == 0)
217
0
        {
218
0
          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Application does not open files");
219
0
          return;
220
0
        }
221
222
      /* freedesktop interface has no hint parameter */
223
0
      if (g_str_equal (interface_name, "org.freedesktop.Application"))
224
0
        {
225
0
          g_variant_get (parameters, "(@as@a{sv})", &array, &platform_data);
226
0
          hint = "";
227
0
        }
228
0
      else
229
0
        g_variant_get (parameters, "(@as&s@a{sv})", &array, &hint, &platform_data);
230
231
0
      n = g_variant_n_children (array);
232
0
      files = g_new (GFile *, n + 1);
233
234
0
      for (i = 0; i < n; i++)
235
0
        {
236
0
          const gchar *uri;
237
238
0
          g_variant_get_child (array, i, "&s", &uri);
239
0
          files[i] = g_file_new_for_uri (uri);
240
0
        }
241
0
      g_variant_unref (array);
242
0
      files[n] = NULL;
243
244
0
      class->before_emit (impl->app, platform_data);
245
0
      g_signal_emit_by_name (impl->app, "open", files, n, hint);
246
0
      class->after_emit (impl->app, platform_data);
247
248
0
      g_variant_unref (platform_data);
249
250
0
      for (i = 0; i < n; i++)
251
0
        g_object_unref (files[i]);
252
0
      g_free (files);
253
254
0
      g_dbus_method_invocation_return_value (invocation, NULL);
255
0
    }
256
257
0
  else if (strcmp (method_name, "CommandLine") == 0)
258
0
    {
259
0
      GApplicationFlags flags;
260
0
      GApplicationCommandLine *cmdline;
261
0
      GVariant *platform_data;
262
0
      int status;
263
264
0
      flags = g_application_get_flags (impl->app);
265
0
      if ((flags & G_APPLICATION_HANDLES_COMMAND_LINE) == 0)
266
0
        {
267
0
          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED,
268
0
                                                 "Application does not handle command line arguments");
269
0
          return;
270
0
        }
271
272
      /* Only on the GtkApplication interface */
273
274
0
      cmdline = g_dbus_command_line_new (invocation);
275
0
      platform_data = g_variant_get_child_value (parameters, 2);
276
0
      class->before_emit (impl->app, platform_data);
277
0
      g_signal_emit_by_name (impl->app, "command-line", cmdline, &status);
278
0
      g_application_command_line_set_exit_status (cmdline, status);
279
0
      class->after_emit (impl->app, platform_data);
280
0
      g_variant_unref (platform_data);
281
0
      g_object_unref (cmdline);
282
0
    }
283
0
  else if (g_str_equal (method_name, "ActivateAction"))
284
0
    {
285
0
      GVariant *parameter = NULL;
286
0
      GVariant *platform_data;
287
0
      GVariantIter *iter;
288
0
      const gchar *name;
289
0
      const GVariantType *parameter_type = NULL;
290
291
      /* Only on the freedesktop interface */
292
293
0
      g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data);
294
0
      g_variant_iter_next (iter, "v", &parameter);
295
0
      g_variant_iter_free (iter);
296
297
      /* Check the action exists and the parameter type matches. */
298
0
      if (!g_action_group_query_action (impl->exported_actions,
299
0
                                        name, NULL, &parameter_type,
300
0
                                        NULL, NULL, NULL))
301
0
        {
302
0
          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
303
0
                                                 "Unknown action ‘%s’", name);
304
0
          g_clear_pointer (&parameter, g_variant_unref);
305
0
          g_variant_unref (platform_data);
306
0
          return;
307
0
        }
308
309
0
      if (!((parameter_type == NULL && parameter == NULL) ||
310
0
            (parameter_type != NULL && parameter != NULL && g_variant_is_of_type (parameter, parameter_type))))
311
0
        {
312
0
          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
313
0
                                                 "Invalid parameter for action ‘%s’: expected type %s but got type %s",
314
0
                                                 name,
315
0
                                                 (parameter_type != NULL) ? (const gchar *) parameter_type : "()",
316
0
                                                 (parameter != NULL) ? g_variant_get_type_string (parameter) : "()");
317
0
          g_clear_pointer (&parameter, g_variant_unref);
318
0
          g_variant_unref (platform_data);
319
0
          return;
320
0
        }
321
322
0
      class->before_emit (impl->app, platform_data);
323
0
      g_action_group_activate_action (impl->exported_actions, name, parameter);
324
0
      class->after_emit (impl->app, platform_data);
325
326
0
      if (parameter)
327
0
        g_variant_unref (parameter);
328
329
0
      g_variant_unref (platform_data);
330
331
0
      g_dbus_method_invocation_return_value (invocation, NULL);
332
0
    }
333
0
  else
334
0
    g_assert_not_reached ();
335
0
}
336
337
static gchar *
338
application_path_from_appid (const gchar *appid)
339
0
{
340
0
  gchar *appid_path, *iter;
341
342
0
  if (appid == NULL)
343
    /* this is a private implementation detail */
344
0
    return g_strdup ("/org/gtk/Application/anonymous");
345
346
0
  appid_path = g_strconcat ("/", appid, NULL);
347
0
  for (iter = appid_path; *iter; iter++)
348
0
    {
349
0
      if (*iter == '.')
350
0
        *iter = '/';
351
352
0
      if (*iter == '-')
353
0
        *iter = '_';
354
0
    }
355
356
0
  return appid_path;
357
0
}
358
359
static void g_application_impl_stop_primary (GApplicationImpl *impl);
360
361
static void
362
name_lost (GDBusConnection *bus,
363
           const char      *sender_name,
364
           const char      *object_path,
365
           const char      *interface_name,
366
           const char      *signal_name,
367
           GVariant        *parameters,
368
           gpointer         user_data)
369
0
{
370
0
  GApplicationImpl *impl = user_data;
371
0
  gboolean handled;
372
373
0
  impl->primary = FALSE;
374
0
  g_application_impl_stop_primary (impl);
375
0
  g_signal_emit_by_name (impl->app, "name-lost", &handled);
376
0
}
377
378
/* Attempt to become the primary instance.
379
 *
380
 * Returns %TRUE if everything went OK, regardless of if we became the
381
 * primary instance or not.  %FALSE is reserved for when something went
382
 * seriously wrong (and @error will be set too, in that case).
383
 *
384
 * After a %TRUE return, impl->primary will be TRUE if we were
385
 * successful.
386
 */
387
static gboolean
388
g_application_impl_attempt_primary (GApplicationImpl  *impl,
389
                                    GCancellable      *cancellable,
390
                                    GError           **error)
391
0
{
392
0
  static const GDBusInterfaceVTable vtable = {
393
0
    g_application_impl_method_call,
394
0
    g_application_impl_get_property,
395
0
    NULL, /* set_property */
396
0
    { 0 }
397
0
  };
398
0
  GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
399
0
  GBusNameOwnerFlags name_owner_flags;
400
0
  GApplicationFlags app_flags;
401
0
  GVariant *reply;
402
0
  guint32 rval;
403
0
  GError *local_error = NULL;
404
405
0
  if (org_gtk_Application == NULL)
406
0
    {
407
0
      GError *my_error = NULL;
408
0
      GDBusNodeInfo *info;
409
410
0
      info = g_dbus_node_info_new_for_xml (org_gtk_Application_xml, &my_error);
411
0
      if G_UNLIKELY (info == NULL)
412
0
        g_error ("%s", my_error->message);
413
0
      org_gtk_Application = g_dbus_node_info_lookup_interface (info, "org.gtk.Application");
414
0
      g_assert (org_gtk_Application != NULL);
415
0
      g_dbus_interface_info_ref (org_gtk_Application);
416
0
      g_dbus_node_info_unref (info);
417
418
0
      info = g_dbus_node_info_new_for_xml (org_freedesktop_Application_xml, &my_error);
419
0
      if G_UNLIKELY (info == NULL)
420
0
        g_error ("%s", my_error->message);
421
0
      org_freedesktop_Application = g_dbus_node_info_lookup_interface (info, "org.freedesktop.Application");
422
0
      g_assert (org_freedesktop_Application != NULL);
423
0
      g_dbus_interface_info_ref (org_freedesktop_Application);
424
0
      g_dbus_node_info_unref (info);
425
0
    }
426
427
  /* We could possibly have been D-Bus activated as a result of incoming
428
   * requests on either the application or actiongroup interfaces.
429
   * Because of how GDBus dispatches messages, we need to ensure that
430
   * both of those things are registered before we attempt to request
431
   * our name.
432
   *
433
   * The action group need not be populated yet, as long as it happens
434
   * before we return to the mainloop.  The reason for that is because
435
   * GDBus does the check to make sure the object exists from the worker
436
   * thread but doesn't actually dispatch the action invocation until we
437
   * hit the mainloop in this thread.  There is also no danger of
438
   * receiving 'activate' or 'open' signals until after 'startup' runs,
439
   * for the same reason.
440
   */
441
0
  impl->object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
442
0
                                                       org_gtk_Application, &vtable, impl, NULL, error);
443
444
0
  if (impl->object_id == 0)
445
0
    return FALSE;
446
447
0
  impl->fdo_object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
448
0
                                                           org_freedesktop_Application, &vtable, impl, NULL, error);
449
450
0
  if (impl->fdo_object_id == 0)
451
0
    return FALSE;
452
453
0
  impl->actions_id = g_dbus_connection_export_action_group (impl->session_bus, impl->object_path,
454
0
                                                            impl->exported_actions, error);
455
456
0
  if (impl->actions_id == 0)
457
0
    return FALSE;
458
459
0
  impl->registered = TRUE;
460
0
  if (!app_class->dbus_register (impl->app,
461
0
                                 impl->session_bus,
462
0
                                 impl->object_path,
463
0
                                 &local_error))
464
0
    {
465
0
      g_return_val_if_fail (local_error != NULL, FALSE);
466
0
      g_propagate_error (error, g_steal_pointer (&local_error));
467
0
      return FALSE;
468
0
    }
469
470
0
  g_return_val_if_fail (local_error == NULL, FALSE);
471
472
0
  if (impl->bus_name == NULL)
473
0
    {
474
      /* If this is a non-unique application then it is sufficient to
475
       * have our object paths registered. We can return now.
476
       *
477
       * Note: non-unique applications always act as primary-instance.
478
       */
479
0
      impl->primary = TRUE;
480
0
      return TRUE;
481
0
    }
482
483
  /* If this is a unique application then we need to attempt to own
484
   * the well-known name and fall back to remote mode (!is_primary)
485
   * in the case that we can't do that.
486
   */
487
0
  name_owner_flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE;
488
0
  app_flags = g_application_get_flags (impl->app);
489
490
0
  if (app_flags & G_APPLICATION_ALLOW_REPLACEMENT)
491
0
    {
492
0
      impl->name_lost_signal = g_dbus_connection_signal_subscribe (impl->session_bus,
493
0
                                                                   "org.freedesktop.DBus",
494
0
                                                                   "org.freedesktop.DBus",
495
0
                                                                   "NameLost",
496
0
                                                                   "/org/freedesktop/DBus",
497
0
                                                                   impl->bus_name,
498
0
                                                                   G_DBUS_SIGNAL_FLAGS_NONE,
499
0
                                                                   name_lost,
500
0
                                                                   impl,
501
0
                                                                   NULL);
502
503
0
      name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
504
0
    }
505
0
  if (app_flags & G_APPLICATION_REPLACE)
506
0
    name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
507
508
0
  reply = g_dbus_connection_call_sync (impl->session_bus,
509
0
                                       "org.freedesktop.DBus",
510
0
                                       "/org/freedesktop/DBus",
511
0
                                       "org.freedesktop.DBus",
512
0
                                       "RequestName",
513
0
                                       g_variant_new ("(su)", impl->bus_name, name_owner_flags),
514
0
                                       G_VARIANT_TYPE ("(u)"),
515
0
                                       0, -1, cancellable, error);
516
517
0
  if (reply == NULL)
518
0
    return FALSE;
519
520
0
  g_variant_get (reply, "(u)", &rval);
521
0
  g_variant_unref (reply);
522
523
  /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
524
0
  impl->primary = (rval != 3);
525
526
0
  if (!impl->primary && impl->name_lost_signal)
527
0
    {
528
0
      g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
529
0
      impl->name_lost_signal = 0;
530
0
    }
531
532
0
  return TRUE;
533
0
}
534
535
/* Stop doing the things that the primary instance does.
536
 *
537
 * This should be called if attempting to become the primary instance
538
 * failed (in order to clean up any partial success) and should also
539
 * be called when freeing the GApplication.
540
 *
541
 * It is safe to call this multiple times.
542
 */
543
static void
544
g_application_impl_stop_primary (GApplicationImpl *impl)
545
0
{
546
0
  GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
547
548
0
  if (impl->registered)
549
0
    {
550
0
      app_class->dbus_unregister (impl->app,
551
0
                                  impl->session_bus,
552
0
                                  impl->object_path);
553
0
      impl->registered = FALSE;
554
0
    }
555
556
0
  if (impl->object_id)
557
0
    {
558
0
      g_dbus_connection_unregister_object (impl->session_bus, impl->object_id);
559
0
      impl->object_id = 0;
560
0
    }
561
562
0
  if (impl->fdo_object_id)
563
0
    {
564
0
      g_dbus_connection_unregister_object (impl->session_bus, impl->fdo_object_id);
565
0
      impl->fdo_object_id = 0;
566
0
    }
567
568
0
  if (impl->actions_id)
569
0
    {
570
0
      g_dbus_connection_unexport_action_group (impl->session_bus, impl->actions_id);
571
0
      impl->actions_id = 0;
572
0
    }
573
574
0
  if (impl->name_lost_signal)
575
0
    {
576
0
      g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
577
0
      impl->name_lost_signal = 0;
578
0
    }
579
580
0
  if (impl->primary && impl->bus_name)
581
0
    {
582
0
      g_dbus_connection_call (impl->session_bus, "org.freedesktop.DBus",
583
0
                              "/org/freedesktop/DBus", "org.freedesktop.DBus",
584
0
                              "ReleaseName", g_variant_new ("(s)", impl->bus_name),
585
0
                              NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
586
0
      impl->primary = FALSE;
587
0
    }
588
0
}
589
590
void
591
g_application_impl_set_busy_state (GApplicationImpl *impl,
592
                                   gboolean          busy)
593
0
{
594
0
  if (impl->busy != busy)
595
0
    {
596
0
      impl->busy = busy;
597
0
      send_property_change (impl);
598
0
    }
599
0
}
600
601
void
602
g_application_impl_destroy (GApplicationImpl *impl)
603
0
{
604
0
  g_application_impl_stop_primary (impl);
605
606
0
  if (impl->session_bus)
607
0
    g_object_unref (impl->session_bus);
608
609
0
  g_free (impl->object_path);
610
611
0
  g_slice_free (GApplicationImpl, impl);
612
0
}
613
614
GApplicationImpl *
615
g_application_impl_register (GApplication        *application,
616
                             const gchar         *appid,
617
                             GApplicationFlags    flags,
618
                             GActionGroup        *exported_actions,
619
                             GRemoteActionGroup **remote_actions,
620
                             GCancellable        *cancellable,
621
                             GError             **error)
622
0
{
623
0
  GDBusActionGroup *actions;
624
0
  GApplicationImpl *impl;
625
626
0
  g_assert ((flags & G_APPLICATION_NON_UNIQUE) || appid != NULL);
627
628
0
  impl = g_slice_new0 (GApplicationImpl);
629
630
0
  impl->app = application;
631
0
  impl->exported_actions = exported_actions;
632
633
  /* non-unique applications do not attempt to acquire a bus name */
634
0
  if (~flags & G_APPLICATION_NON_UNIQUE)
635
0
    impl->bus_name = appid;
636
637
0
  impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL);
638
639
0
  if (impl->session_bus == NULL)
640
0
    {
641
      /* If we can't connect to the session bus, proceed as a normal
642
       * non-unique application.
643
       */
644
0
      *remote_actions = NULL;
645
0
      return impl;
646
0
    }
647
648
0
  impl->object_path = application_path_from_appid (appid);
649
650
  /* Only try to be the primary instance if
651
   * G_APPLICATION_IS_LAUNCHER was not specified.
652
   */
653
0
  if (~flags & G_APPLICATION_IS_LAUNCHER)
654
0
    {
655
0
      if (!g_application_impl_attempt_primary (impl, cancellable, error))
656
0
        {
657
0
          g_application_impl_destroy (impl);
658
0
          return NULL;
659
0
        }
660
661
0
      if (impl->primary)
662
0
        return impl;
663
664
      /* We didn't make it.  Drop our service-side stuff. */
665
0
      g_application_impl_stop_primary (impl);
666
667
0
      if (flags & G_APPLICATION_IS_SERVICE)
668
0
        {
669
0
          g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
670
0
                       "Unable to acquire bus name '%s'", appid);
671
0
          g_application_impl_destroy (impl);
672
673
0
          return NULL;
674
0
        }
675
0
    }
676
677
  /* We are non-primary.  Try to get the primary's list of actions.
678
   * This also serves as a mechanism to ensure that the primary exists
679
   * (ie: D-Bus service files installed correctly, etc).
680
   */
681
0
  actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path);
682
0
  if (!g_dbus_action_group_sync (actions, cancellable, error))
683
0
    {
684
      /* The primary appears not to exist.  Fail the registration. */
685
0
      g_application_impl_destroy (impl);
686
0
      g_object_unref (actions);
687
688
0
      return NULL;
689
0
    }
690
691
0
  *remote_actions = G_REMOTE_ACTION_GROUP (actions);
692
693
0
  return impl;
694
0
}
695
696
void
697
g_application_impl_activate (GApplicationImpl *impl,
698
                             GVariant         *platform_data)
699
0
{
700
0
  g_dbus_connection_call (impl->session_bus,
701
0
                          impl->bus_name,
702
0
                          impl->object_path,
703
0
                          "org.gtk.Application",
704
0
                          "Activate",
705
0
                          g_variant_new ("(@a{sv})", platform_data),
706
0
                          NULL, 0, -1, NULL, NULL, NULL);
707
0
}
708
709
void
710
g_application_impl_open (GApplicationImpl  *impl,
711
                         GFile            **files,
712
                         gint               n_files,
713
                         const gchar       *hint,
714
                         GVariant          *platform_data)
715
0
{
716
0
  GVariantBuilder builder;
717
0
  gint i;
718
719
0
  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})"));
720
0
  g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
721
0
  for (i = 0; i < n_files; i++)
722
0
    {
723
0
      gchar *uri = g_file_get_uri (files[i]);
724
0
      g_variant_builder_add (&builder, "s", uri);
725
0
      g_free (uri);
726
0
    }
727
0
  g_variant_builder_close (&builder);
728
0
  g_variant_builder_add (&builder, "s", hint);
729
0
  g_variant_builder_add_value (&builder, platform_data);
730
731
0
  g_dbus_connection_call (impl->session_bus,
732
0
                          impl->bus_name,
733
0
                          impl->object_path,
734
0
                          "org.gtk.Application",
735
0
                          "Open",
736
0
                          g_variant_builder_end (&builder),
737
0
                          NULL, 0, -1, NULL, NULL, NULL);
738
0
}
739
740
static void
741
g_application_impl_cmdline_method_call (GDBusConnection       *connection,
742
                                        const gchar           *sender,
743
                                        const gchar           *object_path,
744
                                        const gchar           *interface_name,
745
                                        const gchar           *method_name,
746
                                        GVariant              *parameters,
747
                                        GDBusMethodInvocation *invocation,
748
                                        gpointer               user_data)
749
0
{
750
0
  const gchar *message;
751
752
0
  g_variant_get_child (parameters, 0, "&s", &message);
753
754
0
  if (strcmp (method_name, "Print") == 0)
755
0
    g_print ("%s", message);
756
0
  else if (strcmp (method_name, "PrintError") == 0)
757
0
    g_printerr ("%s", message);
758
0
  else
759
0
    g_assert_not_reached ();
760
761
0
  g_dbus_method_invocation_return_value (invocation, NULL);
762
0
}
763
764
typedef struct
765
{
766
  GMainLoop *loop;
767
  int status;
768
} CommandLineData;
769
770
static void
771
g_application_impl_cmdline_done (GObject      *source,
772
                                 GAsyncResult *result,
773
                                 gpointer      user_data)
774
0
{
775
0
  CommandLineData *data = user_data;
776
0
  GError *error = NULL;
777
0
  GVariant *reply;
778
779
0
#ifdef G_OS_UNIX
780
0
  reply = g_dbus_connection_call_with_unix_fd_list_finish (G_DBUS_CONNECTION (source), NULL, result, &error);
781
#else
782
  reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
783
#endif
784
785
786
0
  if (reply != NULL)
787
0
    {
788
0
      g_variant_get (reply, "(i)", &data->status);
789
0
      g_variant_unref (reply);
790
0
    }
791
792
0
  else
793
0
    {
794
0
      g_printerr ("%s\n", error->message);
795
0
      g_error_free (error);
796
0
      data->status = 1;
797
0
    }
798
799
0
  g_main_loop_quit (data->loop);
800
0
}
801
802
int
803
g_application_impl_command_line (GApplicationImpl    *impl,
804
                                 const gchar * const *arguments,
805
                                 GVariant            *platform_data)
806
0
{
807
0
  static const GDBusInterfaceVTable vtable = {
808
0
    g_application_impl_cmdline_method_call, NULL, NULL, { 0 }
809
0
  };
810
0
  const gchar *object_path = "/org/gtk/Application/CommandLine";
811
0
  GMainContext *context;
812
0
  CommandLineData data;
813
0
  guint object_id G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
814
815
0
  context = g_main_context_new ();
816
0
  data.loop = g_main_loop_new (context, FALSE);
817
0
  g_main_context_push_thread_default (context);
818
819
0
  if (org_gtk_private_CommandLine == NULL)
820
0
    {
821
0
      GError *error = NULL;
822
0
      GDBusNodeInfo *info;
823
824
0
      info = g_dbus_node_info_new_for_xml (org_gtk_private_CommandLine_xml, &error);
825
0
      if G_UNLIKELY (info == NULL)
826
0
        g_error ("%s", error->message);
827
0
      org_gtk_private_CommandLine = g_dbus_node_info_lookup_interface (info, "org.gtk.private.CommandLine");
828
0
      g_assert (org_gtk_private_CommandLine != NULL);
829
0
      g_dbus_interface_info_ref (org_gtk_private_CommandLine);
830
0
      g_dbus_node_info_unref (info);
831
0
    }
832
833
0
  object_id = g_dbus_connection_register_object (impl->session_bus, object_path,
834
0
                                                 org_gtk_private_CommandLine,
835
0
                                                 &vtable, &data, NULL, NULL);
836
  /* In theory we should try other paths... */
837
0
  g_assert (object_id != 0);
838
839
0
#ifdef G_OS_UNIX
840
0
  {
841
0
    GError *error = NULL;
842
0
    GUnixFDList *fd_list;
843
844
    /* send along the stdin in case
845
     * g_application_command_line_get_stdin_data() is called
846
     */
847
0
    fd_list = g_unix_fd_list_new ();
848
0
    g_unix_fd_list_append (fd_list, 0, &error);
849
0
    g_assert_no_error (error);
850
851
0
    g_dbus_connection_call_with_unix_fd_list (impl->session_bus, impl->bus_name, impl->object_path,
852
0
                                              "org.gtk.Application", "CommandLine",
853
0
                                              g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data),
854
0
                                              G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, fd_list, NULL,
855
0
                                              g_application_impl_cmdline_done, &data);
856
0
    g_object_unref (fd_list);
857
0
  }
858
#else
859
  g_dbus_connection_call (impl->session_bus, impl->bus_name, impl->object_path,
860
                          "org.gtk.Application", "CommandLine",
861
                          g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data),
862
                          G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, NULL,
863
                          g_application_impl_cmdline_done, &data);
864
#endif
865
866
0
  g_main_loop_run (data.loop);
867
868
0
  g_main_context_pop_thread_default (context);
869
0
  g_main_context_unref (context);
870
0
  g_main_loop_unref (data.loop);
871
872
0
  return data.status;
873
0
}
874
875
void
876
g_application_impl_flush (GApplicationImpl *impl)
877
0
{
878
0
  if (impl->session_bus)
879
0
    g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
880
0
}
881
882
GDBusConnection *
883
g_application_impl_get_dbus_connection (GApplicationImpl *impl)
884
0
{
885
0
  return impl->session_bus;
886
0
}
887
888
const gchar *
889
g_application_impl_get_dbus_object_path (GApplicationImpl *impl)
890
0
{
891
0
  return impl->object_path;
892
0
}
893
894
/* GDBusCommandLine implementation {{{1 */
895
896
typedef GApplicationCommandLineClass GDBusCommandLineClass;
897
static GType g_dbus_command_line_get_type (void);
898
typedef struct
899
{
900
  GApplicationCommandLine  parent_instance;
901
  GDBusMethodInvocation   *invocation;
902
903
  GDBusConnection *connection;
904
  const gchar     *bus_name;
905
  const gchar     *object_path;
906
} GDBusCommandLine;
907
908
909
G_DEFINE_TYPE (GDBusCommandLine,
910
               g_dbus_command_line,
911
               G_TYPE_APPLICATION_COMMAND_LINE)
912
913
static void
914
g_dbus_command_line_print_literal (GApplicationCommandLine *cmdline,
915
                                   const gchar             *message)
916
0
{
917
0
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
918
919
0
  g_dbus_connection_call (gdbcl->connection,
920
0
                          gdbcl->bus_name,
921
0
                          gdbcl->object_path,
922
0
                          "org.gtk.private.CommandLine", "Print",
923
0
                          g_variant_new ("(s)", message),
924
0
                          NULL, 0, -1, NULL, NULL, NULL);
925
0
}
926
927
static void
928
g_dbus_command_line_printerr_literal (GApplicationCommandLine *cmdline,
929
                                      const gchar             *message)
930
0
{
931
0
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
932
933
0
  g_dbus_connection_call (gdbcl->connection,
934
0
                          gdbcl->bus_name,
935
0
                          gdbcl->object_path,
936
0
                          "org.gtk.private.CommandLine", "PrintError",
937
0
                          g_variant_new ("(s)", message),
938
0
                          NULL, 0, -1, NULL, NULL, NULL);
939
0
}
940
941
static GInputStream *
942
g_dbus_command_line_get_stdin (GApplicationCommandLine *cmdline)
943
0
{
944
0
#ifdef G_OS_UNIX
945
0
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
946
0
  GInputStream *result = NULL;
947
0
  GDBusMessage *message;
948
0
  GUnixFDList *fd_list;
949
950
0
  message = g_dbus_method_invocation_get_message (gdbcl->invocation);
951
0
  fd_list = g_dbus_message_get_unix_fd_list (message);
952
953
0
  if (fd_list && g_unix_fd_list_get_length (fd_list))
954
0
    {
955
0
      gint *fds, n_fds, i;
956
957
0
      fds = g_unix_fd_list_steal_fds (fd_list, &n_fds);
958
0
      result = g_unix_input_stream_new (fds[0], TRUE);
959
0
      for (i = 1; i < n_fds; i++)
960
0
        (void) g_close (fds[i], NULL);
961
0
      g_free (fds);
962
0
    }
963
964
0
  return result;
965
#else
966
  return NULL;
967
#endif
968
0
}
969
970
static void
971
g_dbus_command_line_finalize (GObject *object)
972
0
{
973
0
  GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
974
0
  GDBusCommandLine *gdbcl = (GDBusCommandLine *) object;
975
0
  gint status;
976
977
0
  status = g_application_command_line_get_exit_status (cmdline);
978
979
0
  g_dbus_method_invocation_return_value (gdbcl->invocation,
980
0
                                         g_variant_new ("(i)", status));
981
0
  g_object_unref (gdbcl->invocation);
982
983
0
  G_OBJECT_CLASS (g_dbus_command_line_parent_class)
984
0
    ->finalize (object);
985
0
}
986
987
static void
988
g_dbus_command_line_init (GDBusCommandLine *gdbcl)
989
0
{
990
0
}
991
992
static void
993
g_dbus_command_line_class_init (GApplicationCommandLineClass *class)
994
0
{
995
0
  GObjectClass *object_class = G_OBJECT_CLASS (class);
996
997
0
  object_class->finalize = g_dbus_command_line_finalize;
998
0
  class->printerr_literal = g_dbus_command_line_printerr_literal;
999
0
  class->print_literal = g_dbus_command_line_print_literal;
1000
0
  class->get_stdin = g_dbus_command_line_get_stdin;
1001
0
}
1002
1003
static GApplicationCommandLine *
1004
g_dbus_command_line_new (GDBusMethodInvocation *invocation)
1005
0
{
1006
0
  GDBusCommandLine *gdbcl;
1007
0
  GVariant *args;
1008
0
  GVariant *arguments, *platform_data;
1009
1010
0
  args = g_dbus_method_invocation_get_parameters (invocation);
1011
1012
0
  arguments = g_variant_get_child_value (args, 1);
1013
0
  platform_data = g_variant_get_child_value (args, 2);
1014
0
  gdbcl = g_object_new (g_dbus_command_line_get_type (),
1015
0
                        "arguments", arguments,
1016
0
                        "platform-data", platform_data,
1017
0
                        NULL);
1018
0
  g_variant_unref (arguments);
1019
0
  g_variant_unref (platform_data);
1020
1021
0
  gdbcl->connection = g_dbus_method_invocation_get_connection (invocation);
1022
0
  gdbcl->bus_name = g_dbus_method_invocation_get_sender (invocation);
1023
0
  g_variant_get_child (args, 0, "&o", &gdbcl->object_path);
1024
0
  gdbcl->invocation = g_object_ref (invocation);
1025
1026
0
  return G_APPLICATION_COMMAND_LINE (gdbcl);
1027
0
}
1028
1029
/* Epilogue {{{1 */
1030
1031
/* vim:set foldmethod=marker: */