Branch data Line data Source code
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 : 0 : 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 : : {
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 : : g_assert_not_reached ();
149 : :
150 : : return NULL;
151 : : }
152 : :
153 : : static void
154 : 8 : send_property_change (GApplicationImpl *impl)
155 : : {
156 : : GVariantBuilder builder;
157 : :
158 : 8 : g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
159 : 8 : g_variant_builder_add (&builder,
160 : : "{sv}",
161 : : "Busy", g_variant_new_boolean (impl->busy));
162 : :
163 : 8 : g_dbus_connection_emit_signal (impl->session_bus,
164 : : NULL,
165 : 8 : impl->object_path,
166 : : "org.freedesktop.DBus.Properties",
167 : : "PropertiesChanged",
168 : : g_variant_new ("(sa{sv}as)",
169 : : "org.gtk.Application",
170 : : &builder,
171 : : NULL),
172 : : NULL);
173 : 8 : }
174 : :
175 : : static void
176 : 28 : 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 : : {
185 : 28 : GApplicationImpl *impl = user_data;
186 : : GApplicationClass *class;
187 : :
188 : 28 : class = G_APPLICATION_GET_CLASS (impl->app);
189 : :
190 [ + + ]: 28 : if (strcmp (method_name, "Activate") == 0)
191 : : {
192 : : GVariant *platform_data;
193 : :
194 : : /* Completely the same for both freedesktop and gtk interfaces */
195 : :
196 : 6 : g_variant_get (parameters, "(@a{sv})", &platform_data);
197 : :
198 : 6 : class->before_emit (impl->app, platform_data);
199 : 6 : g_signal_emit_by_name (impl->app, "activate");
200 : 6 : class->after_emit (impl->app, platform_data);
201 : 6 : g_variant_unref (platform_data);
202 : :
203 : 6 : g_dbus_method_invocation_return_value (invocation, NULL);
204 : : }
205 : :
206 [ + + ]: 22 : else if (strcmp (method_name, "Open") == 0)
207 : : {
208 : : GApplicationFlags flags;
209 : : GVariant *platform_data;
210 : : const gchar *hint;
211 : : GVariant *array;
212 : : GFile **files;
213 : : gint n, i;
214 : :
215 : 7 : flags = g_application_get_flags (impl->app);
216 [ - + ]: 7 : if ((flags & G_APPLICATION_HANDLES_OPEN) == 0)
217 : : {
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 : : }
221 : :
222 : : /* freedesktop interface has no hint parameter */
223 [ + + ]: 7 : if (g_str_equal (interface_name, "org.freedesktop.Application"))
224 : : {
225 : 4 : g_variant_get (parameters, "(@as@a{sv})", &array, &platform_data);
226 : 4 : hint = "";
227 : : }
228 : : else
229 : 3 : g_variant_get (parameters, "(@as&s@a{sv})", &array, &hint, &platform_data);
230 : :
231 : 7 : n = g_variant_n_children (array);
232 : 7 : files = g_new (GFile *, n + 1);
233 : :
234 [ + + ]: 15 : for (i = 0; i < n; i++)
235 : : {
236 : : const gchar *uri;
237 : :
238 : 8 : g_variant_get_child (array, i, "&s", &uri);
239 : 8 : files[i] = g_file_new_for_uri (uri);
240 : : }
241 : 7 : g_variant_unref (array);
242 : 7 : files[n] = NULL;
243 : :
244 : 7 : class->before_emit (impl->app, platform_data);
245 : 7 : g_signal_emit_by_name (impl->app, "open", files, n, hint);
246 : 7 : class->after_emit (impl->app, platform_data);
247 : :
248 : 7 : g_variant_unref (platform_data);
249 : :
250 [ + + ]: 15 : for (i = 0; i < n; i++)
251 : 8 : g_object_unref (files[i]);
252 : 7 : g_free (files);
253 : :
254 : 7 : g_dbus_method_invocation_return_value (invocation, NULL);
255 : : }
256 : :
257 [ + + ]: 15 : else if (strcmp (method_name, "CommandLine") == 0)
258 : : {
259 : : GApplicationFlags flags;
260 : : GApplicationCommandLine *cmdline;
261 : : GVariant *platform_data;
262 : : int status;
263 : :
264 : 4 : flags = g_application_get_flags (impl->app);
265 [ - + ]: 4 : if ((flags & G_APPLICATION_HANDLES_COMMAND_LINE) == 0)
266 : : {
267 : 0 : g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED,
268 : : "Application does not handle command line arguments");
269 : 0 : return;
270 : : }
271 : :
272 : : /* Only on the GtkApplication interface */
273 : :
274 : 4 : cmdline = g_dbus_command_line_new (invocation);
275 : 4 : platform_data = g_variant_get_child_value (parameters, 2);
276 : 4 : class->before_emit (impl->app, platform_data);
277 : 4 : g_signal_emit_by_name (impl->app, "command-line", cmdline, &status);
278 : 4 : g_application_command_line_set_exit_status (cmdline, status);
279 : 4 : class->after_emit (impl->app, platform_data);
280 : 4 : g_variant_unref (platform_data);
281 : 4 : g_object_unref (cmdline);
282 : : }
283 [ + - ]: 11 : else if (g_str_equal (method_name, "ActivateAction"))
284 : : {
285 : 11 : GVariant *parameter = NULL;
286 : : GVariant *platform_data;
287 : : GVariantIter *iter;
288 : : const gchar *name;
289 : 11 : const GVariantType *parameter_type = NULL;
290 : :
291 : : /* Only on the freedesktop interface */
292 : :
293 : 11 : g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data);
294 : 11 : g_variant_iter_next (iter, "v", ¶meter);
295 : 11 : g_variant_iter_free (iter);
296 : :
297 : : /* Check the action exists and the parameter type matches. */
298 [ + + ]: 11 : if (!g_action_group_query_action (impl->exported_actions,
299 : : name, NULL, ¶meter_type,
300 : : NULL, NULL, NULL))
301 : : {
302 : 1 : g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
303 : : "Unknown action ‘%s’", name);
304 : 1 : g_clear_pointer (¶meter, g_variant_unref);
305 : 1 : g_variant_unref (platform_data);
306 : 4 : return;
307 : : }
308 : :
309 [ + + + + ]: 10 : if (!((parameter_type == NULL && parameter == NULL) ||
310 [ + + + + : 4 : (parameter_type != NULL && parameter != NULL && g_variant_is_of_type (parameter, parameter_type))))
+ + ]
311 : : {
312 : 4 : g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
313 : : "Invalid parameter for action ‘%s’: expected type %s but got type %s",
314 : : name,
315 [ + + ]: 3 : (parameter_type != NULL) ? (const gchar *) parameter_type : "()",
316 [ + + ]: 3 : (parameter != NULL) ? g_variant_get_type_string (parameter) : "()");
317 : 3 : g_clear_pointer (¶meter, g_variant_unref);
318 : 3 : g_variant_unref (platform_data);
319 : 3 : return;
320 : : }
321 : :
322 : 7 : class->before_emit (impl->app, platform_data);
323 : 7 : g_action_group_activate_action (impl->exported_actions, name, parameter);
324 : 7 : class->after_emit (impl->app, platform_data);
325 : :
326 [ + + ]: 7 : if (parameter)
327 : 1 : g_variant_unref (parameter);
328 : :
329 : 7 : g_variant_unref (platform_data);
330 : :
331 : 7 : g_dbus_method_invocation_return_value (invocation, NULL);
332 : : }
333 : : else
334 : : g_assert_not_reached ();
335 : : }
336 : :
337 : : static gchar *
338 : 30 : application_path_from_appid (const gchar *appid)
339 : : {
340 : : gchar *appid_path, *iter;
341 : :
342 [ - + ]: 30 : if (appid == NULL)
343 : : /* this is a private implementation detail */
344 : 0 : return g_strdup ("/org/gtk/Application/anonymous");
345 : :
346 : 30 : appid_path = g_strconcat ("/", appid, NULL);
347 [ + + ]: 982 : for (iter = appid_path; *iter; iter++)
348 : : {
349 [ + + ]: 952 : if (*iter == '.')
350 : 86 : *iter = '/';
351 : :
352 [ - + ]: 952 : if (*iter == '-')
353 : 0 : *iter = '_';
354 : : }
355 : :
356 : 30 : return appid_path;
357 : : }
358 : :
359 : : static void g_application_impl_stop_primary (GApplicationImpl *impl);
360 : :
361 : : static void
362 : 1 : 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 : : {
370 : 1 : GApplicationImpl *impl = user_data;
371 : : gboolean handled;
372 : :
373 : 1 : impl->primary = FALSE;
374 : 1 : g_application_impl_stop_primary (impl);
375 : 1 : g_signal_emit_by_name (impl->app, "name-lost", &handled);
376 : 1 : }
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 : 30 : g_application_impl_attempt_primary (GApplicationImpl *impl,
389 : : GCancellable *cancellable,
390 : : GError **error)
391 : : {
392 : : static const GDBusInterfaceVTable vtable = {
393 : : g_application_impl_method_call,
394 : : g_application_impl_get_property,
395 : : NULL, /* set_property */
396 : : { 0 }
397 : : };
398 : 30 : GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
399 : : GBusNameOwnerFlags name_owner_flags;
400 : : GApplicationFlags app_flags;
401 : : GVariant *reply;
402 : : guint32 rval;
403 : 30 : GError *local_error = NULL;
404 : :
405 [ + + ]: 30 : if (org_gtk_Application == NULL)
406 : : {
407 : 6 : GError *my_error = NULL;
408 : : GDBusNodeInfo *info;
409 : :
410 : 6 : info = g_dbus_node_info_new_for_xml (org_gtk_Application_xml, &my_error);
411 [ - + ]: 6 : if G_UNLIKELY (info == NULL)
412 : 0 : g_error ("%s", my_error->message);
413 : 6 : org_gtk_Application = g_dbus_node_info_lookup_interface (info, "org.gtk.Application");
414 : 6 : g_assert (org_gtk_Application != NULL);
415 : 6 : g_dbus_interface_info_ref (org_gtk_Application);
416 : 6 : g_dbus_node_info_unref (info);
417 : :
418 : 6 : info = g_dbus_node_info_new_for_xml (org_freedesktop_Application_xml, &my_error);
419 [ - + ]: 6 : if G_UNLIKELY (info == NULL)
420 : 0 : g_error ("%s", my_error->message);
421 : 6 : org_freedesktop_Application = g_dbus_node_info_lookup_interface (info, "org.freedesktop.Application");
422 : 6 : g_assert (org_freedesktop_Application != NULL);
423 : 6 : g_dbus_interface_info_ref (org_freedesktop_Application);
424 : 6 : g_dbus_node_info_unref (info);
425 : : }
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 : 30 : impl->object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
442 : : org_gtk_Application, &vtable, impl, NULL, error);
443 : :
444 [ - + ]: 30 : if (impl->object_id == 0)
445 : 0 : return FALSE;
446 : :
447 : 30 : impl->fdo_object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
448 : : org_freedesktop_Application, &vtable, impl, NULL, error);
449 : :
450 [ - + ]: 30 : if (impl->fdo_object_id == 0)
451 : 0 : return FALSE;
452 : :
453 : 30 : impl->actions_id = g_dbus_connection_export_action_group (impl->session_bus, impl->object_path,
454 : : impl->exported_actions, error);
455 : :
456 [ - + ]: 30 : if (impl->actions_id == 0)
457 : 0 : return FALSE;
458 : :
459 : 30 : impl->registered = TRUE;
460 [ - + ]: 30 : if (!app_class->dbus_register (impl->app,
461 : : impl->session_bus,
462 : 30 : impl->object_path,
463 : : &local_error))
464 : : {
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 : : }
469 : :
470 : 30 : g_return_val_if_fail (local_error == NULL, FALSE);
471 : :
472 [ + + ]: 30 : if (impl->bus_name == NULL)
473 : : {
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 : 1 : impl->primary = TRUE;
480 : 1 : return TRUE;
481 : : }
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 : 29 : name_owner_flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE;
488 : 29 : app_flags = g_application_get_flags (impl->app);
489 : :
490 [ + + ]: 29 : if (app_flags & G_APPLICATION_ALLOW_REPLACEMENT)
491 : : {
492 : 3 : impl->name_lost_signal = g_dbus_connection_signal_subscribe (impl->session_bus,
493 : : "org.freedesktop.DBus",
494 : : "org.freedesktop.DBus",
495 : : "NameLost",
496 : : "/org/freedesktop/DBus",
497 : : impl->bus_name,
498 : : G_DBUS_SIGNAL_FLAGS_NONE,
499 : : name_lost,
500 : : impl,
501 : : NULL);
502 : :
503 : 3 : name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
504 : : }
505 [ + + ]: 29 : if (app_flags & G_APPLICATION_REPLACE)
506 : 2 : name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
507 : :
508 : 29 : reply = g_dbus_connection_call_sync (impl->session_bus,
509 : : "org.freedesktop.DBus",
510 : : "/org/freedesktop/DBus",
511 : : "org.freedesktop.DBus",
512 : : "RequestName",
513 : : g_variant_new ("(su)", impl->bus_name, name_owner_flags),
514 : : G_VARIANT_TYPE ("(u)"),
515 : : 0, -1, cancellable, error);
516 : :
517 [ - + ]: 29 : if (reply == NULL)
518 : 0 : return FALSE;
519 : :
520 : 29 : g_variant_get (reply, "(u)", &rval);
521 : 29 : g_variant_unref (reply);
522 : :
523 : : /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
524 : 29 : impl->primary = (rval != 3);
525 : :
526 [ + + + - ]: 29 : if (!impl->primary && impl->name_lost_signal)
527 : : {
528 : 1 : g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
529 : 1 : impl->name_lost_signal = 0;
530 : : }
531 : :
532 : 29 : return TRUE;
533 : : }
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 : 38 : g_application_impl_stop_primary (GApplicationImpl *impl)
545 : : {
546 : 38 : GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
547 : :
548 [ + + ]: 38 : if (impl->registered)
549 : : {
550 : 30 : app_class->dbus_unregister (impl->app,
551 : : impl->session_bus,
552 : 30 : impl->object_path);
553 : 30 : impl->registered = FALSE;
554 : : }
555 : :
556 [ + + ]: 38 : if (impl->object_id)
557 : : {
558 : 30 : g_dbus_connection_unregister_object (impl->session_bus, impl->object_id);
559 : 30 : impl->object_id = 0;
560 : : }
561 : :
562 [ + + ]: 38 : if (impl->fdo_object_id)
563 : : {
564 : 30 : g_dbus_connection_unregister_object (impl->session_bus, impl->fdo_object_id);
565 : 30 : impl->fdo_object_id = 0;
566 : : }
567 : :
568 [ + + ]: 38 : if (impl->actions_id)
569 : : {
570 : 30 : g_dbus_connection_unexport_action_group (impl->session_bus, impl->actions_id);
571 : 30 : impl->actions_id = 0;
572 : : }
573 : :
574 [ + + ]: 38 : if (impl->name_lost_signal)
575 : : {
576 : 2 : g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
577 : 2 : impl->name_lost_signal = 0;
578 : : }
579 : :
580 [ + + + + ]: 38 : if (impl->primary && impl->bus_name)
581 : : {
582 : 27 : g_dbus_connection_call (impl->session_bus, "org.freedesktop.DBus",
583 : : "/org/freedesktop/DBus", "org.freedesktop.DBus",
584 : : "ReleaseName", g_variant_new ("(s)", impl->bus_name),
585 : : NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
586 : 27 : impl->primary = FALSE;
587 : : }
588 : 38 : }
589 : :
590 : : void
591 : 8 : g_application_impl_set_busy_state (GApplicationImpl *impl,
592 : : gboolean busy)
593 : : {
594 [ + - ]: 8 : if (impl->busy != busy)
595 : : {
596 : 8 : impl->busy = busy;
597 : 8 : send_property_change (impl);
598 : : }
599 : 8 : }
600 : :
601 : : void
602 : 36 : g_application_impl_destroy (GApplicationImpl *impl)
603 : : {
604 : 36 : g_application_impl_stop_primary (impl);
605 : :
606 [ + + ]: 36 : if (impl->session_bus)
607 : 30 : g_object_unref (impl->session_bus);
608 : :
609 : 36 : g_free (impl->object_path);
610 : :
611 : 36 : g_slice_free (GApplicationImpl, impl);
612 : 36 : }
613 : :
614 : : GApplicationImpl *
615 : 36 : 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 : : {
623 : : GDBusActionGroup *actions;
624 : : GApplicationImpl *impl;
625 : :
626 : 36 : g_assert ((flags & G_APPLICATION_NON_UNIQUE) || appid != NULL);
627 : :
628 : 36 : impl = g_slice_new0 (GApplicationImpl);
629 : :
630 : 36 : impl->app = application;
631 : 36 : impl->exported_actions = exported_actions;
632 : :
633 : : /* non-unique applications do not attempt to acquire a bus name */
634 [ + + ]: 36 : if (~flags & G_APPLICATION_NON_UNIQUE)
635 : 32 : impl->bus_name = appid;
636 : :
637 : 36 : impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL);
638 : :
639 [ + + ]: 36 : if (impl->session_bus == NULL)
640 : : {
641 : : /* If we can't connect to the session bus, proceed as a normal
642 : : * non-unique application.
643 : : */
644 : 6 : *remote_actions = NULL;
645 : 6 : return impl;
646 : : }
647 : :
648 : 30 : 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 [ + - ]: 30 : if (~flags & G_APPLICATION_IS_LAUNCHER)
654 : : {
655 [ - + ]: 30 : if (!g_application_impl_attempt_primary (impl, cancellable, error))
656 : : {
657 : 0 : g_application_impl_destroy (impl);
658 : 0 : return NULL;
659 : : }
660 : :
661 [ + + ]: 30 : if (impl->primary)
662 : 29 : return impl;
663 : :
664 : : /* We didn't make it. Drop our service-side stuff. */
665 : 1 : g_application_impl_stop_primary (impl);
666 : :
667 [ - + ]: 1 : if (flags & G_APPLICATION_IS_SERVICE)
668 : : {
669 : 0 : g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
670 : : "Unable to acquire bus name '%s'", appid);
671 : 0 : g_application_impl_destroy (impl);
672 : :
673 : 0 : return NULL;
674 : : }
675 : : }
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 : 1 : actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path);
682 [ - + ]: 1 : if (!g_dbus_action_group_sync (actions, cancellable, error))
683 : : {
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 : : }
690 : :
691 : 1 : *remote_actions = G_REMOTE_ACTION_GROUP (actions);
692 : :
693 : 1 : return impl;
694 : : }
695 : :
696 : : void
697 : 1 : g_application_impl_activate (GApplicationImpl *impl,
698 : : GVariant *platform_data)
699 : : {
700 : 1 : g_dbus_connection_call (impl->session_bus,
701 : : impl->bus_name,
702 : 1 : impl->object_path,
703 : : "org.gtk.Application",
704 : : "Activate",
705 : : g_variant_new ("(@a{sv})", platform_data),
706 : : NULL, 0, -1, NULL, NULL, NULL);
707 : 1 : }
708 : :
709 : : void
710 : 0 : g_application_impl_open (GApplicationImpl *impl,
711 : : GFile **files,
712 : : gint n_files,
713 : : const gchar *hint,
714 : : GVariant *platform_data)
715 : : {
716 : : GVariantBuilder builder;
717 : : 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 : : {
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 : : }
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 : : impl->bus_name,
733 : 0 : impl->object_path,
734 : : "org.gtk.Application",
735 : : "Open",
736 : : g_variant_builder_end (&builder),
737 : : NULL, 0, -1, NULL, NULL, NULL);
738 : 0 : }
739 : :
740 : : static void
741 : 0 : 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 : : {
750 : : 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 : : else
759 : : 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 : 0 : g_application_impl_cmdline_done (GObject *source,
772 : : GAsyncResult *result,
773 : : gpointer user_data)
774 : : {
775 : 0 : CommandLineData *data = user_data;
776 : 0 : GError *error = NULL;
777 : : GVariant *reply;
778 : :
779 : : #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 : : {
788 : 0 : g_variant_get (reply, "(i)", &data->status);
789 : 0 : g_variant_unref (reply);
790 : : }
791 : :
792 : : else
793 : : {
794 : 0 : g_printerr ("%s\n", error->message);
795 : 0 : g_error_free (error);
796 : 0 : data->status = 1;
797 : : }
798 : :
799 : 0 : g_main_loop_quit (data->loop);
800 : 0 : }
801 : :
802 : : int
803 : 0 : g_application_impl_command_line (GApplicationImpl *impl,
804 : : const gchar * const *arguments,
805 : : GVariant *platform_data)
806 : : {
807 : : static const GDBusInterfaceVTable vtable = {
808 : : g_application_impl_cmdline_method_call, NULL, NULL, { 0 }
809 : : };
810 : 0 : const gchar *object_path = "/org/gtk/Application/CommandLine";
811 : : GMainContext *context;
812 : : CommandLineData data;
813 : : 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 : : {
821 : 0 : GError *error = NULL;
822 : : 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 : : }
832 : :
833 : 0 : object_id = g_dbus_connection_register_object (impl->session_bus, object_path,
834 : : org_gtk_private_CommandLine,
835 : : &vtable, &data, NULL, NULL);
836 : : /* In theory we should try other paths... */
837 : 0 : g_assert (object_id != 0);
838 : :
839 : : #ifdef G_OS_UNIX
840 : : {
841 : 0 : GError *error = NULL;
842 : : 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 : : "org.gtk.Application", "CommandLine",
853 : : g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data),
854 : : G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, fd_list, NULL,
855 : : g_application_impl_cmdline_done, &data);
856 : 0 : g_object_unref (fd_list);
857 : : }
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 : : }
874 : :
875 : : void
876 : 31 : g_application_impl_flush (GApplicationImpl *impl)
877 : : {
878 [ + + ]: 31 : if (impl->session_bus)
879 : 26 : g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
880 : 31 : }
881 : :
882 : : GDBusConnection *
883 : 8 : g_application_impl_get_dbus_connection (GApplicationImpl *impl)
884 : : {
885 : 8 : return impl->session_bus;
886 : : }
887 : :
888 : : const gchar *
889 : 2 : g_application_impl_get_dbus_object_path (GApplicationImpl *impl)
890 : : {
891 : 2 : return impl->object_path;
892 : : }
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 [ + + + - : 6 : G_DEFINE_TYPE (GDBusCommandLine,
+ + ]
910 : : g_dbus_command_line,
911 : : G_TYPE_APPLICATION_COMMAND_LINE)
912 : :
913 : : static void
914 : 0 : g_dbus_command_line_print_literal (GApplicationCommandLine *cmdline,
915 : : const gchar *message)
916 : : {
917 : 0 : GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
918 : :
919 : 0 : g_dbus_connection_call (gdbcl->connection,
920 : : gdbcl->bus_name,
921 : : gdbcl->object_path,
922 : : "org.gtk.private.CommandLine", "Print",
923 : : g_variant_new ("(s)", message),
924 : : NULL, 0, -1, NULL, NULL, NULL);
925 : 0 : }
926 : :
927 : : static void
928 : 0 : g_dbus_command_line_printerr_literal (GApplicationCommandLine *cmdline,
929 : : const gchar *message)
930 : : {
931 : 0 : GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
932 : :
933 : 0 : g_dbus_connection_call (gdbcl->connection,
934 : : gdbcl->bus_name,
935 : : gdbcl->object_path,
936 : : "org.gtk.private.CommandLine", "PrintError",
937 : : g_variant_new ("(s)", message),
938 : : NULL, 0, -1, NULL, NULL, NULL);
939 : 0 : }
940 : :
941 : : static GInputStream *
942 : 0 : g_dbus_command_line_get_stdin (GApplicationCommandLine *cmdline)
943 : : {
944 : : #ifdef G_OS_UNIX
945 : 0 : GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
946 : 0 : GInputStream *result = NULL;
947 : : GDBusMessage *message;
948 : : 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 : : {
955 : : 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 : : }
963 : :
964 : 0 : return result;
965 : : #else
966 : : return NULL;
967 : : #endif
968 : : }
969 : :
970 : : static void
971 : 4 : g_dbus_command_line_finalize (GObject *object)
972 : : {
973 : 4 : GDBusCommandLine *gdbcl = (GDBusCommandLine *) object;
974 : :
975 : 4 : g_object_unref (gdbcl->invocation);
976 : :
977 : 4 : G_OBJECT_CLASS (g_dbus_command_line_parent_class)
978 : 4 : ->finalize (object);
979 : 4 : }
980 : :
981 : : static void
982 : 4 : g_dbus_command_line_done (GApplicationCommandLine *cmdline)
983 : : {
984 : 4 : GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
985 : : gint status;
986 : :
987 : 4 : status = g_application_command_line_get_exit_status (cmdline);
988 : :
989 : 4 : g_dbus_method_invocation_return_value (gdbcl->invocation,
990 : : g_variant_new ("(i)", status));
991 : :
992 : 4 : G_APPLICATION_COMMAND_LINE_CLASS (g_dbus_command_line_parent_class)->done (cmdline);
993 : 4 : }
994 : :
995 : : static void
996 : 4 : g_dbus_command_line_init (GDBusCommandLine *gdbcl)
997 : : {
998 : 4 : }
999 : :
1000 : : static void
1001 : 1 : g_dbus_command_line_class_init (GApplicationCommandLineClass *class)
1002 : : {
1003 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (class);
1004 : :
1005 : 1 : object_class->finalize = g_dbus_command_line_finalize;
1006 : 1 : class->printerr_literal = g_dbus_command_line_printerr_literal;
1007 : 1 : class->print_literal = g_dbus_command_line_print_literal;
1008 : 1 : class->get_stdin = g_dbus_command_line_get_stdin;
1009 : 1 : class->done = g_dbus_command_line_done;
1010 : 1 : }
1011 : :
1012 : : static GApplicationCommandLine *
1013 : 4 : g_dbus_command_line_new (GDBusMethodInvocation *invocation)
1014 : : {
1015 : : GDBusCommandLine *gdbcl;
1016 : : GVariant *args;
1017 : : GVariant *arguments, *platform_data;
1018 : :
1019 : 4 : args = g_dbus_method_invocation_get_parameters (invocation);
1020 : :
1021 : 4 : arguments = g_variant_get_child_value (args, 1);
1022 : 4 : platform_data = g_variant_get_child_value (args, 2);
1023 : 4 : gdbcl = g_object_new (g_dbus_command_line_get_type (),
1024 : : "arguments", arguments,
1025 : : "platform-data", platform_data,
1026 : : NULL);
1027 : 4 : g_variant_unref (arguments);
1028 : 4 : g_variant_unref (platform_data);
1029 : :
1030 : 4 : gdbcl->connection = g_dbus_method_invocation_get_connection (invocation);
1031 : 4 : gdbcl->bus_name = g_dbus_method_invocation_get_sender (invocation);
1032 : 4 : g_variant_get_child (args, 0, "&o", &gdbcl->object_path);
1033 : 4 : gdbcl->invocation = g_object_ref (invocation);
1034 : :
1035 : 4 : return G_APPLICATION_COMMAND_LINE (gdbcl);
1036 : : }
1037 : :
1038 : : /* Epilogue {{{1 */
1039 : :
1040 : : /* vim:set foldmethod=marker: */
|