hal/hald/linux2 Makefile.am, NONE, 1.1 blockdev.c, NONE, 1.1 blockdev.h, NONE, 1.1 classdev.c, NONE, 1.1 classdev.h, NONE, 1.1 coldplug.c, NONE, 1.1 coldplug.h, NONE, 1.1 hotplug.c, NONE, 1.1 hotplug.h, NONE, 1.1 hotplug_helper.h, NONE, 1.1 ids.c, NONE, 1.1 ids.h, NONE, 1.1 osspec.c, NONE, 1.1 physdev.c, NONE, 1.1 physdev.h, NONE, 1.1 util.c, NONE, 1.1 util.h, NONE, 1.1

David Zeuthen david at freedesktop.org
Tue Jan 18 11:48:15 PST 2005


Update of /cvs/hal/hal/hald/linux2
In directory gabe:/tmp/cvs-serv19239/hald/linux2

Added Files:
	Makefile.am blockdev.c blockdev.h classdev.c classdev.h 
	coldplug.c coldplug.h hotplug.c hotplug.h hotplug_helper.h 
	ids.c ids.h osspec.c physdev.c physdev.h util.c util.h 
Log Message:
2005-01-18  David Zeuthen  <david at fubar.dk>

	* tools/linux/Makefile.am: Remove hal.dev build rules

	* tools/linux/hal_dev.c: Remove

	* configure.in: Set linux2 as the default backend

	* hald/linux: Remove all files here as they will get reimplemented
	in hald/linux2

	* hald/linux2: Add a bunch of new files

	* hald/linux2/probing: Add some new files

2005-01-18  David Zeuthen  <davidz at redhat.com>

	Merge some more changes from the stable branch (except those
	in hald/linux and doc/spec)

	2005-01-12  David Zeuthen  <davidz at redhat.com>

	* hald/callout.c (callout_timeout_handler): Be tough and kill
	the misbehaving child the hard way - suggestion from Joe Shaw.

	2005-01-12  David Zeuthen  <davidz at redhat.com>

	* hald/linux/osspec.c (HOTPLUG_TIMEOUT): Increase to 25 seconds
	to better cope with callouts timeout of 10 seconds

	* hald/callout.c (iochn_data): Cope with callouts terminating
	and free timeout handler
	(callout_timeout_handler): New function; kill callouts if they
	time out
	(process_next_callout): Setup timeout for callouts - set to
	ten seconds

	2005-01-11  David Zeuthen  <davidz at redhat.com>

	* hald/callout.c: Simplify a lot more by demanding that callouts
	are run sequentially - which they are anyway since everything is
	serialized. Make a mental note to review and stress test this in
	the morning.

	2005-01-11  David Zeuthen  <davidz at redhat.com>

	* hald/callout.c: Fix some craziness adding an idle handler for
	detecting when callouts complete - fixes bug on my new AMD64
	system with device add/remove prior to completion of callouts -
	one visible effect was that fstab-sync was crashing since it
	couldn't retrieve the block.device device as the device was
	removed prior to the completion of the callout

	2005-01-07  David Zeuthen  <davidz at redhat.com>

	* fdi/20freedesktop/ide-drives.fdi: Also check IDE floppies for whether
	they are Zip drives

	2005-01-07  Joe Shaw  <joeshaw at novell.com>

	* configure.in: Check for popt when building fstab-sync and error
	out if it's not found.

	* tools/Makefile.am: Build fstab-sync conditionally based on
	whether --enable-fstab-sync is passed in.

	2005-01-06  David Zeuthen  <davidz at redhat.com>

	* libhal/libhal.c (hal_device_query_capability): Patch from Tim
	Müller <t.i.m at zen.co.uk>. The attached patch fixes a small memory
	leak in libhal's hal_device_query_capability().

	2005-01-03  David Zeuthen  <davidz at redhat.com>

	* configure.in: Added it to ALL_LINGUAS

	* po/it.po: Italien translation from Pier Luigi Fiorini
	<pierluigi.fiorini at mockup.org>

	2004-12-15  David Zeuthen  <davidz at redhat.com>

	* fdi/20freedesktop/usb-zip-drives.fdi: Only match on actual
	harddisks to avoid wrong detection of e.g. "Iomega ZipCD 650 USB CDRW"
	drives (Red Hat bug #143834)

	* fdi/20freedesktop/ide-drives.fdi: ditto



--- NEW FILE: Makefile.am ---

SUBDIRS = probing .

INCLUDES = \
	-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
	-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
	-DPACKAGE_LOCALSTATEDIR=\""$(localstatedir)"\" \
	-I$(top_srcdir) -I.. \
	@PACKAGE_CFLAGS@

noinst_LTLIBRARIES = libhald_linux2.la

libhald_linux2_la_SOURCES =					\
				osspec.c			\
	hotplug.h		hotplug.c			\
	coldplug.h		coldplug.c			\
	physdev.h		physdev.c			\
	classdev.h		classdev.c			\
	blockdev.h		blockdev.c			\
	util.h			util.c				\
	ids.h			ids.c







--- NEW FILE: blockdev.c ---
/***************************************************************************
 * CVSID: $Id: blockdev.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * blockdev.c : Handling of block devices 
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../osspec.h"
#include "../logger.h"
#include "../hald.h"
#include "../callout.h"
#include "../device_info.h"
#include "../hald_conf.h"

#include "util.h"
#include "coldplug.h"
#include "hotplug_helper.h"

#include "hotplug.h"
#include "blockdev.h"

void
hotplug_event_begin_add_blockdev (const gchar *sysfs_path, const char *device_file, gboolean is_partition, 
				  HalDevice *parent, void *end_token)
{
#if 0
	HalDevice *d;

	HAL_INFO (("block_add: sysfs_path=%s dev=%s is_part=%d parent=0x%08x", 
		 sysfs_path, device_file, is_partition, parent));

	d = hal_device_new ();
	hal_device_property_set_string (d, "linux.sysfs_path_device", sysfs_path);
	hal_device_property_set_string (d, "info.udi", sysfs_path);
	hal_device_property_set_string (d, "info.bus", "block");
	hal_device_set_udi (d, sysfs_path);
	
	if (parent != NULL) {
		hal_device_property_set_string (d, "info.parent", parent->udi);
	}
	
	hal_device_store_add (hald_get_gdl (), d);
#endif

	hotplug_event_end (end_token);
}

void
hotplug_event_begin_remove_blockdev (const gchar *sysfs_path, gboolean is_partition, void *end_token)
{
#if 0
	HalDevice *d;

	HAL_INFO (("block_rem: sysfs_path=%s is_part=%d", sysfs_path, is_partition));

	d = hal_device_store_match_key_value_string (hald_get_gdl (), 
						     "linux.sysfs_path_device", 
						     sysfs_path);
	if (d == NULL) {
		HAL_WARNING (("Couldn't remove device with sysfs path %s - not found", sysfs_path));
		goto out;
	}

	if (!hal_device_store_remove (hald_get_gdl (), d)) {
		HAL_WARNING (("Error removing device with sysfs path %s", sysfs_path));
	}

out:
#endif
	hotplug_event_end (end_token);
}

--- NEW FILE: blockdev.h ---
/***************************************************************************
 * CVSID: $Id: blockdev.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * blockdev.h : Handling of block devices 
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef BLOCKDEV_H
#define BLOCKDEV_H

#include <glib.h>

void hotplug_event_begin_add_blockdev (const gchar *sysfs_path, const char *device_file, gboolean is_partition, HalDevice *parent, void *end_token);

void hotplug_event_begin_remove_blockdev (const gchar *sysfs_path, gboolean is_partition, void *end_token);

#endif /* BLOCKDEV_H */

--- NEW FILE: classdev.c ---
/***************************************************************************
 * CVSID: $Id: classdev.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * classdev.c : Handling of functional kernel devices
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../osspec.h"
#include "../logger.h"
#include "../hald.h"
#include "../callout.h"
#include "../device_info.h"
#include "../hald_conf.h"

#include "util.h"
#include "coldplug.h"
#include "hotplug_helper.h"

#include "hotplug.h"
#include "classdev.h"


static void 
input_helper_done(HalDevice *d, gboolean timed_out, gint return_code, gpointer data1, gpointer data2)
{
	gchar udi[256];
	void *end_token = (void *) data1;

	HAL_INFO (("entering; timed_out=%d, return_code=%d", timed_out, return_code));

	/* Discard device if probing reports failure */
	if (return_code != 0) {
		hal_device_store_remove (hald_get_tdl (), d);
		goto out;
	}

	/* Merge properties from .fdi files */
	di_search_and_merge (d);
	
	/* TODO: Run callouts */
	
	/* Compute UDI */
	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
			      "%s_logicaldev_input",
			      hal_device_property_get_string (d, "info.parent"));
	hal_device_set_udi (d, udi);
	hal_device_property_set_string (d, "info.udi", udi);
	
	/* Move from temporary to global device store */
	hal_device_store_remove (hald_get_tdl (), d);
	hal_device_store_add (hald_get_gdl (), d);

	/* TODO: adjust capabilities on physdev */

out:
	hotplug_event_end (end_token);
}


static gboolean
input_add (const gchar *sysfs_path, const gchar *device_file, HalDevice *physdev, void *end_token)
{
	HalDevice *d;

	d = hal_device_new ();
	hal_device_property_set_string (d, "linux.sysfs_path_device", sysfs_path);
	if (physdev != NULL) {
		hal_device_property_set_string (d, "input.physical_device", physdev->udi);
		hal_device_property_set_string (d, "info.parent", physdev->udi);
	} else {
		hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/computer");
	}
	hal_device_property_set_string (d, "info.category", "input");
	hal_device_add_capability (d, "input");
	hal_device_property_set_string (d, "info.bus", "unknown");

	hal_device_property_set_string (d, "input.device", device_file);

	/* Add to temporary device store */
	hal_device_store_add (hald_get_tdl (), d);

	/* probe the actual device node */
	if (!helper_invoke ("hald-probe-input", d, (gpointer) end_token, NULL, input_helper_done, HAL_HELPER_TIMEOUT)) {
		hal_device_store_remove (hald_get_tdl (), d);
		goto out;
	}

	return TRUE;
out:
	hotplug_event_end (end_token);
	return TRUE;
}



void
hotplug_event_begin_add_classdev (const gchar *subsystem, const gchar *sysfs_path, const gchar *device_file, 
				  HalDevice *physdev, void *end_token)
{
	HAL_INFO (("class_add: subsys=%s sysfs_path=%s dev=%s physdev=0x%08x", subsystem, sysfs_path, device_file, physdev));

	if (strcmp (subsystem, "input") == 0)
		input_add (sysfs_path, device_file, physdev, end_token);
	else
		hotplug_event_end (end_token);
}

void
hotplug_event_begin_remove_classdev (const gchar *subsystem, const gchar *sysfs_path, void *end_token)
{
	HalDevice *d;

	HAL_INFO (("class_rem: subsys=%s sysfs_path=%s", subsystem, sysfs_path));

	d = hal_device_store_match_key_value_string (hald_get_gdl (), "linux.sysfs_path_device", sysfs_path);
	if (d == NULL) {
		HAL_WARNING (("Error removing device"));
	} else {
		const gchar *physdev_udi;
		HalDevice *physdev;

		physdev_udi = hal_device_property_get_string (d, "info.physical_device");

		if (!hal_device_store_remove (hald_get_gdl (), d)) {
			HAL_WARNING (("Error removing device"));
		}

		if (physdev_udi != NULL) {
			physdev = hal_device_store_find (hald_get_gdl (), physdev_udi);
			/* TODO: adjust capabilities on physdev */
		}

	}

	hotplug_event_end (end_token);
}

