Merge branch 'dbus-1.4'
[platform/upstream/dbus.git] / tools / dbus-monitor.c
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
56 GetSystemTimeAsFileTime (LPFILETIME ftp)
57 {
58   SYSTEMTIME st;
59   GetSystemTime (&st);
60   SystemTimeToFileTime (&st, ftp);
61 }
62 #endif
63
64 static int
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
82 oom (const char *doing)
83 {
84   fprintf (stderr, "OOM while %s\n", doing);
85   exit (1);
86 }
87
88 static DBusHandlerResult
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
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
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
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
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
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 static dbus_bool_t sigint_received = FALSE;
243
244 static void
245 sigint_handler (int signum)
246 {
247   sigint_received = TRUE;
248 }
249
250 int
251 main (int argc, char *argv[])
252 {
253   DBusConnection *connection;
254   DBusError error;
255   DBusBusType type = DBUS_BUS_SESSION;
256   DBusHandleMessageFunction filter_func = monitor_filter_func;
257   char *address = NULL;
258   dbus_bool_t seen_bus_type = FALSE;
259   
260   int i = 0, j = 0, numFilters = 0;
261   char **filters = NULL;
262
263   /* Set stdout to be unbuffered; this is basically so that if people
264    * do dbus-monitor > file, then send SIGINT via Control-C, they
265    * don't lose the last chunk of messages.
266    */
267
268 #ifdef DBUS_WIN
269   setvbuf (stdout, NULL, _IONBF, 0);
270 #else
271   setvbuf (stdout, NULL, _IOLBF, 0);
272 #endif
273
274   for (i = 1; i < argc; i++)
275     {
276       char *arg = argv[i];
277
278       if (!strcmp (arg, "--system"))
279         {
280           only_one_type (&seen_bus_type, argv[0]);
281           type = DBUS_BUS_SYSTEM;
282         }
283       else if (!strcmp (arg, "--session"))
284         {
285           only_one_type (&seen_bus_type, argv[0]);
286           type = DBUS_BUS_SESSION;
287         }
288       else if (!strcmp (arg, "--address"))
289         {
290           only_one_type (&seen_bus_type, argv[0]);
291
292           if (i+1 < argc)
293             {
294               address = argv[i+1];
295               i++;
296             }
297           else
298             usage (argv[0], 1);
299         }
300       else if (!strcmp (arg, "--help"))
301         usage (argv[0], 0);
302       else if (!strcmp (arg, "--monitor"))
303         filter_func = monitor_filter_func;
304       else if (!strcmp (arg, "--profile"))
305         filter_func = profile_filter_func;
306       else if (!strcmp (arg, "--"))
307         continue;
308       else if (arg[0] == '-')
309         usage (argv[0], 1);
310       else {
311           unsigned int filter_len;
312           numFilters++;
313           /* Prepend a rule (and a comma) to enable the monitor to eavesdrop.
314            * Prepending allows the user to add eavesdrop=false at command line
315            * in order to disable eavesdropping when needed */
316           filter_len = strlen (EAVESDROPPING_RULE) + 1 + strlen (arg) + 1;
317
318           filters = (char **) realloc (filters, numFilters * sizeof (char *));
319           if (filters == NULL)
320             oom ("adding a new filter slot");
321           filters[j] = (char *) malloc (filter_len * sizeof (char *));
322           if (filters[j] == NULL)
323             oom ("adding a new filter");
324           snprintf (filters[j], filter_len, "%s,%s", EAVESDROPPING_RULE, arg);
325           j++;
326       }
327     }
328
329   dbus_error_init (&error);
330   
331   if (address != NULL)
332     {
333       connection = dbus_connection_open (address, &error);
334       if (connection)
335         {
336           if (!dbus_bus_register (connection, &error))
337             {
338               fprintf (stderr, "Failed to register connection to bus at %s: %s\n",
339                        address, error.message);
340               dbus_error_free (&error);
341               exit (1);
342             }
343         }
344     }
345   else
346     connection = dbus_bus_get (type, &error);
347   if (connection == NULL)
348     {
349       const char *where;
350       if (address != NULL)
351         where = address;
352       else
353         {
354           switch (type)
355             {
356             case DBUS_BUS_SYSTEM:
357               where = "system bus";
358               break;
359             case DBUS_BUS_SESSION:
360               where = "session bus";
361               break;
362             default:
363               where = "";
364             }
365         }
366       fprintf (stderr, "Failed to open connection to %s: %s\n",
367                where,
368                error.message);
369       dbus_error_free (&error);
370       exit (1);
371     }
372
373   if (numFilters)
374     {
375       for (i = 0; i < j; i++)
376         {
377           dbus_bus_add_match (connection, filters[i], &error);
378           if (dbus_error_is_set (&error))
379             {
380               fprintf (stderr, "Failed to setup match \"%s\": %s\n",
381                        filters[i], error.message);
382               dbus_error_free (&error);
383               exit (1);
384             }
385           free(filters[i]);
386         }
387     }
388   else
389     {
390       dbus_bus_add_match (connection,
391                           EAVESDROPPING_RULE ",type='signal'",
392                           &error);
393       if (dbus_error_is_set (&error))
394         goto lose;
395       dbus_bus_add_match (connection,
396                           EAVESDROPPING_RULE ",type='method_call'",
397                           &error);
398       if (dbus_error_is_set (&error))
399         goto lose;
400       dbus_bus_add_match (connection,
401                           EAVESDROPPING_RULE ",type='method_return'",
402                           &error);
403       if (dbus_error_is_set (&error))
404         goto lose;
405       dbus_bus_add_match (connection,
406                           EAVESDROPPING_RULE ",type='error'",
407                           &error);
408       if (dbus_error_is_set (&error))
409         goto lose;
410     }
411
412   if (!dbus_connection_add_filter (connection, filter_func, NULL, NULL)) {
413     fprintf (stderr, "Couldn't add filter!\n");
414     exit (1);
415   }
416
417   while (dbus_connection_read_write_dispatch(connection, -1))
418     ;
419   exit (0);
420  lose:
421   fprintf (stderr, "Error: %s\n", error.message);
422   exit (1);
423 }
424