/*
* Copyright © 2012 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the licence, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see .
*
* Author: Ryan Lortie
*/
#include "config.h"
#include "dconf-blame.h"
#include "dconf-generated.h"
#include
#include
#include
typedef DConfDBusServiceInfoSkeletonClass DConfBlameClass;
struct _DConfBlame
{
DConfDBusServiceInfoSkeleton parent_instance;
GString *blame_info;
};
static void dconf_blame_iface_init (DConfDBusServiceInfoIface *iface);
G_DEFINE_TYPE_WITH_CODE (DConfBlame, dconf_blame, DCONF_DBUS_TYPE_SERVICE_INFO_SKELETON,
G_IMPLEMENT_INTERFACE (DCONF_DBUS_TYPE_SERVICE_INFO, dconf_blame_iface_init))
#include "../common/dconf-changeset.h"
#include "dconf-writer.h"
void
dconf_blame_record (GDBusMethodInvocation *invocation)
{
DConfBlame *blame = dconf_blame_get ();
GError *error = NULL;
GVariant *parameters;
GVariant *reply;
GString *info;
if (!blame)
return;
if (blame->blame_info->len)
g_string_append (blame->blame_info, "\n====================================================================\n");
info = blame->blame_info;
g_string_append_printf (info, "Sender: %s\n", g_dbus_method_invocation_get_sender (invocation));
g_string_append_printf (info, "Object path: %s\n", g_dbus_method_invocation_get_object_path (invocation));
g_string_append_printf (info, "Method: %s\n", g_dbus_method_invocation_get_method_name (invocation));
if ((parameters = g_dbus_method_invocation_get_parameters (invocation)))
{
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 (g_dbus_method_invocation_get_connection (invocation),
"org.freedesktop.DBus", "/", "org.freedesktop.DBus",
"GetConnectionUnixProcessID",
g_variant_new ("(s)", g_dbus_method_invocation_get_sender (invocation)),
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);
}
}
}
static gboolean
dconf_blame_handle_blame (DConfDBusServiceInfo *info,
GDBusMethodInvocation *invocation)
{
DConfBlame *blame = DCONF_BLAME (info);
dconf_blame_record (invocation);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", blame->blame_info->str));
return TRUE;
}
static void
dconf_blame_init (DConfBlame *blame)
{
blame->blame_info = g_string_new (NULL);
}
static void
dconf_blame_class_init (DConfBlameClass *class)
{
}
static void
dconf_blame_iface_init (DConfDBusServiceInfoIface *iface)
{
iface->handle_blame = dconf_blame_handle_blame;
}
static gboolean
dconf_blame_enabled (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;
}
DConfBlame *
dconf_blame_get (void)
{
static DConfBlame *blame;
static gboolean checked;
if (!checked)
{
if (dconf_blame_enabled ())
blame = g_object_new (DCONF_TYPE_BLAME, NULL);
checked = TRUE;
}
return blame;
}