LCOV - code coverage report
Current view: top level - glib/gio - gdebugcontrollerdbus.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 166 175 94.9 %
Date: 2024-04-16 05:15:53 Functions: 23 23 100.0 %
Branches: 40 50 80.0 %

           Branch data     Line data    Source code
       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   [ +  +  +  -  :        324 : 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                 :          4 : g_debug_controller_dbus_init (GDebugControllerDBus *self)
     211                 :            : {
     212                 :          4 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     213                 :            : 
     214                 :          4 :   priv->cancellable = g_cancellable_new ();
     215                 :          4 : }
     216                 :            : 
     217                 :            : static void
     218                 :          5 : set_debug_enabled (GDebugControllerDBus *self,
     219                 :            :                    gboolean              debug_enabled)
     220                 :            : {
     221                 :          5 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     222                 :            : 
     223         [ -  + ]:          5 :   if (g_cancellable_is_cancelled (priv->cancellable))
     224                 :          0 :     return;
     225                 :            : 
     226         [ +  + ]:          5 :   if (debug_enabled != priv->debug_enabled)
     227                 :            :     {
     228                 :            :       GVariantBuilder builder;
     229                 :            : 
     230                 :          4 :       priv->debug_enabled = debug_enabled;
     231                 :            : 
     232                 :            :       /* Change the default log writer’s behaviour in GLib. */
     233                 :          4 :       g_log_set_debug_enabled (debug_enabled);
     234                 :            : 
     235                 :            :       /* Notify internally and externally of the property change. */
     236                 :          4 :       g_object_notify (G_OBJECT (self), "debug-enabled");
     237                 :            : 
     238                 :          4 :       g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
     239                 :          4 :       g_variant_builder_add (&builder, "{sv}", "DebugEnabled", g_variant_new_boolean (priv->debug_enabled));
     240                 :            : 
     241                 :          4 :       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         [ +  + ]:          4 :       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                 :          1 : 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                 :          1 :   GDebugControllerDBus *self = user_data;
     268                 :          1 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     269                 :            : 
     270         [ +  - ]:          1 :   if (g_str_equal (property_name, "DebugEnabled"))
     271                 :          1 :     return g_variant_new_boolean (priv->debug_enabled);
     272                 :            : 
     273                 :            :   g_assert_not_reached ();
     274                 :            : 
     275                 :            :   return NULL;
     276                 :            : }
     277                 :            : 
     278                 :            : static GWeakRef *
     279                 :          3 : weak_ref_new (GObject *obj)
     280                 :            : {
     281                 :          3 :   GWeakRef *weak_ref = g_new0 (GWeakRef, 1);
     282                 :            : 
     283                 :          3 :   g_weak_ref_init (weak_ref, obj);
     284                 :            : 
     285                 :          3 :   return g_steal_pointer (&weak_ref);
     286                 :            : }
     287                 :            : 
     288                 :            : static void
     289                 :          3 : weak_ref_free (GWeakRef *weak_ref)
     290                 :            : {
     291                 :          3 :   g_weak_ref_clear (weak_ref);
     292                 :          3 :   g_free (weak_ref);
     293                 :          3 : }
     294                 :            : 
     295                 :            : /* Called in the #GMainContext which was default when the #GDebugControllerDBus
     296                 :            :  * was initialised. */
     297                 :            : static void
     298                 :          4 : garbage_collect_weak_refs (GDebugControllerDBus *self)
     299                 :            : {
     300                 :          4 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     301                 :            :   guint i;
     302                 :            : 
     303         [ -  + ]:          4 :   if (priv->pending_authorize_tasks == NULL)
     304                 :          0 :     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         [ +  + ]:         10 :   for (i = priv->pending_authorize_tasks->len; i > 0; i--)
     309                 :            :     {
     310                 :          6 :       GWeakRef *weak_ref = g_ptr_array_index (priv->pending_authorize_tasks, i - 1);
     311                 :          6 :       GObject *obj = g_weak_ref_get (weak_ref);
     312                 :            : 
     313         [ +  + ]:          6 :       if (obj == NULL)
     314                 :          3 :         g_ptr_array_remove_index_fast (priv->pending_authorize_tasks, i - 1);
     315                 :            :       else
     316                 :          3 :         g_object_unref (obj);
     317                 :            :     }
     318                 :            : 
     319                 :            :   /* Don’t need to keep the array around any more if it’s empty. */
     320         [ +  + ]:          4 :   if (priv->pending_authorize_tasks->len == 0)
     321                 :          1 :     g_clear_pointer (&priv->pending_authorize_tasks, g_ptr_array_unref);
     322                 :            : }
     323                 :            : 
     324                 :            : /* Called in a worker thread. */
     325                 :            : static void
     326                 :          2 : authorize_task_cb (GTask        *task,
     327                 :            :                    gpointer      source_object,
     328                 :            :                    gpointer      task_data,
     329                 :            :                    GCancellable *cancellable)
     330                 :            : {
     331                 :          2 :   GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (source_object);
     332                 :          2 :   GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (task_data);
     333                 :          2 :   gboolean authorized = TRUE;
     334                 :            : 
     335                 :          2 :   g_signal_emit (self, signals[SIGNAL_AUTHORIZE], 0, invocation, &authorized);
     336                 :            : 
     337                 :          2 :   g_task_return_boolean (task, authorized);
     338                 :          2 : }
     339                 :            : 
     340                 :            : /* Called in the #GMainContext which was default when the #GDebugControllerDBus
     341                 :            :  * was initialised. */
     342                 :            : static void
     343                 :          3 : authorize_cb (GObject      *object,
     344                 :            :               GAsyncResult *result,
     345                 :            :               gpointer      user_data)
     346                 :            : {
     347                 :          3 :   GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     348                 :            :   GDebugControllerDBusPrivate *priv G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
     349                 :          3 :   GTask *task = G_TASK (result);
     350                 :          3 :   GDBusMethodInvocation *invocation = g_task_get_task_data (task);
     351                 :          3 :   GVariant *parameters = g_dbus_method_invocation_get_parameters (invocation);
     352                 :          3 :   gboolean enabled = FALSE;
     353                 :            :   gboolean authorized;
     354                 :            : 
     355                 :          3 :   priv = g_debug_controller_dbus_get_instance_private (self);
     356                 :          3 :   authorized = g_task_propagate_boolean (task, NULL);
     357                 :            : 
     358         [ +  + ]:          3 :   if (!authorized)
     359                 :            :     {
     360                 :          2 :       GError *local_error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
     361                 :          2 :                                          _("Not authorized to change debug settings"));
     362                 :          2 :       g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
     363                 :            :     }
     364                 :            :   else
     365                 :            :     {
     366                 :            :       /* Update the property value. */
     367                 :          1 :       g_variant_get (parameters, "(b)", &enabled);
     368                 :          1 :       set_debug_enabled (self, enabled);
     369                 :            : 
     370                 :          1 :       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                 :          3 :   g_assert (priv->pending_authorize_tasks != NULL && priv->pending_authorize_tasks->len > 0);
     376                 :          3 : }
     377                 :            : 
     378                 :            : /* Called in the #GMainContext which was default when the #GDebugControllerDBus
     379                 :            :  * was initialised. */
     380                 :            : static void
     381                 :          3 : 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                 :          3 :   GDebugControllerDBus *self = user_data;
     391                 :          3 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     392                 :          3 :   GDebugControllerDBusClass *klass = G_DEBUG_CONTROLLER_DBUS_GET_CLASS (self);
     393                 :            : 
     394                 :            :   /* Only on the org.gtk.Debugging interface */
     395         [ +  - ]:          3 :   if (g_str_equal (method_name, "SetDebugEnabled"))
     396                 :            :     {
     397                 :          3 :       GTask *task = NULL;
     398                 :            : 
     399                 :          3 :       task = g_task_new (self, priv->cancellable, authorize_cb, NULL);
     400         [ +  - ]:          3 :       g_task_set_source_tag (task, dbus_method_call);
     401                 :          3 :       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         [ +  + ]:          3 :       if (priv->pending_authorize_tasks == NULL)
     407                 :          1 :         priv->pending_authorize_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) weak_ref_free);
     408                 :          3 :       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                 :          3 :       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         [ +  + ]:          3 :       if (g_signal_has_handler_pending (self, signals[SIGNAL_AUTHORIZE], 0, FALSE) ||
     423         [ -  + ]:          1 :           klass->authorize != g_debug_controller_dbus_authorize_default)
     424                 :          2 :         g_task_run_in_thread (task, authorize_task_cb);
     425                 :            :       else
     426                 :          1 :         g_task_return_boolean (task, FALSE);
     427                 :            : 
     428                 :          3 :       g_clear_object (&task);
     429                 :            :     }
     430                 :            :   else
     431                 :            :     g_assert_not_reached ();
     432                 :          3 : }
     433                 :            : 
     434                 :            : static gboolean
     435                 :          4 : g_debug_controller_dbus_initable_init (GInitable     *initable,
     436                 :            :                                        GCancellable  *cancellable,
     437                 :            :                                        GError       **error)
     438                 :            : {
     439                 :          4 :   GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (initable);
     440                 :          4 :   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         [ +  + ]:          4 :   if (org_gtk_Debugging == NULL)
     449                 :            :     {
     450                 :          1 :       GError *local_error = NULL;
     451                 :            :       GDBusNodeInfo *info;
     452                 :            : 
     453                 :          1 :       info = g_dbus_node_info_new_for_xml (org_gtk_Debugging_xml, &local_error);
     454         [ -  + ]:          1 :       if G_UNLIKELY (info == NULL)
     455                 :          0 :         g_error ("%s", local_error->message);
     456                 :          1 :       org_gtk_Debugging = g_dbus_node_info_lookup_interface (info, "org.gtk.Debugging");
     457                 :          1 :       g_assert (org_gtk_Debugging != NULL);
     458                 :          1 :       g_dbus_interface_info_ref (org_gtk_Debugging);
     459                 :          1 :       g_dbus_node_info_unref (info);
     460                 :            :     }
     461                 :            : 
     462                 :          4 :   priv->object_id = g_dbus_connection_register_object (priv->connection,
     463                 :            :                                                        "/org/gtk/Debugging",
     464                 :            :                                                        org_gtk_Debugging,
     465                 :            :                                                        &vtable, self, NULL, error);
     466         [ +  + ]:          4 :   if (priv->object_id == 0)
     467                 :          1 :     return FALSE;
     468                 :            : 
     469                 :          3 :   return TRUE;
     470                 :            : }
     471                 :            : 
     472                 :            : static void
     473                 :         10 : g_debug_controller_dbus_get_property (GObject    *object,
     474                 :            :                                       guint       prop_id,
     475                 :            :                                       GValue     *value,
     476                 :            :                                       GParamSpec *pspec)
     477                 :            : {
     478                 :         10 :   GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     479                 :         10 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     480                 :            : 
     481      [ +  +  - ]:         10 :   switch ((GDebugControllerDBusProperty) prop_id)
     482                 :            :     {
     483                 :          1 :     case PROP_CONNECTION:
     484                 :          1 :       g_value_set_object (value, priv->connection);
     485                 :          1 :       break;
     486                 :          9 :     case PROP_DEBUG_ENABLED:
     487                 :          9 :       g_value_set_boolean (value, priv->debug_enabled);
     488                 :          9 :       break;
     489                 :          0 :     default:
     490                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     491                 :          0 :       break;
     492                 :            :     }
     493                 :         10 : }
     494                 :            : 
     495                 :            : static void
     496                 :          8 : g_debug_controller_dbus_set_property (GObject      *object,
     497                 :            :                                       guint         prop_id,
     498                 :            :                                       const GValue *value,
     499                 :            :                                       GParamSpec   *pspec)
     500                 :            : {
     501                 :          8 :   GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     502                 :          8 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     503                 :            : 
     504      [ +  +  - ]:          8 :   switch ((GDebugControllerDBusProperty) prop_id)
     505                 :            :     {
     506                 :          4 :     case PROP_CONNECTION:
     507                 :            :       /* Construct only */
     508                 :          4 :       g_assert (priv->connection == NULL);
     509                 :          4 :       priv->connection = g_value_dup_object (value);
     510                 :          4 :       break;
     511                 :          4 :     case PROP_DEBUG_ENABLED:
     512                 :          4 :       set_debug_enabled (self, g_value_get_boolean (value));
     513                 :          4 :       break;
     514                 :          0 :     default:
     515                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     516                 :          0 :       break;
     517                 :            :     }
     518                 :          8 : }
     519                 :            : 
     520                 :            : static void
     521                 :          4 : g_debug_controller_dbus_dispose (GObject *object)
     522                 :            : {
     523                 :          4 :   GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
     524                 :          4 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     525                 :            : 
     526                 :          4 :   g_debug_controller_dbus_stop (self);
     527                 :          4 :   g_assert (priv->pending_authorize_tasks == NULL);
     528                 :          4 :   g_clear_object (&priv->connection);
     529                 :          4 :   g_clear_object (&priv->cancellable);
     530                 :            : 
     531                 :          4 :   G_OBJECT_CLASS (g_debug_controller_dbus_parent_class)->dispose (object);
     532                 :          4 : }
     533                 :            : 
     534                 :            : static gboolean
     535                 :          1 : g_debug_controller_dbus_authorize_default (GDebugControllerDBus  *self,
     536                 :            :                                            GDBusMethodInvocation *invocation)
     537                 :            : {
     538                 :          1 :   return TRUE;
     539                 :            : }
     540                 :            : 
     541                 :            : static void
     542                 :          2 : g_debug_controller_dbus_class_init (GDebugControllerDBusClass *klass)
     543                 :            : {
     544                 :          2 :   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     545                 :            : 
     546                 :          2 :   gobject_class->get_property = g_debug_controller_dbus_get_property;
     547                 :          2 :   gobject_class->set_property = g_debug_controller_dbus_set_property;
     548                 :          2 :   gobject_class->dispose = g_debug_controller_dbus_dispose;
     549                 :            : 
     550                 :          2 :   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                 :          2 :   props[PROP_CONNECTION] =
     564                 :          2 :       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                 :          2 :   g_object_class_install_properties (gobject_class, G_N_ELEMENTS (props), props);
     571                 :            : 
     572                 :          2 :   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                 :          2 :   signals[SIGNAL_AUTHORIZE] =
     602                 :          2 :     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                 :          2 :   g_signal_set_va_marshaller (signals[SIGNAL_AUTHORIZE],
     613                 :            :                               G_TYPE_FROM_CLASS (klass),
     614                 :            :                               _g_cclosure_marshal_BOOLEAN__OBJECTv);
     615                 :          2 : }
     616                 :            : 
     617                 :            : static void
     618                 :          2 : g_debug_controller_dbus_iface_init (GDebugControllerInterface *iface)
     619                 :            : {
     620                 :          2 : }
     621                 :            : 
     622                 :            : static void
     623                 :          2 : g_debug_controller_dbus_initable_iface_init (GInitableIface *iface)
     624                 :            : {
     625                 :          2 :   iface->init = g_debug_controller_dbus_initable_init;
     626                 :          2 : }
     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                 :          4 : g_debug_controller_dbus_new (GDBusConnection  *connection,
     648                 :            :                              GCancellable     *cancellable,
     649                 :            :                              GError          **error)
     650                 :            : {
     651                 :          4 :   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
     652                 :          4 :   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
     653                 :          4 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     654                 :            : 
     655                 :          4 :   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                 :          7 : g_debug_controller_dbus_stop (GDebugControllerDBus *self)
     687                 :            : {
     688                 :          7 :   GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
     689                 :            : 
     690                 :          7 :   g_cancellable_cancel (priv->cancellable);
     691                 :            : 
     692         [ +  + ]:          7 :   if (priv->object_id != 0)
     693                 :            :     {
     694                 :          3 :       g_dbus_connection_unregister_object (priv->connection, priv->object_id);
     695                 :          3 :       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         [ +  + ]:          8 :   while (priv->pending_authorize_tasks != NULL)
     707                 :            :     {
     708                 :          1 :       garbage_collect_weak_refs (self);
     709                 :          1 :       g_thread_yield ();
     710                 :            :     }
     711                 :          7 : }

Generated by: LCOV version 1.14