Coverage Report

Created: 2026-02-14 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/irssi/subprojects/glib-2.74.7/gio/gdebugcontrollerdbus.c
Line
Count
Source
1
/* GIO - GLib Input, Output and Streaming Library
2
 *
3
 * Copyright © 2021 Endless OS Foundation, LLC
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General
18
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19
 *
20
 * SPDX-License-Identifier: LGPL-2.1-or-later
21
 */
22
23
#include "config.h"
24
25
#include <gio/gio.h>
26
#include "gdebugcontroller.h"
27
#include "gdebugcontrollerdbus.h"
28
#include "giomodule-priv.h"
29
#include "gi18n.h"
30
#include "gio/gdbusprivate.h"
31
#include "gio/gmarshal-internal.h"
32
33
/**
34
 * SECTION:gdebugcontrollerdbus
35
 * @title: GDebugControllerDBus
36
 * @short_description: Debugging controller D-Bus implementation
37
 * @include: gio/gio.h
38
 *
39
 * #GDebugControllerDBus is an implementation of #GDebugController which exposes
40
 * debug settings as a D-Bus object.
41
 *
42
 * It is a #GInitable object, and will register an object at
43
 * `/org/gtk/Debugging` on the bus given as
44
 * #GDebugControllerDBus:connection once it’s initialized. The object will be
45
 * unregistered when the last reference to the #GDebugControllerDBus is dropped.
46
 *
47
 * This D-Bus object can be used by remote processes to enable or disable debug
48
 * output in this process. Remote processes calling
49
 * `org.gtk.Debugging.SetDebugEnabled()` will affect the value of
50
 * #GDebugController:debug-enabled and, by default, g_log_get_debug_enabled().
51
 * default.
52
 *
53
 * By default, all processes will be able to call `SetDebugEnabled()`. If this
54
 * process is privileged, or might expose sensitive information in its debug
55
 * output, you may want to restrict the ability to enable debug output to
56
 * privileged users or processes.
57
 *
58
 * One option is to install a D-Bus security policy which restricts access to
59
 * `SetDebugEnabled()`, installing something like the following in
60
 * `$datadir/dbus-1/system.d/`:
61
 * |[<!-- language="XML" -->
62
 * <?xml version="1.0"?> <!--*-nxml-*-->
63
 * <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
64
 *      "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
65
 * <busconfig>
66
 *   <policy user="root">
67
 *     <allow send_destination="com.example.MyService" send_interface="org.gtk.Debugging"/>
68
 *   </policy>
69
 *   <policy context="default">
70
 *     <deny send_destination="com.example.MyService" send_interface="org.gtk.Debugging"/>
71
 *   </policy>
72
 * </busconfig>
73
 * ]|
74
 *
75
 * This will prevent the `SetDebugEnabled()` method from being called by all
76
 * except root. It will not prevent the `DebugEnabled` property from being read,
77
 * as it’s accessed through the `org.freedesktop.DBus.Properties` interface.
78
 *
79
 * Another option is to use polkit to allow or deny requests on a case-by-case
80
 * basis, allowing for the possibility of dynamic authorisation. To do this,
81
 * connect to the #GDebugControllerDBus::authorize signal and query polkit in
82
 * it:
83
 * |[<!-- language="C" -->
84
 *   g_autoptr(GError) child_error = NULL;
85
 *   g_autoptr(GDBusConnection) connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
86
 *   gulong debug_controller_authorize_id = 0;
87
 *
88
 *   // Set up the debug controller.
89
 *   debug_controller = G_DEBUG_CONTROLLER (g_debug_controller_dbus_new (priv->connection, NULL, &child_error));
90
 *   if (debug_controller == NULL)
91
 *     {
92
 *       g_error ("Could not register debug controller on bus: %s"),
93
 *                child_error->message);
94
 *     }
95
 *
96
 *   debug_controller_authorize_id = g_signal_connect (debug_controller,
97
 *                                                     "authorize",
98
 *                                                     G_CALLBACK (debug_controller_authorize_cb),
99
 *                                                     self);
100
 *
101
 *   static gboolean
102
 *   debug_controller_authorize_cb (GDebugControllerDBus  *debug_controller,
103
 *                                  GDBusMethodInvocation *invocation,
104
 *                                  gpointer               user_data)
105
 *   {
106
 *     g_autoptr(PolkitAuthority) authority = NULL;
107
 *     g_autoptr(PolkitSubject) subject = NULL;
108
 *     g_autoptr(PolkitAuthorizationResult) auth_result = NULL;
109
 *     g_autoptr(GError) local_error = NULL;
110
 *     GDBusMessage *message;
111
 *     GDBusMessageFlags message_flags;
112
 *     PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
113
 *
114
 *     message = g_dbus_method_invocation_get_message (invocation);
115
 *     message_flags = g_dbus_message_get_flags (message);
116
 *
117
 *     authority = polkit_authority_get_sync (NULL, &local_error);
118
 *     if (authority == NULL)
119
 *       {
120
 *         g_warning ("Failed to get polkit authority: %s", local_error->message);
121
 *         return FALSE;
122
 *       }
123
 *
124
 *     if (message_flags & G_DBUS_MESSAGE_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION)
125
 *       flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
126
 *
127
 *     subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation));
128
 *
129
 *     auth_result = polkit_authority_check_authorization_sync (authority,
130
 *                                                              subject,
131
 *                                                              "com.example.MyService.set-debug-enabled",
132
 *                                                              NULL,
133
 *                                                              flags,
134
 *                                                              NULL,
135
 *                                                              &local_error);
136
 *     if (auth_result == NULL)
137
 *       {
138
 *         g_warning ("Failed to get check polkit authorization: %s", local_error->message);
139
 *         return FALSE;
140
 *       }
141
 *
142
 *     return polkit_authorization_result_get_is_authorized (auth_result);
143
 *   }
144
 * ]|
145
 *
146
 * Since: 2.72
147
 */
