1 /*
2 * Copyright © 2012 Canonical Limited
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Ryan Lortie <desrt@desrt.ca>
18 */
19
20 #include "config.h"
21
22 #include "dconf-blame.h"
23
24 #include "dconf-generated.h"
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29
30 typedef DConfDBusServiceInfoSkeletonClass DConfBlameClass;
31 struct _DConfBlame
32 {
33 DConfDBusServiceInfoSkeleton parent_instance;
34
35 GString *blame_info;
36 };
37
38 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))39 G_DEFINE_TYPE_WITH_CODE (DConfBlame, dconf_blame, DCONF_DBUS_TYPE_SERVICE_INFO_SKELETON,
40 G_IMPLEMENT_INTERFACE (DCONF_DBUS_TYPE_SERVICE_INFO, dconf_blame_iface_init))
41
42 #include "../common/dconf-changeset.h"
43 #include "dconf-writer.h"
44
45 void
46 dconf_blame_record (GDBusMethodInvocation *invocation)
47 {
48 DConfBlame *blame = dconf_blame_get ();
49 GError *error = NULL;
50 GVariant *parameters;
51 GVariant *reply;
52 GString *info;
53
54 if (!blame)
55 return;
56
57 if (blame->blame_info->len)
58 g_string_append (blame->blame_info, "\n====================================================================\n");
59
60 info = blame->blame_info;
61
62 g_string_append_printf (info, "Sender: %s\n", g_dbus_method_invocation_get_sender (invocation));
63 g_string_append_printf (info, "Object path: %s\n", g_dbus_method_invocation_get_object_path (invocation));
64 g_string_append_printf (info, "Method: %s\n", g_dbus_method_invocation_get_method_name (invocation));
65
66 if ((parameters = g_dbus_method_invocation_get_parameters (invocation)))
67 {
68 gchar *tmp;
69
70 tmp = g_variant_print (parameters, FALSE);
71 g_string_append_printf (info, "Parameters: %s\n", tmp);
72 g_free (tmp);
73 }
74
75 reply = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (invocation),
76 "org.freedesktop.DBus", "/", "org.freedesktop.DBus",
77 "GetConnectionUnixProcessID",
78 g_variant_new ("(s)", g_dbus_method_invocation_get_sender (invocation)),
79 G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
80
81 if (reply != NULL)
82 {
83 guint pid;
84
85 g_variant_get (reply, "(u)", &pid);
86 g_string_append_printf (info, "PID: %u\n", pid);
87 g_variant_unref (reply);
88 }
89 else
90 {
91 g_string_append_printf (info, "Unable to acquire PID: %s\n", error->message);
92 g_error_free (error);
93 }
94
95 {
96 const gchar * const ps_fx[] = { "ps", "fx", NULL };
97 gchar *result_out;
98 gchar *result_err;
99 gint status;
100
101 if (g_spawn_sync (NULL, (gchar **) ps_fx, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
102 &result_out, &result_err, &status, &error))
103 {
104 g_string_append (info, "\n=== Process table from time of call follows ('ps fx') ===\n");
105 g_string_append (info, result_out);
106 g_string_append (info, result_err);
107 g_string_append_printf (info, "\nps exit status: %u\n", status);
108 }
109 else
110 {
111 g_string_append_printf (info, "\nUnable to spawn 'ps fx': %s\n", error->message);
112 g_error_free (error);
113 }
114 }
115 }
116
117 static gboolean
dconf_blame_handle_blame(DConfDBusServiceInfo * info,GDBusMethodInvocation * invocation)118 dconf_blame_handle_blame (DConfDBusServiceInfo *info,
119 GDBusMethodInvocation *invocation)
120 {
121 DConfBlame *blame = DCONF_BLAME (info);
122
123 dconf_blame_record (invocation);
124
125 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", blame->blame_info->str));
126
127 return TRUE;
128 }
129
130 static void
dconf_blame_init(DConfBlame * blame)131 dconf_blame_init (DConfBlame *blame)
132 {
133 blame->blame_info = g_string_new (NULL);
134 }
135
136 static void
dconf_blame_class_init(DConfBlameClass * class)137 dconf_blame_class_init (DConfBlameClass *class)
138 {
139 }
140
141 static void
dconf_blame_iface_init(DConfDBusServiceInfoIface * iface)142 dconf_blame_iface_init (DConfDBusServiceInfoIface *iface)
143 {
144 iface->handle_blame = dconf_blame_handle_blame;
145 }
146
147 static gboolean
dconf_blame_enabled(void)148 dconf_blame_enabled (void)
149 {
150 gint fd;
151
152 if (getenv ("DCONF_BLAME"))
153 return TRUE;
154
155 fd = open ("/proc/cmdline", O_RDONLY);
156 if (fd != -1)
157 {
158 gchar buffer[1024];
159 gssize s;
160
161 s = read (fd, buffer, sizeof buffer - 1);
162 close (fd);
163
164 if (0 < s && s < sizeof buffer)
165 {
166 buffer[s] = '\0';
167 if (strstr (buffer, "DCONF_BLAME"))
168 return TRUE;
169 }
170 }
171
172 return FALSE;
173 }
174
175 DConfBlame *
dconf_blame_get(void)176 dconf_blame_get (void)
177 {
178 static DConfBlame *blame;
179 static gboolean checked;
180
181 if (!checked)
182 {
183 if (dconf_blame_enabled ())
184 blame = g_object_new (DCONF_TYPE_BLAME, NULL);
185
186 checked = TRUE;
187 }
188
189 return blame;
190 }
191