--- NEW FILE: classdev.h ---
/***************************************************************************
 * CVSID: $Id: classdev.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * classdev.h : Handling of functional kernel devices
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef CLASSDEV_H
#define CLASSDEV_H

#include <glib.h>

void hotplug_event_begin_add_classdev (const gchar *subsystem, const gchar *sysfs_path, const gchar *device_file, HalDevice *physdev, void *end_token);

void hotplug_event_begin_remove_classdev (const gchar *subsystem, const gchar *sysfs_path, void *end_token);


#endif /* CLASSDEV_H */

--- NEW FILE: coldplug.c ---
/***************************************************************************
 * CVSID: $Id: coldplug.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * coldplug.c : Synthesize hotplug events when starting up
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../osspec.h"
#include "../logger.h"
#include "../hald.h"
#include "../callout.h"
#include "../device_info.h"
#include "../hald_conf.h"

#include "util.h"
#include "coldplug.h"
#include "hotplug.h"


static void 
coldplug_compute_visit_device (const gchar *path, GHashTable *sysfs_to_bus_map);

/*#define HAL_COLDPLUG_VERBOSE*/

/** This function serves one major purpose : build an ordered list of
 *  pairs (sysfs path, subsystem) to process when starting up:
 *  coldplugging. The ordering is arranged such that all bus-devices
 *  are visited in the same order as performing a traversal through
 *  the tree; e.g. bus-device A is not processed before bus-device B
 *  if B is a parent of A connection-wise.
 *
 *  After all bus-devices are added to the list, then all block are
 *  processed in the order they appear.
 *
 *  Finally, all class devices are added to the list.
 *
 *  @return                     Ordered list of sysfs paths or NULL 
 *                              if there was an error
 */
gboolean 
coldplug_synthesize_events (void)
{
	GDir *dir;
	GError *err = NULL;
	gchar path[HAL_PATH_MAX];
	gchar path1[HAL_PATH_MAX];
	const gchar *f;
	const gchar *f1;
	const gchar *f2;

	/** Mapping from sysfs path to subsystem for bus devices. This is consulted
	 *  when traversing /sys/devices
	 *
	 *  Example:
	 *
	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1:1.0/host7/7:0:0:0  -> scsi
	 * /sys/devices/pci0000:00/0000:00:07.1/ide1/1.1                        -> ide
	 * /sys/devices/pci0000:00/0000:00:07.1/ide1/1.0                        -> ide
	 * /sys/devices/pci0000:00/0000:00:07.1/ide0/0.0                        -> ide
	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1:1.0                -> usb
	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1                        -> usb
	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-0:1.0                    -> usb
	 * /sys/devices/pci0000:00/0000:00:07.2/usb1                            -> usb
	 * /sys/devices/pci0000:00/0000:00:04.1/0000:06:00.0                    -> pci
	 * /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0                    -> pci
	 * /sys/devices/pci0000:00/0000:00:08.0                                 -> pci
	 * /sys/devices/platform/vesafb0                                        -> platform
	 */
	GHashTable *sysfs_to_bus_map = NULL;

	/* build bus map */
	sysfs_to_bus_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
	g_snprintf (path, HAL_PATH_MAX, "%s/bus", hal_sysfs_path);
	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
		HAL_ERROR (("Unable to open %/bus: %s", hal_sysfs_path, err->message));
		g_error_free (err);
		goto error;
	}
	while ((f = g_dir_read_name (dir)) != NULL) {
		GDir *dir1;

		g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s", hal_sysfs_path, f);
		if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
			HAL_ERROR (("Unable to open %/bus/%s: %s", hal_sysfs_path, f, err->message));
			g_error_free (err);
			goto error;
		}
		while ((f1 = g_dir_read_name (dir1)) != NULL) {

			if (strcmp (f1, "devices") == 0) {
				GDir *dir2;

				g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s/%s", 
					    hal_sysfs_path, f, f1);
				if ((dir2 = g_dir_open (path, 0, &err)) == NULL) {
					HAL_ERROR (("Unable to open %s/bus/%s/%s: %s", 
						    hal_sysfs_path, f, f1, err->message));
					g_error_free (err);
					goto error;
				}
				while ((f2 = g_dir_read_name (dir2)) != NULL) {
					gchar *target;
					gchar *normalized_target;
					g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s/%s/%s", 
						    hal_sysfs_path, f, f1, f2);
					if ((target = g_file_read_link (path, &err)) == NULL) {
						HAL_ERROR (("%s/bus/%s/%s/%s is not a symlink: %s!", 
							    hal_sysfs_path, 
							    f, f1, f2, err->message));
						g_error_free (err);
						goto error;
					}

					g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s/%s", hal_sysfs_path, f, f1);
					normalized_target = hal_util_get_normalized_path (path, target);
					g_free (target);

					g_hash_table_insert (sysfs_to_bus_map, normalized_target, g_strdup(f));

				}
				g_dir_close (dir2);
			}
		}
		g_dir_close (dir1);
	}
	g_dir_close (dir);

	/* Now traverse /sys/devices and consult the map we've just
	 * built; this includes adding a) bus devices; and b) class
	 * devices that sit in /sys/devices */
	g_snprintf (path, HAL_PATH_MAX, "%s/devices", hal_sysfs_path);
	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
		HAL_ERROR (("Unable to open %/devices: %s", hal_sysfs_path, err->message));
		g_error_free (err);
		goto error;
	}
	while ((f = g_dir_read_name (dir)) != NULL) {
		GDir *dir1;

		g_snprintf (path, HAL_PATH_MAX, "%s/devices/%s", hal_sysfs_path, f);
		if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
			HAL_ERROR (("Unable to open %/devices/%s: %s", hal_sysfs_path, f, err->message));
			g_error_free (err);
			goto error;
		}
		while ((f1 = g_dir_read_name (dir1)) != NULL) {

			g_snprintf (path, HAL_PATH_MAX, "%s/devices/%s/%s", hal_sysfs_path, f, f1);
			coldplug_compute_visit_device (path, sysfs_to_bus_map);

		}
		g_dir_close (dir1);
	}
	g_dir_close (dir);

	g_hash_table_destroy (sysfs_to_bus_map);


	/* add class devices */
	g_snprintf (path, HAL_PATH_MAX, "%s/class", hal_sysfs_path);
	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
		HAL_ERROR (("Unable to open %/class: %s", hal_sysfs_path, err->message));
		goto error;
	}
	while ((f = g_dir_read_name (dir)) != NULL) {
		GDir *dir1;

		g_snprintf (path, HAL_PATH_MAX, "%s/class/%s", hal_sysfs_path, f);
		if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
			HAL_ERROR (("Unable to open %/class/%s: %s", hal_sysfs_path, f, err->message));
			g_error_free (err);
			goto error;
		}
		while ((f1 = g_dir_read_name (dir1)) != NULL) {
			HotplugEvent *hotplug_event;
			gchar *target;
			gchar *normalized_target;

			g_snprintf (path, HAL_PATH_MAX, "%s/class/%s/%s", hal_sysfs_path, f, f1);
#ifdef HAL_COLDPLUG_VERBOSE
			printf ("class: %s (%s)\n", path, f);
#endif

			g_snprintf (path1, HAL_PATH_MAX, "%s/class/%s/%s/device", hal_sysfs_path, f, f1);
			if (((target = g_file_read_link (path1, NULL)) != NULL)) {
				normalized_target = hal_util_get_normalized_path (path1, target);
				g_free (target);
			} else {
				normalized_target = NULL;
			}


			hotplug_event = g_new0 (HotplugEvent, 1);
			hotplug_event->is_add = TRUE;
			g_strlcpy (hotplug_event->subsystem, f, sizeof (hotplug_event->subsystem));
			g_strlcpy (hotplug_event->sysfs_path, path, sizeof (hotplug_event->sysfs_path));
			hal_util_get_device_file (path, hotplug_event->device_file, sizeof (hotplug_event->device_file));
			if (normalized_target != NULL)
				g_strlcpy (hotplug_event->wait_for_sysfs_path, normalized_target, sizeof (hotplug_event->wait_for_sysfs_path));
			else
				hotplug_event->wait_for_sysfs_path[0] = '\0';
			hotplug_event->net_ifindex = -1;

			hotplug_event_enqueue (hotplug_event);

			g_free (normalized_target);

		}
		g_dir_close (dir1);
	}
	g_dir_close (dir);

	/* add block devices */
	g_snprintf (path, HAL_PATH_MAX, "%s/block", hal_sysfs_path);
	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
		HAL_ERROR (("Unable to open %s: %s", path, err->message));
		g_error_free (err);
		goto error;
	}
	while ((f = g_dir_read_name (dir)) != NULL) {
		GDir *dir1;
		gsize flen;
		HotplugEvent *hotplug_event;
		gchar *target;
		gchar *normalized_target;

		g_snprintf (path, HAL_PATH_MAX, "%s/block/%s", hal_sysfs_path, f);
#ifdef HAL_COLDPLUG_VERBOSE
		printf ("block: %s (block)\n",  path);
#endif

		g_snprintf (path1, HAL_PATH_MAX, "%s/block/%s/device", hal_sysfs_path, f);
		if (((target = g_file_read_link (path1, NULL)) != NULL)) {
			normalized_target = hal_util_get_normalized_path (path1, target);
			g_free (target);
		} else {
			normalized_target = NULL;
		}

		hotplug_event = g_new0 (HotplugEvent, 1);
		hotplug_event->is_add = TRUE;
		g_strlcpy (hotplug_event->subsystem, "block", sizeof (hotplug_event->subsystem));
		g_strlcpy (hotplug_event->sysfs_path, path, sizeof (hotplug_event->sysfs_path));
		hal_util_get_device_file (path, hotplug_event->device_file, sizeof (hotplug_event->device_file));
		if (normalized_target != NULL)
			g_strlcpy (hotplug_event->wait_for_sysfs_path, normalized_target, sizeof (hotplug_event->wait_for_sysfs_path));
		else
			hotplug_event->wait_for_sysfs_path[0] = '\0';
		hotplug_event->net_ifindex = -1;
		hotplug_event_enqueue (hotplug_event);
		g_free (normalized_target);

		flen = strlen (f);

		if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
			HAL_ERROR (("Unable to open %s: %s", path, err->message));
			g_error_free (err);
			goto error;
		}
		while ((f1 = g_dir_read_name (dir1)) != NULL) {
			if (strncmp (f, f1, flen) == 0) {
				g_snprintf (path1, HAL_PATH_MAX, "%s/%s", path, f1);
#ifdef HAL_COLDPLUG_VERBOSE
				printf ("block: %s (block)\n", path1);
#endif

				hotplug_event = g_new0 (HotplugEvent, 1);
				hotplug_event->is_add = TRUE;
				g_strlcpy (hotplug_event->subsystem, "block", sizeof (hotplug_event->subsystem));
				g_strlcpy (hotplug_event->sysfs_path, path1, sizeof (hotplug_event->sysfs_path));
				g_strlcpy (hotplug_event->wait_for_sysfs_path, path, sizeof (hotplug_event->wait_for_sysfs_path));
				hal_util_get_device_file (path1, hotplug_event->device_file, sizeof (hotplug_event->device_file));
				hotplug_event->net_ifindex = -1;
				hotplug_event_enqueue (hotplug_event);
			}
		}
		g_dir_close (dir1);		
	}
	g_dir_close (dir);

	return TRUE;
