1 /*************************************************************************** 2 * CVSID: $Id$ 3 * 4 * lshal.c : Show devices managed by HAL 5 * 6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk> 7 * Copyright (C) 2005 Pierre Ossman, <drzeus@drzeus.cx> 8 * 9 * Licensed under the Academic Free License version 2.1 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 * 25 **************************************************************************/ 26 27 28 #ifdef HAVE_CONFIG_H 29 # include <config.h> 30 #endif 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <getopt.h> 37 38 #include <glib.h> 39 #include <dbus/dbus-glib-lowlevel.h> 40 #include <dbus/dbus-glib.h> 41 #include <libhal.h> 42 43 #ifdef __SUNPRO_C 44 #define __FUNCTION__ __func__ 45 #endif 46 47 /** 48 * @defgroup HalLsHal List HAL devices 49 * @ingroup HalMisc 50 * 51 * @brief A commandline tool, lshal, for displaying and, optionally, 52 * monitor the devices managed by the HAL daemon. Uses libhal. 53 * 54 * @{ 55 */ 56 57 /** Macro for terminating the program on an unrecoverable error */ 58 #define DIE(expr) do {printf("*** [DIE] %s:%s():%d : ", __FILE__, __FUNCTION__, __LINE__); printf expr; printf("\n"); exit(1); } while(0) 59 60 #define UDI_BASE "/org/freedesktop/Hal/devices/" 61 62 static LibHalContext *hal_ctx; 63 static dbus_bool_t long_list = FALSE; 64 static dbus_bool_t tree_view = FALSE; 65 static dbus_bool_t short_list = FALSE; 66 static char *show_device = NULL; 67 68 struct Device { 69 char *name; 70 char *parent; 71 }; 72 73 /** Generate a short name for a device 74 * 75 * @param udi Universal Device Id 76 */ 77 static const char * 78 short_name (const char *udi) 79 { 80 return &udi[sizeof(UDI_BASE) - 1]; 81 } 82 83 /** Print all properties of a device 84 * 85 * @param udi Universal Device Id 86 */ 87 88 static void 89 print_props (const char *udi) 90 { 91 DBusError error; 92 LibHalPropertySet *props; 93 LibHalPropertySetIterator it; 94 int type; 95 96 dbus_error_init (&error); 97 98 props = libhal_device_get_all_properties (hal_ctx, udi, &error); 99 100 /* NOTE : This may be NULL if the device was removed 101 * in the daemon; this is because 102 * hal_device_get_all_properties() is a in 103 * essence an IPC call and other stuff may 104 * be happening.. 105 */ 106 if (props == NULL) { 107 LIBHAL_FREE_DBUS_ERROR (&error); 108 return; 109 } 110 111 for (libhal_psi_init (&it, props); libhal_psi_has_more (&it); libhal_psi_next (&it)) { 112 type = libhal_psi_get_type (&it); 113 switch (type) { 114 case LIBHAL_PROPERTY_TYPE_STRING: 115 printf (" %s = '%s' (string)\n", 116 libhal_psi_get_key (&it), 117 libhal_psi_get_string (&it)); 118 break; 119 120 case LIBHAL_PROPERTY_TYPE_INT32: 121 printf (" %s = %d (0x%x) (int)\n", 122 libhal_psi_get_key (&it), 123 libhal_psi_get_int (&it), 124 libhal_psi_get_int (&it)); 125 break; 126 127 case LIBHAL_PROPERTY_TYPE_UINT64: 128 printf (" %s = %llu (0x%llx) (uint64)\n", 129 libhal_psi_get_key (&it), 130 (long long unsigned int) libhal_psi_get_uint64 (&it), 131 (long long unsigned int) libhal_psi_get_uint64 (&it)); 132 break; 133 134 case LIBHAL_PROPERTY_TYPE_DOUBLE: 135 printf (" %s = %g (double)\n", 136 libhal_psi_get_key (&it), 137 libhal_psi_get_double (&it)); 138 break; 139 140 case LIBHAL_PROPERTY_TYPE_BOOLEAN: 141 printf (" %s = %s (bool)\n", 142 libhal_psi_get_key (&it), 143 libhal_psi_get_bool (&it) ? "true" : 144 "false"); 145 break; 146 147 case LIBHAL_PROPERTY_TYPE_STRLIST: 148 { 149 unsigned int i; 150 char **strlist; 151 152 printf (" %s = {", libhal_psi_get_key (&it)); 153 154 strlist = libhal_psi_get_strlist (&it); 155 for (i = 0; strlist[i] != 0; i++) { 156 printf ("'%s'", strlist[i]); 157 if (strlist[i+1] != NULL) 158 printf (", "); 159 } 160 printf ("} (string list)\n"); 161 break; 162 } 163 164 default: 165 printf ("Unknown type %d=0x%02x\n", type, type); 166 break; 167 } 168 } 169 170 libhal_free_property_set (props); 171 } 172 173 /** Dumps information about a single device 174 * 175 * @param udi Universal Device Id 176 */ 177 178 static void 179 dump_device (const char *udi) 180 { 181 DBusError error; 182 183 dbus_error_init (&error); 184 185 if (!libhal_device_exists (hal_ctx, udi, &error)) { 186 LIBHAL_FREE_DBUS_ERROR (&error); 187 return; 188 } 189 190 if (long_list) { 191 printf ("udi = '%s'\n", udi); 192 193 print_props (udi); 194 printf ("\n"); 195 } 196 else 197 printf ("%s\n", short_name (udi)); 198 } 199 200 /** Dump all children of device 201 * 202 * @param udi Universal Device Id of parent 203 * @param num_devices Total number of devices in device list 204 * @param devices List of devices 205 * @param depth Current recursion depth 206 */ 207 208 static void 209 dump_children (char *udi, int num_devices, struct Device *devices, int depth) 210 { 211 int i; 212 213 for (i = 0; i < num_devices; i++) { 214 if (!udi) { 215 if (devices[i].parent) 216 continue; 217 } 218 else { 219 if (!devices[i].parent) 220 continue; 221 if (strcmp (devices[i].parent, udi)) 222 continue; 223 } 224 225 if (long_list) 226 printf ("udi = '%s'\n", devices[i].name); 227 else { 228 int j; 229 if (tree_view) { 230 for (j = 0;j < depth;j++) 231 printf(" "); 232 } 233 printf ("%s\n", short_name (devices[i].name)); 234 } 235 236 if (long_list) { 237 print_props (devices[i].name); 238 printf ("\n"); 239 } 240 241 dump_children(devices[i].name, num_devices, devices, depth + 1); 242 } 243 } 244 245 /** Dump all devices to stdout 246 * 247 */ 248 static void 249 dump_devices (void) 250 { 251 int i; 252 int num_devices; 253 char **device_names; 254 struct Device *devices; 255 DBusError error; 256 257 dbus_error_init (&error); 258 259 device_names = libhal_get_all_devices (hal_ctx, &num_devices, &error); 260 if (device_names == NULL) { 261 LIBHAL_FREE_DBUS_ERROR (&error); 262 DIE (("Couldn't obtain list of devices\n")); 263 } 264 265 devices = malloc (sizeof(struct Device) * num_devices); 266 if (!devices) { 267 libhal_free_string_array (device_names); 268 return; 269 } 270 271 for (i = 0;i < num_devices;i++) { 272 devices[i].name = device_names[i]; 273 devices[i].parent = libhal_device_get_property_string (hal_ctx, 274 device_names[i], "info.parent", &error); 275 276 if (dbus_error_is_set (&error)) { 277 /* Free the error (which include a dbus_error_init()) 278 This should prevent errors if a call above fails */ 279 dbus_error_free (&error); 280 } 281 } 282 283 if (long_list) { 284 printf ("\n" 285 "Dumping %d device(s) from the Global Device List:\n" 286 "-------------------------------------------------\n", 287 num_devices); 288 } 289 290 dump_children(NULL, num_devices, devices, 0); 291 292 for (i = 0;i < num_devices;i++) { 293 if (devices[i].parent) 294 libhal_free_string (devices[i].parent); 295 } 296 297 free (devices); 298 libhal_free_string_array (device_names); 299 300 if (long_list) { 301 printf ("\n" 302 "Dumped %d device(s) from the Global Device List.\n" 303 "------------------------------------------------\n", 304 num_devices); 305 306 printf ("\n"); 307 } 308 } 309 310 /** Invoked when a device is added to the Global Device List. Simply prints 311 * a message on stdout. 312 * 313 * @param udi Universal Device Id 314 */ 315 static void 316 device_added (LibHalContext *ctx, 317 const char *udi) 318 { 319 if (show_device && strcmp(show_device, udi)) 320 return; 321 322 if (long_list) { 323 printf ("*** lshal: device_added, udi='%s'\n", udi); 324 print_props (udi); 325 } else 326 printf ("%s added\n", short_name (udi)); 327 } 328 329 /** Invoked when a device is removed from the Global Device List. Simply 330 * prints a message on stdout. 331 * 332 * @param udi Universal Device Id 333 */ 334 static void 335 device_removed (LibHalContext *ctx, 336 const char *udi) 337 { 338 if (show_device && strcmp(show_device, udi)) 339 return; 340 341 if (long_list) 342 printf ("*** lshal: device_removed, udi='%s'\n", udi); 343 else 344 printf ("%s removed\n", short_name (udi)); 345 } 346 347 /** Invoked when device in the Global Device List acquires a new capability. 348 * Prints the name of the capability to stdout. 349 * 350 * @param udi Universal Device Id 351 * @param capability Name of capability 352 */ 353 static void 354 device_new_capability (LibHalContext *ctx, 355 const char *udi, 356 const char *capability) 357 { 358 if (show_device && strcmp(show_device, udi)) 359 return; 360 361 if (long_list) { 362 printf ("*** lshal: new_capability, udi='%s'\n", udi); 363 printf ("*** capability: %s\n", capability); 364 } else 365 printf ("%s capability %s added\n", short_name (udi), 366 capability); 367 } 368 369 /** Invoked when device in the Global Device List loses a capability. 370 * Prints the name of the capability to stdout. 371 * 372 * @param udi Universal Device Id 373 * @param capability Name of capability 374 */ 375 static void 376 device_lost_capability (LibHalContext *ctx, 377 const char *udi, 378 const char *capability) 379 { 380 if (show_device && strcmp(show_device, udi)) 381 return; 382 383 if (long_list) { 384 printf ("*** lshal: lost_capability, udi='%s'\n", udi); 385 printf ("*** capability: %s\n", capability); 386 } else 387 printf ("%s capability %s lost\n", short_name (udi), 388 capability); 389 } 390 391 /** Acquires and prints the value of of a property to stdout. 392 * 393 * @param udi Universal Device Id 394 * @param key Key of property 395 */ 396 static void 397 print_property (const char *udi, const char *key) 398 { 399 int type; 400 char *str; 401 DBusError error; 402 403 dbus_error_init (&error); 404 405 type = libhal_device_get_property_type (hal_ctx, udi, key, &error); 406 407 switch (type) { 408 case LIBHAL_PROPERTY_TYPE_STRING: 409 str = libhal_device_get_property_string (hal_ctx, udi, key, &error); 410 printf (long_list?"*** new value: '%s' (string)\n":"'%s'", str); 411 libhal_free_string (str); 412 break; 413 case LIBHAL_PROPERTY_TYPE_INT32: 414 { 415 dbus_int32_t value = libhal_device_get_property_int (hal_ctx, udi, key, &error); 416 printf (long_list?"*** new value: %d (0x%x) (int)\n":"%d (0x%x)", 417 value, value); 418 } 419 break; 420 case LIBHAL_PROPERTY_TYPE_UINT64: 421 { 422 dbus_uint64_t value = libhal_device_get_property_uint64 (hal_ctx, udi, key, &error); 423 printf (long_list?"*** new value: %llu (0x%llx) (uint64)\n":"%llu (0x%llx)", 424 (long long unsigned int) value, (long long unsigned int) value); 425 } 426 break; 427 case LIBHAL_PROPERTY_TYPE_DOUBLE: 428 printf (long_list?"*** new value: %g (double)\n":"%g", 429 libhal_device_get_property_double (hal_ctx, udi, key, &error)); 430 break; 431 case LIBHAL_PROPERTY_TYPE_BOOLEAN: 432 printf (long_list?"*** new value: %s (bool)\n":"%s", 433 libhal_device_get_property_bool (hal_ctx, udi, key, &error) ? "true" : "false"); 434 break; 435 case LIBHAL_PROPERTY_TYPE_STRLIST: 436 { 437 unsigned int i; 438 char **strlist; 439 440 if (long_list) 441 printf ("*** new value: {"); 442 else 443 printf ("{"); 444 445 strlist = libhal_device_get_property_strlist (hal_ctx, udi, key, &error); 446 for (i = 0; strlist[i] != 0; i++) { 447 printf ("'%s'", strlist[i]); 448 if (strlist[i+1] != NULL) 449 printf (", "); 450 } 451 if (long_list) 452 printf ("} (string list)\n"); 453 else 454 printf ("}"); 455 libhal_free_string_array (strlist); 456 break; 457 } 458 459 default: 460 fprintf (stderr, "Unknown type %d='%c'\n", type, type); 461 break; 462 } 463 464 if (dbus_error_is_set (&error)) 465 dbus_error_free (&error); 466 } 467 468 /** Invoked when a property of a device in the Global Device List is 469 * changed, and we have we have subscribed to changes for that device. 470 * 471 * @param udi Univerisal Device Id 472 * @param key Key of property 473 */ 474 static void 475 property_modified (LibHalContext *ctx, 476 const char *udi, 477 const char *key, 478 dbus_bool_t is_removed, 479 dbus_bool_t is_added) 480 { 481 if (show_device && strcmp(show_device, udi)) 482 return; 483 484 if (long_list) { 485 printf ("*** lshal: property_modified, udi=%s, key=%s\n", 486 udi, key); 487 printf (" is_removed=%s, is_added=%s\n", 488 is_removed ? "true" : "false", 489 is_added ? "true" : "false"); 490 if (!is_removed) 491 print_property (udi, key); 492 printf ("\n"); 493 } else { 494 printf ("%s property %s ", short_name (udi), key); 495 if (is_removed) 496 printf ("removed"); 497 else { 498 printf ("= "); 499 print_property (udi, key); 500 501 if (is_added) 502 printf (" (new)"); 503 } 504 printf ("\n"); 505 } 506 } 507 508 509 /** Invoked when a property of a device in the Global Device List is 510 * changed, and we have we have subscribed to changes for that device. 511 * 512 * @param udi Univerisal Device Id 513 * @param condition_name Name of condition 514 * @param message D-BUS message with parameters 515 */ 516 static void 517 device_condition (LibHalContext *ctx, 518 const char *udi, 519 const char *condition_name, 520 const char *condition_details) 521 { 522 if (show_device && strcmp(show_device, udi)) 523 return; 524 525 if (long_list) { 526 printf ("*** lshal: device_condition, udi=%s\n", udi); 527 printf (" condition_name=%s\n", condition_name); 528 printf (" condition_details=%s\n", condition_details); 529 printf ("\n"); 530 } else { 531 printf ("%s condition %s = %s\n", short_name (udi), 532 condition_name, condition_details); 533 } 534 } 535 536 537 /** Print out program usage. 538 * 539 * @param argc Number of arguments given to program 540 * @param argv Arguments given to program 541 */ 542 static void 543 usage (int argc, char *argv[]) 544 { 545 fprintf (stderr, "lshal version " PACKAGE_VERSION "\n"); 546 547 fprintf (stderr, "\n" "usage : %s [options]\n", argv[0]); 548 fprintf (stderr, 549 "\n" 550 "Options:\n" 551 " -m, --monitor Monitor device list\n" 552 " -s, --short short output (print only nonstatic part of udi)\n" 553 " -l, --long Long output\n" 554 " -t, --tree Tree view\n" 555 " -u, --show <udi> Show only the specified device\n" 556 "\n" 557 " -h, --help Show this information and exit\n" 558 " -V, --version Print version number\n" 559 "\n" 560 "Without any given options lshal will start with option --long." 561 "\n" 562 "Shows all devices and their properties. If the --monitor option is given\n" 563 "then the device list and all devices are monitored for changes.\n" 564 "\n"); 565 } 566 567 /** Entry point 568 * 569 * @param argc Number of arguments given to program 570 * @param argv Arguments given to program 571 * @return Return code 572 */ 573 int 574 main (int argc, char *argv[]) 575 { 576 DBusError error; 577 dbus_bool_t do_monitor = FALSE; 578 GMainLoop *loop; 579 DBusConnection *conn; 580 581 if (argc == 1) { 582 /* This is the default case lshal without any options */ 583 long_list = TRUE; 584 } 585 else { 586 static const struct option long_options[] = { 587 {"monitor", no_argument, NULL, 'm'}, 588 {"long", no_argument, NULL, 'l'}, 589 {"short", no_argument, NULL, 's'}, 590 {"tree", no_argument, NULL, 't'}, 591 {"show", required_argument, NULL, 'u'}, 592 {"help", no_argument, NULL, 'h'}, 593 {"usage", no_argument, NULL, 'U'}, 594 {"version", no_argument, NULL, 'V'}, 595 {NULL, 0, NULL, 0} 596 }; 597 598 while (1) { 599 int c; 600 601 c = getopt_long (argc, argv, "mlstu:hUV", long_options, NULL); 602 603 if (c == -1) { 604 /* this should happen e.g. if 'lshal -' and this is incorrect/incomplete option */ 605 if (!do_monitor && !long_list && !short_list && !tree_view && !show_device) { 606 usage (argc, argv); 607 return 1; 608 } 609 610 break; 611 } 612 613 switch (c) { 614 case 'm': 615 do_monitor = TRUE; 616 break; 617 618 case 'l': 619 long_list = TRUE; 620 break; 621 622 case 's': 623 short_list = TRUE; 624 long_list = FALSE; 625 break; 626 627 case 't': 628 tree_view = TRUE; 629 break; 630 631 case 'u': 632 if (strchr(optarg, '/') != NULL) 633 show_device = strdup(optarg); 634 else { 635 show_device = malloc(strlen(UDI_BASE) + strlen(optarg) + 1); 636 memcpy(show_device, UDI_BASE, strlen(UDI_BASE)); 637 memcpy(show_device + strlen(UDI_BASE), optarg, strlen(optarg) + 1); 638 } 639 640 break; 641 642 case 'h': 643 case 'U': 644 usage (argc, argv); 645 return 0; 646 647 case 'V': 648 printf ("lshal version " PACKAGE_VERSION "\n"); 649 return 0; 650 651 default: 652 usage (argc, argv); 653 return 1; 654 } 655 } 656 } 657 658 if (do_monitor) 659 loop = g_main_loop_new (NULL, FALSE); 660 else 661 loop = NULL; 662 663 dbus_error_init (&error); 664 conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error); 665 if (conn == NULL) { 666 fprintf (stderr, "error: dbus_bus_get: %s: %s\n", 667 error.name, error.message); 668 LIBHAL_FREE_DBUS_ERROR (&error); 669 return 1; 670 } 671 672 if (do_monitor) 673 dbus_connection_setup_with_g_main (conn, NULL); 674 675 if ((hal_ctx = libhal_ctx_new ()) == NULL) { 676 fprintf (stderr, "error: libhal_ctx_new\n"); 677 return 1; 678 } 679 if (!libhal_ctx_set_dbus_connection (hal_ctx, conn)) { 680 fprintf (stderr, "error: libhal_ctx_set_dbus_connection: %s: %s\n", 681 error.name, error.message); 682 return 1; 683 } 684 if (!libhal_ctx_init (hal_ctx, &error)) { 685 if (dbus_error_is_set(&error)) { 686 fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message); 687 LIBHAL_FREE_DBUS_ERROR (&error); 688 } 689 fprintf (stderr, "Could not initialise connection to hald.\n" 690 "Normally this means the HAL daemon (hald) is not running or not ready.\n"); 691 return 1; 692 } 693 694 libhal_ctx_set_device_added (hal_ctx, device_added); 695 libhal_ctx_set_device_removed (hal_ctx, device_removed); 696 libhal_ctx_set_device_new_capability (hal_ctx, device_new_capability); 697 libhal_ctx_set_device_lost_capability (hal_ctx, device_lost_capability); 698 libhal_ctx_set_device_property_modified (hal_ctx, property_modified); 699 libhal_ctx_set_device_condition (hal_ctx, device_condition); 700 701 if (show_device) 702 dump_device (show_device); 703 else if (!do_monitor) 704 dump_devices (); 705 706 /* run the main loop only if we should monitor */ 707 if (do_monitor && loop != NULL) { 708 if( long_list || short_list || tree_view ) 709 dump_devices (); 710 711 if ( libhal_device_property_watch_all (hal_ctx, &error) == FALSE) { 712 fprintf (stderr, "error: monitoring devicelist - libhal_device_property_watch_all: %s: %s\n", 713 error.name, error.message); 714 LIBHAL_FREE_DBUS_ERROR (&error); 715 return 1; 716 } 717 printf ("\nStart monitoring devicelist:\n" 718 "-------------------------------------------------\n"); 719 g_main_loop_run (loop); 720 } 721 722 if ( libhal_ctx_shutdown (hal_ctx, &error) == FALSE) 723 LIBHAL_FREE_DBUS_ERROR (&error); 724 libhal_ctx_free (hal_ctx); 725 726 dbus_connection_close (conn); 727 dbus_connection_unref (conn); 728 729 if (show_device) 730 free(show_device); 731 732 return 0; 733 } 734 735 /** 736 * @} 737 */ 738