No issues found
1 /*
2 * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
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.1 of the License, 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, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21
22 #include <gio/gio.h>
23 #include <gio/gunixinputstream.h>
24 #include <gio/gunixoutputstream.h>
25
26 #ifdef __OpenBSD__
27 #include <sys/param.h>
28 #include <sys/proc.h>
29 #include <sys/sysctl.h>
30 #include <fcntl.h>
31 #include <kvm.h>
32 #endif
33
34 #include "tracker-dbus.h"
35 #include "tracker-log.h"
36
37 /* How long clients can exist since last D-Bus call before being
38 * cleaned up.
39 */
40 #define CLIENT_CLEAN_UP_TIME 300
41
42 typedef struct {
43 gchar *sender;
44 gchar *binary;
45 gulong pid;
46 guint clean_up_id;
47 gint n_active_requests;
48 } ClientData;
49
50 struct _TrackerDBusRequest {
51 guint request_id;
52 ClientData *cd;
53 };
54
55 static gboolean client_lookup_enabled;
56 static GHashTable *clients;
57 static GDBusConnection *connection;
58
59 static void client_data_free (gpointer data);
60 static gboolean client_clean_up_cb (gpointer data);
61
62 static gboolean
63 clients_init (void)
64 {
65 GError *error = NULL;
66 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
67
68 if (error) {
69 g_critical ("Could not connect to the D-Bus session bus, %s",
70 error ? error->message : "no error given.");
71 g_clear_error (&error);
72 connection = NULL;
73 }
74
75 clients = g_hash_table_new_full (g_str_hash,
76 g_str_equal,
77 NULL,
78 client_data_free);
79
80 return TRUE;
81 }
82
83 static gboolean
84 clients_shutdown (void)
85 {
86 if (clients) {
87 g_hash_table_unref (clients);
88 clients = NULL;
89 }
90
91 if (connection) {
92 g_object_unref (connection);
93 connection = NULL;
94 }
95
96 return TRUE;
97 }
98
99 static void
100 client_data_free (gpointer data)
101 {
102 ClientData *cd = data;
103
104 if (!cd) {
105 return;
106 }
107
108 g_source_remove (cd->clean_up_id);
109
110 g_free (cd->sender);
111 g_free (cd->binary);
112
113 g_slice_free (ClientData, cd);
114 }
115
116 static ClientData *
117 client_data_new (gchar *sender)
118 {
119 ClientData *cd;
120 gboolean get_binary = TRUE;
121 GError *error = NULL;
122
123 cd = g_slice_new0 (ClientData);
124 cd->sender = sender;
125
126 if (connection) {
127 GVariant *v;
128
129 v = g_dbus_connection_call_sync (connection,
130 "org.freedesktop.DBus",
131 "/org/freedesktop/DBus",
132 "org.freedesktop.DBus",
133 "GetConnectionUnixProcessID",
134 g_variant_new ("(s)", sender),
135 G_VARIANT_TYPE ("(u)"),
136 G_DBUS_CALL_FLAGS_NONE,
137 -1,
138 NULL,
139 &error);
140
141 if (!error) {
142 g_variant_get (v, "(u)", &cd->pid);
143 g_variant_unref (v);
144 } else {
145 g_error_free (error);
146 }
147 }
148
149 if (get_binary) {
150 #ifndef __OpenBSD__
151 gchar *filename;
152 gchar *pid_str;
153 gchar *contents = NULL;
154 GError *error = NULL;
155 gchar **strv;
156
157 pid_str = g_strdup_printf ("%ld", cd->pid);
158 filename = g_build_filename (G_DIR_SEPARATOR_S,
159 "proc",
160 pid_str,
161 "cmdline",
162 NULL);
163 g_free (pid_str);
164
165 if (!g_file_get_contents (filename, &contents, NULL, &error)) {
166 g_warning ("Could not get process name from id %ld, %s",
167 cd->pid,
168 error ? error->message : "no error given");
169 g_clear_error (&error);
170 g_free (filename);
171 return cd;
172 }
173
174 g_free (filename);
175
176 strv = g_strsplit (contents, "^@", 2);
177 if (strv && strv[0]) {
178 cd->binary = g_path_get_basename (strv[0]);
179 }
180
181 g_strfreev (strv);
182 g_free (contents);
183 #else
184 gint nproc;
185 struct kinfo_proc *kp;
186 kvm_t *kd;
187 gchar **strv;
188
189 if ((kd = kvm_openfiles (NULL, NULL, NULL, KVM_NO_FILES, NULL)) == NULL)
190 return cd;
191
192 if ((kp = kvm_getprocs (kd, KERN_PROC_PID, cd->pid, sizeof (*kp), &nproc)) == NULL) {
193 g_warning ("Could not get process name: %s", kvm_geterr (kd));
194 kvm_close(kd);
195 return cd;
196 }
197
198 if ((kp->p_flag & P_SYSTEM) != 0) {
199 kvm_close(kd);
200 return cd;
201 }
202
203 strv = kvm_getargv (kd, kp, 0);
204
205 if (strv == NULL) {
206 kvm_close(kd);
207 return cd;
208 } else {
209 cd->binary = g_path_get_basename (strv[0]);
210 kvm_close(kd);
211 }
212 #endif
213 }
214
215 return cd;
216 }
217
218 static gboolean
219 client_clean_up_cb (gpointer data)
220 {
221 ClientData *cd;
222
223 cd = data;
224
225 g_debug ("Removing D-Bus client data for '%s' (pid: %lu) with id:'%s'",
226 cd->binary, cd->pid, cd->sender);
227 g_hash_table_remove (clients, cd->sender);
228
229 if (g_hash_table_size (clients) < 1) {
230 /* Clean everything up. */
231 clients_shutdown ();
232 }
233
234 return FALSE;
235 }
236
237 static ClientData *
238 client_get_for_sender (const gchar *sender)
239 {
240 ClientData *cd;
241
242 if (!client_lookup_enabled) {
243 return NULL;
244 }
245
246 /* Only really done with tracker-extract where we use
247 * functions from the command line with dbus code in them.
248 */
249 if (!sender) {
250 return NULL;
251 }
252
253 if (G_UNLIKELY (!clients)) {
254 clients_init ();
255 }
256
257 cd = g_hash_table_lookup (clients, sender);
258 if (!cd) {
259 gchar *sender_dup;
260
261 sender_dup = g_strdup (sender);
262 cd = client_data_new (sender_dup);
263 g_hash_table_insert (clients, sender_dup, cd);
264 }
265
266 if (cd->clean_up_id) {
267 g_source_remove (cd->clean_up_id);
268 cd->clean_up_id = 0;
269 }
270
271 cd->n_active_requests++;
272
273 return cd;
274 }
275
276 GQuark
277 tracker_dbus_error_quark (void)
278 {
279 return g_quark_from_static_string (TRACKER_DBUS_ERROR_DOMAIN);
280 }
281
282 gchar **
283 tracker_dbus_slist_to_strv (GSList *list)
284 {
285 GSList *l;
286 gchar **strv;
287 gint i = 0;
288
289 strv = g_new0 (gchar*, g_slist_length (list) + 1);
290
291 for (l = list; l != NULL; l = l->next) {
292 if (!g_utf8_validate (l->data, -1, NULL)) {
293 g_message ("Could not add string:'%s' to GStrv, invalid UTF-8",
294 (gchar*) l->data);
295 continue;
296 }
297
298 strv[i++] = g_strdup (l->data);
299 }
300
301 strv[i] = NULL;
302
303 return strv;
304 }
305
306 static guint
307 get_next_request_id (void)
308 {
309 static guint request_id = 1;
310
311 return request_id++;
312 }
313
314 TrackerDBusRequest *
315 tracker_dbus_request_begin (const gchar *sender,
316 const gchar *format,
317 ...)
318 {
319 TrackerDBusRequest *request;
320 gchar *str;
321 va_list args;
322
323 va_start (args, format);
324 str = g_strdup_vprintf (format, args);
325 va_end (args);
326
327 request = g_slice_new (TrackerDBusRequest);
328 request->request_id = get_next_request_id ();
329 request->cd = client_get_for_sender (sender);
330
331 g_debug ("<--- [%d%s%s|%lu] %s",
332 request->request_id,
333 request->cd ? "|" : "",
334 request->cd ? request->cd->binary : "",
335 request->cd ? request->cd->pid : 0,
336 str);
337
338 g_free (str);
339
340 return request;
341 }
342
343 void
344 tracker_dbus_request_end (TrackerDBusRequest *request,
345 GError *error)
346 {
347 if (!error) {
348 g_debug ("---> [%d%s%s|%lu] Success, no error given",
349 request->request_id,
350 request->cd ? "|" : "",
351 request->cd ? request->cd->binary : "",
352 request->cd ? request->cd->pid : 0);
353 } else {
354 g_message ("---> [%d%s%s|%lu] Failed, %s",
355 request->request_id,
356 request->cd ? "|" : "",
357 request->cd ? request->cd->binary : "",
358 request->cd ? request->cd->pid : 0,
359 error->message);
360 }
361
362 if (request->cd) {
363 request->cd->n_active_requests--;
364
365 if (request->cd->n_active_requests == 0) {
366 request->cd->clean_up_id = g_timeout_add_seconds (CLIENT_CLEAN_UP_TIME, client_clean_up_cb, request->cd);
367 }
368 }
369
370 g_slice_free (TrackerDBusRequest, request);
371 }
372
373 void
374 tracker_dbus_request_info (TrackerDBusRequest *request,
375 const gchar *format,
376 ...)
377 {
378 gchar *str;
379 va_list args;
380
381 va_start (args, format);
382 str = g_strdup_vprintf (format, args);
383 va_end (args);
384
385 tracker_info ("---- [%d%s%s|%lu] %s",
386 request->request_id,
387 request->cd ? "|" : "",
388 request->cd ? request->cd->binary : "",
389 request->cd ? request->cd->pid : 0,
390 str);
391 g_free (str);
392 }
393
394 void
395 tracker_dbus_request_comment (TrackerDBusRequest *request,
396 const gchar *format,
397 ...)
398 {
399 gchar *str;
400 va_list args;
401
402 va_start (args, format);
403 str = g_strdup_vprintf (format, args);
404 va_end (args);
405
406 g_message ("---- [%d%s%s|%lu] %s",
407 request->request_id,
408 request->cd ? "|" : "",
409 request->cd ? request->cd->binary : "",
410 request->cd ? request->cd->pid : 0,
411 str);
412 g_free (str);
413 }
414
415 void
416 tracker_dbus_request_debug (TrackerDBusRequest *request,
417 const gchar *format,
418 ...)
419 {
420 gchar *str;
421 va_list args;
422
423 va_start (args, format);
424 str = g_strdup_vprintf (format, args);
425 va_end (args);
426
427 g_debug ("---- [%d%s%s|%lu] %s",
428 request->request_id,
429 request->cd ? "|" : "",
430 request->cd ? request->cd->binary : "",
431 request->cd ? request->cd->pid : 0,
432 str);
433 g_free (str);
434 }
435
436 void
437 tracker_dbus_enable_client_lookup (gboolean enabled)
438 {
439 /* If this changed and we disabled everything, simply shut it
440 * all down.
441 */
442 if (client_lookup_enabled != enabled && !enabled) {
443 clients_shutdown ();
444 }
445
446 client_lookup_enabled = enabled;
447 }
448
449 TrackerDBusRequest *
450 tracker_g_dbus_request_begin (GDBusMethodInvocation *invocation,
451 const gchar *format,
452 ...)
453 {
454 TrackerDBusRequest *request;
455 gchar *str;
456 const gchar *sender;
457 va_list args;
458
459 va_start (args, format);
460 str = g_strdup_vprintf (format, args);
461 va_end (args);
462
463 sender = g_dbus_method_invocation_get_sender (invocation);
464 request = tracker_dbus_request_begin (sender, "%s", str);
465
466 g_free (str);
467
468 return request;
469 }