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