summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2012-03-07 12:22:23 -0500
committerRyan Lortie <desrt@desrt.ca>2012-03-07 12:22:23 -0500
commitcee3985ad474e849f699ad1ecce44d581f79271a (patch)
treed3249f369706ae4bd3f14a324e96f0fa818c8fcb
parent28536bd9794ca1a383fa2abbb627ac858b87226e (diff)
downloaddconf-cee3985ad474e849f699ad1ecce44d581f79271a.tar.gz
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.
-rw-r--r--service/dconf-interfaces.c13
-rw-r--r--service/dconf-state.c32
-rw-r--r--service/dconf-state.h2
-rw-r--r--service/service.c107
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;