/***************************************************************************
* CVSID: $Id$
*
* lshal.c : Show devices managed by HAL
*
* Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
* Copyright (C) 2005 Pierre Ossman, <drzeus@drzeus.cx>
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
**************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <dbus/dbus-glib.h>
#include <libhal.h>
#ifdef __SUNPRO_C
#define __FUNCTION__ __func__
#endif
/**
* @defgroup HalLsHal List HAL devices
* @ingroup HalMisc
*
* @brief A commandline tool, lshal, for displaying and, optionally,
* monitor the devices managed by the HAL daemon. Uses libhal.
*
* @{
*/
/** Macro for terminating the program on an unrecoverable error */
#define DIE(expr) do {printf("*** [DIE] %s:%s():%d : ", __FILE__, __FUNCTION__, __LINE__); printf expr; printf("\n"); exit(1); } while(0)
#define UDI_BASE "/org/freedesktop/Hal/devices/"
static LibHalContext *hal_ctx;
static dbus_bool_t long_list = FALSE;
static dbus_bool_t tree_view = FALSE;
static dbus_bool_t short_list = FALSE;
static char *show_device = NULL;
struct Device {
char *name;
char *parent;
};
/** Generate a short name for a device
*
* @param udi Universal Device Id
*/
static const char *
short_name (const char *udi)
{
return &udi[sizeof(UDI_BASE) - 1];
}
/** Print all properties of a device
*
* @param udi Universal Device Id
*/
static void
print_props (const char *udi)
{
DBusError error;
LibHalPropertySet *props;
LibHalPropertySetIterator it;
int type;
dbus_error_init (&error);
props = libhal_device_get_all_properties (hal_ctx, udi, &error);
/* NOTE : This may be NULL if the device was removed
* in the daemon; this is because
* hal_device_get_all_properties() is a in
* essence an IPC call and other stuff may
* be happening..
*/
if (props == NULL) {
LIBHAL_FREE_DBUS_ERROR (&error);
return;
}
for (libhal_psi_init (&it, props); libhal_psi_has_more (&it); libhal_psi_next (&it)) {
type = libhal_psi_get_type (&it);
switch (type) {
case LIBHAL_PROPERTY_TYPE_STRING:
printf (" %s = '%s' (string)\n",
libhal_psi_get_key (&it),
libhal_psi_get_string (&it));
break;
case LIBHAL_PROPERTY_TYPE_INT32:
printf (" %s = %d (0x%x) (int)\n",
libhal_psi_get_key (&it),
libhal_psi_get_int (&it),
libhal_psi_get_int (&it));
break;
case LIBHAL_PROPERTY_TYPE_UINT64:
printf (" %s = %llu (0x%llx) (uint64)\n",
libhal_psi_get_key (&it),
(long long unsigned int) libhal_psi_get_uint64 (&it),
(long long unsigned int) libhal_psi_get_uint64 (&it));
break;
case LIBHAL_PROPERTY_TYPE_DOUBLE:
printf (" %s = %g (double)\n",
libhal_psi_get_key (&it),
libhal_psi_get_double (&it));
break;
case LIBHAL_PROPERTY_TYPE_BOOLEAN:
printf (" %s = %s (bool)\n",
libhal_psi_get_key (&it),
libhal_psi_get_bool (&it) ? "true" :
"false");
break;
case LIBHAL_PROPERTY_TYPE_STRLIST:
{
unsigned int i;
char **strlist;
printf (" %s = {", libhal_psi_get_key (&it));
strlist = libhal_psi_get_strlist (&it);
for (i = 0; strlist[i] != 0; i++) {
printf ("'%s'", strlist[i]);
if (strlist[i+1] != NULL)
printf (", ");
}
printf ("} (string list)\n");
break;
}
default:
printf ("Unknown type %d=0x%02x\n", type, type);
break;
}
}
libhal_free_property_set (props);
}
/** Dumps information about a single device
*
* @param udi Universal Device Id
*/
static void
dump_device (const char *udi)
{
DBusError error;
dbus_error_init (&error);
if (!libhal_device_exists (hal_ctx, udi, &error)) {
LIBHAL_FREE_DBUS_ERROR (&error);
return;
}
if (long_list) {
printf ("udi = '%s'\n", udi);
print_props (udi);
printf ("\n");
}
else
printf ("%s\n", short_name (udi));
}
/** Dump all children of device
*
* @param udi Universal Device Id of parent
* @param num_devices Total number of devices in device list
* @param devices List of devices
* @param depth Current recursion depth
*/
static void
dump_children (char *udi, int num_devices, struct Device *devices, int depth)
{
int i;
for (i = 0; i < num_devices; i++) {
if (!udi) {
if (devices[i].parent)
continue;
}
else {
if (!devices[i].parent)
continue;
if (strcmp (devices[i].parent, udi))
continue;
}
if (long_list)
printf ("udi = '%s'\n", devices[i].name);
else {
int j;
if (tree_view) {
for (j = 0;j < depth;j++)
printf(" ");
}
printf ("%s\n", short_name (devices[i].name));
}
if (long_list) {
print_props (devices[i].name);
printf ("\n");
}
dump_children(devices[i].name, num_devices, devices, depth + 1);
}
}
/** Dump all devices to stdout
*
*/
static void
dump_devices (void)
{
int i;
int num_devices;
char **device_names;
struct Device *devices;
DBusError error;
dbus_error_init (&error);
device_names = libhal_get_all_devices (hal_ctx, &num_devices, &error);
if (device_names == NULL) {
LIBHAL_FREE_DBUS_ERROR (&error);
DIE (("Couldn't obtain list of devices\n"));
}
devices = malloc (sizeof(struct Device) * num_devices);
if (!devices) {
libhal_free_string_array (device_names);
return;
}
for (i = 0;i < num_devices;i++) {
devices[i].name = device_names[i];
devices[i].parent = libhal_device_get_property_string (hal_ctx,
device_names[i], "info.parent", &error);
if (dbus_error_is_set (&error)) {
/* Free the error (which include a dbus_error_init())
This should prevent errors if a call above fails */
dbus_error_free (&error);
}
}
if (long_list) {
printf ("\n"
"Dumping %d device(s) from the Global Device List:\n"
"-------------------------------------------------\n",
num_devices);
}
dump_children(NULL, num_devices, devices, 0);
for (i = 0;i < num_devices;i++) {
if (devices[i].parent)
libhal_free_string (devices[i].parent);
}
free (devices);
libhal_free_string_array (device_names);
if (long_list) {
printf ("\n"
"Dumped %d device(s) from the Global Device List.\n"
"------------------------------------------------\n",
num_devices);
printf ("\n");
}
}
/** Invoked when a device is added to the Global Device List. Simply prints
* a message on stdout.
*
* @param udi Universal Device Id
*/
static void
device_added (LibHalContext *ctx,
const char *udi)
{
if (show_device && strcmp(show_device, udi))
return;
if (long_list) {
printf ("*** lshal: device_added, udi='%s'\n", udi);
print_props (udi);
} else
printf ("%s added\n", short_name (udi));
}
/** Invoked when a device is removed from the Global Device List. Simply
* prints a message on stdout.
*
* @param udi Universal Device Id
*/
static void
device_removed (LibHalContext *ctx,
const char *udi)
{
if (show_device && strcmp(show_device, udi))
return;
if (long_list)
printf ("*** lshal: device_removed, udi='%s'\n", udi);
else
printf ("%s removed\n", short_name (udi));
}
/** Invoked when device in the Global Device List acquires a new capability.
* Prints the name of the capability to stdout.
*
* @param udi Universal Device Id
* @param capability Name of capability
*/
static void
device_new_capability (LibHalContext *ctx,
const char *udi,
const char *capability)
{
if (show_device && strcmp(show_device, udi))
return;
if (long_list) {
printf ("*** lshal: new_capability, udi='%s'\n", udi);
printf ("*** capability: %s\n", capability);
} else
printf ("%s capability %s added\n", short_name (udi),
capability);
}
/** Invoked when device in the Global Device List loses a capability.
* Prints the name of the capability to stdout.
*
* @param udi Universal Device Id
* @param capability Name of capability
*/
static void
device_lost_capability (LibHalContext *ctx,
const char *udi,
const char *capability)
{
if (show_device && strcmp(show_device, udi))
return;
if (long_list) {
printf ("*** lshal: lost_capability, udi='%s'\n", udi);
printf ("*** capability: %s\n", capability);
} else
printf ("%s capability %s lost\n", short_name (udi),
capability);
}
/** Acquires and prints the value of of a property to stdout.
*
* @param udi Universal Device Id
* @param key Key of property
*/
static void
print_property (const char *udi, const char *key)
{
int type;
char *str;
DBusError error;
dbus_error_init (&error);
type = libhal_device_get_property_type (hal_ctx, udi, key, &error);
switch (type) {
case LIBHAL_PROPERTY_TYPE_STRING:
str = libhal_device_get_property_string (hal_ctx, udi, key, &error);
printf (long_list?"*** new value: '%s' (string)\n":"'%s'", str);
libhal_free_string (str);
break;
case LIBHAL_PROPERTY_TYPE_INT32:
{
dbus_int32_t value = libhal_device_get_property_int (hal_ctx, udi, key, &error);
printf (long_list?"*** new value: %d (0x%x) (int)\n":"%d (0x%x)",
value, value);
}
break;
case LIBHAL_PROPERTY_TYPE_UINT64:
{
dbus_uint64_t value = libhal_device_get_property_uint64 (hal_ctx, udi, key, &error);
printf (long_list?"*** new value: %llu (0x%llx) (uint64)\n":"%llu (0x%llx)",
(long long unsigned int) value, (long long unsigned int) value);
}
break;
case LIBHAL_PROPERTY_TYPE_DOUBLE:
printf (long_list?"*** new value: %g (double)\n":"%g",
libhal_device_get_property_double (hal_ctx, udi, key, &error));
break;
case LIBHAL_PROPERTY_TYPE_BOOLEAN:
printf (long_list?"*** new value: %s (bool)\n":"%s",
libhal_device_get_property_bool (hal_ctx, udi, key, &error) ? "true" : "false");
break;
case LIBHAL_PROPERTY_TYPE_STRLIST:
{
unsigned int i;
char **strlist;
if (long_list)
printf ("*** new value: {");
else
printf ("{");
strlist = libhal_device_get_property_strlist (hal_ctx, udi, key, &error);
for (i = 0; strlist[i] != 0; i++) {
printf ("'%s'", strlist[i]);
if (strlist[i+1] != NULL)
printf (", ");
}
if (long_list)
printf ("} (string list)\n");
else
printf ("}");
libhal_free_string_array (strlist);
break;
}
default:
fprintf (stderr, "Unknown type %d='%c'\n", type, type);
break;
}
if (dbus_error_is_set (&error))
dbus_error_free (&error);
}
/** Invoked when a property of a device in the Global Device List is
* changed, and we have we have subscribed to changes for that device.
*
* @param udi Univerisal Device Id
* @param key Key of property
*/
static void
property_modified (LibHalContext *ctx,
const char *udi,
const char *key,
dbus_bool_t is_removed,
dbus_bool_t is_added)
{
if (show_device && strcmp(show_device, udi))
return;
if (long_list) {
printf ("*** lshal: property_modified, udi=%s, key=%s\n",
udi, key);
printf (" is_removed=%s, is_added=%s\n",
is_removed ? "true" : "false",
is_added ? "true" : "false");
if (!is_removed)
print_property (udi, key);
printf ("\n");
} else {
printf ("%s property %s ", short_name (udi), key);
if (is_removed)
printf ("removed");
else {
printf ("= ");
print_property (udi, key);
if (is_added)
printf (" (new)");
}
printf ("\n");
}
}
/** Invoked when a property of a device in the Global Device List is
* changed, and we have we have subscribed to changes for that device.
*
* @param udi Univerisal Device Id
* @param condition_name Name of condition
* @param message D-BUS message with parameters
*/
static void
device_condition (LibHalContext *ctx,
const char *udi,
const char *condition_name,
const char *condition_details)
{
if (show_device && strcmp(show_device, udi))
return;
if (long_list) {
printf ("*** lshal: device_condition, udi=%s\n", udi);
printf (" condition_name=%s\n", condition_name);
printf (" condition_details=%s\n", condition_details);
printf ("\n");
} else {
printf ("%s condition %s = %s\n", short_name (udi),
condition_name, condition_details);
}
}
/** Print out program usage.
*
* @param argc Number of arguments given to program
* @param argv Arguments given to program
*/
static void
usage (int argc, char *argv[])
{
fprintf (stderr, "lshal version " PACKAGE_VERSION "\n");
fprintf (stderr, "\n" "usage : %s [options]\n", argv[0]);
fprintf (stderr,
"\n"
"Options:\n"
" -m, --monitor Monitor device list\n"
" -s, --short short output (print only nonstatic part of udi)\n"
" -l, --long Long output\n"
" -t, --tree Tree view\n"
" -u, --show <udi> Show only the specified device\n"
"\n"
" -h, --help Show this information and exit\n"
" -V, --version Print version number\n"
"\n"
"Without any given options lshal will start with option --long."
"\n"
"Shows all devices and their properties. If the --monitor option is given\n"
"then the device list and all devices are monitored for changes.\n"
"\n");
}
/** Entry point
*
* @param argc Number of arguments given to program
* @param argv Arguments given to program
* @return Return code
*/
int
main (int argc, char *argv[])
{
DBusError error;
dbus_bool_t do_monitor = FALSE;
GMainLoop *loop;
DBusConnection *conn;
if (argc == 1) {
/* This is the default case lshal without any options */
long_list = TRUE;
}
else {
static const struct option long_options[] = {
{"monitor", no_argument, NULL, 'm'},
{"long", no_argument, NULL, 'l'},
{"short", no_argument, NULL, 's'},
{"tree", no_argument, NULL, 't'},
{"show", required_argument, NULL, 'u'},
{"help", no_argument, NULL, 'h'},
{"usage", no_argument, NULL, 'U'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
while (1) {
int c;
c = getopt_long (argc, argv, "mlstu:hUV", long_options, NULL);
if (c == -1) {
/* this should happen e.g. if 'lshal -' and this is incorrect/incomplete option */
if (!do_monitor && !long_list && !short_list && !tree_view && !show_device) {
usage (argc, argv);
return 1;
}
break;
}
switch (c) {
case 'm':
do_monitor = TRUE;
break;
case 'l':
long_list = TRUE;
break;
case 's':
short_list = TRUE;
long_list = FALSE;
break;
case 't':
tree_view = TRUE;
break;
case 'u':
if (strchr(optarg, '/') != NULL)
show_device = strdup(optarg);
else {
show_device = malloc(strlen(UDI_BASE) + strlen(optarg) + 1);
memcpy(show_device, UDI_BASE, strlen(UDI_BASE));
memcpy(show_device + strlen(UDI_BASE), optarg, strlen(optarg) + 1);
}
break;
case 'h':
case 'U':
usage (argc, argv);
return 0;
case 'V':
printf ("lshal version " PACKAGE_VERSION "\n");
return 0;
default:
usage (argc, argv);
return 1;
}
}
}
if (do_monitor)
loop = g_main_loop_new (NULL, FALSE);
else
loop = NULL;
dbus_error_init (&error);
conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
if (conn == NULL) {
fprintf (stderr, "error: dbus_bus_get: %s: %s\n",
error.name, error.message);
LIBHAL_FREE_DBUS_ERROR (&error);
return 1;
}
if (do_monitor)
dbus_connection_setup_with_g_main (conn, NULL);
if ((hal_ctx = libhal_ctx_new ()) == NULL) {
fprintf (stderr, "error: libhal_ctx_new\n");
return 1;
}
if (!libhal_ctx_set_dbus_connection (hal_ctx, conn)) {
fprintf (stderr, "error: libhal_ctx_set_dbus_connection: %s: %s\n",
error.name, error.message);
return 1;
}
if (!libhal_ctx_init (hal_ctx, &error)) {
if (dbus_error_is_set(&error)) {
fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message);
LIBHAL_FREE_DBUS_ERROR (&error);
}
fprintf (stderr, "Could not initialise connection to hald.\n"
"Normally this means the HAL daemon (hald) is not running or not ready.\n");
return 1;
}
libhal_ctx_set_device_added (hal_ctx, device_added);
libhal_ctx_set_device_removed (hal_ctx, device_removed);
libhal_ctx_set_device_new_capability (hal_ctx, device_new_capability);
libhal_ctx_set_device_lost_capability (hal_ctx, device_lost_capability);
libhal_ctx_set_device_property_modified (hal_ctx, property_modified);
libhal_ctx_set_device_condition (hal_ctx, device_condition);
if (show_device)
dump_device (show_device);
else if (!do_monitor)
dump_devices ();
/* run the main loop only if we should monitor */
if (do_monitor && loop != NULL) {
if( long_list || short_list || tree_view )
dump_devices ();
if ( libhal_device_property_watch_all (hal_ctx, &error) == FALSE) {
fprintf (stderr, "error: monitoring devicelist - libhal_device_property_watch_all: %s: %s\n",
error.name, error.message);
LIBHAL_FREE_DBUS_ERROR (&error);
return 1;
}
printf ("\nStart monitoring devicelist:\n"
"-------------------------------------------------\n");
g_main_loop_run (loop);
}
if ( libhal_ctx_shutdown (hal_ctx, &error) == FALSE)
LIBHAL_FREE_DBUS_ERROR (&error);
libhal_ctx_free (hal_ctx);
dbus_connection_unref (conn);
if (show_device)
free(show_device);
return 0;
}
/**
* @}
*/