diff options
author | Ryan Lortie <desrt@desrt.ca> | 2012-03-07 12:22:23 -0500 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2012-03-07 12:22:23 -0500 |
commit | cee3985ad474e849f699ad1ecce44d581f79271a (patch) | |
tree | d3249f369706ae4bd3f14a324e96f0fa818c8fcb /service | |
parent | 28536bd9794ca1a383fa2abbb627ac858b87226e (diff) | |
download | dconf-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.
Diffstat (limited to 'service')
-rw-r--r-- | service/dconf-interfaces.c | 13 | ||||
-rw-r--r-- | service/dconf-state.c | 32 | ||||
-rw-r--r-- | service/dconf-state.h | 2 | ||||
-rw-r--r-- | 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[] = { ¬ify_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; |