[dconf] service: add "blame mode"



commit cee3985ad474e849f699ad1ecce44d581f79271a
Author: Ryan Lortie <desrt desrt ca>
Date:   Wed Mar 7 12:22:23 2012 -0500

    service: add "blame mode"
    
    If DCONF_BLAME is in the environment or kernel commandline then
    dconf-service will take steps to gather information about the first
    request it received (ie: the one that caused it to be activated).
    
    This is useful for helping to track down writes that occur at login.

 service/dconf-interfaces.c |   13 +++++-
 service/dconf-state.c      |   32 +++++++++++++
 service/dconf-state.h      |    2 +
 service/service.c          |  107 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 152 insertions(+), 2 deletions(-)
---
diff --git a/service/dconf-interfaces.c b/service/dconf-interfaces.c
index 180f3a3..c3c57c0 100644
--- a/service/dconf-interfaces.c
+++ b/service/dconf-interfaces.c
@@ -32,6 +32,7 @@ static const GDBusArgInfo *write_in[] = { &name_arg, &value_arg, NULL };
 static const GDBusArgInfo *write_out[] = { &tag_arg, NULL };
 static const GDBusArgInfo *many_in[] = { &path_arg, &values_arg, NULL };
 static const GDBusArgInfo *many_out[] = { &tag_arg, NULL };
+static const GDBusArgInfo *blame_out[] = { &tag_arg, NULL };
 static const GDBusArgInfo *notify_args[] = { &path_arg, &names_arg, &tag_arg, NULL };
 
 static const GDBusMethodInfo write_method = {
@@ -51,6 +52,12 @@ static const GDBusSignalInfo notify_signal = {
   (GDBusArgInfo **) notify_args
 };
 
+static const GDBusMethodInfo blame_method = {
+  -1, (gchar *) "Blame",
+  NULL,
+  (GDBusArgInfo **) blame_out
+};
+
 static const GDBusPropertyInfo shmdir_property = {
   -1, (gchar *) "ShmDirectory", (gchar *) "s", G_DBUS_PROPERTY_INFO_FLAGS_READABLE
 };
@@ -63,6 +70,10 @@ static const GDBusSignalInfo *writer_signals[] = {
   &notify_signal, NULL
 };
 
+static const GDBusMethodInfo *writer_info_methods[] = {
+  &blame_method, NULL
+};
+
 static const GDBusPropertyInfo *writer_info_properties[] = {
   &shmdir_property, NULL
 };
@@ -76,7 +87,7 @@ const GDBusInterfaceInfo ca_desrt_dconf_Writer = {
 
 const GDBusInterfaceInfo ca_desrt_dconf_WriterInfo = {
   -1, (gchar *) "ca.desrt.dconf.WriterInfo",
-  (GDBusMethodInfo **) NULL,
+  (GDBusMethodInfo **) writer_info_methods,
   (GDBusSignalInfo **) NULL,
   (GDBusPropertyInfo **) writer_info_properties
 };
diff --git a/service/dconf-state.c b/service/dconf-state.c
index 0ed156e..a30054c 100644
--- a/service/dconf-state.c
+++ b/service/dconf-state.c
@@ -4,6 +4,8 @@
 
 #include <string.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <errno.h>
 
@@ -64,9 +66,39 @@ dconf_state_init_session (DConfState *state)
     }
 }
 