148
149
static const gchar org_gtk_Debugging_xml[] =
150
  "<node>"
151
    "<interface name='org.gtk.Debugging'>"
152
      "<property name='DebugEnabled' type='b' access='read'/>"
153
      "<method name='SetDebugEnabled'>"
154
        "<arg type='b' name='debug-enabled' direction='in'/>"
155
      "</method>"
156
    "</interface>"
157
  "</node>";
158
159
static GDBusInterfaceInfo *org_gtk_Debugging;
160
161
#define G_DEBUG_CONTROLLER_DBUS_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
162
163
static void g_debug_controller_dbus_iface_init (GDebugControllerInterface *iface);
164
static void g_debug_controller_dbus_initable_iface_init (GInitableIface *iface);
165
static gboolean g_debug_controller_dbus_authorize_default (GDebugControllerDBus  *self,
166
                                                           GDBusMethodInvocation *invocation);
167
168
typedef enum
169
{
170
  PROP_CONNECTION = 1,
171
  /* Overrides: */
172
  PROP_DEBUG_ENABLED,
173
} GDebugControllerDBusProperty;
174
175
static GParamSpec *props[PROP_CONNECTION + 1] = { NULL, };
176
177
typedef enum
178
{
179
  SIGNAL_AUTHORIZE,
180
} GDebugControllerDBusSignal;
181
182
static guint signals[SIGNAL_AUTHORIZE + 1] = {0};
183
184
typedef struct
185
{
186
  GObject parent_instance;
187
188
  GCancellable *cancellable;  /* (owned) */
189
  GDBusConnection *connection;  /* (owned) */
190
  guint object_id;
191
  GPtrArray *pending_authorize_tasks;  /* (element-type GWeakRef) (owned) (nullable) */
192
193
  gboolean debug_enabled;
194
} GDebugControllerDBusPrivate;
195
196
0
G_DEFINE_TYPE_WITH_CODE (GDebugControllerDBus, g_debug_controller_dbus, G_TYPE_OBJECT,
197
0
                         G_ADD_PRIVATE (GDebugControllerDBus)
198
0
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
199
0
                                                g_debug_controller_dbus_initable_iface_init)
