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