error:
	HAL_ERROR (("Error building the orderered list of sysfs paths"));
	return FALSE;
}

static void
coldplug_compute_visit_device (const gchar *path, GHashTable *sysfs_to_bus_map)
{
	gchar *bus;
	GError *err;
	GDir *dir;
	const gchar *f;

	bus = g_hash_table_lookup (sysfs_to_bus_map, path);
	if (bus != NULL) {
		HotplugEvent *hotplug_event;
		gchar *parent_sysfs_path;

#ifdef HAL_COLDPLUG_VERBOSE
		printf ("bus:   %s (%s)\n", path, bus);
#endif

		hotplug_event = g_new0 (HotplugEvent, 1);
		hotplug_event->is_add = TRUE;
		g_strlcpy (hotplug_event->subsystem, bus, sizeof (hotplug_event->subsystem));
		g_strlcpy (hotplug_event->sysfs_path, path, sizeof (hotplug_event->sysfs_path));
		hotplug_event->net_ifindex = -1;

		parent_sysfs_path = hal_util_get_parent_sysfs_path (path);
		g_strlcpy (hotplug_event->wait_for_sysfs_path, parent_sysfs_path, sizeof (hotplug_event->wait_for_sysfs_path));
		g_free (parent_sysfs_path);

		hotplug_event->device_file[0] = '\0';

		hotplug_event_enqueue (hotplug_event);
	}

	/* visit children; dont follow symlinks though.. */
	err = NULL;
	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
		/*HAL_ERROR (("Unable to open directory: %s", path, err->message));*/
		g_error_free (err);
		goto error;
	}
	while ((f = g_dir_read_name (dir)) != NULL) {
		gchar path_child[HAL_PATH_MAX];
		struct stat statbuf;
	
		g_snprintf (path_child, HAL_PATH_MAX, "%s/%s", path, f);

		if (lstat (path_child, &statbuf) == 0) {

			if (!S_ISLNK (statbuf.st_mode)) {
				/* recursion fun */
				coldplug_compute_visit_device (path_child, sysfs_to_bus_map);
			}
		}
	}
	g_dir_close (dir);

error:
	return;
}


--- NEW FILE: coldplug.h ---
/***************************************************************************
 * CVSID: $Id: coldplug.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * coldplug.h : Synthesize hotplug events when starting up
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef COLDPLUG_H
#define COLDPLUG_H

#include <glib.h>

gboolean coldplug_synthesize_events (void);

#endif /* COLDPLUG_H */


--- NEW FILE: hotplug.c ---
/***************************************************************************
 * CVSID: $Id: hotplug.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * hotplug.c : Handling of hotplug events
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../osspec.h"
#include "../logger.h"
#include "../hald.h"
#include "../callout.h"
#include "../device_info.h"
#include "../hald_conf.h"

#include "hotplug.h"
#include "physdev.h"
#include "classdev.h"
#include "blockdev.h"


/** Queue of ordered hotplug events */
GQueue *hotplug_event_queue;

/** List of HotplugEvent objects we are currently processing */
GSList *hotplug_events_in_progress = NULL;

void 
hotplug_event_end (void *end_token)
{
	HotplugEvent *hotplug_event = (HotplugEvent *) end_token;

	hotplug_events_in_progress = g_slist_remove (hotplug_events_in_progress, hotplug_event);
	g_free (hotplug_event);
	hotplug_event_process_queue ();
}

static void
hotplug_event_begin (HotplugEvent *hotplug_event)
{
	static char sys_devices_path[HAL_PATH_MAX];
	static char sys_class_path[HAL_PATH_MAX];
	static char sys_block_path[HAL_PATH_MAX];
	static gsize sys_devices_path_len = 0;
	static gsize sys_class_path_len = 0;
	static gsize sys_block_path_len = 0;

	if (sys_block_path_len == 0) {
		sys_devices_path_len = g_snprintf (sys_devices_path, HAL_PATH_MAX, "%s/devices", hal_sysfs_path);
		sys_class_path_len   = g_snprintf (sys_class_path, HAL_PATH_MAX, "%s/class", hal_sysfs_path);
		sys_block_path_len   = g_snprintf (sys_block_path, HAL_PATH_MAX, "%s/block", hal_sysfs_path);
	}


	if (strncmp (hotplug_event->sysfs_path, sys_devices_path, sys_devices_path_len) == 0) {		
		if (hotplug_event->is_add) {
			HalDevice *parent;
			parent = hal_util_find_closest_ancestor (hotplug_event->sysfs_path);
			hotplug_event_begin_add_physdev (hotplug_event->subsystem, 
							 hotplug_event->sysfs_path, 
							 parent,
							 (void *) hotplug_event);
		} else {
			hotplug_event_begin_remove_physdev (hotplug_event->subsystem, 
							    hotplug_event->sysfs_path, 
							    (void *) hotplug_event);
		}
	} else if (strncmp (hotplug_event->sysfs_path, sys_class_path, sys_class_path_len) == 0) {
		if (hotplug_event->is_add) {
			gchar *target;
			HalDevice *physdev;
			char physdevpath[256];

			/* TODO: fixup net devices by looking at ifindex */
			
			g_snprintf (physdevpath, HAL_PATH_MAX, "%s/device", hotplug_event->sysfs_path);
			if (((target = g_file_read_link (physdevpath, NULL)) != NULL)) {
				gchar *normalized_target;

				normalized_target = hal_util_get_normalized_path (hotplug_event->sysfs_path, target);
				g_free (target);

				physdev = hal_device_store_match_key_value_string (hald_get_gdl (), 
										   "linux.sysfs_path_device", 
										   normalized_target);
				g_free (normalized_target);
			} else {
				physdev = NULL;
			}

			hotplug_event_begin_add_classdev (hotplug_event->subsystem,
							  hotplug_event->sysfs_path,
							  hotplug_event->device_file,
							  physdev,
							  (void *) hotplug_event);
		} else {
			hotplug_event_begin_remove_classdev (hotplug_event->subsystem,
							     hotplug_event->sysfs_path,
							     (void *) hotplug_event);
		}
	} else if (strncmp (hotplug_event->sysfs_path, sys_block_path, sys_block_path_len) == 0) {
		gchar *parent_path;
		gboolean is_partition;
		
		parent_path = hal_util_get_parent_sysfs_path (hotplug_event->sysfs_path);
		is_partition = (strcmp (parent_path, sys_block_path) != 0);
		
		if (hotplug_event->is_add) {
			HalDevice *parent;

			if (is_partition) {
				parent = hal_device_store_match_key_value_string (hald_get_gdl (), 
										  "linux.sysfs_path_device", 
										  parent_path);
			} else {
				gchar *target;
				char physdevpath[256];
				
				g_snprintf (physdevpath, HAL_PATH_MAX, "%s/device", hotplug_event->sysfs_path);
				if (((target = g_file_read_link (physdevpath, NULL)) != NULL)) {
					gchar *normalized_target;

					normalized_target = hal_util_get_normalized_path (hotplug_event->sysfs_path, target);
					g_free (target);
					parent = hal_device_store_match_key_value_string (hald_get_gdl (), 
											  "linux.sysfs_path_device", 
											  normalized_target);
					g_free (normalized_target);
				} else {
					parent = NULL;
				}
			}
			
			hotplug_event_begin_add_blockdev (hotplug_event->sysfs_path,
							  hotplug_event->device_file,
							  is_partition,
							  parent,
							  (void *) hotplug_event);
		} else {
			hotplug_event_begin_remove_blockdev (hotplug_event->sysfs_path,
							     is_partition,
							     (void *) hotplug_event);
		}
	} else {
		/* just ignore this hotplug event */
		hotplug_event_end ((void *) hotplug_event);
	}
}

void 
hotplug_event_enqueue (HotplugEvent *hotplug_event)
{
	if (hotplug_event_queue == NULL)
		hotplug_event_queue = g_queue_new ();

	g_queue_push_tail (hotplug_event_queue, hotplug_event);
}

void 
hotplug_event_process_queue (void)
{
	HotplugEvent *hotplug_event;


	if (hotplug_event_queue == NULL)
		goto out;

	/* do not process events if some other event is in progress 
	 *
	 * TODO: optimize so we can do add events in parallel by inspecting the
	 *       wait_for_sysfs_path parameter and hotplug_events_in_progress list
	 */
	if (hotplug_events_in_progress != NULL && g_slist_length (hotplug_events_in_progress) > 0)
		goto out;

	hotplug_event = g_queue_pop_head (hotplug_event_queue);
	if (hotplug_event == NULL)
		goto out;

	hotplug_events_in_progress = g_slist_append (hotplug_events_in_progress, hotplug_event);
	hotplug_event_begin (hotplug_event);

out:
	;	
}

--- NEW FILE: hotplug.h ---
/***************************************************************************
 * CVSID: $Id: hotplug.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * hotplug.h : Handling of hotplug events
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef HOTPLUG_H
#define HOTPLUG_H

#include <glib.h>

#include "util.h"

/** Data structure representing a hotplug event; also used for
 *  coldplugging.
 */
typedef struct
{
	gboolean is_add;                        /**< Whether the event is add or remove */
	char subsystem[HAL_PATH_MAX];           /**< Subsystem e.g. usb, pci (only for hotplug msg) */
	char sysfs_path[HAL_PATH_MAX];          /**< Path into sysfs e.g. /sys/block/sda */

	char wait_for_sysfs_path[HAL_PATH_MAX];	/**< Wait for completion of events that a) comes before this one; AND
						 *   b) has a sysfs path that is contained in or equals this */

	char device_file [HAL_PATH_MAX];        /**< Path to special device (may be NULL) */

	int net_ifindex;                        /**< For network class devices only; the value of the ifindex file */
} HotplugEvent;

void hotplug_event_enqueue (HotplugEvent *event);

void hotplug_event_process_queue (void);

void hotplug_event_end (void *end_token);

#endif /* HOTPLUG_H */

--- NEW FILE: hotplug_helper.h ---
/***************************************************************************
 * CVSID: $Id: hotplug_helper.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * HAL daemon hotplug.d and dev.d helper details
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef HALD_HELPER_H
#define HALD_HELPER_H

#define HALD_HELPER_MAGIC 0x68616c64
#define HALD_HELPER_SOCKET_PATH "/var/run/hal/hotplug_socket2"
#define HALD_HELPER_STRLEN 256

struct hald_helper_msg
{
	unsigned int magic;			/**< magic */
	unsigned long long seqnum;		/**< Sequence number (may be 0 if for dev if udev has no support) */
	char action[HALD_HELPER_STRLEN];	/**< hotplug action */
	char subsystem[HALD_HELPER_STRLEN];	/**< subsystem e.g. usb, pci (only for hotplug msg) */
	char sysfs_path[HALD_HELPER_STRLEN];	/**< path into sysfs without sysfs mountpoint, e.g. /block/sda */
	char device_name[HALD_HELPER_STRLEN];	/**< absolute path of device node (only for device msg) */
	int net_ifindex;                        /**< For networking class devices only; the value of the ifindex file*/
	time_t time_stamp;                      /**< Time of day we received the hotplug event */
};