+static gboolean
+dconf_state_is_blame_mode (void)
+{
+  gint fd;
+
+  if (getenv ("DCONF_BLAME"))
+    return TRUE;
+
+  fd = open ("/proc/cmdline", O_RDONLY);
+  if (fd != -1)
+    {
+      gchar buffer[1024];
+      gssize s;
+
+      s = read (fd, buffer, sizeof buffer - 1);
+      close (fd);
+
+      if (0 < s && s < sizeof buffer)
+        {
+          buffer[s] = '\0';
+          if (strstr (buffer, "DCONF_BLAME"))
+            return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
 void
 dconf_state_init (DConfState *state)
 {
+  state->blame_mode = dconf_state_is_blame_mode ();
+  state->blame_info = NULL;
   state->is_session = strcmp (g_get_user_name (), "dconf") != 0;
   state->main_loop = g_main_loop_new (NULL, FALSE);
   state->serial = 0;
diff --git a/service/dconf-state.h b/service/dconf-state.h
index 8bbe612..40c08af 100644
--- a/service/dconf-state.h
+++ b/service/dconf-state.h
@@ -5,6 +5,8 @@
 
 typedef struct
 {
+  gboolean blame_mode;
+  gchar *blame_info;
   gboolean is_session;
   GMainLoop *main_loop;
   guint64 serial;
diff --git a/service/service.c b/service/service.c
index b2ab1cf..106c683 100644
--- a/service/service.c
+++ b/service/service.c
@@ -140,6 +140,75 @@ unwrap_maybe (GVariant **ptr)
 }
 
 static void
+gather_blame_info (DConfState      *state,
+                   GDBusConnection *connection,
+                   const gchar     *sender,
+                   const gchar     *object_path,
+                   const gchar     *method_name,
+                   GVariant        *parameters)
+{
+  GError *error = NULL;
+  GVariant *reply;
+  GString *info;
+
+  info = g_string_new (NULL);
+
+  g_string_append_printf (info, "Sender: %s\n", sender);
+  g_string_append_printf (info, "Object path: %s\n", object_path);
+  g_string_append_printf (info, "Method: %s\n", method_name);
+
+  if (parameters)
+    {
+      gchar *tmp;
+
+      tmp = g_variant_print (parameters, FALSE);
+      g_string_append_printf (info, "Parameters: %s\n", tmp);
+      g_free (tmp);
+    }
+
+  reply = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", "org.freedesktop.DBus",
+                                       "GetConnectionUnixProcessID", g_variant_new ("(s)", sender),
+                                       G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+  if (reply != NULL)
+    {
+      guint pid;
+
+      g_variant_get (reply, "(u)", &pid);
+      g_string_append_printf (info, "PID: %u\n", pid);
+      g_variant_unref (reply);
+    }
+  else
+    {
+      g_string_append_printf (info, "Unable to acquire PID: %s\n", error->message);
+      g_error_free (error);
+    }
+
+  {
+    const gchar * const ps_fx[] = { "ps", "fx", NULL };
+    gchar *result_out;
+    gchar *result_err;
+    gint status;
+
+    if (g_spawn_sync (NULL, (gchar **) ps_fx, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+                      &result_out, &result_err, &status, &error))
+      {
+        g_string_append (info, "\n=== Process table from time of call follows ('ps fx') ===\n");
+        g_string_append (info, result_out);
+        g_string_append (info, result_err);
+        g_string_append_printf (info, "\nps exit status: %u\n", status);
+      }
+    else
+      {
+        g_string_append_printf (info, "\nUnable to spawn 'ps fx': %s\n", error->message);
+        g_error_free (error);
+      }
+  }
+
+  state->blame_info = g_string_free (info, FALSE);
+}
+
+static void
 method_call (GDBusConnection       *connection,
              const gchar           *sender,
              const gchar           *object_path,
@@ -154,6 +223,10 @@ method_call (GDBusConnection       *connection,
 
   state = dconf_writer_get_state (writer);
 
+  /* debugging... */
+  if (state->blame_mode && state->blame_info == NULL)
+    gather_blame_info (state, connection, sender, object_path, method_name, parameters);
+
   if (strcmp (method_name, "Write") == 0)
     {
       GError *error = NULL;
@@ -285,6 +358,34 @@ method_call (GDBusConnection       *connection,
     g_assert_not_reached ();
 }
 
+static void
+writer_info_method (GDBusConnection       *connection,
+                    const gchar           *sender,
+                    const gchar           *object_path,
+                    const gchar           *interface_name,
+                    const gchar           *method_name,
+                    GVariant              *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer               user_data)
+{
+  DConfState *state = user_data;
+
+  /* debugging... */
+  if (state->blame_mode && state->blame_info == NULL)
+    gather_blame_info (state, connection, sender, object_path, method_name, parameters);
+
+  if (g_str_equal (method_name, "Blame"))
+    {
+      if (state->blame_info == NULL)
+        state->blame_info = g_strdup ("DCONF_BLAME is not in the environment of dconf-service\n");
+
+      g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", state->blame_info));
+    }
+
+  else
+    g_assert_not_reached ();
+}
+
 static GVariant *
 writer_info_get_property (GDBusConnection  *connection,
                           const gchar      *sender,
@@ -296,6 +397,10 @@ writer_info_get_property (GDBusConnection  *connection,
 {
   DConfState *state = user_data;
 
+  /* debugging... */
+  if (state->blame_mode && state->blame_info == NULL)
+    gather_blame_info (state, connection, sender, object_path, "GetProperty", NULL);
+
   return g_variant_new_string (state->shm_dir);
 }
 
@@ -340,7 +445,7 @@ subtree_dispatch (GDBusConnection *connection,
   else if (strcmp (interface_name, "ca.desrt.dconf.WriterInfo") == 0)
     {
       static const GDBusInterfaceVTable vtable = {
-        NULL, writer_info_get_property, NULL
+        writer_info_method, writer_info_get_property, NULL
       };
 
       *out_user_data = state;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]