(root)/
glib-2.79.0/
gio/
gdebugcontrollerdbus.c
       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   * GDebugControllerDBus:
      35   *
      36   * `GDebugControllerDBus` is an implementation of [iface@Gio.DebugController]
      37   * which exposes debug settings as a D-Bus object.
      38   *
      39   * It is a [iface@Gio.Initable] object, and will register an object at
      40   * `/org/gtk/Debugging` on the bus given as
      41   * [property@Gio.DebugControllerDBus:connection] once it’s initialized. The
      42   * object will be unregistered when the last reference to the
      43   * `GDebugControllerDBus` is dropped.
      44   *
      45   * This D-Bus object can be used by remote processes to enable or disable debug
      46   * output in this process. Remote processes calling
      47   * `org.gtk.Debugging.SetDebugEnabled()` will affect the value of
      48   * [property@Gio.DebugController:debug-enabled] and, by default,
      49   * [func@GLib.log_get_debug_enabled].
      50   *
      51   * By default, no processes are allowed to call `SetDebugEnabled()` unless a
      52   * [signal@Gio.DebugControllerDBus::authorize] signal handler is installed. This
      53   * is because the process may be privileged, or might expose sensitive
      54   * information in its debug output. You may want to restrict the ability to
      55   * enable debug output to privileged users or processes.
      56   *
      57   * One option is to install a D-Bus security policy which restricts access to
      58   * `SetDebugEnabled()`, installing something like the following in
      59   * `$datadir/dbus-1/system.d/`:
      60   *
      61   * ```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 [signal@Gio.DebugControllerDBus::authorize] signal and query
      82   * polkit in it:
      83   *
      84   * ```c
      85   *   g_autoptr(GError) child_error = NULL;
      86   *   g_autoptr(GDBusConnection) connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
      87   *   gulong debug_controller_authorize_id = 0;
      88   *
      89   *   // Set up the debug controller.
      90   *   debug_controller = G_DEBUG_CONTROLLER (g_debug_controller_dbus_new (priv->connection, NULL, &child_error));
      91   *   if (debug_controller == NULL)
      92   *     {
      93   *       g_error ("Could not register debug controller on bus: %s"),
      94   *                child_error->message);
      95   *     }
      96   *
      97   *   debug_controller_authorize_id = g_signal_connect (debug_controller,
      98   *                                                     "authorize",
      99   *                                                     G_CALLBACK (debug_controller_authorize_cb),
     100   *                                                     self);
     101   *
     102   *   static gboolean
     103   *   debug_controller_authorize_cb (GDebugControllerDBus  *debug_controller,
     104   *                                  GDBusMethodInvocation *invocation,
     105   *                                  gpointer               user_data)
     106   *   {
     107   *     g_autoptr(PolkitAuthority) authority = NULL;
     108   *     g_autoptr(PolkitSubject) subject = NULL;
     109   *     g_autoptr(PolkitAuthorizationResult) auth_result = NULL;
     110   *     g_autoptr(GError) local_error = NULL;
     111   *     GDBusMessage *message;
     112   *     GDBusMessageFlags message_flags;
     113   *     PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
     114   *
     115   *     message = g_dbus_method_invocation_get_message (invocation);
     116   *     message_flags = g_dbus_message_get_flags (message);
     117   *
     118   *     authority = polkit_authority_get_sync (NULL, &local_error);
     119   *     if (authority == NULL)
     120   *       {
     121   *         g_warning ("Failed to get polkit authority: %s", local_error->message);
     122   *         return FALSE;
     123   *       }
     124   *
     125   *     if (message_flags & G_DBUS_MESSAGE_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION)
     126   *       flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
     127   *
     128   *     subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation));
     129   *
     130   *     auth_result = polkit_authority_check_authorization_sync (authority,
     131   *                                                              subject,
     132   *                                                              "com.example.MyService.set-debug-enabled",
     133   *                                                              NULL,
     134   *                                                              flags,
     135   *                                                              NULL,
     136   *                                                              &local_error);
     137   *     if (auth_result == NULL)
     138   *       {
     139   *         g_warning ("Failed to get check polkit authorization: %s", local_error->message);
     140   *         return FALSE;
     141   *       }
     142   *
     143   *     return polkit_authorization_result_get_is_authorized (auth_result);
     144   *   }
     145   * ```
     146   *
     147   * Since: 2.72
     148   */
     149  
     150  static const gchar org_gtk_Debugging_xml[] =
     151    "<node>"
     152      "<interface name='org.gtk.Debugging'>"
     153        "<property name='DebugEnabled' type='b' access='read'/>"
     154        "<method name='SetDebugEnabled'>"
     155          "<arg type='b' name='debug-enabled' direction='in'/>"
     156        "</method>"
     157      "</interface>"
     158    "</node>";
     159  
     160  static GDBusInterfaceInfo *org_gtk_Debugging;
     161  
     162  #define G_DEBUG_CONTROLLER_DBUS_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
     163  
     164  static void g_debug_controller_dbus_iface_init (GDebugControllerInterface *iface);
     165  static void g_debug_controller_dbus_initable_iface_init (GInitableIface *iface);
     166  static gboolean g_debug_controller_dbus_authorize_default (GDebugControllerDBus  *self,
     167                                                             GDBusMethodInvocation *invocation);
     168  
     169  typedef enum
     170  {
     171    PROP_CONNECTION = 1,
     172    /* Overrides: */
     173    PROP_DEBUG_ENABLED,
     174  } GDebugControllerDBusProperty;
     175  
     176  static GParamSpec *props[PROP_CONNECTION + 1] = { NULL, };
     177  
     178  typedef enum
     179  {
     180    SIGNAL_AUTHORIZE,
     181  } GDebugControllerDBusSignal;
     182  
     183  static guint signals[SIGNAL_AUTHORIZE + 1] = {0};
     184  
     185  typedef struct
     186  {
     187    GObject parent_instance;
     188  
     189    GCancellable *cancellable;  /* (owned) */
     190    GDBusConnection *connection;  /* (owned) */
     191    guint object_id;
     192    GPtrArray *pending_authorize_tasks;  /* (element-type GWeakRef) (owned) (nullable) */
     193  
     194    gboolean debug_enabled;
     195  } GDebugControllerDBusPrivate;
     196  
     197  G_DEFINE_TYPE_WITH_CODE (GDebugControllerDBus, g_debug_controller_dbus, G_TYPE_OBJECT,
     198                           G_ADD_PRIVATE (GDebugControllerDBus)
     199                           G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
     200                                                  g_debug_controller_dbus_initable_iface_init)
     201                           G_IMPLEMENT_INTERFACE (G_TYPE_DEBUG_CONTROLLER,
     202                                                  g_debug_controller_dbus_iface_init)
     203                           _g_io_modules_ensure_extension_points_registered ();
     204                           g_io_extension_point_implement (G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME,
     205                                                           g_define_type_id,
     206                                                           "dbus",
     207                                                           30))
     208  
     209  static void
     210  g_debug_controller_dbus_init (GDebugControllerDBus *self)
     211  {
     212    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     213  
     214    priv->cancellable = g_cancellable_new ();
     215  }
     216  
     217  static void
     218  set_debug_enabled (GDebugControllerDBus *self,
     219                     gboolean              debug_enabled)
     220  {
     221    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     222  
     223    if (g_cancellable_is_cancelled (priv->cancellable))
     224      return;
     225  
     226    if (debug_enabled != priv->debug_enabled)
     227      {
     228        GVariantBuilder builder;
     229  
     230        priv->debug_enabled = debug_enabled;
     231  
     232        /* Change the default log writer’s behaviour in GLib. */
     233        g_log_set_debug_enabled (debug_enabled);
     234  
     235        /* Notify internally and externally of the property change. */
     236        g_object_notify (G_OBJECT (self), "debug-enabled");
     237  
     238        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
     239        g_variant_builder_add (&builder, "{sv}", "DebugEnabled", g_variant_new_boolean (priv->debug_enabled));
     240  
     241        g_dbus_connection_emit_signal (priv->connection,
     242                                       NULL,
     243                                       "/org/gtk/Debugging",
     244                                       "org.freedesktop.DBus.Properties",
     245                                       "PropertiesChanged",
     246                                       g_variant_new ("(sa{sv}as)",
     247                                                      "org.gtk.Debugging",
     248                                                      &builder,
     249                                                      NULL),
     250                                       NULL);
     251  
     252        g_debug ("Debug output %s", debug_enabled ? "enabled" : "disabled");
     253      }
     254  }
     255  
     256  /* Called in the #GMainContext which was default when the #GDebugControllerDBus
     257   * was initialised. */
     258  static GVariant *
     259  dbus_get_property (GDBusConnection  *connection,
     260                     const gchar      *sender,
     261                     const gchar      *object_path,
     262                     const gchar      *interface_name,
     263                     const gchar      *property_name,
     264                     GError          **error,
     265                     gpointer          user_data)
     266  {
     267    GDebugControllerDBus *self = user_data;
     268    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     269  
     270    if (g_str_equal (property_name, "DebugEnabled"))
     271      return g_variant_new_boolean (priv->debug_enabled);
     272  
     273    g_assert_not_reached ();
     274  
     275    return NULL;
     276  }
     277  
     278  static GWeakRef *
     279  weak_ref_new (GObject *obj)
     280  {
     281    GWeakRef *weak_ref = g_new0 (GWeakRef, 1);
     282  
     283    g_weak_ref_init (weak_ref, obj);
     284  
     285    return g_steal_pointer (&weak_ref);
     286  }
     287  
     288  static void
     289  weak_ref_free (GWeakRef *weak_ref)
     290  {
     291    g_weak_ref_clear (weak_ref);
     292    g_free (weak_ref);
     293  }
     294  
     295  /* Called in the #GMainContext which was default when the #GDebugControllerDBus
     296   * was initialised. */
     297  static void
     298  garbage_collect_weak_refs (GDebugControllerDBus *self)
     299  {
     300    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     301    guint i;
     302  
     303    if (priv->pending_authorize_tasks == NULL)
     304      return;
     305  
     306    /* Iterate in reverse order so that if we remove an element the hole won’t be
     307     * filled by an element we haven’t checked yet. */
     308    for (i = priv->pending_authorize_tasks->len; i > 0; i--)
     309      {
     310        GWeakRef *weak_ref = g_ptr_array_index (priv->pending_authorize_tasks, i - 1);
     311        GObject *obj = g_weak_ref_get (weak_ref);
     312  
     313        if (obj == NULL)
     314          g_ptr_array_remove_index_fast (priv->pending_authorize_tasks, i - 1);
     315        else
     316          g_object_unref (obj);
     317      }
     318  
     319    /* Don’t need to keep the array around any more if it’s empty. */
     320    if (priv->pending_authorize_tasks->len == 0)
     321      g_clear_pointer (&priv->pending_authorize_tasks, g_ptr_array_unref);
     322  }
     323  
     324  /* Called in a worker thread. */
     325  static void
     326  authorize_task_cb (GTask        *task,
     327                     gpointer      source_object,
     328                     gpointer      task_data,
     329                     GCancellable *cancellable)
     330  {
     331    GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (source_object);
     332    GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (task_data);
     333    gboolean authorized = TRUE;
     334  
     335    g_signal_emit (self, signals[SIGNAL_AUTHORIZE], 0, invocation, &authorized);
     336  
     337    g_task_return_boolean (task, authorized);
     338  }
     339  
     340  /* Called in the #GMainContext which was default when the #GDebugControllerDBus
     341   * was initialised. */
     342  static void
     343  authorize_cb (GObject      *object,
     344                GAsyncResult *result,
     345                gpointer      user_data)
     346  {
     347    GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     348    GDebugControllerDBusPrivate *priv G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
     349    GTask *task = G_TASK (result);
     350    GDBusMethodInvocation *invocation = g_task_get_task_data (task);
     351    GVariant *parameters = g_dbus_method_invocation_get_parameters (invocation);
     352    gboolean enabled = FALSE;
     353    gboolean authorized;
     354  
     355    priv = g_debug_controller_dbus_get_instance_private (self);
     356    authorized = g_task_propagate_boolean (task, NULL);
     357  
     358    if (!authorized)
     359      {
     360        GError *local_error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
     361                                           _("Not authorized to change debug settings"));
     362        g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
     363      }
     364    else
     365      {
     366        /* Update the property value. */
     367        g_variant_get (parameters, "(b)", &enabled);
     368        set_debug_enabled (self, enabled);
     369  
     370        g_dbus_method_invocation_return_value (invocation, NULL);
     371      }
     372  
     373    /* The GTask will stay alive for a bit longer as the worker thread is
     374     * potentially still in the process of dropping its reference to it. */
     375    g_assert (priv->pending_authorize_tasks != NULL && priv->pending_authorize_tasks->len > 0);
     376  }
     377  
     378  /* Called in the #GMainContext which was default when the #GDebugControllerDBus
     379   * was initialised. */
     380  static void
     381  dbus_method_call (GDBusConnection       *connection,
     382                    const gchar           *sender,
     383                    const gchar           *object_path,
     384                    const gchar           *interface_name,
     385                    const gchar           *method_name,
     386                    GVariant              *parameters,
     387                    GDBusMethodInvocation *invocation,
     388                    gpointer               user_data)
     389  {
     390    GDebugControllerDBus *self = user_data;
     391    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     392    GDebugControllerDBusClass *klass = G_DEBUG_CONTROLLER_DBUS_GET_CLASS (self);
     393  
     394    /* Only on the org.gtk.Debugging interface */
     395    if (g_str_equal (method_name, "SetDebugEnabled"))
     396      {
     397        GTask *task = NULL;
     398  
     399        task = g_task_new (self, priv->cancellable, authorize_cb, NULL);
     400        g_task_set_source_tag (task, dbus_method_call);
     401        g_task_set_task_data (task, g_object_ref (invocation), (GDestroyNotify) g_object_unref);
     402  
     403        /* Track the pending #GTask with a weak ref as its final strong ref could
     404         * be dropped from this thread or an arbitrary #GTask worker thread. The
     405         * weak refs will be evaluated in g_debug_controller_dbus_stop(). */
     406        if (priv->pending_authorize_tasks == NULL)
     407          priv->pending_authorize_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) weak_ref_free);
     408        g_ptr_array_add (priv->pending_authorize_tasks, weak_ref_new (G_OBJECT (task)));
     409  
     410        /* Take the opportunity to clean up a bit. */
     411        garbage_collect_weak_refs (self);
     412  
     413        /* Check the calling peer is authorised to change the debug mode. So that
     414         * the signal handler can block on checking polkit authorisation (which
     415         * definitely involves D-Bus calls, and might involve user interaction),
     416         * emit the #GDebugControllerDBus::authorize signal in a worker thread, so
     417         * that handlers can synchronously block it. This is similar to how
     418         * #GDBusInterfaceSkeleton::g-authorize-method works.
     419         *
     420         * If no signal handlers are connected, don’t bother running the worker
     421         * thread, and just return a default value of %FALSE. Fail closed. */
     422        if (g_signal_has_handler_pending (self, signals[SIGNAL_AUTHORIZE], 0, FALSE) ||
     423            klass->authorize != g_debug_controller_dbus_authorize_default)
     424          g_task_run_in_thread (task, authorize_task_cb);
     425        else
     426          g_task_return_boolean (task, FALSE);
     427  
     428        g_clear_object (&task);
     429      }
     430    else
     431      g_assert_not_reached ();
     432  }
     433  
     434  static gboolean
     435  g_debug_controller_dbus_initable_init (GInitable     *initable,
     436                                         GCancellable  *cancellable,
     437                                         GError       **error)
     438  {
     439    GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (initable);
     440    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     441    static const GDBusInterfaceVTable vtable = {
     442      dbus_method_call,
     443      dbus_get_property,
     444      NULL /* set_property */,
     445      { 0 }
     446    };
     447  
     448    if (org_gtk_Debugging == NULL)
     449      {
     450        GError *local_error = NULL;
     451        GDBusNodeInfo *info;
     452  
     453        info = g_dbus_node_info_new_for_xml (org_gtk_Debugging_xml, &local_error);
     454        if G_UNLIKELY (info == NULL)
     455          g_error ("%s", local_error->message);
     456        org_gtk_Debugging = g_dbus_node_info_lookup_interface (info, "org.gtk.Debugging");
     457        g_assert (org_gtk_Debugging != NULL);
     458        g_dbus_interface_info_ref (org_gtk_Debugging);
     459        g_dbus_node_info_unref (info);
     460      }
     461  
     462    priv->object_id = g_dbus_connection_register_object (priv->connection,
     463                                                         "/org/gtk/Debugging",
     464                                                         org_gtk_Debugging,
     465                                                         &vtable, self, NULL, error);
     466    if (priv->object_id == 0)
     467      return FALSE;
     468  
     469    return TRUE;
     470  }
     471  
     472  static void
     473  g_debug_controller_dbus_get_property (GObject    *object,
     474                                        guint       prop_id,
     475                                        GValue     *value,
     476                                        GParamSpec *pspec)
     477  {
     478    GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     479    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     480  
     481    switch ((GDebugControllerDBusProperty) prop_id)
     482      {
     483      case PROP_CONNECTION:
     484        g_value_set_object (value, priv->connection);
     485        break;
     486      case PROP_DEBUG_ENABLED:
     487        g_value_set_boolean (value, priv->debug_enabled);
     488        break;
     489      default:
     490        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     491        break;
     492      }
     493  }
     494  
     495  static void
     496  g_debug_controller_dbus_set_property (GObject      *object,
     497                                        guint         prop_id,
     498                                        const GValue *value,
     499                                        GParamSpec   *pspec)
     500  {
     501    GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     502    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     503  
     504    switch ((GDebugControllerDBusProperty) prop_id)
     505      {
     506      case PROP_CONNECTION:
     507        /* Construct only */
     508        g_assert (priv->connection == NULL);
     509        priv->connection = g_value_dup_object (value);
     510        break;
     511      case PROP_DEBUG_ENABLED:
     512        set_debug_enabled (self, g_value_get_boolean (value));
     513        break;
     514      default:
     515        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     516        break;
     517      }
     518  }
     519  
     520  static void
     521  g_debug_controller_dbus_dispose (GObject *object)
     522  {
     523    GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     524    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     525  
     526    g_debug_controller_dbus_stop (self);
     527    g_assert (priv->pending_authorize_tasks == NULL);
     528    g_clear_object (&priv->connection);
     529    g_clear_object (&priv->cancellable);
     530  
     531    G_OBJECT_CLASS (g_debug_controller_dbus_parent_class)->dispose (object);
     532  }
     533  
     534  static gboolean
     535  g_debug_controller_dbus_authorize_default (GDebugControllerDBus  *self,
     536                                             GDBusMethodInvocation *invocation)
     537  {
     538    return TRUE;
     539  }
     540  
     541  static void
     542  g_debug_controller_dbus_class_init (GDebugControllerDBusClass *klass)
     543  {
     544    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     545  
     546    gobject_class->get_property = g_debug_controller_dbus_get_property;
     547    gobject_class->set_property = g_debug_controller_dbus_set_property;
     548    gobject_class->dispose = g_debug_controller_dbus_dispose;
     549  
     550    klass->authorize = g_debug_controller_dbus_authorize_default;
     551  
     552    /**
     553     * GDebugControllerDBus:connection:
     554     *
     555     * The D-Bus connection to expose the debugging interface on.
     556     *
     557     * Typically this will be the same connection (to the system or session bus)
     558     * which the rest of the application or service’s D-Bus objects are registered
     559     * on.
     560     *
     561     * Since: 2.72
     562     */
     563    props[PROP_CONNECTION] =
     564        g_param_spec_object ("connection", NULL, NULL,
     565                             G_TYPE_DBUS_CONNECTION,
     566                             G_PARAM_READWRITE |
     567                             G_PARAM_CONSTRUCT_ONLY |
     568                             G_PARAM_STATIC_STRINGS);
     569  
     570    g_object_class_install_properties (gobject_class, G_N_ELEMENTS (props), props);
     571  
     572    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    signals[SIGNAL_AUTHORIZE] =
     602      g_signal_new ("authorize",
     603                    G_TYPE_DEBUG_CONTROLLER_DBUS,
     604                    G_SIGNAL_RUN_LAST,
     605                    G_STRUCT_OFFSET (GDebugControllerDBusClass, authorize),
     606                    _g_signal_accumulator_false_handled,
     607                    NULL,
     608                    _g_cclosure_marshal_BOOLEAN__OBJECT,
     609                    G_TYPE_BOOLEAN,
     610                    1,
     611                    G_TYPE_DBUS_METHOD_INVOCATION);
     612    g_signal_set_va_marshaller (signals[SIGNAL_AUTHORIZE],
     613                                G_TYPE_FROM_CLASS (klass),
     614                                _g_cclosure_marshal_BOOLEAN__OBJECTv);
     615  }
     616  
     617  static void
     618  g_debug_controller_dbus_iface_init (GDebugControllerInterface *iface)
     619  {
     620  }
     621  
     622  static void
     623  g_debug_controller_dbus_initable_iface_init (GInitableIface *iface)
     624  {
     625    iface->init = g_debug_controller_dbus_initable_init;
     626  }
     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  {
     651    g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
     652    g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
     653    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     654  
     655    return g_initable_new (G_TYPE_DEBUG_CONTROLLER_DBUS,
     656                           cancellable,
     657                           error,
     658                           "connection", connection,
     659                           NULL);
     660  }
     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  {
     688    GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     689  
     690    g_cancellable_cancel (priv->cancellable);
     691  
     692    if (priv->object_id != 0)
     693      {
     694        g_dbus_connection_unregister_object (priv->connection, priv->object_id);
     695        priv->object_id = 0;
     696      }
     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    while (priv->pending_authorize_tasks != NULL)
     707      {
     708        garbage_collect_weak_refs (self);
     709        g_thread_yield ();
     710      }
     711  }