#endif /* HALD_HELPER_H */

--- NEW FILE: ids.c ---
/***************************************************************************
 * CVSID: $Id: ids.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * classdev.c : Handling of functional kernel devices
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../logger.h"

#include "ids.h"

/** Pointer to where the pci.ids file is loaded */
static char *pci_ids = NULL;

/** Length of data store at at pci_ids */
static unsigned int pci_ids_len;

/** Iterator position into pci_ids */
static unsigned int pci_ids_iter_pos;

/** Initialize the pci.ids line iterator to the beginning of the file */
static void
pci_ids_line_iter_init ()
{
	pci_ids_iter_pos = 0;
}

/** Maximum length of lines in pci.ids */
#define PCI_IDS_MAX_LINE_LEN 512

/** Get the next line from pci.ids
 *
 *  @param  line_len            Pointer to where number of bytes in line will
 *                              be stored
 *  @return                     Pointer to the line; only valid until the
 *                              next invocation of this function
 */
static char *
pci_ids_line_iter_get_line (unsigned int *line_len)
{
	unsigned int i;
	static char line[PCI_IDS_MAX_LINE_LEN];

	for (i = 0;
	     pci_ids_iter_pos < pci_ids_len &&
	     i < PCI_IDS_MAX_LINE_LEN - 1 &&
	     pci_ids[pci_ids_iter_pos] != '\n'; i++, pci_ids_iter_pos++) {
		line[i] = pci_ids[pci_ids_iter_pos];
	}

	line[i] = '\0';
	if (line_len != NULL)
		*line_len = i;

	pci_ids_iter_pos++;

	return line;
}

/** See if there are more lines to process in pci.ids
 *
 *  @return                     #TRUE iff there are more lines to process
 */
static dbus_bool_t
pci_ids_line_iter_has_more ()
{
	return pci_ids_iter_pos < pci_ids_len;
}


/** Find the names for a PCI device.
 *
 *  The pointers returned are only valid until the next invocation of this
 *  function.
 *
 *  @param  vendor_id           PCI vendor id or 0 if unknown
 *  @param  product_id          PCI product id or 0 if unknown
 *  @param  subsys_vendor_id    PCI subsystem vendor id or 0 if unknown
 *  @param  subsys_product_id   PCI subsystem product id or 0 if unknown
 *  @param  vendor_name         Set to pointer of result or #NULL
 *  @param  product_name        Set to pointer of result or #NULL
 *  @param  subsys_vendor_name  Set to pointer of result or #NULL
 *  @param  subsys_product_name Set to pointer of result or #NULL
 */
void
ids_find_pci (int vendor_id, int product_id,
	      int subsys_vendor_id, int subsys_product_id,
	      char **vendor_name, char **product_name,
	      char **subsys_vendor_name, char **subsys_product_name)
{
	char *line;
	unsigned int i;
	unsigned int line_len;
	unsigned int num_tabs;
	char rep_vi[8];
	char rep_pi[8];
	char rep_svi[8];
	char rep_spi[8];
	dbus_bool_t vendor_matched = FALSE;
	dbus_bool_t product_matched = FALSE;
	static char store_vn[PCI_IDS_MAX_LINE_LEN];
	static char store_pn[PCI_IDS_MAX_LINE_LEN];
	static char store_svn[PCI_IDS_MAX_LINE_LEN];
	static char store_spn[PCI_IDS_MAX_LINE_LEN];

	snprintf (rep_vi, 8, "%04x", vendor_id);
	snprintf (rep_pi, 8, "%04x", product_id);
	snprintf (rep_svi, 8, "%04x", subsys_vendor_id);
	snprintf (rep_spi, 8, "%04x", subsys_product_id);

	*vendor_name = NULL;
	*product_name = NULL;
	*subsys_vendor_name = NULL;
	*subsys_product_name = NULL;

	for (pci_ids_line_iter_init (); pci_ids_line_iter_has_more ();) {
		line = pci_ids_line_iter_get_line (&line_len);

		/* skip lines with no content */
		if (line_len < 4)
			continue;

		/* skip comments */
		if (line[0] == '#')
			continue;

		/* count number of tabs */
		num_tabs = 0;
		for (i = 0; i < line_len; i++) {
			if (line[i] != '\t')
				break;
			num_tabs++;
		}

		switch (num_tabs) {
		case 0:
			/* vendor names */
			vendor_matched = FALSE;

			/* first check subsys_vendor_id, if haven't done 
			 * already */
			if (*subsys_vendor_name == NULL
			    && subsys_vendor_id != 0) {
				if ((*((dbus_uint32_t *) line)) ==
				    (*((dbus_uint32_t *) rep_svi))) {
					/* found it */
					for (i = 4; i < line_len; i++) {
						if (!isspace (line[i]))
							break;
					}
					strncpy (store_svn, line + i,
						 PCI_IDS_MAX_LINE_LEN);
					*subsys_vendor_name = store_svn;
				}
			}

			/* check vendor_id */
			if (vendor_id != 0) {
				if (memcmp (line, rep_vi, 4) == 0) {
					/* found it */
					vendor_matched = TRUE;

					for (i = 4; i < line_len; i++) {
						if (!isspace (line[i]))
							break;
					}
					strncpy (store_vn, line + i,
						 PCI_IDS_MAX_LINE_LEN);
					*vendor_name = store_vn;
				}
			}

			break;

		case 1:
			product_matched = FALSE;

			/* product names */
			if (!vendor_matched)
				continue;

			/* check product_id */
			if (product_id != 0) {
				if (memcmp (line + 1, rep_pi, 4) == 0) {
					/* found it */

					product_matched = TRUE;

					for (i = 5; i < line_len; i++) {
						if (!isspace (line[i]))
							break;
					}
					strncpy (store_pn, line + i,
						 PCI_IDS_MAX_LINE_LEN);
					*product_name = store_pn;
				}
			}
			break;

		case 2:
			/* subsystem_vendor subsystem_product */
			if (!vendor_matched || !product_matched)
				continue;

			/* check product_id */
			if (subsys_vendor_id != 0
			    && subsys_product_id != 0) {
				if (memcmp (line + 2, rep_svi, 4) == 0
				    && memcmp (line + 7, rep_spi,
					       4) == 0) {
					/* found it */
					for (i = 11; i < line_len; i++) {
						if (!isspace (line[i]))
							break;
					}
					strncpy (store_spn, line + i,
						 PCI_IDS_MAX_LINE_LEN);
					*subsys_product_name = store_spn;
				}
			}

			break;

		default:
			break;
		}

	}
}

/** Load the PCI database used for mapping vendor, product, subsys_vendor
 *  and subsys_product numbers into names.
 *
 *  @param  path                Path of the pci.ids file, e.g. 
 *                              /usr/share/hwdata/pci.ids
 *  @return                     #TRUE if the file was succesfully loaded
 */
static dbus_bool_t
pci_ids_load (const char *path)
{
	FILE *fp;
	unsigned int num_read;

	fp = fopen (path, "r");
	if (fp == NULL) {
		HAL_ERROR (("couldn't open PCI database at %s,", path));
		return FALSE;
	}

	fseek (fp, 0, SEEK_END);
	pci_ids_len = ftell (fp);
	fseek (fp, 0, SEEK_SET);

	pci_ids = malloc (pci_ids_len);
	if (pci_ids == NULL) {
		DIE (("Couldn't allocate %d bytes for PCI database file\n",
		      pci_ids_len));
	}

	num_read = fread (pci_ids, sizeof (char), pci_ids_len, fp);
	if (pci_ids_len != num_read) {
		HAL_ERROR (("Error loading PCI database file"));
		free (pci_ids);
		pci_ids = NULL;
		fclose(fp);
		return FALSE;
	}

	fclose(fp);
	return TRUE;
}

/** Free resources used by to store the PCI database
 *
 *  @param                      #FALSE if the PCI database wasn't loaded
 */
static dbus_bool_t
pci_ids_free ()
{
	if (pci_ids != NULL) {
		free (pci_ids);
		pci_ids = NULL;
		return TRUE;
	}
	return FALSE;
}





/*==========================================================================*/

/** Pointer to where the usb.ids file is loaded */
static char *usb_ids = NULL;

/** Length of data store at at usb_ids */
static unsigned int usb_ids_len;

/** Iterator position into usb_ids */
static unsigned int usb_ids_iter_pos;

/** Initialize the usb.ids line iterator to the beginning of the file */
static void
usb_ids_line_iter_init ()
{
	usb_ids_iter_pos = 0;
}

/** Maximum length of lines in usb.ids */
#define USB_IDS_MAX_LINE_LEN 512

/** Get the next line from usb.ids
 *
 *  @param  line_len            Pointer to where number of bytes in line will
 *                              be stored
 *  @return                     Pointer to the line; only valid until the
 *                              next invocation of this function
 */
static char *
usb_ids_line_iter_get_line (unsigned int *line_len)
{
	unsigned int i;
	static char line[USB_IDS_MAX_LINE_LEN];

	for (i = 0;
	     usb_ids_iter_pos < usb_ids_len &&
	     i < USB_IDS_MAX_LINE_LEN - 1 &&
	     usb_ids[usb_ids_iter_pos] != '\n'; i++, usb_ids_iter_pos++) {
		line[i] = usb_ids[usb_ids_iter_pos];
	}

	line[i] = '\0';
	if (line_len != NULL)
		*line_len = i;

	usb_ids_iter_pos++;

	return line;
}

/** See if there are more lines to process in usb.ids
 *
 *  @return                     #TRUE iff there are more lines to process
 */
static dbus_bool_t
usb_ids_line_iter_has_more ()
{
	return usb_ids_iter_pos < usb_ids_len;
}

/** Find the names for a USB device.
 *
 *  The pointers returned are only valid until the next invocation of this
 *  function.
 *
 *  @param  vendor_id           USB vendor id or 0 if unknown
 *  @param  product_id          USB product id or 0 if unknown
 *  @param  vendor_name         Set to pointer of result or #NULL
 *  @param  product_name        Set to pointer of result or #NULL
 */
void
ids_find_usb (int vendor_id, int product_id,
	      char **vendor_name, char **product_name)
{
	char *line;
	unsigned int i;
	unsigned int line_len;
	unsigned int num_tabs;
	char rep_vi[8];
	char rep_pi[8];
	static char store_vn[USB_IDS_MAX_LINE_LEN];
	static char store_pn[USB_IDS_MAX_LINE_LEN];
	dbus_bool_t vendor_matched = FALSE;

	snprintf (rep_vi, 8, "%04x", vendor_id);
	snprintf (rep_pi, 8, "%04x", product_id);

	*vendor_name = NULL;
	*product_name = NULL;

	for (usb_ids_line_iter_init (); usb_ids_line_iter_has_more ();) {
		line = usb_ids_line_iter_get_line (&line_len);

		/* skip lines with no content */
		if (line_len < 4)
			continue;

		/* skip comments */
		if (line[0] == '#')
			continue;

		/* count number of tabs */
		num_tabs = 0;
		for (i = 0; i < line_len; i++) {
			if (line[i] != '\t')
				break;
			num_tabs++;
		}

		switch (num_tabs) {
		case 0:
			/* vendor names */
			vendor_matched = FALSE;

			/* check vendor_id */
			if (vendor_id != 0) {
				if (memcmp (line, rep_vi, 4) == 0) {
					/* found it */
					vendor_matched = TRUE;

					for (i = 4; i < line_len; i++) {
						if (!isspace (line[i]))
							break;
					}
					strncpy (store_vn, line + i,
						 USB_IDS_MAX_LINE_LEN);
					*vendor_name = store_vn;
				}
			}
			break;

		case 1:
			/* product names */
			if (!vendor_matched)
				continue;

			/* check product_id */
			if (product_id != 0) {
				if (memcmp (line + 1, rep_pi, 4) == 0) {
					/* found it */
					for (i = 5; i < line_len; i++) {
						if (!isspace (line[i]))
							break;
					}
					strncpy (store_pn, line + i,
						 USB_IDS_MAX_LINE_LEN);
					*product_name = store_pn;

					/* no need to continue the search */
					return;
				}
			}
			break;

		default:
			break;
		}

	}
}