200
0
                         G_IMPLEMENT_INTERFACE (G_TYPE_DEBUG_CONTROLLER,
201
0
                                                g_debug_controller_dbus_iface_init)
202
0
                         _g_io_modules_ensure_extension_points_registered ();
203
0
                         g_io_extension_point_implement (G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME,
204
0
                                                         g_define_type_id,
205
0
                                                         "dbus",
206
0
                                                         30))
207
0
208
0
static void
209
0
g_debug_controller_dbus_init (GDebugControllerDBus *self)
210
0
{
211
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
212
213
0
  priv->cancellable = g_cancellable_new ();
214
0
}
215
216
static void
217
set_debug_enabled (GDebugControllerDBus *self,
218
                   gboolean              debug_enabled)
219
0
{
220
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
221
222
0
  if (g_cancellable_is_cancelled (priv->cancellable))
223
0
    return;
224
225
0
  if (debug_enabled != priv->debug_enabled)
226
0
    {
227
0
      GVariantBuilder builder;
228
229
0
      priv->debug_enabled = debug_enabled;
230
231
      /* Change the default log writer’s behaviour in GLib. */
232
0
      g_log_set_debug_enabled (debug_enabled);
233
234
      /* Notify internally and externally of the property change. */
235
0
      g_object_notify (G_OBJECT (self), "debug-enabled");
236
237
0
      g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
238
0
      g_variant_builder_add (&builder, "{sv}", "DebugEnabled", g_variant_new_boolean (priv->debug_enabled));
239
240
0
      g_dbus_connection_emit_signal (priv->connection,
241
0
                                     NULL,
242
0
                                     "/org/gtk/Debugging",
243
0
                                     "org.freedesktop.DBus.Properties",
244
0
                                     "PropertiesChanged",
245
0
                                     g_variant_new ("(sa{sv}as)",
246
0
                                                    "org.gtk.Debugging",
247
0
                                                    &builder,
248
0
                                                    NULL),
249
0
                                     NULL);
250
251
0
      g_debug ("Debug output %s", debug_enabled ? "enabled" : "disabled");
252
0
    }
253
0
}
254
255
/* Called in the #GMainContext which was default when the #GDebugControllerDBus
256
 * was initialised. */
257
static GVariant *
258
dbus_get_property (GDBusConnection  *connection,
259
                   const gchar      *sender,
260
                   const gchar      *object_path,
261
                   const gchar      *interface_name,
262
                   const gchar      *property_name,
263
                   GError          **error,
264
                   gpointer          user_data)
265
0
{
266
0
  GDebugControllerDBus *self = user_data;
267
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
268
269
0
  if (g_str_equal (property_name, "DebugEnabled"))
270
0
    return g_variant_new_boolean (priv->debug_enabled);
271
272
0
  g_assert_not_reached ();
273
274
0
  return NULL;
275
0
}
276
277
static GWeakRef *
278
weak_ref_new (GObject *obj)
279
0
{
280
0
  GWeakRef *weak_ref = g_new0 (GWeakRef, 1);
281
282
0
  g_weak_ref_init (weak_ref, obj);
283
284
0
  return g_steal_pointer (&weak_ref);
285
0
}
286
287
static void
288
weak_ref_free (GWeakRef *weak_ref)
289
0
{
290
0
  g_weak_ref_clear (weak_ref);
291
0
  g_free (weak_ref);
292
0
}
293
294
/* Called in the #GMainContext which was default when the #GDebugControllerDBus
295
 * was initialised. */
