1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-monitor.c Utility program to monitor messages on the bus
3 *
4 * Copyright (C) 2003 Philip Blundell <philb@gnu.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #ifdef DBUS_WIN
28 #include <winsock2.h>
29 #undef interface
30 #else
31 #include <sys/time.h>
32 #endif
33
34 #include <time.h>
35
36 #include "dbus-print-message.h"
37
38 #define EAVESDROPPING_RULE "eavesdrop=true"
39
40 #ifdef DBUS_WIN
41
42 /* gettimeofday is not defined on windows */
43 #define DBUS_SECONDS_SINCE_1601 11644473600LL
44 #define DBUS_USEC_IN_SEC 1000000LL
45
46 #ifdef DBUS_WINCE
47
48 #ifndef _IOLBF
49 #define _IOLBF 0x40
50 #endif
51 #ifndef _IONBF
52 #define _IONBF 0x04
53 #endif
54
55 void
GetSystemTimeAsFileTime(LPFILETIME ftp)56 GetSystemTimeAsFileTime (LPFILETIME ftp)
57 {
58 SYSTEMTIME st;
59 GetSystemTime (&st);
60 SystemTimeToFileTime (&st, ftp);
61 }
62 #endif
63
64 static int
gettimeofday(struct timeval * __p,void * __t)65 gettimeofday (struct timeval *__p,
66 void *__t)
67 {
68 union {
69 unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */
70 FILETIME ft;
71 } now;
72
73 GetSystemTimeAsFileTime (&now.ft);
74 __p->tv_usec = (long) ((now.ns100 / 10LL) % DBUS_USEC_IN_SEC);
75 __p->tv_sec = (long)(((now.ns100 / 10LL) / DBUS_SECONDS_SINCE_1601) - DBUS_SECONDS_SINCE_1601);
76
77 return 0;
78 }
79 #endif
80
81 inline static void
oom(const char * doing)82 oom (const char *doing)
83 {
84 fprintf (stderr, "OOM while %s\n", doing);
85 exit (1);
86 }
87
88 static DBusHandlerResult
monitor_filter_func(DBusConnection * connection,DBusMessage * message,void * user_data)89 monitor_filter_func (DBusConnection *connection,
90 DBusMessage *message,
91 void *user_data)
92 {
93 print_message (message, FALSE);
94
95 if (dbus_message_is_signal (message,
96 DBUS_INTERFACE_LOCAL,
97 "Disconnected"))
98 exit (0);
99
100 /* Conceptually we want this to be
101 * DBUS_HANDLER_RESULT_NOT_YET_HANDLED, but this raises
102 * some problems. See bug 1719.
103 */
104 return DBUS_HANDLER_RESULT_HANDLED;
105 }
106
107 #ifdef __APPLE__
108 #define PROFILE_TIMED_FORMAT "%s\t%lu\t%d"
109 #else
110 #define PROFILE_TIMED_FORMAT "%s\t%lu\t%lu"
111 #endif
112 #define TRAP_NULL_STRING(str) ((str) ? (str) : "<none>")
113
114 typedef enum
115 {
116 PROFILE_ATTRIBUTE_FLAG_SERIAL = 1,
117 PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL = 2,
118 PROFILE_ATTRIBUTE_FLAG_SENDER = 4,
119 PROFILE_ATTRIBUTE_FLAG_DESTINATION = 8,
120 PROFILE_ATTRIBUTE_FLAG_PATH = 16,
121 PROFILE_ATTRIBUTE_FLAG_INTERFACE = 32,
122 PROFILE_ATTRIBUTE_FLAG_MEMBER = 64,
123 PROFILE_ATTRIBUTE_FLAG_ERROR_NAME = 128
124 } ProfileAttributeFlags;
125
126 static void
profile_print_with_attrs(const char * type,DBusMessage * message,struct timeval * t,ProfileAttributeFlags attrs)127 profile_print_with_attrs (const char *type, DBusMessage *message,
128 struct timeval *t, ProfileAttributeFlags attrs)
129 {
130 printf (PROFILE_TIMED_FORMAT, type, t->tv_sec, t->tv_usec);
131
132 if (attrs & PROFILE_ATTRIBUTE_FLAG_SERIAL)
133 printf ("\t%u", dbus_message_get_serial (message));
134
135 if (attrs & PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL)
136 printf ("\t%u", dbus_message_get_reply_serial (message));
137
138 if (attrs & PROFILE_ATTRIBUTE_FLAG_SENDER)
139 printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_sender (message)));
140
141 if (attrs & PROFILE_ATTRIBUTE_FLAG_DESTINATION)
142 printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_destination (message)));
143
144 if (attrs & PROFILE_ATTRIBUTE_FLAG_PATH)
145 printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_path (message)));
146
147 if (attrs & PROFILE_ATTRIBUTE_FLAG_INTERFACE)
148 printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_interface (message)));
149
150 if (attrs & PROFILE_ATTRIBUTE_FLAG_MEMBER)
151 printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_member (message)));
152
153 if (attrs & PROFILE_ATTRIBUTE_FLAG_ERROR_NAME)
154 printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_error_name (message)));
155
156 printf ("\n");
157 }
158
159 static void
print_message_profile(DBusMessage * message)160 print_message_profile (DBusMessage *message)
161 {
162 struct timeval t;
163
164 if (gettimeofday (&t, NULL) < 0)
165 {
166 printf ("un\n");
167 return;
168 }
169
170 switch (dbus_message_get_type (message))
171 {
172 case DBUS_MESSAGE_TYPE_METHOD_CALL:
173 profile_print_with_attrs ("mc", message, &t,
174 PROFILE_ATTRIBUTE_FLAG_SERIAL |
175 PROFILE_ATTRIBUTE_FLAG_SENDER |
176 PROFILE_ATTRIBUTE_FLAG_PATH |
177 PROFILE_ATTRIBUTE_FLAG_INTERFACE |
178 PROFILE_ATTRIBUTE_FLAG_MEMBER);
179 break;
180 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
181 profile_print_with_attrs ("mr", message, &t,
182 PROFILE_ATTRIBUTE_FLAG_SERIAL |
183 PROFILE_ATTRIBUTE_FLAG_DESTINATION |
184 PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL);
185 break;
186 case DBUS_MESSAGE_TYPE_ERROR:
187 profile_print_with_attrs ("err", message, &t,
188 PROFILE_ATTRIBUTE_FLAG_SERIAL |
189 PROFILE_ATTRIBUTE_FLAG_DESTINATION |
190 PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL);
191 break;
192 case DBUS_MESSAGE_TYPE_SIGNAL:
193 profile_print_with_attrs ("sig", message, &t,
194 PROFILE_ATTRIBUTE_FLAG_SERIAL |
195 PROFILE_ATTRIBUTE_FLAG_PATH |
196 PROFILE_ATTRIBUTE_FLAG_INTERFACE |
197 PROFILE_ATTRIBUTE_FLAG_MEMBER);
198 break;
199 default:
200 printf (PROFILE_TIMED_FORMAT "\n", "tun", t.tv_sec, t.tv_usec);
201 break;
202 }
203 }
204
205 static DBusHandlerResult
profile_filter_func(DBusConnection * connection,DBusMessage * message,void * user_data)206 profile_filter_func (DBusConnection *connection,
207 DBusMessage *message,
208 void *user_data)
209 {
210 print_message_profile (message);
211
212 if (dbus_message_is_signal (message,
213 DBUS_INTERFACE_LOCAL,
214 "Disconnected"))
215 exit (0);
216
217 return DBUS_HANDLER_RESULT_HANDLED;
218 }
219
220 static void
usage(char * name,int ecode)221 usage (char *name, int ecode)
222 {
223 fprintf (stderr, "Usage: %s [--system | --session | --address ADDRESS] [--monitor | --profile ] [watch expressions]\n", name);
224 exit (ecode);
225 }
226
227 static void
only_one_type(dbus_bool_t * seen_bus_type,char * name)228 only_one_type (dbus_bool_t *seen_bus_type,
229 char *name)
230 {
231 if (*seen_bus_type)
232 {
233 fprintf (stderr, "I only support monitoring one bus at a time!\n");
234 usage (name, 1);
235 }
236 else
237 {
238 *seen_bus_type = TRUE;
239 }
240 }
241
242 int
main(int argc,char * argv[])243 main (int argc, char *argv[])
244 {
245 DBusConnection *connection;
246 DBusError error;
247 DBusBusType type = DBUS_BUS_SESSION;
248 DBusHandleMessageFunction filter_func = monitor_filter_func;
249 char *address = NULL;
250 dbus_bool_t seen_bus_type = FALSE;
251
252 int i = 0, j = 0, numFilters = 0;
253 char **filters = NULL;
254
255 /* Set stdout to be unbuffered; this is basically so that if people
256 * do dbus-monitor > file, then send SIGINT via Control-C, they
257 * don't lose the last chunk of messages.
258 */
259
260 #ifdef DBUS_WIN
261 setvbuf (stdout, NULL, _IONBF, 0);
262 #else
263 setvbuf (stdout, NULL, _IOLBF, 0);
264 #endif
265
266 for (i = 1; i < argc; i++)
267 {
268 char *arg = argv[i];
269
270 if (!strcmp (arg, "--system"))
271 {
272 only_one_type (&seen_bus_type, argv[0]);
273 type = DBUS_BUS_SYSTEM;
274 }
275 else if (!strcmp (arg, "--session"))
276 {
277 only_one_type (&seen_bus_type, argv[0]);
278 type = DBUS_BUS_SESSION;
279 }
280 else if (!strcmp (arg, "--address"))
281 {
282 only_one_type (&seen_bus_type, argv[0]);
283
284 if (i+1 < argc)
285 {
286 address = argv[i+1];
287 i++;
288 }
289 else
290 usage (argv[0], 1);
291 }
292 else if (!strcmp (arg, "--help"))
293 usage (argv[0], 0);
294 else if (!strcmp (arg, "--monitor"))
295 filter_func = monitor_filter_func;
296 else if (!strcmp (arg, "--profile"))
297 filter_func = profile_filter_func;
298 else if (!strcmp (arg, "--"))
299 continue;
300 else if (arg[0] == '-')
301 usage (argv[0], 1);
302 else {
303 unsigned int filter_len;
304 numFilters++;
305 /* Prepend a rule (and a comma) to enable the monitor to eavesdrop.
306 * Prepending allows the user to add eavesdrop=false at command line
307 * in order to disable eavesdropping when needed */
308 filter_len = strlen (EAVESDROPPING_RULE) + 1 + strlen (arg) + 1;
309
310 filters = (char **) realloc (filters, numFilters * sizeof (char *));
311 if (filters == NULL)
312 oom ("adding a new filter slot");
313 filters[j] = (char *) malloc (filter_len * sizeof (char *));
314 if (filters[j] == NULL)
315 oom ("adding a new filter");
316 snprintf (filters[j], filter_len, "%s,%s", EAVESDROPPING_RULE, arg);
317 j++;
318 }
319 }
320
321 dbus_error_init (&error);
322
323 if (address != NULL)
324 {
325 connection = dbus_connection_open (address, &error);
326 if (connection)
327 {
328 if (!dbus_bus_register (connection, &error))
329 {
330 fprintf (stderr, "Failed to register connection to bus at %s: %s\n",
331 address, error.message);
332 dbus_error_free (&error);
333 exit (1);
334 }
335 }
336 }
337 else
338 connection = dbus_bus_get (type, &error);
339 if (connection == NULL)
340 {
341 const char *where;
342 if (address != NULL)
343 where = address;
344 else
345 {
346 switch (type)
347 {
348 case DBUS_BUS_SYSTEM:
349 where = "system bus";
350 break;
351 case DBUS_BUS_SESSION:
352 where = "session bus";
353 break;
354 default:
355 where = "";
356 }
357 }
358 fprintf (stderr, "Failed to open connection to %s: %s\n",
359 where,
360 error.message);
361 dbus_error_free (&error);
362 exit (1);
363 }
364
365 if (numFilters)
366 {
367 for (i = 0; i < j; i++)
368 {
369 dbus_bus_add_match (connection, filters[i], &error);
370 if (dbus_error_is_set (&error))
371 {
372 fprintf (stderr, "Failed to setup match \"%s\": %s\n",
373 filters[i], error.message);
374 dbus_error_free (&error);
375 exit (1);
376 }
377 free(filters[i]);
378 }
379 }
380 else
381 {
382 dbus_bus_add_match (connection,
383 EAVESDROPPING_RULE ",type='signal'",
384 &error);
385 if (dbus_error_is_set (&error))
386 goto lose;
387 dbus_bus_add_match (connection,
388 EAVESDROPPING_RULE ",type='method_call'",
389 &error);
390 if (dbus_error_is_set (&error))
391 goto lose;
392 dbus_bus_add_match (connection,
393 EAVESDROPPING_RULE ",type='method_return'",
394 &error);
395 if (dbus_error_is_set (&error))
396 goto lose;
397 dbus_bus_add_match (connection,
398 EAVESDROPPING_RULE ",type='error'",
399 &error);
400 if (dbus_error_is_set (&error))
401 goto lose;
402 }
403
404 if (!dbus_connection_add_filter (connection, filter_func, NULL, NULL)) {
405 fprintf (stderr, "Couldn't add filter!\n");
406 exit (1);
407 }
408
409 while (dbus_connection_read_write_dispatch(connection, -1))
410 ;
411 exit (0);
412 lose:
413 fprintf (stderr, "Error: %s\n", error.message);
414 exit (1);
415 }
416
417