/** Load the USB database used for mapping vendor, product, subsys_vendor
 *  and subsys_product numbers into names.
 *
 *  @param  path                Path of the usb.ids file, e.g. 
 *                              /usr/share/hwdata/usb.ids
 *  @return                     #TRUE if the file was succesfully loaded
 */
static dbus_bool_t
usb_ids_load (const char *path)
{
	FILE *fp;
	unsigned int num_read;

	fp = fopen (path, "r");
	if (fp == NULL) {
		printf ("couldn't open USB database at %s,", path);
		return FALSE;
	}

	fseek (fp, 0, SEEK_END);
	usb_ids_len = ftell (fp);
	fseek (fp, 0, SEEK_SET);

	usb_ids = malloc (usb_ids_len);
	if (usb_ids == NULL) {
		printf
		    ("Couldn't allocate %d bytes for USB database file\n",
		     usb_ids_len);
		fclose(fp);
		return FALSE;
	}

	num_read = fread (usb_ids, sizeof (char), usb_ids_len, fp);
	if (usb_ids_len != num_read) {
		printf ("Error loading USB database file\n");
		free (usb_ids);
		usb_ids = NULL;
		fclose(fp);
		return FALSE;
	}

	fclose(fp);
	return TRUE;
}

/** Free resources used by to store the USB database
 *
 *  @param                      #FALSE if the USB database wasn't loaded
 */
static dbus_bool_t
usb_ids_free ()
{
	if (usb_ids != NULL) {
		free (usb_ids);
		usb_ids = NULL;
		return TRUE;
	}
	return FALSE;
}

void 
ids_init (void)
{
	/* Load /usr/share/hwdata/pci.ids */
	pci_ids_load (HWDATA_DIR "/pci.ids");

	/* Load /usr/share/hwdata/usb.ids */
	usb_ids_load (HWDATA_DIR "/usb.ids");
}


--- NEW FILE: ids.h ---
/***************************************************************************
 * CVSID: $Id: ids.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * ids.h : Lookup names from hardware identifiers
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef IDS_H
#define IDS_H

#include <glib.h>

void ids_init (void);

void
ids_find_pci (int vendor_id, int product_id,
	      int subsys_vendor_id, int subsys_product_id,
	      char **vendor_name, char **product_name,
	      char **subsys_vendor_name, char **subsys_product_name);

void
ids_find_usb (int vendor_id, int product_id,
	      char **vendor_name, char **product_name);


#endif /* IDS_H */

--- NEW FILE: osspec.c ---
/***************************************************************************
 * CVSID: $Id: osspec.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * osspec.c : New and improved HAL backend for Linux 2.6
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../osspec.h"
#include "../logger.h"
#include "../hald.h"
#include "../callout.h"
#include "../device_info.h"
#include "../hald_conf.h"

#include "util.h"
#include "hotplug.h"
#include "coldplug.h"

#include "hotplug_helper.h"

#include "ids.h"

char hal_sysfs_path [HAL_PATH_MAX];
char hal_proc_path [HAL_PATH_MAX];

static gboolean
hald_helper_data (GIOChannel *source, 
		  GIOCondition condition, 
		  gpointer user_data)
{
	struct hald_helper_msg msg;
	int fd;
	int retval;
	struct msghdr smsg;
	struct cmsghdr *cmsg;
	struct iovec iov;
	struct ucred *cred;
	char cred_msg[CMSG_SPACE(sizeof(struct ucred))];

	fd = g_io_channel_unix_get_fd (source);

	iov.iov_base = &msg;
	iov.iov_len = sizeof (struct hald_helper_msg);

	memset(&smsg, 0x00, sizeof (struct msghdr));
	smsg.msg_iov = &iov;
	smsg.msg_iovlen = 1;
	smsg.msg_control = cred_msg;
	smsg.msg_controllen = sizeof (cred_msg);

	retval = recvmsg (fd, &smsg, 0);
	if (retval <  0) {
		if (errno != EINTR)
			HAL_INFO (("Unable to receive message, errno=%d", errno));
		goto out;
	}
	cmsg = CMSG_FIRSTHDR (&smsg);
	cred = (struct ucred *) CMSG_DATA (cmsg);

	if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
		HAL_INFO (("No sender credentials received, message ignored"));
		goto out;
	}

	if (cred->uid != 0) {
		HAL_INFO (("Sender uid=%i, message ignored", cred->uid));
		goto out;
	}

	if (msg.magic != HALD_HELPER_MAGIC) {
		HAL_INFO (("Magic is wrong, message ignored", cred->uid));
		goto out;
	}

	HAL_INFO (("SEQNUM=%lld, TIMESTAMP=%d, ACTION=%s, SUBSYS=%s, SYSFSPATH=%s, DEVNAME=%s, IFINDEX=%d", 
		   msg.seqnum, msg.time_stamp, msg.action, msg.subsystem, 
		   msg.sysfs_path, msg.device_name, msg.net_ifindex));

	if (strcmp (msg.action, "add") == 0) {
		HotplugEvent *hotplug_event;

		hotplug_event = g_new0 (HotplugEvent, 1);
		hotplug_event->is_add = TRUE;
		g_strlcpy (hotplug_event->subsystem, msg.subsystem, sizeof (hotplug_event->subsystem));
		g_snprintf (hotplug_event->sysfs_path, sizeof (hotplug_event->sysfs_path), "%s%s", 
			    hal_sysfs_path, msg.sysfs_path);
		g_strlcpy (hotplug_event->device_file, msg.device_name, sizeof (hotplug_event->device_file));
		/* TODO: set wait_for_sysfs_path */
		hotplug_event->net_ifindex = msg.net_ifindex;

		/* queue up and process */
		hotplug_event_enqueue (hotplug_event);
		hotplug_event_process_queue ();

	} else if (strcmp (msg.action, "remove") == 0) {
		HotplugEvent *hotplug_event;

		hotplug_event = g_new0 (HotplugEvent, 1);
		hotplug_event->is_add = FALSE;
		g_strlcpy (hotplug_event->subsystem, msg.subsystem, sizeof (hotplug_event->subsystem));
		g_snprintf (hotplug_event->sysfs_path, sizeof (hotplug_event->sysfs_path), "%s%s", 
			    hal_sysfs_path, msg.sysfs_path);
		g_strlcpy (hotplug_event->device_file, msg.device_name, sizeof (hotplug_event->device_file));
		/* TODO: set wait_for_sysfs_path */
		hotplug_event->net_ifindex = msg.net_ifindex;

		/* queue up and process */
		hotplug_event_enqueue (hotplug_event);
		hotplug_event_process_queue ();
	}


out:
	return TRUE;
}

void
osspec_init (void)
{
	int socketfd;
	struct sockaddr_un saddr;
	socklen_t addrlen;
	GIOChannel *channel;	
	const int on = 1;

	/* setup socket for listening from datagrams from the hal.hotplug helper */
	memset(&saddr, 0x00, sizeof(saddr));
	saddr.sun_family = AF_LOCAL;
	/* use abstract namespace for socket path */
	strcpy(&saddr.sun_path[1], HALD_HELPER_SOCKET_PATH);
	addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;

	socketfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
	if (socketfd == -1) {
		DIE (("Couldn't open socket"));
	}

	if (bind(socketfd, (struct sockaddr *) &saddr, addrlen) < 0) {
		fprintf (stderr, "Error binding to %s: %s\n", HALD_HELPER_SOCKET_PATH, strerror(errno));
		exit (1);
	}

	/* enable receiving of the sender credentials */
	setsockopt(socketfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));

	channel = g_io_channel_unix_new (socketfd);
	g_io_add_watch (channel, G_IO_IN, hald_helper_data, NULL);
	g_io_channel_unref (channel);


	/* Get mount points for /proc and /sys */
	if (!hal_util_get_fs_mnt_path ("sysfs", hal_sysfs_path, sizeof (hal_sysfs_path))) {
		HAL_ERROR (("Could not get sysfs mount point"));
		goto error;
	}
	HAL_INFO (("sysfs mount point is '%s'", hal_sysfs_path));

	if (!hal_util_get_fs_mnt_path ("proc", hal_proc_path, sizeof (hal_proc_path))) {
		HAL_ERROR (("Could not get proc mount point"));
		goto error;
	}
	HAL_INFO (("proc mount point is '%s'", hal_proc_path));

	/* Load various hardware id databases */
	ids_init ();

error:
	;
}

void
osspec_shutdown (void)
{
}

void 
osspec_probe (void)
{
	HalDevice *root;

	root = hal_device_new ();
	hal_device_property_set_string (root, "info.bus", "unknown");
	hal_device_property_set_string (root, "linux.sysfs_path_device", "(none)");
	hal_device_property_set_string (root, "info.product", "Computer");
	hal_device_property_set_string (root, "info.udi", "/org/freedesktop/Hal/devices/computer");
	hal_device_set_udi (root, "/org/freedesktop/Hal/devices/computer");

	hal_device_store_add (hald_get_tdl (), root);

	di_search_and_merge (root);

	hal_device_store_remove (hald_get_tdl (), root);
	hal_device_store_add (hald_get_gdl (), root);

	/* will enqueue hotplug events for entire system */
	coldplug_synthesize_events ();

	/* start processing events */
	hotplug_event_process_queue ();

	/*osspec_probe_done ();*/
}