296
static void
297
garbage_collect_weak_refs (GDebugControllerDBus *self)
298
0
{
299
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
300
0
  guint i;
301
302
0
  if (priv->pending_authorize_tasks == NULL)
303
0
    return;
304
305
  /* Iterate in reverse order so that if we remove an element the hole won’t be
306
   * filled by an element we haven’t checked yet. */
307
0
  for (i = priv->pending_authorize_tasks->len; i > 0; i--)
308
0
    {
309
0
      GWeakRef *weak_ref = g_ptr_array_index (priv->pending_authorize_tasks, i - 1);
310
0
      GObject *obj = g_weak_ref_get (weak_ref);
311
312
0
      if (obj == NULL)
313
0
        g_ptr_array_remove_index_fast (priv->pending_authorize_tasks, i - 1);
314
0
      else
315
0
        g_object_unref (obj);
316
0
    }
317
318
  /* Don’t need to keep the array around any more if it’s empty. */
319
0
  if (priv->pending_authorize_tasks->len == 0)
320
0
    g_clear_pointer (&priv->pending_authorize_tasks, g_ptr_array_unref);
321
0
}
322
323
/* Called in a worker thread. */
324
static void
325
authorize_task_cb (GTask        *task,
326
                   gpointer      source_object,
327
                   gpointer      task_data,
328
                   GCancellable *cancellable)
329
0
{
330
0
  GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (source_object);
331
0
  GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (task_data);
332
0
  gboolean authorized = TRUE;
333
334
0
  g_signal_emit (self, signals[SIGNAL_AUTHORIZE], 0, invocation, &authorized);
335
336
0
  g_task_return_boolean (task, authorized);
337
0
}
338
339
/* Called in the #GMainContext which was default when the #GDebugControllerDBus
340
 * was initialised. */
341
static void
342
authorize_cb (GObject      *object,
343
              GAsyncResult *result,
344
              gpointer      user_data)
345
0
{
346
0
  GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
347
0
  GDebugControllerDBusPrivate *priv G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
348
0
  GTask *task = G_TASK (result);
349
0
  GDBusMethodInvocation *invocation = g_task_get_task_data (task);
350
0
  GVariant *parameters = g_dbus_method_invocation_get_parameters (invocation);
351
0
  gboolean enabled = FALSE;
352
0
  gboolean authorized;
353
354
0
  priv = g_debug_controller_dbus_get_instance_private (self);
355
0
  authorized = g_task_propagate_boolean (task, NULL);
356
357
0
  if (!authorized)
358
0
    {
359
0
      GError *local_error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
360
0
                                         _("Not authorized to change debug settings"));
361
0
      g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
362
0
    }
363
0
  else
364
0
    {
365
      /* Update the property value. */
366
0
      g_variant_get (parameters, "(b)", &enabled);
367
0
      set_debug_enabled (self, enabled);
368
369
0
      g_dbus_method_invocation_return_value (invocation, NULL);
370
0
    }
371
372
  /* The GTask will stay alive for a bit longer as the worker thread is
373
   * potentially still in the process of dropping its reference to it. */
374
0
  g_assert (priv->pending_authorize_tasks != NULL && priv->pending_authorize_tasks->len > 0);
375
0
}
376
377
/* Called in the #GMainContext which was default when the #GDebugControllerDBus
378
 * was initialised. */
379
static void
380
dbus_method_call (GDBusConnection       *connection,
381
                  const gchar           *sender,
382
                  const gchar           *object_path,
383
                  const gchar           *interface_name,
384
                  const gchar           *method_name,
385
                  GVariant              *parameters,
386
                  GDBusMethodInvocation *invocation,
387
                  gpointer               user_data)
388
0
{
389
0
  GDebugControllerDBus *self = user_data;
390
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
391
0
  GDebugControllerDBusClass *klass = G_DEBUG_CONTROLLER_DBUS_GET_CLASS (self);
392
393
  /* Only on the org.gtk.Debugging interface */
394
0
  if (g_str_equal (method_name, "SetDebugEnabled"))
395
0
    {
396
0
      GTask *task = NULL;
397
398
0
      task = g_task_new (self, priv->cancellable, authorize_cb, NULL);
399
0
      g_task_set_source_tag (task, dbus_method_call);
400
0
      g_task_set_task_data (task, g_object_ref (invocation), (GDestroyNotify) g_object_unref);
401
402
      /* Track the pending #GTask with a weak ref as its final strong ref could
403
       * be dropped from this thread or an arbitrary #GTask worker thread. The
404
       * weak refs will be evaluated in g_debug_controller_dbus_stop(). */
405
0
      if (priv->pending_authorize_tasks == NULL)
406
0
        priv->pending_authorize_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) weak_ref_free);
