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 55void 56GetSystemTimeAsFileTime (LPFILETIME ftp) 57{ 58 SYSTEMTIME st; 59 GetSystemTime (&st); 60 SystemTimeToFileTime (&st, ftp); 61} 62#endif 63 64static int 65gettimeofday (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 81inline static void 82oom (const char *doing) 83{ 84 fprintf (stderr, "OOM while %s\n", doing); 85 exit (1); 86} 87 88static DBusHandlerResult 89monitor_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 114typedef 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 126static void 127profile_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 159static void 160print_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 205static DBusHandlerResult 206profile_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 220static void 221usage (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 227static void 228only_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 242int 243main (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