DBusHandlerResult
osspec_filter_function (DBusConnection *connection, DBusMessage *message, void *user_data)
{
	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

--- NEW FILE: physdev.c ---
/***************************************************************************
 * CVSID: $Id: physdev.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * physdev.c : Handling of physical kernel devices 
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../osspec.h"
#include "../logger.h"
#include "../hald.h"
#include "../callout.h"
#include "../device_info.h"
#include "../hald_conf.h"

#include "util.h"
#include "coldplug.h"
#include "hotplug_helper.h"

#include "hotplug.h"
#include "physdev.h"

#include "ids.h"

static gboolean
pci_add (const gchar *sysfs_path, HalDevice *parent, void *end_token)
{
	HalDevice *d;
	gint device_class;
	gchar udi[256];

	d = hal_device_new ();
	hal_device_property_set_string (d, "linux.sysfs_path_device", sysfs_path);
	hal_device_property_set_string (d, "info.bus", "pci");
	if (parent != NULL) {
		hal_device_property_set_string (d, "info.parent", parent->udi);
	} else {
		hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/computer");
	}

	hal_device_property_set_string (d, "pci.linux.sysfs_path", sysfs_path);

	hal_util_set_int_from_file (d, "pci.product_id", sysfs_path, "device", 16);
	hal_util_set_int_from_file (d, "pci.vendor_id", sysfs_path, "vendor", 16);
	hal_util_set_int_from_file (d, "pci.subsys_product_id", sysfs_path, "subsystem_device", 16);
	hal_util_set_int_from_file (d, "pci.subsys_vendor_id", sysfs_path, "subsystem_vendor", 16);

	if (hal_util_get_int_from_file (sysfs_path, "class", &device_class, 16)) {
		hal_device_property_set_int (d, "pci.device_class", ((device_class >> 16) & 0xff));
		hal_device_property_set_int (d, "pci.device_subclass", ((device_class >> 8) & 0xff));
		hal_device_property_set_int (d, "pci.device_protocol", (device_class & 0xff));
	}

	{
		gchar buf[64];
		char *vendor_name;
		char *product_name;
		char *subsys_vendor_name;
		char *subsys_product_name;

		ids_find_pci (hal_device_property_get_int (d, "pci.vendor_id"), 
			      hal_device_property_get_int (d, "pci.product_id"), 
			      hal_device_property_get_int (d, "pci.subsys_vendor_id"), 
			      hal_device_property_get_int (d, "pci.subsys_product_id"), 
			      &vendor_name, &product_name, &subsys_vendor_name, &subsys_product_name);

		if (vendor_name != NULL) {
			hal_device_property_set_string (d, "pci.vendor", vendor_name);
			hal_device_property_set_string (d, "info.vendor", vendor_name);
		} else {
			g_snprintf (buf, sizeof (buf), "Unknown (0x%04x)", 
				    hal_device_property_get_int (d, "pci.vendor_id"));
			hal_device_property_set_string (d, "pci.vendor", buf);
			hal_device_property_set_string (d, "info.vendor", buf);
		}

		if (product_name != NULL) {
			hal_device_property_set_string (d, "pci.product", product_name);
			hal_device_property_set_string (d, "info.product", product_name);
		} else {
			g_snprintf (buf, sizeof (buf), "Unknown (0x%04x)", 
				    hal_device_property_get_int (d, "pci.product_id"));
			hal_device_property_set_string (d, "pci.product", buf);
			hal_device_property_set_string (d, "info.product", buf);
		}

		if (subsys_vendor_name != NULL) {
			hal_device_property_set_string (d, "pci.subsys_vendor", subsys_vendor_name);
		} else {
			g_snprintf (buf, sizeof (buf), "Unknown (0x%04x)", 
				    hal_device_property_get_int (d, "pci.subsys_vendor_id"));
			hal_device_property_set_string (d, "pci.subsys_vendor", buf);
		}

		if (subsys_product_name != NULL) {
			hal_device_property_set_string (d, "pci.subsys_product", subsys_product_name);
		} else {
			g_snprintf (buf, sizeof (buf), "Unknown (0x%04x)", 
				    hal_device_property_get_int (d, "pci.subsys_product_id"));
			hal_device_property_set_string (d, "pci.subsys_product", buf);
		}
	}

	/* Add to temporary device store */
	hal_device_store_add (hald_get_tdl (), d);

	/* Merge properties from .fdi files */
	di_search_and_merge (d);

	/* TODO: Run callouts */

	/* Compute UDI */
	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
			      "/org/freedesktop/Hal/devices/pci_%x_%x",
			      hal_device_property_get_int (d, "pci.vendor_id"),
			      hal_device_property_get_int (d, "pci.product_id"));
	hal_device_set_udi (d, udi);
	hal_device_property_set_string (d, "info.udi", udi);

	/* Move from temporary to global device store */
	hal_device_store_remove (hald_get_tdl (), d);
	hal_device_store_add (hald_get_gdl (), d);
	
	hotplug_event_end (end_token);
	return TRUE;
}

static void 
usbif_set_name (HalDevice *d, int ifclass, int ifsubclass, int ifprotocol)
{
	const char *name;

	switch (ifclass) {
	default:
	case 0x00:
		name = "USB Interface";
		break;
	case 0x01:
		name = "USB Audio Interface";
		break;
	case 0x02:
		name = "USB Communications Interface";
		break;
	case 0x03:
		name = "USB HID Interface";
		break;
	case 0x06:
		name = "USB Imaging Interface";
		break;
	case 0x07:
		name = "USB Printer Interface";
		break;
	case 0x08:
		name = "USB Mass Storage Interface";
		break;
	case 0x09:
		name = "USB Hub Interface";
		break;
	case 0x0a:
		name = "USB Data Interface";
		break;
	case 0x0b:
		name = "USB Chip/Smartcard Interface";
		break;
	case 0x0d:
		name = "USB Content Security Interface";
		break;
	case 0x0e:
		name = "USB Video Interface";
		break;
	case 0xdc:
		name = "USB Diagnostic Interface";
		break;
	case 0xe0:
		name = "USB Wireless Interface";
		break;
	case 0xef:
		name = "USB Miscelleneous Interface";
		break;
	case 0xfe:
		name = "USB Application Specific Interface";
		break;
	case 0xff:
		name = "USB Vendor Specific Interface";
		break;
	}

	hal_device_property_set_string (d, "usb.product", name);
	hal_device_property_set_string (d, "info.product", name);
}

static gboolean
usb_add (const gchar *sysfs_path, HalDevice *parent, void *end_token)
{
	HalDevice *d;
	const gchar *bus_id;
	gchar udi[256];

	d = hal_device_new ();
	hal_device_property_set_string (d, "linux.sysfs_path_device", sysfs_path);
	if (parent != NULL) {
		hal_device_property_set_string (d, "info.parent", parent->udi);
	}

	/* only USB interfaces got a : in the bus_id */
	bus_id = hal_util_get_last_element (sysfs_path);
	if (strchr (bus_id, ':') == NULL) {
		gint bmAttributes;

		hal_device_property_set_string (d, "info.bus", "usb_device");

		hal_device_property_set_string (d, "usb_device.linux.sysfs_path", sysfs_path);

		hal_util_set_int_from_file (d, "usb_device.configuration_value", sysfs_path, "bConfigurationValue", 10);
		hal_util_set_int_from_file (d, "usb_device.num_configurations", sysfs_path, "bNumConfigurations", 10);
		hal_util_set_int_from_file (d, "usb_device.num_interfaces", sysfs_path, "bNumInterfaces", 10);

		hal_util_set_int_from_file (d, "usb_device.device_class", sysfs_path, "bDeviceClass", 16);
		hal_util_set_int_from_file (d, "usb_device.device_subclass", sysfs_path, "bDeviceSubClass", 16);
		hal_util_set_int_from_file (d, "usb_device.device_protocol", sysfs_path, "bDeviceProtocol", 16);

		hal_util_set_int_from_file (d, "usb_device.vendor_id", sysfs_path, "idVendor", 16);
		hal_util_set_int_from_file (d, "usb_device.product_id", sysfs_path, "idProduct", 16);

		{
			gchar buf[64];
			char *vendor_name;
			char *product_name;

			ids_find_usb (hal_device_property_get_int (d, "usb_device.vendor_id"), 
				      hal_device_property_get_int (d, "usb_device.product_id"), 
				      &vendor_name, &product_name);

			if (vendor_name != NULL) {
				hal_device_property_set_string (d, "usb_device.vendor", vendor_name);
			} else {
				if (!hal_util_set_string_from_file (d, "usb_device.vendor", 
								    sysfs_path, "manufacturer")) {
					g_snprintf (buf, sizeof (buf), "Unknown (0x%04x)", 
						    hal_device_property_get_int (d, "usb_device.vendor_id"));
					hal_device_property_set_string (d, "usb_device.vendor", buf); 
				}
			}
			hal_device_property_set_string (d, "info.vendor",
							hal_device_property_get_string (d, "usb_device.vendor"));

			if (product_name != NULL) {
				hal_device_property_set_string (d, "usb_device.product", product_name);
			} else {
				if (!hal_util_set_string_from_file (d, "usb_device.product", 
								    sysfs_path, "product")) {
					g_snprintf (buf, sizeof (buf), "Unknown (0x%04x)", 
						    hal_device_property_get_int (d, "usb_device.product_id"));
					hal_device_property_set_string (d, "usb_device.product", buf); 
				}
			}
			hal_device_property_set_string (d, "info.product",
							hal_device_property_get_string (d, "usb_device.product"));
		}

		hal_util_set_int_from_file (d, "usb_device.device_revision_bcd", sysfs_path, "bcdDevice", 16);

		hal_util_set_int_from_file (d, "usb_device.max_power", sysfs_path, "bMaxPower", 10);
		hal_util_set_int_from_file (d, "usb_device.num_ports", sysfs_path, "maxchild", 10);
		hal_util_set_int_from_file (d, "usb_device.linux.device_number", sysfs_path, "devnum", 10);

		hal_util_set_string_from_file (d, "usb_device.serial", sysfs_path, "serial");

		hal_util_set_string_from_file (d, "usb_device.serial", sysfs_path, "serial");
		hal_util_set_bcd2_from_file (d, "usb_device.speed_bcd", sysfs_path, "speed");
		hal_util_set_bcd2_from_file (d, "usb_device.version_bcd", sysfs_path, "version");

		hal_util_get_int_from_file (sysfs_path, "bmAttributes", &bmAttributes, 16);
		hal_device_property_set_bool (d, "usb_device.is_self_powered", (bmAttributes & 0x40) != 0);
		hal_device_property_set_bool (d, "usb_device.can_wake_up", (bmAttributes & 0x20) != 0);

		if (strncmp (bus_id, "usb", 3) == 0)
			hal_device_property_set_int (d, "usb_device.bus_number", atoi (bus_id + 3));
		else
			hal_device_property_set_int (d, "usb_device.bus_number", atoi (bus_id));

		/* TODO:  .level_number .parent_number  */

		/* Add to temporary device store */
		hal_device_store_add (hald_get_tdl (), d);

		/* Merge properties from .fdi files */
		di_search_and_merge (d);
		
		/* TODO: Run callouts */
		
		/* Compute UDI */
		hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
				      "/org/freedesktop/Hal/devices/usb_device_%x_%x_%s",
				      hal_device_property_get_int (d, "usb_device.vendor_id"),
				      hal_device_property_get_int (d, "usb_device.product_id"),
				      hal_device_has_property (d, "usb_device.serial") ?
				        hal_device_property_get_string (d, "usb_device.serial") :
				        "noserial");
		hal_device_set_udi (d, udi);
		hal_device_property_set_string (d, "info.udi", udi);
		
		/* Move from temporary to global device store */
		hal_device_store_remove (hald_get_tdl (), d);
		hal_device_store_add (hald_get_gdl (), d);

	} else {
		hal_device_property_set_string (d, "info.bus", "usb");

		/* take all usb_device.* properties from parent and make them usb.* on this object */
		hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");

		hal_device_property_set_string (d, "usb.linux.sysfs_path", sysfs_path);

		hal_util_set_int_from_file (d, "usb.interface.number", sysfs_path, "bInterfaceNumber", 10);

		hal_util_set_int_from_file (d, "usb.interface.class", sysfs_path, "bInterfaceClass", 16);
		hal_util_set_int_from_file (d, "usb.interface.subclass", sysfs_path, "bInterfaceSubClass", 16);
		hal_util_set_int_from_file (d, "usb.interface.protocol", sysfs_path, "bInterfaceProtocol", 16);

		usbif_set_name (d, 
				hal_device_property_get_int (d, "usb.interface.class"),
				hal_device_property_get_int (d, "usb.interface.subclass"),
				hal_device_property_get_int (d, "usb.interface.protocol"));

		/* Add to temporary device store */
		hal_device_store_add (hald_get_tdl (), d);
		
		/* Merge properties from .fdi files */
		di_search_and_merge (d);
		
		/* TODO: Run callouts */
		
		/* Compute UDI */
		hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
				      "%s_if%d",
				      hal_device_property_get_string (d, "info.parent"),
				      hal_device_property_get_int (d, "usb.interface.number"));
		hal_device_set_udi (d, udi);
		hal_device_property_set_string (d, "info.udi", udi);
		
		/* Move from temporary to global device store */
		hal_device_store_remove (hald_get_tdl (), d);
		hal_device_store_add (hald_get_gdl (), d);
	}

	hotplug_event_end (end_token);
	return TRUE;
}