407
0
      g_ptr_array_add (priv->pending_authorize_tasks, weak_ref_new (G_OBJECT (task)));
408
409
      /* Take the opportunity to clean up a bit. */
410
0
      garbage_collect_weak_refs (self);
411
412
      /* Check the calling peer is authorised to change the debug mode. So that
413
       * the signal handler can block on checking polkit authorisation (which
414
       * definitely involves D-Bus calls, and might involve user interaction),
415
       * emit the #GDebugControllerDBus::authorize signal in a worker thread, so
416
       * that handlers can synchronously block it. This is similar to how
417
       * #GDBusInterfaceSkeleton::g-authorize-method works.
418
       *
419
       * If no signal handlers are connected, don’t bother running the worker
420
       * thread, and just return a default value of %FALSE. Fail closed. */
421
0
      if (g_signal_has_handler_pending (self, signals[SIGNAL_AUTHORIZE], 0, FALSE) ||
422
0
          klass->authorize != g_debug_controller_dbus_authorize_default)
423
0
        g_task_run_in_thread (task, authorize_task_cb);
424
0
      else
425
0
        g_task_return_boolean (task, FALSE);
426
427
0
      g_clear_object (&task);
428
0
    }
429
0
  else
430
0
    g_assert_not_reached ();
431
0
}
432
433
static gboolean
434
g_debug_controller_dbus_initable_init (GInitable     *initable,
435
                                       GCancellable  *cancellable,
436
                                       GError       **error)
437
0
{
438
0
  GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (initable);
439
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
440
0
  static const GDBusInterfaceVTable vtable = {
441
0
    dbus_method_call,
442
0
    dbus_get_property,
443
0
    NULL /* set_property */,
444
0
    { 0 }
445
0
  };
446
447
0
  if (org_gtk_Debugging == NULL)
448
0
    {
449
0
      GError *local_error = NULL;
450
0
      GDBusNodeInfo *info;
451
452
0
      info = g_dbus_node_info_new_for_xml (org_gtk_Debugging_xml, &local_error);
453
0
      if G_UNLIKELY (info == NULL)
454
0
        g_error ("%s", local_error->message);
455
0
      org_gtk_Debugging = g_dbus_node_info_lookup_interface (info, "org.gtk.Debugging");
456
0
      g_assert (org_gtk_Debugging != NULL);
457
0
      g_dbus_interface_info_ref (org_gtk_Debugging);
458
0
      g_dbus_node_info_unref (info);
459
0
    }
460
461
0
  priv->object_id = g_dbus_connection_register_object (priv->connection,
462
0
                                                       "/org/gtk/Debugging",
463
0
                                                       org_gtk_Debugging,
464
0
                                                       &vtable, self, NULL, error);
465
0
  if (priv->object_id == 0)
466
0
    return FALSE;
467
468
0
  return TRUE;
469
0
}
470
471
static void
472
g_debug_controller_dbus_get_property (GObject    *object,
473
                                      guint       prop_id,
474
                                      GValue     *value,
475
                                      GParamSpec *pspec)
476
0
{
477
0
  GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
478
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
479
480
0
  switch ((GDebugControllerDBusProperty) prop_id)
481
0
    {
482
0
    case PROP_CONNECTION:
483
0
      g_value_set_object (value, priv->connection);
484
0
      break;
485
0
    case PROP_DEBUG_ENABLED:
486
0
      g_value_set_boolean (value, priv->debug_enabled);
487
0
      break;
488
0
    default:
489
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
490
0
      break;
491
0
    }
492
0
}
493
494
static void
495
g_debug_controller_dbus_set_property (GObject      *object,
496
                                      guint         prop_id,
497
                                      const GValue *value,
498
                                      GParamSpec   *pspec)
