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 *
short_name(const char * udi)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
print_props(const char * udi)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
dump_device(const char * udi)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
dump_children(char * udi,int num_devices,struct Device * devices,int depth)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
dump_devices(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
device_added(LibHalContext * ctx,const char * udi)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
device_removed(LibHalContext * ctx,const char * udi)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
device_new_capability(LibHalContext * ctx,const char * udi,const char * capability)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
device_lost_capability(LibHalContext * ctx,const char * udi,const char * capability)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
print_property(const char * udi,const char * key)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
property_modified(LibHalContext * ctx,const char * udi,const char * key,dbus_bool_t is_removed,dbus_bool_t is_added)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
device_condition(LibHalContext * ctx,const char * udi,const char * condition_name,const char * condition_details)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
usage(int argc,char * argv[])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
main(int argc,char * argv[])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_unref (conn);
727
728 if (show_device)
729 free(show_device);
730
731 return 0;
732 }
733
734 /**
735 * @}
736 */
737