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