499
0
{
500
0
  GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
501
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
502
503
0
  switch ((GDebugControllerDBusProperty) prop_id)
504
0
    {
505
0
    case PROP_CONNECTION:
506
      /* Construct only */
507
0
      g_assert (priv->connection == NULL);
508
0
      priv->connection = g_value_dup_object (value);
509
0
      break;
510
0
    case PROP_DEBUG_ENABLED:
511
0
      set_debug_enabled (self, g_value_get_boolean (value));
512
0
      break;
513
0
    default:
514
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
515
0
      break;
516
0
    }
517
0
}
518
519
static void
520
g_debug_controller_dbus_dispose (GObject *object)
521
0
{
522
0
  GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
523
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
524
525
0
  g_debug_controller_dbus_stop (self);
526
0
  g_assert (priv->pending_authorize_tasks == NULL);
527
0
  g_clear_object (&priv->connection);
528
0
  g_clear_object (&priv->cancellable);
529
530
0
  G_OBJECT_CLASS (g_debug_controller_dbus_parent_class)->dispose (object);
531
0
}
532
533
static gboolean
534
g_debug_controller_dbus_authorize_default (GDebugControllerDBus  *self,
535
                                           GDBusMethodInvocation *invocation)
536
0
{
537
0
  return TRUE;
538
0
}
539
540
static void
541
g_debug_controller_dbus_class_init (GDebugControllerDBusClass *klass)
542
0
{
543
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
544
545
0
  gobject_class->get_property = g_debug_controller_dbus_get_property;
546
0
  gobject_class->set_property = g_debug_controller_dbus_set_property;
547
0
  gobject_class->dispose = g_debug_controller_dbus_dispose;
548
549
0
  klass->authorize = g_debug_controller_dbus_authorize_default;
550
551
  /**
552
   * GDebugControllerDBus:connection:
553
   *
554
   * The D-Bus connection to expose the debugging interface on.
555
   *
556
   * Typically this will be the same connection (to the system or session bus)
557
   * which the rest of the application or service’s D-Bus objects are registered
558
   * on.
559
   *
560
   * Since: 2.72
561
   */
562
0
  props[PROP_CONNECTION] =
563
0
      g_param_spec_object ("connection", "D-Bus Connection",
564
0
                           "The D-Bus connection to expose the debugging interface on.",
565
0
                           G_TYPE_DBUS_CONNECTION,
566
0
                           G_PARAM_READWRITE |
567
0
                           G_PARAM_CONSTRUCT_ONLY |
568
0
                           G_PARAM_STATIC_STRINGS);
569
570
0
  g_object_class_install_properties (gobject_class, G_N_ELEMENTS (props), props);
571
572
0
  g_object_class_override_property (gobject_class, PROP_DEBUG_ENABLED, "debug-enabled");
573
574
  /**
575
   * GDebugControllerDBus::authorize:
576
   * @controller: The #GDebugControllerDBus emitting the signal.
577
   * @invocation: A #GDBusMethodInvocation.
578
   *
579
   * Emitted when a D-Bus peer is trying to change the debug settings and used
580
   * to determine if that is authorized.
581
   *
582
   * This signal is emitted in a dedicated worker thread, so handlers are
583
   * allowed to perform blocking I/O. This means that, for example, it is
584
   * appropriate to call `polkit_authority_check_authorization_sync()` to check
585
   * authorization using polkit.
586
   *
587
   * If %FALSE is returned then no further handlers are run and the request to
588
   * change the debug settings is rejected.
589
   *
590
   * Otherwise, if %TRUE is returned, signal emission continues. If no handlers
591
   * return %FALSE, then the debug settings are allowed to be changed.
592
   *
593
   * Signal handlers must not modify @invocation, or cause it to return a value.
594
   *
595
   * The default class handler just returns %TRUE.
596
   *
597
   * Returns: %TRUE if the call is authorized, %FALSE otherwise.
598
   *
599
   * Since: 2.72
600
   */
601
0
  signals[SIGNAL_AUTHORIZE] =
602
0
    g_signal_new ("authorize",
603
0
                  G_TYPE_DEBUG_CONTROLLER_DBUS,
604
0
                  G_SIGNAL_RUN_LAST,
605
0
                  G_STRUCT_OFFSET (GDebugControllerDBusClass, authorize),
606
0
                  _g_signal_accumulator_false_handled,
607
0
                  NULL,
608
0
                  _g_cclosure_marshal_BOOLEAN__OBJECT,
609
0
                  G_TYPE_BOOLEAN,
610
0
                  1,
611
0
                  G_TYPE_DBUS_METHOD_INVOCATION);
612
0
  g_signal_set_va_marshaller (signals[SIGNAL_AUTHORIZE],
613
0
                              G_TYPE_FROM_CLASS (klass),
614
0
                              _g_cclosure_marshal_BOOLEAN__OBJECTv);
615
0
}
616
617
static void
618
g_debug_controller_dbus_iface_init (GDebugControllerInterface *iface)
619
0
{
620
0
}
621
622
static void
623
g_debug_controller_dbus_initable_iface_init (GInitableIface *iface)
624
0
{
625
0
  iface->init = g_debug_controller_dbus_initable_init;
626
0
}
627
628
/**
629
 * g_debug_controller_dbus_new:
630
 * @connection: a #GDBusConnection to register the debug object on
631
 * @cancellable: (nullable): a #GCancellable, or %NULL
632
 * @error: return location for a #GError, or %NULL
633
 *
634
 * Create a new #GDebugControllerDBus and synchronously initialize it.
635
 *
636
 * Initializing the object will export the debug object on @connection. The
637
 * object will remain registered until the last reference to the
638
 * #GDebugControllerDBus is dropped.
639
 *
640
 * Initialization may fail if registering the object on @connection fails.
641
 *
642
 * Returns: (nullable) (transfer full): a new #GDebugControllerDBus, or %NULL
643
 *   on failure
644
 * Since: 2.72
645
 */
