tracker-0.16.2/src/libtracker-common/tracker-dbus.c

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 }