xref: /illumos-gate/usr/src/cmd/hal/tools/lshal.c (revision ba2be53024c0b999e74ba9adcd7d80fec5df8c57)
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_unref (conn);
727 
728 	if (show_device)
729 		free(show_device);
730 
731 	return 0;
732 }
733 
734 /**
735  * @}
736  */
737