static gboolean
physdev_remove (HalDevice *d, void *end_token)
{
	if (!hal_device_store_remove (hald_get_gdl (), d)) {
		HAL_WARNING (("Error removing device"));
	}

	hotplug_event_end (end_token);
	return TRUE;
}

typedef struct
{
	const gchar *subsystem;
	gboolean (*add) (const gchar *sysfs_path, HalDevice *parent, void *end_token);
	gboolean (*remove) (HalDevice *d, void *end_token);
} PhysDevHandler;

static PhysDevHandler physdev_handler_pci = { 
	"pci",
	pci_add,
	physdev_remove
};

static PhysDevHandler physdev_handler_usb = { 
	"usb",
	usb_add,
	physdev_remove
};
	

static PhysDevHandler *phys_handlers[] = {
	&physdev_handler_pci,
	&physdev_handler_usb,
	NULL
};


void
hotplug_event_begin_add_physdev (const gchar *subsystem, const gchar *sysfs_path, HalDevice *parent, void *end_token)
{
	guint i;

	HAL_INFO (("phys_add: subsys=%s sysfs_path=%s, parent=0x%08x", subsystem, sysfs_path, parent));

	for (i = 0; phys_handlers [i] != NULL; i++) {
		PhysDevHandler *handler;

		handler = phys_handlers[i];
		if (strcmp (handler->subsystem, subsystem) == 0) {
			if (handler->add (sysfs_path, parent, end_token)) {
				/* let the handler end the event */
				goto out;
			}
		}
	}

	/* didn't find anything - thus, ignore this hotplug event */
	hotplug_event_end (end_token);
out:
	;
}

void
hotplug_event_begin_remove_physdev (const gchar *subsystem, const gchar *sysfs_path, void *end_token)
{
	guint i;
	HalDevice *d;

	HAL_INFO (("phys_rem: subsys=%s sysfs_path=%s", subsystem, sysfs_path));

	d = hal_device_store_match_key_value_string (hald_get_gdl (), 
						     "linux.sysfs_path_device", 
						     sysfs_path);
	if (d == NULL) {
		HAL_WARNING (("Couldn't remove device with sysfs path %s - not found", sysfs_path));
		goto out;
	}

	for (i = 0; phys_handlers [i] != NULL; i++) {
		PhysDevHandler *handler;

		handler = phys_handlers[i];
		if (strcmp (handler->subsystem, subsystem) == 0) {
			if (handler->remove (d, end_token)) {
				/* let the handler end the event */
				goto out2;
			}
		}
	}

out:
	/* didn't find anything - thus, ignore this hotplug event */
	hotplug_event_end (end_token);
out2:
	;
}

--- NEW FILE: physdev.h ---
/***************************************************************************
 * CVSID: $Id: physdev.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * physdev.h : Handling of physical kernel devices
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef PHYSDEV_H
#define PHYSDEV_H

#include <glib.h>

void hotplug_event_begin_add_physdev (const gchar *subsystem, const gchar *sysfs_path, HalDevice *parent, void *end_token);

void hotplug_event_begin_remove_physdev (const gchar *subsystem, const gchar *sysfs_path, void *end_token);

#endif /* PHYSDEV_H */

--- NEW FILE: util.c ---
/***************************************************************************
 * CVSID: $Id: util.c,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * util.c - Various utilities
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <mntent.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>

#include "../osspec.h"
#include "../logger.h"
#include "../hald.h"
#include "../callout.h"
#include "../device_info.h"
#include "../hald_conf.h"

#include "util.h"

gboolean 
hal_util_remove_trailing_slash (gchar *path)
{
	gchar *c = NULL;

	if (path == NULL) {
		return FALSE;
	}

	c = strrchr (path, '/');
	if (c == NULL) {
		HAL_WARNING (("Invalid path %s", path));
		return 1;
	}
	if (*(c+1) == '\0') 
		*c = '\0';

	return TRUE;
}

gboolean 
hal_util_get_fs_mnt_path (const gchar *fs_type, gchar *mnt_path, gsize len)
{
	FILE *mnt;
	struct mntent *mntent;
	gboolean rc;
	gsize dirlen;

	rc = FALSE;
	dirlen = 0;

	if (fs_type == NULL || mnt_path == NULL || len == 0) {
		HAL_ERROR (("Arguments not sane"));
		return -1;
	}

	if ((mnt = setmntent ("/proc/mounts", "r")) == NULL) {
		HAL_ERROR (("Error getting mount information"));
		return -1;
	}

	while (rc == FALSE && dirlen == 0 && (mntent = getmntent(mnt)) != NULL) {
		if (strcmp (mntent->mnt_type, fs_type) == 0) {
			dirlen = strlen (mntent->mnt_dir);
			if (dirlen <= (len - 1)) {
				g_strlcpy (mnt_path, mntent->mnt_dir, len);
				rc = TRUE;
			} else {
				HAL_ERROR (("Error - mount path too long"));
				rc = FALSE;
			}
		}
	}
	endmntent (mnt);
	
	if (dirlen == 0 && rc == TRUE) {
		HAL_ERROR (("Filesystem %s not found", fs_type));
		rc = FALSE;
	}

	if ((!hal_util_remove_trailing_slash (mnt_path)))
		rc = FALSE;
	
	return rc;
}

/** Given a path, /foo/bar/bat/foobar, return the last element, e.g.
 *  foobar.
 *
 *  @param  path                Path
 *  @return                     Pointer into given string
 */
const gchar *
hal_util_get_last_element (const gchar *s)
{
	int len;
	const gchar *p;

	len = strlen (s);
	for (p = s + len - 1; p > s; --p) {
		if ((*p) == '/')
			return p + 1;
	}

	return s;
}

/** Given a sysfs-path for a device, this functions finds the sysfs
 *  path representing the parent of the given device by truncation.
 *
 *  @param  path                Sysfs-path of device to find parent for
 *  @return                     Path for parent or NULL if there is no parent; 
 *                              must be freed by caller
 */
gchar *
hal_util_get_parent_sysfs_path (const gchar *path)
{
	guint i;
	guint len;
	gchar *parent_path;

	/* Find parent device by truncating our own path */
	parent_path = g_strndup (path, HAL_PATH_MAX);
	len = strlen (parent_path);
	for (i = len - 1; parent_path[i] != '/'; --i) {
		parent_path[i] = '\0';
	}
	parent_path[i] = '\0';

	return parent_path;
}



/* Returns the path of the udevinfo program 
 *
 * @return                      Path or NULL if udevinfo program is not found
 */
static const gchar *
hal_util_get_udevinfo_path (void)
{
	guint i;
	struct stat s;
	static gchar *path = NULL;
	gchar *possible_paths[] = { 
		"/sbin/udevinfo",
		"/usr/bin/udevinfo",
		"/usr/sbin/udevinfo",
		"/usr/local/sbin/udevinfo"
	};

	if (path != NULL)
		return path;

	for (i = 0; i < sizeof (possible_paths) / sizeof (char *); i++) {
		if (stat (possible_paths[i], &s) == 0 && S_ISREG (s.st_mode)) {
			path = possible_paths[i];
			break;
		}
	}
	return path;
}

/** Get the name of the special device file given the sysfs path.
 *
 *  @param  sysfs_path          Path to class device in sysfs
 *  @param  dev_file            Where the special device file name should be stored
 *  @param  dev_file_length     Size of dev_file character array
 *  @return                     TRUE only if the device file could be found
 */
gboolean
hal_util_get_device_file (const gchar *sysfs_path, gchar *dev_file, gsize dev_file_length)
{
	int i;
	gsize sysfs_path_len;
	gsize sysfs_mount_path_len;
	gchar sysfs_path_trunc[HAL_PATH_MAX];
	gchar sysfs_path_dev_trunc[HAL_PATH_MAX + 4];
	char *udev_argv[7] = { NULL, 
			       "-r", "-q", "name", "-p",
			       sysfs_path_trunc, NULL };
	char *udev_stdout;
	char *udev_stderr;
	int udev_exitcode;
	struct stat statbuf;

	/* check for dev file in sysfs path */
	sysfs_path_len = strlen (sysfs_path);
	strncpy (sysfs_path_dev_trunc, sysfs_path, HAL_PATH_MAX);
	strncat (sysfs_path_dev_trunc + sysfs_path_len, "/dev", 4);
	if (g_stat (sysfs_path_dev_trunc, &statbuf) != 0)
		return FALSE;

	/* get path to udevinfo */
	udev_argv[0] = (char *) hal_util_get_udevinfo_path ();
	if (udev_argv[0] == NULL)
		return FALSE;

	/* compute truncated sysfs path as udevinfo doesn't want the sysfs_mount_path (e.g. /sys) prefix */
	sysfs_mount_path_len = strlen (hal_sysfs_path);
	if (strlen (sysfs_path) > sysfs_mount_path_len) {
		strncpy (sysfs_path_trunc, sysfs_path + sysfs_mount_path_len, HAL_PATH_MAX - sysfs_mount_path_len);
	}

	/* Now invoke udevinfo */
	if (udev_argv[0] == NULL || g_spawn_sync ("/",
						  udev_argv,
						  NULL,
						  0,
						  NULL,
						  NULL,
						  &udev_stdout,
						  &udev_stderr,
						  &udev_exitcode,
						  NULL) != TRUE) {
		HAL_ERROR (("Couldn't invoke %s", udev_argv[0]));
		return FALSE;
	}

	if (udev_exitcode != 0) {
		HAL_ERROR (("%s returned %d for %s", udev_argv[0], udev_exitcode, sysfs_path_trunc));
		return FALSE;
	}

	/* sanitize string returned by udev */
	for (i = 0; udev_stdout[i] != 0; i++) {
		if (udev_stdout[i] == '\r' || udev_stdout[i] == '\n') {
			udev_stdout[i] = 0;
			break;
		}
	}

	/*HAL_INFO (("got device file %s for %s", udev_stdout, sysfs_path));*/

	strncpy (dev_file, udev_stdout, dev_file_length);
	return TRUE;
}


/** Find the closest ancestor by looking at sysfs paths
 *
 *  @param  sysfs_path           Path into sysfs, e.g. /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-0:1.0
 *  @return                      Parent Hal Device Object or #NULL if there is none
 */
HalDevice *
hal_util_find_closest_ancestor (const gchar *sysfs_path)
{	
	gchar buf[512];
	HalDevice *parent;

	parent = NULL;

	strncpy (buf, sysfs_path, sizeof (buf));
	do {
		char *p;

		p = strrchr (buf, '/');
		if (p == NULL)
			break;
		*p = '\0';

		parent = hal_device_store_match_key_value_string (hald_get_gdl (), 
								  "linux.sysfs_path_device", 
								  buf);
		if (parent != NULL)
			break;

	} while (TRUE);

	return parent;
}