646
GDebugControllerDBus *
647
g_debug_controller_dbus_new (GDBusConnection  *connection,
648
                             GCancellable     *cancellable,
649
                             GError          **error)
650
0
{
651
0
  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
652
0
  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
653
0
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
654
655
0
  return g_initable_new (G_TYPE_DEBUG_CONTROLLER_DBUS,
656
0
                         cancellable,
657
0
                         error,
658
0
                         "connection", connection,
659
0
                         NULL);
660
0
}
661
662
/**
663
 * g_debug_controller_dbus_stop:
664
 * @self: a #GDebugControllerDBus
665
 *
666
 * Stop the debug controller, unregistering its object from the bus.
667
 *
668
 * Any pending method calls to the object will complete successfully, but new
669
 * ones will return an error. This method will block until all pending
670
 * #GDebugControllerDBus::authorize signals have been handled. This is expected
671
 * to not take long, as it will just be waiting for threads to join. If any
672
 * #GDebugControllerDBus::authorize signal handlers are still executing in other
673
 * threads, this will block until after they have returned.
674
 *
675
 * This method will be called automatically when the final reference to the
676
 * #GDebugControllerDBus is dropped. You may want to call it explicitly to know
677
 * when the controller has been fully removed from the bus, or to break
678
 * reference count cycles.
679
 *
680
 * Calling this method from within a #GDebugControllerDBus::authorize signal
681
 * handler will cause a deadlock and must not be done.
682
 *
683
 * Since: 2.72
684
 */
685
void
686
g_debug_controller_dbus_stop (GDebugControllerDBus *self)
687
0
{
688
0
  GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
689
690
0
  g_cancellable_cancel (priv->cancellable);
691
692
0
  if (priv->object_id != 0)
693
0
    {
694
0
      g_dbus_connection_unregister_object (priv->connection, priv->object_id);
695
0
      priv->object_id = 0;
696
0
    }
697
698
  /* Wait for any pending authorize tasks to finish. These will just be waiting
699
   * for threads to join at this point, as the D-Bus object has been
700
   * unregistered and the cancellable cancelled.
701
   *
702
   * The loop will never terminate if g_debug_controller_dbus_stop() is
703
   * called from within an ::authorize callback.
704
   *
705
   * See discussion in https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2486 */
706
0
  while (priv->pending_authorize_tasks != NULL)
707
0
    {
708
0
      garbage_collect_weak_refs (self);
709
0
      g_thread_yield ();
710
0
    }
711
0
}