gchar *
hal_util_get_normalized_path (const gchar *path1, const gchar *path2)
{
	int i;
	int len1;
	int len2;
	const gchar *p1;
	const gchar *p2;
	gchar buf[HAL_PATH_MAX];

	len1 = strlen (path1);
	len2 = strlen (path1);

	p1 = path1 + len1;

	i = 0;
	p2 = path2;
	while (p2 < path2 + len2 && strncmp (p2, "../", 3) == 0) {
		p2 += 3;

		while (p1 >= path1 && *(--p1)!='/')
			;

	}

	strncpy (buf, path1, (p1-path1));
	buf[p1-path1] = '\0';

	return g_strdup_printf ("%s/%s", buf, p2);
}

gboolean
hal_util_get_int_from_file (const gchar *directory, const gchar *file, gint *result, gint base)
{
	FILE *f;
	char buf[64];
	gchar path[HAL_PATH_MAX];
	gboolean ret;

	f = NULL;
	ret = FALSE;

	g_snprintf (path, sizeof (path), "%s/%s", directory, file);

	f = fopen (path, "rb");
	if (f == NULL) {
		HAL_ERROR (("Cannot open '%s'", path));
		goto out;
	}

	if (fgets (buf, sizeof (buf), f) == NULL) {
		HAL_ERROR (("Cannot read from '%s'", path));
		goto out;
	}

	/* TODO: handle error condition */
	*result = strtol (buf, NULL, base);
	ret = TRUE;

out:
	if (f != NULL)
		fclose (f);

	return ret;
}

gboolean
hal_util_set_int_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file, gint base)
{
	gint value;
	gboolean ret;

	ret = FALSE;

	if (hal_util_get_int_from_file (directory, file, &value, base))
		ret = hal_device_property_set_int (d, key, value);

	return ret;
}

gboolean
hal_util_get_bcd2_from_file (const gchar *directory, const gchar *file, gint *result)
{
	FILE *f;
	char buf[64];
	gchar path[HAL_PATH_MAX];
	gboolean ret;
	gint digit;
	gint left, right;
	gboolean passed_white_space;
	gint num_prec;
	gsize len;
	gchar c;
	guint i;

	f = NULL;
	ret = FALSE;

	g_snprintf (path, sizeof (path), "%s/%s", directory, file);

	f = fopen (path, "rb");
	if (f == NULL) {
		HAL_ERROR (("Cannot open '%s'", path));
		goto out;
	}

	if (fgets (buf, sizeof (buf), f) == NULL) {
		HAL_ERROR (("Cannot read from '%s'", path));
		goto out;
	}

	left = 0;
	len = strlen (buf);
	passed_white_space = FALSE;
	for (i = 0; i < len && buf[i] != '.'; i++) {
		if (g_ascii_isspace (buf[i])) {
			if (passed_white_space)
				break;
			else
				continue;
		}
		passed_white_space = TRUE;
		left *= 16;
		c = buf[i];
		digit = (int) (c - '0');
		left += digit;
	}
	i++;
	right = 0;
	num_prec = 0;
	for (; i < len; i++) {
		if (g_ascii_isspace (buf[i]))
			break;
		if (num_prec == 2)	        /* Only care about two digits 
						 * of precision */
			break;
		right *= 16;
		c = buf[i];
		digit = (int) (c - '0');
		right += digit;
		num_prec++;
	}

	for (; num_prec < 2; num_prec++)
		right *= 16;

	*result = left * 256 + (right & 255);
	ret = TRUE;

out:
	if (f != NULL)
		fclose (f);

	return ret;
}

gboolean
hal_util_set_bcd2_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file)
{
	gint value;
	gboolean ret;

	ret = FALSE;

	if (hal_util_get_bcd2_from_file (directory, file, &value))
		ret = hal_device_property_set_int (d, key, value);

	return ret;
}

gboolean
hal_util_set_string_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file)
{
	FILE *f;
	gchar buf[256];
	gchar path[HAL_PATH_MAX];
	gboolean ret;
	gsize len;

	f = NULL;
	ret = FALSE;

	g_snprintf (path, sizeof (path), "%s/%s", directory, file);

	f = fopen (path, "rb");
	if (f == NULL) {
		HAL_ERROR (("Cannot open '%s'", path));
		goto out;
	}

	if (fgets (buf, sizeof (buf), f) == NULL) {
		HAL_ERROR (("Cannot read from '%s'", path));
		goto out;
	}

	len = strlen (buf);
	if (len>0)
		buf[len-1] = '\0';

	ret = hal_device_property_set_string (d, key, buf);

out:
	if (f != NULL)
		fclose (f);

	return ret;
}

void
hal_util_compute_udi (HalDeviceStore *store, gchar *dst, gsize dstsize, const gchar *format, ...)
{
	guint i;
	va_list args;
	gchar buf[256];

	va_start (args, format);
	g_vsnprintf (buf, sizeof (buf), format, args);
	va_end (args);

	g_strlcpy (dst, buf, dstsize);
	if (hal_device_store_find (store, dst) == NULL)
		goto out;

	for (i = 0; ; i++) {
		g_snprintf (dst, dstsize, "%s-%d", buf, i);
		if (hal_device_store_find (store, dst) == NULL)
			goto out;
	}

out:
	;
}


typedef struct
{
	GPid pid;
	guint timeout_watch_id;
	guint child_watch_id;

	HelperTerminatedCB cb;
	gpointer data1;
	gpointer data2;

	HalDevice *d;
} HelperData;

static gboolean
helper_child_timeout (gpointer data)
{
	HelperData *ed = (HelperData *) data;

	HAL_INFO (("child timeout for pid %d", ed->pid));

	/* kill kenny! kill it!! */
	kill (ed->pid, SIGTERM);
	/* TODO: yikes; what about removing the zombie? */

	g_source_remove (ed->child_watch_id);
	g_spawn_close_pid (ed->pid);

	ed->cb (ed->d, TRUE, -1, ed->data1, ed->data2);

	g_free (ed);
	return FALSE;
}

static void 
helper_child_exited (GPid pid, gint status, gpointer data)
{
	HelperData *ed = (HelperData *) data;

	HAL_INFO (("child exited for pid %d", ed->pid));

	g_source_remove (ed->timeout_watch_id);
	g_spawn_close_pid (ed->pid);

	ed->cb (ed->d, FALSE, WEXITSTATUS (status), ed->data1, ed->data2);

	g_free (ed);
}

static gboolean
helper_add_property_to_env (HalDevice *device, HalProperty *property, gpointer user_data)
{
	char *prop_upper, *value;
	char *c;
	gchar ***ienvp = (gchar ***) user_data;
	gchar **envp;

	envp = *ienvp;
	*ienvp = *ienvp + 1;

	prop_upper = g_ascii_strup (hal_property_get_key (property), -1);
	
	/* periods aren't valid in the environment, so replace them with
	 * underscores. */
	for (c = prop_upper; *c; c++) {
		if (*c == '.')
			*c = '_';
	}
	
	value = hal_property_to_string (property);
	
	*envp = g_strdup_printf ("HAL_PROP_%s=%s", prop_upper, value);

	g_free (value);
	g_free (prop_upper);

	return TRUE;
}


gboolean
helper_invoke (const gchar *path, HalDevice *d, gpointer data1, gpointer data2, HelperTerminatedCB cb, guint timeout)
{
	gboolean ret;
	HelperData *ed;
	gchar *argv[] = {(gchar *) path, NULL};
	gchar **envp;
	gchar **ienvp;
	GError *err;
	guint num_env_vars;
	guint i;
	guint num_properties;

	ed = g_new0 (HelperData, 1);
	ed->data1 = data1;
	ed->data2 = data2;
	ed->d = d;
	ed->cb = cb;

	num_properties = hal_device_num_properties (d);
	num_env_vars = num_properties + 2;
	if (hald_is_verbose)
		num_env_vars++;
	if (hald_is_initialising)
		num_env_vars++;
	if (hald_is_shutting_down)
		num_env_vars++;

	envp = g_new (char *, num_env_vars);
	ienvp = envp;
	hal_device_property_foreach (d, helper_add_property_to_env, &ienvp);
	i = num_properties;
	envp[i++] = g_strdup_printf ("UDI=%s", hal_device_get_udi (d));
	if (hald_is_verbose)
		envp[i++] = g_strdup ("HALD_VERBOSE=1");
	if (hald_is_initialising)
		envp[i++] = g_strdup ("HALD_STARTUP=1");
	if (hald_is_shutting_down)
		envp[i++] = g_strdup ("HALD_SHUTDOWN=1");
	envp[i++] = NULL;

	
	err = NULL;
	if (!g_spawn_async (NULL, 
			    argv, 
			    envp, 
			    G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH,
			    NULL,
			    NULL,
			    &ed->pid,
			    &err)) {
		HAL_ERROR (("Couldn't spawn '%s' err=%s!", path, err->message));
		g_error_free (err);
		ret = FALSE;
		g_free (ed);
	} else {
		ed->child_watch_id = g_child_watch_add (ed->pid, helper_child_exited, (gpointer) ed);
		ed->timeout_watch_id = g_timeout_add (timeout, helper_child_timeout, (gpointer) ed);
		ret = TRUE;
	}

	g_strfreev (envp);

	return ret;
}


--- NEW FILE: util.h ---
/***************************************************************************
 * CVSID: $Id: util.h,v 1.1 2005/01/18 19:48:13 david Exp $
 *
 * util.h - Various utilities
 *
 * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef UTIL_H
#define UTIL_H

#include "../device.h"

gboolean hal_util_remove_trailing_slash (gchar *path);

gboolean hal_util_get_fs_mnt_path (const gchar *fs_type, gchar *mnt_path, gsize len);

const gchar *hal_util_get_last_element (const gchar *s);

gchar *hal_util_get_parent_sysfs_path (const gchar *path);

gboolean hal_util_get_device_file (const gchar *sysfs_path, gchar *dev_file, gsize dev_file_length);

HalDevice *hal_util_find_closest_ancestor (const gchar *sysfs_path);

gchar *hal_util_get_normalized_path (const gchar *path1, const gchar *path2);

gboolean hal_util_get_int_from_file (const gchar *directory, const gchar *file, gint *result, gint base);

gboolean hal_util_set_int_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file, gint base);

gboolean hal_util_set_string_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file);

gboolean hal_util_get_bcd2_from_file (const gchar *directory, const gchar *file, gint *result);

gboolean hal_util_set_bcd2_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file);

void hal_util_compute_udi (HalDeviceStore *store, gchar *dst, gsize dstsize, const gchar *format, ...);

typedef void (*HelperTerminatedCB)(HalDevice *d, gboolean timed_out, gint return_code, gpointer data1, gpointer data2);

gboolean helper_invoke (const gchar *path, HalDevice *d, gpointer data1, gpointer data2, HelperTerminatedCB cb, guint timeout);

#define HAL_HELPER_TIMEOUT 10000

#define HAL_PATH_MAX 256

extern char hal_sysfs_path [HAL_PATH_MAX];
extern char hal_proc_path [HAL_PATH_MAX];

#endif /* UTIL_H */




More information about the hal-commit mailing list