hal: Branch 'master'

Artem Kachitchkine artem at kemper.freedesktop.org
Mon Nov 6 11:55:42 PST 2006


 configure.in                         |   12 
 hald-runner/main.c                   |    4 
 hald/Makefile.am                     |    2 
 hald/hald_dbus.c                     |    8 
 hald/solaris/Makefile.am             |    6 
 hald/solaris/addons/Makefile.am      |   17 
 hald/solaris/addons/addon-storage.c  |  357 +++++++
 hald/solaris/devinfo.c               |  380 +++++++
 hald/solaris/devinfo.h               |   72 +
 hald/solaris/devinfo_ieee1394.c      |   90 +
 hald/solaris/devinfo_ieee1394.h      |   21 
 hald/solaris/devinfo_misc.c          |  147 +++
 hald/solaris/devinfo_misc.h          |   23 
 hald/solaris/devinfo_pci.c           |  122 ++
 hald/solaris/devinfo_pci.h           |   21 
 hald/solaris/devinfo_storage.c       | 1673 +++++++++++++++++++++++++++++++++++
 hald/solaris/devinfo_storage.h       |   31 
 hald/solaris/devinfo_usb.c           |  231 ++++
 hald/solaris/devinfo_usb.h           |   21 
 hald/solaris/hal.xml                 |   89 +
 hald/solaris/hotplug.c               |  193 ++++
 hald/solaris/hotplug.h               |   60 +
 hald/solaris/osspec.c                |  207 +++-
 hald/solaris/osspec_solaris.h        |   23 
 hald/solaris/probing/Makefile.am     |   22 
 hald/solaris/probing/cdutils.c       |  484 ++++++++++
 hald/solaris/probing/cdutils.h       |   61 +
 hald/solaris/probing/fsutils.c       |  250 +++++
 hald/solaris/probing/fsutils.h       |   28 
 hald/solaris/probing/probe-storage.c |  488 ++++++++++
 hald/solaris/probing/probe-volume.c  |  673 ++++++++++++++
 hald/solaris/svc-hal                 |   39 
 hald/solaris/sysevent.c              |  295 ++++++
 hald/solaris/sysevent.h              |   22 
 hald/util_helper.c                   |   45 
 partutil/Makefile.am                 |    3 
 tools/hal-storage-shared.c           |    2 
 37 files changed, 6196 insertions(+), 26 deletions(-)

New commits:
diff-tree 49abf2145106009fe92a4efd82ff510a5c945d45 (from a56a15b90177734c70a8b03d961a7bcabdea2af8)
Author: Artem Kachitchkine <artem.kachitchkin at sun.com>
Date:   Mon Nov 6 11:55:58 2006 -0800

    solaris backend
    
    Initial commit of the Solaris backend.

diff --git a/configure.in b/configure.in
index c123c21..f4eb57b 100644
--- a/configure.in
+++ b/configure.in
@@ -12,7 +12,7 @@ AM_INIT_AUTOMAKE(hal, 0.5.9)
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 
-glib_module="glib-2.0 >= 2.6.0 dbus-glib-1 >= 0.61"
+glib_module="glib-2.0 >= 2.6.0 gobject-2.0 > 2.6.0 dbus-glib-1 >= 0.61"
 dbus_module="dbus-1 >= 0.61"
 volume_id_module="libvolume_id >= 0.61"
 polkit_module="polkit >= 0.2"
@@ -316,6 +316,14 @@ case "$host" in
 	AC_SUBST(VOLUME_ID_LIBS)
 esac
 
+# OS specific libs
+case "$host" in
+*-*-solaris*)
+	HALD_OS_LIBS="-lsysevent -lnvpair -ldevinfo"
+	AC_SUBST(HALD_OS_LIBS)
+	;;
+esac
+
 # Check for BLKGETSIZE64
 AC_CHECK_TYPE(pgoff_t, ,
               [AC_DEFINE(pgoff_t, unsigned long, [Index into the pagecache])],
@@ -549,6 +557,8 @@ hald/linux/Makefile
 hald/linux/probing/Makefile
 hald/linux/addons/Makefile
 hald/solaris/Makefile
+hald/solaris/probing/Makefile
+hald/solaris/addons/Makefile
 hald/haldaemon
 hald-runner/Makefile
 libhal/Makefile
diff --git a/hald-runner/main.c b/hald-runner/main.c
index a9f366d..b509ad7 100644
--- a/hald-runner/main.c
+++ b/hald-runner/main.c
@@ -31,6 +31,10 @@
 #include "utils.h"
 #include "runner.h"
 
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
 static gboolean
 parse_first_part(run_request *r, DBusMessage *msg, DBusMessageIter *iter)
 {
diff --git a/hald/Makefile.am b/hald/Makefile.am
index 7201b1a..248f27e 100644
--- a/hald/Makefile.am
+++ b/hald/Makefile.am
@@ -51,7 +51,7 @@ hald_SOURCES =                          
 	osspec.h							\
 	ids.h				ids.c			
 
-hald_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
+hald_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
 
 #### Init scripts fun
 SCRIPT_IN_FILES=haldaemon.in
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 7b40903..7434230 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -3941,10 +3941,10 @@ local_server_message_handler (DBusConnec
 			      void *user_data)
 {
 	HAL_INFO (("local_server_message_handler: destination=%s obj_path=%s interface=%s method=%s", 
-		   dbus_message_get_destination (message), 
-		   dbus_message_get_path (message), 
-		   dbus_message_get_interface (message),
-		   dbus_message_get_member (message)));
+		   dbus_message_get_destination (message) ? dbus_message_get_destination (message) : "",
+		   dbus_message_get_path (message) ?  dbus_message_get_path (message) : "" , 
+		   dbus_message_get_interface (message) ? dbus_message_get_interface (message) : "",
+		   dbus_message_get_member (message) ? dbus_message_get_member (message) : ""));
 
 	if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch")) {
 		DBusMessage *reply;
diff --git a/hald/solaris/Makefile.am b/hald/solaris/Makefile.am
index 81266f7..d90beb7 100644
--- a/hald/solaris/Makefile.am
+++ b/hald/solaris/Makefile.am
@@ -1,3 +1,6 @@
+
+SUBDIRS = probing addons .
+
 INCLUDES = \
 	-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
 	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
@@ -12,5 +15,6 @@ noinst_LTLIBRARIES = libhald_solaris.la
 endif
 
 libhald_solaris_la_SOURCES = \
-osspec.c 
+	devinfo.c devinfo_ieee1394.c devinfo_misc.c devinfo_pci.c devinfo_storage.c devinfo_usb.c \
+	hotplug.c osspec.c sysevent.c
 
diff --git a/hald/solaris/addons/Makefile.am b/hald/solaris/addons/Makefile.am
new file mode 100644
index 0000000..87ce550
--- /dev/null
+++ b/hald/solaris/addons/Makefile.am
@@ -0,0 +1,17 @@
+
+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$(top_srcdir)/hald -I$(top_srcdir)/libhal -I$(top_srcdir)/libhal-storage \
+        @GLIB_CFLAGS@ @DBUS_CFLAGS@
+
+if HALD_COMPILE_SOLARIS
+libexec_PROGRAMS  = hald-addon-storage
+endif
+
+hald_addon_storage_SOURCES = addon-storage.c ../../logger.c
+hald_addon_storage_LDADD = $(top_builddir)/libhal/libhal.la
+
diff --git a/hald/solaris/addons/addon-storage.c b/hald/solaris/addons/addon-storage.c
new file mode 100644
index 0000000..1429a27
--- /dev/null
+++ b/hald/solaris/addons/addon-storage.c
@@ -0,0 +1,357 @@
+/***************************************************************************
+ *
+ * addon-storage.c : watch removable media state changes
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)addon-storage.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mnttab.h>
+#include <sys/dkio.h>
+#include <priv.h>
+
+#include <libhal.h>
+
+#include <logger.h>
+
+#define	SLEEP_PERIOD	5
+
+static void
+my_dbus_error_free(DBusError *error)
+{
+	if (dbus_error_is_set(error)) {
+		dbus_error_free(error);
+	}
+}
+
+static void
+force_unmount (LibHalContext *ctx, const char *udi)
+{
+	DBusError error;
+	DBusMessage *msg = NULL;
+	DBusMessage *reply = NULL;
+	char **options = NULL;
+	unsigned int num_options = 0;
+	DBusConnection *dbus_connection;
+	char *device_file;
+
+	dbus_error_init (&error);
+
+	dbus_connection = libhal_ctx_get_dbus_connection (ctx);
+
+	msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
+					    "org.freedesktop.Hal.Device.Volume",
+					    "Unmount");
+	if (msg == NULL) {
+		HAL_DEBUG (("Could not create dbus message for %s", udi));
+		goto out;
+	}
+
+
+	options = calloc (1, sizeof (char *));
+	if (options == NULL) {
+		HAL_DEBUG (("Could not allocate options array"));
+		goto out;
+	}
+
+	device_file = libhal_device_get_property_string (ctx, udi, "block.device", &error);
+	if (device_file != NULL) {
+		libhal_free_string (device_file);
+	}
+	dbus_error_free (&error);
+
+	if (!dbus_message_append_args (msg, 
+				       DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
+				       DBUS_TYPE_INVALID)) {
+		HAL_DEBUG (("Could not append args to dbus message for %s", udi));
+		goto out;
+	}
+	
+	if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &error))) {
+		HAL_DEBUG (("Unmount failed for %s: %s : %s\n", udi, error.name, error.message));
+		goto out;
+	}
+
+	if (dbus_error_is_set (&error)) {
+		HAL_DEBUG (("Unmount failed for %s\n%s : %s\n", udi, error.name, error.message));
+		goto out;
+	}
+
+	HAL_DEBUG (("Succesfully unmounted udi '%s'", udi));
+
+out:
+	dbus_error_free (&error);
+	if (options != NULL)
+		free (options);
+	if (msg != NULL)
+		dbus_message_unref (msg);
+	if (reply != NULL)
+		dbus_message_unref (reply);
+}
+
+static void 
+unmount_childs (LibHalContext *ctx, const char *udi)
+{
+	DBusError error;
+	int num_volumes;
+	char **volumes;
+
+	dbus_error_init (&error);
+
+	/* need to force unmount all partitions */
+	if ((volumes = libhal_manager_find_device_string_match (
+	     ctx, "block.storage_device", udi, &num_volumes, &error)) != NULL) {
+		dbus_error_free (&error);
+		int i;
+
+		for (i = 0; i < num_volumes; i++) {
+			char *vol_udi;
+
+			vol_udi = volumes[i];
+			if (libhal_device_get_property_bool (ctx, vol_udi, "block.is_volume", &error)) {
+				dbus_error_free (&error);
+				if (libhal_device_get_property_bool (ctx, vol_udi, "volume.is_mounted", &error)) {
+					dbus_error_free (&error);
+					HAL_DEBUG (("Forcing unmount of child '%s'", vol_udi));
+					force_unmount (ctx, vol_udi);
+				}
+			}
+		}
+		libhal_free_string_array (volumes);
+	}
+	my_dbus_error_free (&error);
+}
+
+/** Check if a filesystem on a special device file is mounted
+ *
+ *  @param  device_file         Special device file, e.g. /dev/cdrom
+ *  @return                     TRUE iff there is a filesystem system mounted
+ *                              on the special device file
+ */
+static dbus_bool_t
+is_mounted (const char *device_file)
+{
+	FILE *f;
+	dbus_bool_t rc = FALSE;
+	struct mnttab mp;
+	struct mnttab mpref;
+
+	if ((f = fopen ("/etc/mnttab", "r")) == NULL)
+		return rc;
+
+	bzero(&mp, sizeof (mp));
+	bzero(&mpref, sizeof (mpref));
+	mpref.mnt_special = (char *)device_file;
+	if (getmntany(f, &mp, &mpref) == 0) {
+		rc = TRUE;
+	}
+
+	fclose (f);
+	return rc;
+}
+
+void
+close_device (int *fd)
+{
+	if (*fd > 0) {
+		close (*fd);
+		*fd = -1;
+	}
+}
+
+void
+drop_privileges ()
+{
+	priv_set_t *pPrivSet = NULL;
+	priv_set_t *lPrivSet = NULL;
+
+	/*
+	 * Start with the 'basic' privilege set and then remove any
+	 * of the 'basic' privileges that will not be needed.
+	 */
+	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
+		return;
+	}
+
+	/* Clear privileges we will not need from the 'basic' set */
+	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
+	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
+	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
+
+	/* to open logindevperm'd devices */
+	(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
+
+	/* Set the permitted privilege set. */
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
+		return;
+	}
+
+	/* Clear the limit set. */
+	if ((lPrivSet = priv_allocset()) == NULL) {
+		return;
+	}
+
+	priv_emptyset(lPrivSet);
+
+	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
+		return;
+	}
+
+	priv_freeset(lPrivSet);
+}
+
+int 
+main (int argc, char *argv[])
+{
+	char *udi;
+	char *device_file, *raw_device_file;
+	LibHalContext *ctx = NULL;
+	DBusError error;
+	char *bus;
+	char *drive_type;
+	int state, last_state;
+	char *support_media_changed_str;
+	int support_media_changed;
+	int fd = -1;
+
+	if ((udi = getenv ("UDI")) == NULL)
+		goto out;
+	if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL)
+		goto out;
+	if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL)
+		goto out;
+	if ((bus = getenv ("HAL_PROP_STORAGE_BUS")) == NULL)
+		goto out;
+	if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL)
+		goto out;
+
+	drop_privileges ();
+
+	setup_logger ();
+
+	support_media_changed_str = getenv ("HAL_PROP_STORAGE_CDROM_SUPPORT_MEDIA_CHANGED");
+	if (support_media_changed_str != NULL && strcmp (support_media_changed_str, "true") == 0)
+		support_media_changed = TRUE;
+	else
+		support_media_changed = FALSE;
+
+	dbus_error_init (&error);
+
+	if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
+		goto out;
+	}
+	my_dbus_error_free (&error);
+
+	if (!libhal_device_addon_is_ready (ctx, udi, &error)) {
+		goto out;
+	}
+	my_dbus_error_free (&error);
+
+	printf ("Doing addon-storage for %s (bus %s) (drive_type %s) (udi %s)\n", device_file, bus, drive_type, udi);
+
+	last_state = state = DKIO_NONE;
+
+	/* Linux version of this addon attempts to re-open the device O_EXCL
+	 * every 2 seconds, trying to figure out if some other app,
+	 * like a cd burner, is using the device. Aside from questionable
+	 * value of this (apps should use HAL's locked property or/and
+	 * Solaris in_use facility), but also frequent opens/closes
+	 * keeps media constantly spun up. All this needs more thought.
+	 */
+	for (;;) {
+		if (is_mounted (device_file)) {
+			close_device (&fd);
+			sleep (SLEEP_PERIOD);
+		} else if ((fd < 0) && ((fd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0)) {
+			HAL_DEBUG (("open failed for %s: %s", raw_device_file, strerror (errno)));
+			sleep (SLEEP_PERIOD);
+		} else {
+			/* Check if a disc is in the drive */
+			/* XXX initial call always returns inserted
+			 * causing unnecessary rescan - optimize?
+			 */
+			if (ioctl (fd, DKIOCSTATE, &state) == 0) {
+				if (state == last_state) {
+					HAL_DEBUG (("state has not changed %d %s", state, device_file));
+					continue;
+				} else {
+					HAL_DEBUG (("new state %d %s", state, device_file));
+				}
+
+				switch (state) {
+				case DKIO_EJECTED:
+					HAL_DEBUG (("Media removal detected on %s", device_file));
+					last_state = state;
+
+					libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", FALSE, &error);
+					my_dbus_error_free (&error);
+
+					/* attempt to unmount all childs */
+					unmount_childs (ctx, udi);
+
+					/* could have a fs on the main block device; do a rescan to remove it */
+					libhal_device_rescan (ctx, udi, &error);
+					my_dbus_error_free (&error);
+					break;
+
+				case DKIO_INSERTED:
+					HAL_DEBUG (("Media insertion detected on %s", device_file));
+					last_state = state;
+
+					libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", TRUE, &error);
+					my_dbus_error_free (&error);
+
+					/* could have a fs on the main block device; do a rescan to add it */
+					libhal_device_rescan (ctx, udi, &error);
+					my_dbus_error_free (&error);
+					break;
+
+				case DKIO_DEV_GONE:
+					HAL_DEBUG (("Device gone detected on %s", device_file));
+					last_state = state;
+
+					unmount_childs (ctx, udi);
+					close_device (&fd);
+					goto out;
+
+				case DKIO_NONE:
+				default:
+					break;
+				}
+			} else {
+				HAL_DEBUG (("DKIOCSTATE failed: %s\n", strerror(errno)));
+				sleep (SLEEP_PERIOD);
+			}
+		}
+	}
+
+out:
+	if (ctx != NULL) {
+		my_dbus_error_free (&error);
+		libhal_ctx_shutdown (ctx, &error);
+		libhal_ctx_free (ctx);
+	}
+
+	return 0;
+}
diff --git a/hald/solaris/devinfo.c b/hald/solaris/devinfo.c
new file mode 100644
index 0000000..e82317f
--- /dev/null
+++ b/hald/solaris/devinfo.c
@@ -0,0 +1,380 @@
+/***************************************************************************
+ *
+ * devinfo.c : main file for libdevinfo-based device enumeration
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <libdevinfo.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "../hald_runner.h"
+#include "osspec_solaris.h"
+#include "hotplug.h"
+#include "devinfo.h"
+#include "devinfo_pci.h"
+#include "devinfo_storage.h"
+#include "devinfo_ieee1394.h"
+#include "devinfo_usb.h"
+#include "devinfo_misc.h"
+
+void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root);
+HalDevice *devinfo_add_node(HalDevice *parent, di_node_t node);
+
+void
+devinfo_add(HalDevice *parent, gchar *path)
+{
+	di_node_t	root;
+
+	if (strcmp (path, "/") == 0) {
+		if ((root = di_init(path, DINFOCACHE)) == DI_NODE_NIL) {
+			HAL_INFO (("di_init() failed %d", errno));
+			return;
+		}
+	} else {
+		if ((root = di_init(path, DINFOCPYALL)) == DI_NODE_NIL) {
+			HAL_INFO (("di_init() failed %d", errno));
+			return;
+		}
+	}
+
+	devinfo_add_subtree(parent, root, TRUE);
+
+	di_fini (root);
+}
+
+void
+devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root)
+{
+	HalDevice *d;
+	di_node_t root_node, child_node;
+
+	HAL_INFO (("add_subtree: %s", di_node_name (node)));
+
+	root_node = node;
+	do {
+		d = devinfo_add_node (parent, node);
+
+		if ((d != NULL) &&
+		    (child_node = di_child_node (node)) != DI_NODE_NIL) {
+			devinfo_add_subtree (d, child_node, FALSE);
+		}
+
+		node = di_sibling_node (node);
+	} while ((node != DI_NODE_NIL) &&
+		(!is_root || di_parent_node (node) == root_node));
+}
+
+void
+devinfo_set_default_properties (HalDevice *d, HalDevice *parent, di_node_t node, char *devfs_path)
+{
+	char	*driver_name, *s;
+	const char *s1;
+	char	udi[HAL_PATH_MAX];
+
+	if (parent != NULL) {
+		hal_device_property_set_string (d, "info.parent", hal_device_get_udi (parent));
+	} else {
+		hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/local");
+	}
+
+	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+				"/org/freedesktop/Hal/devices%s_%d",
+				devfs_path,
+				di_instance (node));
+	hal_device_set_udi (d, udi);
+	hal_device_property_set_string (d, "info.udi", udi);
+
+	if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) {
+		hal_device_property_set_string (d, "info.product", s);
+	} else {
+		hal_device_property_set_string (d, "info.product", di_node_name (node));
+	}
+
+	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
+
+	if ((driver_name = di_driver_name (node)) != NULL) {
+		hal_device_property_set_string (d, "info.solaris.driver",
+						driver_name);
+	}
+
+
+	/* inherit parent's claim attributes */
+	if (hal_device_property_get_bool (parent, "info.claimed")) {
+		s1 = hal_device_property_get_string (parent, "info.claimed.service");
+		if (s1 != NULL) {
+			hal_device_property_set_bool (d, "info.claimed", TRUE);
+			hal_device_property_set_string (d, "info.claimed.service", s1);
+		}
+	}
+}
+
+/* device handlers, ordered specific to generic */
+static DevinfoDevHandler *devinfo_handlers[] = {
+	&devinfo_computer_handler,
+	&devinfo_cpu_handler,
+	&devinfo_ide_handler,
+	&devinfo_scsi_handler,
+	&devinfo_floppy_handler,
+	&devinfo_usb_handler,
+	&devinfo_ieee1394_handler,
+	&devinfo_pci_handler,
+	&devinfo_lofi_handler,
+	&devinfo_default_handler,
+	NULL
+};
+
+HalDevice *
+devinfo_add_node(HalDevice *parent, di_node_t node)
+{
+	HalDevice *d = NULL;
+	char	*devfs_path;
+	char	*device_type = NULL;
+	DevinfoDevHandler *handler;
+	int	i;
+
+	devfs_path = di_devfs_path (node);
+
+        (void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type",
+	    &device_type);
+
+	for (i = 0; (d == NULL) && (devinfo_handlers[i] != NULL); i++) {
+		handler = devinfo_handlers[i];
+		d = handler->add (parent, node, devfs_path, device_type);
+	}
+
+	di_devfs_path_free(devfs_path);
+
+	HAL_INFO (("add_node: %s", d ? hal_device_get_udi (d) : "none"));
+	return (d);
+}
+
+void
+devinfo_hotplug_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler, int action, int front)
+{
+	HotplugEvent *hotplug_event;
+
+	hotplug_event = g_new0 (HotplugEvent, 1);
+	hotplug_event->action = action;
+	hotplug_event->type = HOTPLUG_EVENT_DEVFS;
+	hotplug_event->d = d;
+	strlcpy (hotplug_event->un.devfs.devfs_path, devfs_path,
+		sizeof (hotplug_event->un.devfs.devfs_path));
+	hotplug_event->un.devfs.handler = handler;
+
+	hotplug_event_enqueue (hotplug_event, front);
+}
+
+void
+devinfo_add_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
+{
+	devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 0);
+}
+
+void
+devinfo_add_enqueue_at_front(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
+{
+	devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 1);
+}
+
+void
+devinfo_remove_enqueue(gchar *devfs_path, DevinfoDevHandler *handler)
+{
+	devinfo_hotplug_enqueue (NULL, devfs_path, handler, HOTPLUG_ACTION_REMOVE, 0);
+}
+
+void
+devinfo_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
+{
+        void *end_token = (void *) userdata1;
+
+        /* 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);
+}
+
+void
+devinfo_callouts_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
+{
+        void *end_token = (void *) userdata1;
+
+        /* Discard device if probing reports failure */
+        if (exit_type != HALD_RUN_SUCCESS || (return_code != 0)) {
+		HAL_INFO (("Probing for %s failed %d", hal_device_get_udi (d), return_code));
+                hal_device_store_remove (hald_get_tdl (), d);
+                g_object_unref (d);
+                hotplug_event_end (end_token);
+		return;
+        }
+
+        /* Merge properties from .fdi files */
+        di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
+        di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
+
+	hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
+}
+
+void
+devinfo_callouts_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
+{
+        void *end_token = (void *) userdata1;
+	DevinfoDevHandler *handler = (DevinfoDevHandler *) userdata2;
+	void (*probing_done) (HalDevice *, guint32, gint, char **, gpointer, gpointer);
+	const gchar *prober;
+	int prober_timeout;
+
+        if (hal_device_property_get_bool (d, "info.ignore")) {
+		HAL_INFO (("Preprobing merged info.ignore==TRUE"));
+
+                /* Leave device with info.ignore==TRUE so we won't pick up children */
+		hal_device_property_remove (d, "info.category");
+		hal_device_property_remove (d, "info.capabilities");
+
+		hal_device_store_remove (hald_get_tdl (), d);
+		hal_device_store_add (hald_get_gdl (), d);
+
+		hotplug_event_end (end_token);
+		return;
+        }
+
+        if (handler != NULL && handler->get_prober != NULL) {
+                prober = handler->get_prober (d, &prober_timeout);
+        } else {
+                prober = NULL;
+	}
+
+	if (handler->probing_done != NULL) {
+		probing_done = handler->probing_done;
+	} else {
+		probing_done = devinfo_callouts_probing_done;
+	}
+
+        if (prober != NULL) {
+                /* probe the device */
+		HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
+                hald_runner_run (d,
+				prober, NULL,
+				prober_timeout,
+				probing_done,
+				(gpointer) end_token, (gpointer) handler);
+	} else {
+		probing_done (d, 0, 0, NULL, userdata1, userdata2);
+	}
+}
+
+/* This is the beginning of hotplug even handling */
+void
+hotplug_event_begin_add_devinfo (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
+{
+	HAL_INFO(("Preprobing udi=%s", hal_device_get_udi (d)));
+
+	if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) {
+		HAL_INFO (("Ignoring device since parent has info.ignore==TRUE"));
+
+		hotplug_event_end (end_token);
+		return;
+	}
+
+        /* add to TDL so preprobing callouts and prober can access it */
+        hal_device_store_add (hald_get_tdl (), d);
+
+        /* Process preprobe fdi files */
+        di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
+
+        /* Run preprobe callouts */
+        hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
+}
+
+void
+devinfo_remove (gchar *devfs_path)
+{
+	devinfo_remove_enqueue ((gchar *)devfs_path, NULL);
+}
+
+/* generate hotplug event for each device in this branch */
+void
+devinfo_remove_branch (gchar *devfs_path, HalDevice *d)
+{
+	GSList *i;
+	GSList *children;
+	HalDevice *child;
+	char *child_devfs_path;
+
+	if (d == NULL) {
+		d = hal_device_store_match_key_value_string (hald_get_gdl (),
+			"solaris.devfs_path", devfs_path);
+		if (d == NULL)
+			return;
+	}
+
+	HAL_INFO (("remove_branch: %s %s\n", devfs_path, hal_device_get_udi (d)));
+
+	/* first remove children */
+	children = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
+		"info.parent", hal_device_get_udi (d));
+        for (i = children; i != NULL; i = g_slist_next (i)) {
+                child = HAL_DEVICE (i->data);
+		HAL_INFO (("remove_branch: child %s\n", hal_device_get_udi (child)));
+		devinfo_remove_branch ((gchar *)hal_device_property_get_string (child, "solaris.devfs_path"), child);
+	}
+	g_slist_free (children);
+	HAL_INFO (("remove_branch: done with children"));
+
+	/* then remove self */
+	HAL_INFO (("remove_branch: queueing %s", devfs_path));
+	devinfo_remove_enqueue (devfs_path, NULL);
+}
+
+void
+devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
+{
+        void *end_token = (void *) userdata1;
+
+        HAL_INFO (("Remove callouts completed udi=%s", hal_device_get_udi (d)));
+
+        if (!hal_device_store_remove (hald_get_gdl (), d)) {
+                HAL_WARNING (("Error removing device"));
+        }
+        g_object_unref (d);
+
+        hotplug_event_end (end_token);
+}
+
+void
+hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token)
+{
+	if (hal_device_has_capability (d, "volume")) {
+		devinfo_volume_hotplug_begin_remove (d, devfs_path, end_token);
+	} else {
+		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
+	}
+}
+
+gboolean
+devinfo_device_rescan (HalDevice *d)
+{
+	if (hal_device_has_capability (d, "block")) {
+		return (devinfo_storage_device_rescan (d));
+	} else {
+		return (FALSE);
+	}
+}
diff --git a/hald/solaris/devinfo.h b/hald/solaris/devinfo.h
new file mode 100644
index 0000000..27b9b48
--- /dev/null
+++ b/hald/solaris/devinfo.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ *
+ * devinfo.h : definitions for libdevinfo-based device enumeration
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo.h	1.2	06/10/13 SMI"
+
+#ifndef DEVINFO_H
+#define DEVINFO_H
+
+#include <glib.h>
+#include <libdevinfo.h>
+
+#include "../hald.h"
+#include "../device_info.h"
+
+typedef struct DevinfoDevHandler_s
+{
+	HalDevice *(*add) (HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+
+	/* yet unused */
+	void (*remove) (char *devfs_path);
+
+	void (*hotplug_begin_add) (HalDevice *d, HalDevice *parent, struct DevinfoDevHandler_s *handler, void *end_token);
+
+	void (*hotplug_begin_remove) (HalDevice *d, struct DevinfoDevHandler_s *handler, void *end_token);
+
+	void (*probing_done) (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2);
+
+	const gchar *(*get_prober) (HalDevice *d, int *timeout);
+} DevinfoDevHandler;
+
+#define PROP_INT(d, node, v, diprop, halprop) \
+	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, diprop, &(v)) > 0) { \
+		hal_device_property_set_int (d, halprop, *(v)); \
+	}
+
+#define PROP_STR(d, node, v, diprop, halprop) \
+	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, diprop, &(v)) > 0) { \
+		hal_device_property_set_string (d, halprop, v); \
+	}
+
+#define PROP_BOOL(d, node, v, diprop, halprop) \
+	hal_device_property_set_bool (d, halprop, \
+	    (di_prop_lookup_ints(DDI_DEV_T_ANY, node, diprop, &(v)) >= 0));
+
+#define	NELEM(a)	(sizeof (a) / sizeof (*(a)))
+
+void devinfo_add (HalDevice *parent, gchar *path);
+void devinfo_set_default_properties (HalDevice *d, HalDevice *parent, di_node_t node, char *devfs_path);
+void devinfo_callouts_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2);
+void devinfo_callouts_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error,
+	gpointer userdata1, gpointer userdata2);
+void devinfo_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2);
+void devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2);
+void hotplug_event_begin_add_devinfo (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
+void devinfo_remove (gchar *path);
+void devinfo_remove_branch (gchar *path, HalDevice *d);
+void hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token);
+void devinfo_hotplug_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler, int action, int front);
+void devinfo_add_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler);
+void devinfo_add_enqueue_at_front(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler);
+void devinfo_remove_enqueue(gchar *devfs_path, DevinfoDevHandler *handler);
+gboolean devinfo_device_rescan (HalDevice *d);
+
+#endif /* DEVINFO_H */
diff --git a/hald/solaris/devinfo_ieee1394.c b/hald/solaris/devinfo_ieee1394.c
new file mode 100644
index 0000000..240af8c
--- /dev/null
+++ b/hald/solaris/devinfo_ieee1394.c
@@ -0,0 +1,90 @@
+/***************************************************************************
+ *
+ * devinfo_ieee1394.c : IEEE 1394/FireWire devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_ieee1394.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <libdevinfo.h>
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "../ids.h"
+#include "hotplug.h"
+#include "devinfo.h"
+#include "devinfo_ieee1394.h"
+
+HalDevice *devinfo_ieee1394_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+static HalDevice *devinfo_scsa1394_add(HalDevice *d, di_node_t node, gchar *devfs_path);
+
+DevinfoDevHandler devinfo_ieee1394_handler = {
+        devinfo_ieee1394_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+
+HalDevice *
+devinfo_ieee1394_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	HalDevice *d = NULL;
+	char	*compat;
+	char	*driver_name;
+
+	/*
+	 * we distinguish 1394 devices by compatible name
+	 * starting with 'firewire'
+	 */
+	if ((di_compatible_names (node, &compat) < 1) ||
+	    (strncmp (compat, "firewire", sizeof ("firewire") - 1) != 0)) {
+		return (NULL);
+	}
+
+	if ((driver_name = di_driver_name (node)) == NULL) {
+		return (NULL);
+	}
+
+	if (strcmp (driver_name, "scsa1394") == 0) {
+		d = devinfo_scsa1394_add (parent, node, devfs_path);
+	}
+
+	return (d);
+}
+
+static HalDevice *
+devinfo_scsa1394_add(HalDevice *parent, di_node_t node, gchar *devfs_path)
+{
+	HalDevice *d = NULL;
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+	hal_device_property_set_string (d, "info.bus", "ieee1394");
+	hal_device_property_set_string (d, "info.product", "FireWire SBP-2 device");
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_ieee1394_handler);
+
+	return (d);
+}
+
diff --git a/hald/solaris/devinfo_ieee1394.h b/hald/solaris/devinfo_ieee1394.h
new file mode 100644
index 0000000..5f82443
--- /dev/null
+++ b/hald/solaris/devinfo_ieee1394.h
@@ -0,0 +1,21 @@
+/***************************************************************************
+ *
+ * devinfo_ieee1394.h : definitions for IEEE 1394 devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_ieee1394.h	1.2	06/10/13 SMI"
+
+#ifndef DEVINFO_IEEE1394_H
+#define DEVINFO_IEEE1394_H
+
+#include "devinfo.h"
+
+extern DevinfoDevHandler devinfo_ieee1394_handler;
+
+#endif /* DEVINFO_IEEE1394_H */
diff --git a/hald/solaris/devinfo_misc.c b/hald/solaris/devinfo_misc.c
new file mode 100644
index 0000000..2e1be2b
--- /dev/null
+++ b/hald/solaris/devinfo_misc.c
@@ -0,0 +1,147 @@
+/***************************************************************************
+ *
+ * devinfo_misc : misc devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_misc.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <libdevinfo.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "devinfo_misc.h"
+
+static HalDevice *devinfo_computer_add(HalDevice *, di_node_t, char *, char *);
+static HalDevice *devinfo_cpu_add(HalDevice *, di_node_t, char *,char *);
+static HalDevice *devinfo_default_add(HalDevice *, di_node_t, char *, char *);
+
+DevinfoDevHandler devinfo_computer_handler = {
+        devinfo_computer_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+DevinfoDevHandler devinfo_cpu_handler = {
+        devinfo_cpu_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+DevinfoDevHandler devinfo_default_handler = {
+        devinfo_default_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+
+static HalDevice *
+devinfo_computer_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	HalDevice *d, *local_d;
+	struct utsname un;
+
+	if (strcmp (devfs_path, "/") != 0) {
+		return (NULL);
+	}
+
+	d = hal_device_new ();
+
+        hal_device_property_set_string (d, "info.bus", "unknown");
+        hal_device_property_set_string (d, "info.product", "Computer");
+        hal_device_property_set_string (d, "info.udi", "/org/freedesktop/Hal/devices/computer");
+        hal_device_set_udi (d, "/org/freedesktop/Hal/devices/computer");
+	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
+
+	if (uname (&un) >= 0) {
+		hal_device_property_set_string (d, "system.kernel.name", un.sysname);
+		hal_device_property_set_string (d, "system.kernel.version", un.release);
+		hal_device_property_set_string (d, "system.kernel.machine", un.machine);
+	}
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_computer_handler);
+
+	/* all devinfo devices belong to the 'local' branch */
+	local_d = hal_device_new ();
+
+	hal_device_property_set_string (local_d, "info.parent", hal_device_get_udi (d));
+        hal_device_property_set_string (local_d, "info.bus", "unknown");
+        hal_device_property_set_string (local_d, "info.product", "Local devices");
+        hal_device_property_set_string (local_d, "info.udi", "/org/freedesktop/Hal/devices/local");
+        hal_device_set_udi (local_d, "/org/freedesktop/Hal/devices/local");
+	hal_device_property_set_string (local_d, "solaris.devfs_path", "/local");
+
+	devinfo_add_enqueue (local_d, "/local", &devinfo_default_handler);
+
+	return (local_d);
+}
+
+static HalDevice *
+devinfo_cpu_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	HalDevice *d;
+
+	if ((device_type == NULL) || (strcmp(device_type, "cpu") != 0)) {
+		return (NULL);
+	}
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+	hal_device_add_capability (d, "processor");
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_cpu_handler);
+
+	return (d);
+}
+
+static HalDevice *
+devinfo_default_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	char *driver_name;
+	const char *parent_path;
+	HalDevice *d;
+
+	/* ignore all children of the 'pseudo' node except lofi */
+	if (parent != NULL) {
+		parent_path = hal_device_property_get_string(parent, "solaris.devfs_path");
+		if ((parent_path != NULL) &&
+		    (strcmp (parent_path, "/pseudo") == 0)) {
+			driver_name = di_driver_name (node);
+			if ((driver_name != NULL) &&
+			    (strcmp (driver_name, "lofi") != 0)) {
+				return (NULL);
+			}
+		}
+	}
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_default_handler);
+
+	return (d);
+}
diff --git a/hald/solaris/devinfo_misc.h b/hald/solaris/devinfo_misc.h
new file mode 100644
index 0000000..bc5f071
--- /dev/null
+++ b/hald/solaris/devinfo_misc.h
@@ -0,0 +1,23 @@
+/***************************************************************************
+ *
+ * devinfo_misc.h : definitions for misc devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_misc.h	1.2	06/10/13 SMI"
+
+#ifndef DEVINFO_MISC_H
+#define DEVINFO_MISC_H
+
+#include "devinfo.h"
+
+extern DevinfoDevHandler devinfo_cpu_handler;
+extern DevinfoDevHandler devinfo_computer_handler;
+extern DevinfoDevHandler devinfo_default_handler;
+
+#endif /* DEVINFO_MISC_H */
diff --git a/hald/solaris/devinfo_pci.c b/hald/solaris/devinfo_pci.c
new file mode 100644
index 0000000..8bd026b
--- /dev/null
+++ b/hald/solaris/devinfo_pci.c
@@ -0,0 +1,122 @@
+/***************************************************************************
+ *
+ * devinfo_pci.c : PCI devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_pci.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <libdevinfo.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "../ids.h"
+#include "devinfo_pci.h"
+
+HalDevice *devinfo_pci_add (HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+
+DevinfoDevHandler devinfo_pci_handler = {
+        devinfo_pci_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+
+HalDevice *devinfo_pci_add (HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	HalDevice *d;
+	char	*s;
+	int	*i;
+	int	vid, pid, svid, spid;
+
+	if ((device_type == NULL) ||
+	    ((strcmp (device_type, "pci") != 0) &&
+	    (strcmp (device_type, "pci-ide") != 0))) {
+		if (parent == NULL) {
+			return (NULL);
+		} else {
+			s = (char *)hal_device_property_get_string (parent, "info.bus");
+			if ((s == NULL) || (strcmp (s, "pci") != 0)) {
+				return (NULL);
+			}
+		}
+	}
+
+	d = hal_device_new ();
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+
+	hal_device_property_set_string (d, "info.bus", "pci");
+
+	vid = pid = svid = spid = 0;
+        if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "vendor-id", &i) > 0) {
+		vid = i[0];
+	}
+        if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "device-id", &i) > 0) {
+		pid = i[0];
+	}
+        if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "subsystem-vendor-id", &i) > 0) {
+		svid = i[0];
+	}
+        if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "subsystem-id", &i) > 0) {
+		spid = i[0];
+	}
+	hal_device_property_set_int (d, "pci.vendor_id", vid);
+	hal_device_property_set_int (d, "pci.product_id", pid);
+	hal_device_property_set_int (d, "pci.subsys_vendor_id", svid);
+	hal_device_property_set_int (d, "pci.subsys_product_id", spid);
+
+        {
+                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);
+                }
+
+                if (product_name != NULL) {
+                        hal_device_property_set_string (d, "pci.product", product_name);
+                        hal_device_property_set_string (d, "info.product", product_name);
+                }
+
+                if (subsys_vendor_name != NULL) {
+                        hal_device_property_set_string (d, "pci.subsys_vendor",
+subsys_vendor_name);
+                }
+
+                if (subsys_product_name != NULL) {
+                        hal_device_property_set_string (d, "pci.subsys_product", subsys_product_name);
+                }
+        }
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_pci_handler);
+
+	return (d);
+}
+
diff --git a/hald/solaris/devinfo_pci.h b/hald/solaris/devinfo_pci.h
new file mode 100644
index 0000000..d0d0d8a
--- /dev/null
+++ b/hald/solaris/devinfo_pci.h
@@ -0,0 +1,21 @@
+/***************************************************************************
+ *
+ * devinfo_pci.h : definitions for PCI devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_pci.h	1.2	06/10/13 SMI"
+
+#ifndef DEVINFO_PCI_H
+#define DEVINFO_PCI_H
+
+#include "devinfo.h"
+
+extern DevinfoDevHandler devinfo_pci_handler;
+
+#endif /* DEVINFO_PCI_H */
diff --git a/hald/solaris/devinfo_storage.c b/hald/solaris/devinfo_storage.c
new file mode 100644
index 0000000..0026b70
--- /dev/null
+++ b/hald/solaris/devinfo_storage.c
@@ -0,0 +1,1673 @@
+/***************************************************************************
+ *
+ * devinfo_storage.c : storage devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_storage.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <libdevinfo.h>
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "../hald_runner.h"
+#include "hotplug.h"
+#include "devinfo.h"
+#include "devinfo_misc.h"
+#include "devinfo_storage.h"
+#include "osspec_solaris.h"
+
+#ifdef sparc
+#define	WHOLE_DISK	"s2"
+#else
+#define	WHOLE_DISK	"p0"
+#endif
+
+/* some devices,especially CDROMs, may take a while to be probed (values in ms) */
+#define	DEVINFO_PROBE_STORAGE_TIMEOUT	60000
+#define	DEVINFO_PROBE_VOLUME_TIMEOUT	60000
+
+typedef struct devinfo_storage_minor {
+	char	*devpath;
+	char	*devlink;
+	char	*slice;
+	dev_t	dev;
+	int	dosnum;	/* dos disk number or -1 */
+} devinfo_storage_minor_t;
+
+HalDevice *devinfo_ide_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+static HalDevice *devinfo_ide_host_add(HalDevice *parent, di_node_t node, char *devfs_path);
+static HalDevice *devinfo_ide_device_add(HalDevice *parent, di_node_t node, char *devfs_path);
+static HalDevice *devinfo_ide_storage_add(HalDevice *grampa, HalDevice *parent, di_node_t node, char *devfs_path);
+HalDevice *devinfo_scsi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+static HalDevice *devinfo_scsi_storage_add(HalDevice *grampa, HalDevice *parent, di_node_t node, char *devfs_path);
+HalDevice *devinfo_floppy_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+static void devinfo_floppy_add_volume(HalDevice *parent, di_node_t node);
+static HalDevice *devinfo_lofi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+static void devinfo_lofi_add_minor(HalDevice *parent, di_node_t node, char *minor_path, char *devlink, dev_t dev);
+static int walk_devlinks(di_devlink_t devlink, void *arg);
+static char *get_devlink(di_devlink_handle_t devlink_hdl, char *path);
+static void devinfo_storage_minors(HalDevice *parent, di_node_t node, gchar *devfs_path, gboolean);
+static struct devinfo_storage_minor *devinfo_storage_new_minor(char *maindev_path, char *slice,
+    char *devlink, dev_t dev, int dosnum);
+static void devinfo_storage_free_minor(struct devinfo_storage_minor *m);
+HalDevice *devinfo_volume_add(HalDevice *parent, di_node_t node, devinfo_storage_minor_t *m);
+static void devinfo_volume_preprobing_done(HalDevice *d, gpointer userdata1, gpointer userdata2);
+static void devinfo_volume_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
+static void devinfo_storage_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
+static void devinfo_storage_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2);
+const gchar *devinfo_volume_get_prober (HalDevice *d, int *timeout);
+const gchar *devinfo_storage_get_prober (HalDevice *d, int *timeout);
+
+static char *devinfo_scsi_dtype2str(int dtype);
+static char *devinfo_volume_get_slice_name (char *devlink);
+static gboolean dos_to_dev(char *path, char **devpath, int *partnum);
+static gboolean is_dos_path(char *path, int *partnum);
+
+static void devinfo_storage_set_nicknames (HalDevice *d);
+
+DevinfoDevHandler devinfo_ide_handler = {
+        devinfo_ide_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+DevinfoDevHandler devinfo_scsi_handler = {
+        devinfo_scsi_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+DevinfoDevHandler devinfo_floppy_handler = {
+        devinfo_floppy_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+DevinfoDevHandler devinfo_lofi_handler = {
+        devinfo_lofi_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+DevinfoDevHandler devinfo_storage_handler = {
+	NULL,
+	NULL,
+	devinfo_storage_hotplug_begin_add,
+	NULL,
+	devinfo_storage_probing_done,
+	devinfo_storage_get_prober
+};
+DevinfoDevHandler devinfo_volume_handler = {
+	NULL,
+	NULL,
+	devinfo_volume_hotplug_begin_add,
+	NULL,
+	NULL,
+	devinfo_volume_get_prober
+};
+
+/* IDE */
+
+HalDevice *
+devinfo_ide_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	char	*s;
+
+	if ((device_type != NULL) && (strcmp(device_type, "ide") == 0)) {
+		return (devinfo_ide_host_add(parent, node, devfs_path));
+	}
+
+        if ((di_prop_lookup_strings (DDI_DEV_T_ANY, node, "class", &s) > 0) &&
+	    (strcmp (s, "dada") == 0)) {
+		return (devinfo_ide_device_add(parent, node, devfs_path));
+	}
+
+	return (NULL);
+}
+
+static HalDevice *
+devinfo_ide_host_add(HalDevice *parent, di_node_t node, char *devfs_path)
+{
+	HalDevice *d;
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+	hal_device_property_set_string (d, "info.product", "IDE host controller");
+	hal_device_property_set_string (d, "info.bus", "ide_host");
+	hal_device_property_set_int (d, "ide_host.number", 0); /* XXX */
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_ide_handler);
+
+	return (d);
+}
+
+static HalDevice *
+devinfo_ide_device_add(HalDevice *parent, di_node_t node, char *devfs_path)
+{
+	HalDevice *d;
+
+	d = hal_device_new();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+        hal_device_property_set_string (parent, "info.product", "IDE device");
+	hal_device_property_set_string (parent, "info.bus", "ide");
+	hal_device_property_set_int (parent, "ide.host", 0); /* XXX */
+	hal_device_property_set_int (parent, "ide.channel", 0);
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_ide_handler);
+
+	return (devinfo_ide_storage_add (parent, d, node, devfs_path));
+}
+
+static HalDevice *
+devinfo_ide_storage_add(HalDevice *grampa, HalDevice *parent, di_node_t node, char *devfs_path)
+{
+	HalDevice *d;
+	char	*s;
+	int	*i;
+	char	*driver_name;
+	char	udi[HAL_PATH_MAX];
+
+	if ((driver_name = di_driver_name (node)) == NULL) {
+		return (NULL);
+	}
+
+        d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+        hal_device_property_set_string (d, "info.category", "storage");
+
+        hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+                "%s/%s%d", hal_device_get_udi (parent), driver_name, di_instance (node));
+        hal_device_set_udi (d, udi);
+        hal_device_property_set_string (d, "info.udi", udi);
+	PROP_STR(d, node, s, "devid", "info.product");
+
+        hal_device_add_capability (d, "storage");
+        hal_device_property_set_string (d, "storage.bus", "ide");
+        hal_device_property_set_int (d, "storage.lun", 0);
+	hal_device_property_set_string (d, "storage.drive_type", "disk");
+
+	PROP_BOOL(d, node, i, "hotpluggable", "storage.hotpluggable");
+	PROP_BOOL(d, node, i, "removable-media", "storage.removable");
+
+        hal_device_property_set_bool (d, "storage.media_check_enabled", FALSE);
+
+	/* XXX */
+        hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
+
+	hal_device_add_capability (d, "block");
+
+	devinfo_storage_minors (d, node, (char *)devfs_path, FALSE);
+
+	return (d);
+}
+
+/* SCSI */
+
+HalDevice *
+devinfo_scsi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	int	*i;
+	char	*driver_name;
+	HalDevice *d;
+	char	udi[HAL_PATH_MAX];
+
+	driver_name = di_driver_name (node);
+	if ((driver_name == NULL) || (strcmp (driver_name, "sd") != 0)) {
+		return (NULL);
+	}
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+	hal_device_property_set_string (d, "info.bus", "scsi");
+
+        hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+                "%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
+        hal_device_set_udi (d, udi);
+        hal_device_property_set_string (d, "info.udi", udi);
+
+	hal_device_property_set_int (d, "scsi.host", 
+		hal_device_property_get_int (parent, "scsi_host.host"));
+	hal_device_property_set_int (d, "scsi.bus", 0);
+	PROP_INT(d, node, i, "target", "scsi.target");
+	PROP_INT(d, node, i, "lun", "scsi.lun");
+        hal_device_property_set_string (d, "info.product", "SCSI Device");
+
+        devinfo_add_enqueue (d, devfs_path, &devinfo_scsi_handler);
+
+        return (devinfo_scsi_storage_add (parent, d, node, devfs_path));
+}
+
+static HalDevice *
+devinfo_scsi_storage_add(HalDevice *grampa, HalDevice *parent, di_node_t node, char *devfs_path)
+{
+	HalDevice *d;
+	int	*i;
+	char	*s;
+	char	udi[HAL_PATH_MAX];
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+        hal_device_property_set_string (d, "info.category", "storage");
+
+        hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+		"%s/sd%d", hal_device_get_udi (parent), di_instance (node));
+        hal_device_set_udi (d, udi);
+        hal_device_property_set_string (d, "info.udi", udi);
+	PROP_STR(d, node, s, "inquiry-product-id", "info.product");
+
+        hal_device_add_capability (d, "storage");
+
+        hal_device_property_set_int (d, "storage.lun",
+		hal_device_property_get_int (parent, "scsi.lun"));
+	PROP_BOOL(d, node, i, "hotpluggable", "storage.hotpluggable");
+	PROP_BOOL(d, node, i, "removable-media", "storage.removable");
+        hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
+
+	/*
+	 * We have to enable polling not only for drives with removable media,
+	 * but also for hotpluggable devices, because when a disk is
+	 * unplugged while busy/mounted, there is not sysevent generated.
+	 * Instead, the HBA driver (scsa2usb, scsa1394) will notify sd driver
+	 * and the latter will report DKIO_DEV_GONE via DKIOCSTATE ioctl.
+	 * So we have to enable media check so that hald-addon-storage notices
+	 * the "device gone" condition and unmounts all associated volumes.
+	 */
+	hal_device_property_set_bool (d, "storage.media_check_enabled",
+	    ((di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media", &i) >= 0) ||
+	    (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable", &i) >= 0)));
+
+        if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
+	    &i) > 0) {
+		s = devinfo_scsi_dtype2str (*i);
+        	hal_device_property_set_string (d, "storage.drive_type", s);
+
+		if (strcmp (s, "cdrom") == 0) {
+			hal_device_add_capability (d, "storage.cdrom");
+			hal_device_property_set_bool (d, "storage.no_partitions_hint", TRUE);
+        		hal_device_property_set_bool (d, "storage.requires_eject", TRUE);
+		}
+	}
+
+        hal_device_add_capability (d, "block");
+
+	devinfo_storage_minors (d, node, devfs_path, FALSE);
+
+	return (d);
+}
+
+static char *devinfo_scsi_dtype2str(int dtype)
+{
+        char *dtype2str[] = {
+                "disk"	,         /* DTYPE_DIRECT         0x00 */
+                "tape"	,         /* DTYPE_SEQUENTIAL     0x01 */
+                "printer",         /* DTYPE_PRINTER        0x02 */
+                "processor",         /* DTYPE_PROCESSOR      0x03 */
+                "worm"	,         /* DTYPE_WORM           0x04 */
+                "cdrom"	,         /* DTYPE_RODIRECT       0x05 */
+                "scanner",         /* DTYPE_SCANNER        0x06 */
+                "cdrom"	,         /* DTYPE_OPTICAL        0x07 */
+                "changer",         /* DTYPE_CHANGER        0x08 */
+                "comm"	,         /* DTYPE_COMM           0x09 */
+                "scsi"	,         /* DTYPE_???            0x0A */
+                "scsi"	,         /* DTYPE_???            0x0B */
+                "array_ctrl",         /* DTYPE_ARRAY_CTRL     0x0C */
+                "esi"	,         /* DTYPE_ESI            0x0D */
+                "disk"	          /* DTYPE_RBC            0x0E */
+        };
+
+        if (dtype < NELEM(dtype2str)) {
+                return (dtype2str[dtype]);
+        } else {
+		return ("scsi");
+        }
+
+}
+
+/* floppy */
+
+HalDevice *
+devinfo_floppy_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	char	*driver_name;
+	char	*raw;
+	char	udi[HAL_PATH_MAX];
+	di_devlink_handle_t devlink_hdl;
+        int     major;
+        di_minor_t minor;
+        dev_t   dev;
+	HalDevice *d = NULL;
+        char    *minor_path = NULL;
+	char	*devlink = NULL;
+
+	driver_name = di_driver_name (node);
+	if ((driver_name == NULL) || (strcmp (driver_name, "fd") != 0)) {
+		return (NULL);
+	}
+
+	/*
+	 * The only minor node we're interested in is /dev/diskette*
+	 */
+	major = di_driver_major(node);
+	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
+		return (NULL);
+	}
+	minor = DI_MINOR_NIL;
+	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+		dev = di_minor_devt(minor);
+		if ((major != major(dev)) ||
+		    (di_minor_type(minor) != DDM_MINOR) ||
+		    (di_minor_spectype(minor) != S_IFBLK) ||
+		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
+			continue;
+		}
+		if (((devlink = get_devlink(devlink_hdl, minor_path)) != NULL) &&
+		    (strncmp (devlink, "/dev/diskette", sizeof ("/dev/diskette") - 1) == 0)) {
+			break;
+		}
+		di_devfs_path_free (minor_path);
+		minor_path = NULL;
+		free(devlink);
+		devlink = NULL;
+	}
+	di_devlink_fini (&devlink_hdl);
+
+	if ((devlink == NULL) || (minor_path == NULL)) {
+		HAL_INFO (("floppy devlink not found %s", devfs_path));
+		goto out;
+	}
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+        hal_device_property_set_string (d, "info.category", "storage");
+        hal_device_add_capability (d, "storage");
+       	hal_device_property_set_string (d, "storage.bus", "platform");
+        hal_device_property_set_bool (d, "storage.hotpluggable", FALSE);
+        hal_device_property_set_bool (d, "storage.removable", TRUE);
+        hal_device_property_set_bool (d, "storage.requires_eject", TRUE);
+        hal_device_property_set_bool (d, "storage.media_check_enabled", FALSE);
+       	hal_device_property_set_string (d, "storage.drive_type", "floppy");
+
+        hal_device_add_capability (d, "block");
+	hal_device_property_set_bool (d, "block.is_volume", FALSE);
+	hal_device_property_set_int (d, "block.major", major(dev));
+	hal_device_property_set_int (d, "block.minor", minor(dev));
+	hal_device_property_set_string (d, "block.device", devlink);
+	raw = dsk_to_rdsk (devlink);
+	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
+	free (raw);
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_storage_handler);
+
+	/* trigger initial probe-volume */
+	devinfo_floppy_add_volume(d, node);
+
+out:
+	di_devfs_path_free (minor_path);
+	free(devlink);
+
+	return (d);
+}
+
+static void
+devinfo_floppy_add_volume(HalDevice *parent, di_node_t node)
+{
+	char	*devlink;
+	char	*devfs_path;
+	int	minor, major;
+	dev_t	dev;
+	struct devinfo_storage_minor *m;
+
+	devfs_path = (char *)hal_device_property_get_string (parent, "solaris.devfs_path");
+	devlink = (char *)hal_device_property_get_string (parent, "block.device");
+	major = hal_device_property_get_int (parent, "block.major");
+	minor = hal_device_property_get_int (parent, "block.minor");
+	dev = makedev (major, minor);
+
+	m = devinfo_storage_new_minor (devfs_path, WHOLE_DISK, devlink, dev, -1);
+	devinfo_volume_add (parent, node, m);
+	devinfo_storage_free_minor (m);
+}
+
+/*
+ * After reprobing storage, reprobe its volumes.
+ */
+static void
+devinfo_floppy_rescan_probing_done (HalDevice *d, guint32 exit_type, gint return_code,
+    char **error, gpointer userdata1, gpointer userdata2)
+{
+        void *end_token = (void *) userdata1;
+	const char *devfs_path;
+	di_node_t node;
+	HalDevice *v;
+
+	if (!hal_device_property_get_bool (d, "storage.removable.media_available")) {
+		HAL_INFO (("no floppy media", hal_device_get_udi (d)));
+
+		/* remove child (can only be single volume) */
+		if (((v = hal_device_store_match_key_value_string (hald_get_gdl(),
+        	    "info.parent", hal_device_get_udi (d))) != NULL) &&
+		    ((devfs_path = hal_device_property_get_string (v,
+		    "solaris.devfs_path")) != NULL)) {
+			devinfo_remove_enqueue ((char *)devfs_path, NULL);
+		}
+	} else {
+		HAL_INFO (("floppy media found", hal_device_get_udi (d)));
+
+		if ((devfs_path = hal_device_property_get_string(d, "solaris.devfs_path")) == NULL) {
+			HAL_INFO (("no devfs_path", hal_device_get_udi (d)));
+			hotplug_event_process_queue ();
+			return;
+		}
+		if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
+			HAL_INFO (("di_init %s failed %d", devfs_path, errno));
+			hotplug_event_process_queue ();
+			return;
+		}
+
+		devinfo_floppy_add_volume (d, node);
+
+		di_fini (node);
+	}
+
+	hotplug_event_process_queue ();
+}
+	
+/* lofi */
+
+HalDevice *
+devinfo_lofi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	return (devinfo_lofi_add_major(parent,node, devfs_path, device_type, FALSE, NULL));
+}
+
+HalDevice *
+devinfo_lofi_add_major(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type,
+    gboolean rescan, HalDevice *lofi_d)
+{
+	char	*driver_name;
+	HalDevice *d = NULL;
+	char	udi[HAL_PATH_MAX];
+	di_devlink_handle_t devlink_hdl;
+        int     major;
+        di_minor_t minor;
+        dev_t   dev;
+        char    *minor_path = NULL;
+        char    *devpath, *devlink;
+
+	driver_name = di_driver_name (node);
+	if ((driver_name == NULL) || (strcmp (driver_name, "lofi") != 0)) {
+		return (NULL);
+	}
+
+	if (!rescan) {
+		d = hal_device_new ();
+
+		devinfo_set_default_properties (d, parent, node, devfs_path);
+		hal_device_property_set_string (d, "info.bus", "pseudo");
+
+        	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+                	"%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
+        	hal_device_set_udi (d, udi);
+        	hal_device_property_set_string (d, "info.udi", udi);
+
+        	devinfo_add_enqueue (d, devfs_path, &devinfo_lofi_handler);
+	} else {
+		d = lofi_d;
+	}
+
+	/*
+	 * Unlike normal storage, as in devinfo_storage_minors(), where
+	 * sd instance -> HAL storage, sd minor node -> HAL volume,
+	 * lofi always has one instance, lofi minor -> HAL storage.
+	 * lofi storage never has slices, but it can have
+	 * embedded pcfs partitions that fstyp would recognize
+	 */
+	major = di_driver_major(node);
+	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
+		return (d);
+	}
+	minor = DI_MINOR_NIL;
+	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+		dev = di_minor_devt(minor);
+		if ((major != major(dev)) ||
+		    (di_minor_type(minor) != DDM_MINOR) ||
+		    (di_minor_spectype(minor) != S_IFBLK) ||
+		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
+			continue;
+		}
+		if ((devlink = get_devlink(devlink_hdl, minor_path)) == NULL) {
+			di_devfs_path_free (minor_path);
+        		continue;
+		}
+
+		if (!rescan ||
+		    (hal_device_store_match_key_value_string (hald_get_gdl (),
+		    "solaris.devfs_path", minor_path) == NULL)) {
+			devinfo_lofi_add_minor(d, node, minor_path, devlink, dev);
+		}
+
+		di_devfs_path_free (minor_path);
+		free(devlink);
+	}
+	di_devlink_fini (&devlink_hdl);
+
+	return (d);
+}
+
+static void
+devinfo_lofi_add_minor(HalDevice *parent, di_node_t node, char *minor_path, char *devlink, dev_t dev)
+{
+	HalDevice *d;
+	char	*raw;
+	char	*doslink;
+	char	dospath[64];
+	struct devinfo_storage_minor *m;
+	int	i;
+
+	/* add storage */
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, minor_path);
+        hal_device_property_set_string (d, "info.category", "storage");
+        hal_device_add_capability (d, "storage");
+       	hal_device_property_set_string (d, "storage.bus", "lofi");
+        hal_device_property_set_bool (d, "storage.hotpluggable", TRUE);
+        hal_device_property_set_bool (d, "storage.removable", FALSE);
+        hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
+       	hal_device_property_set_string (d, "storage.drive_type", "disk");
+        hal_device_add_capability (d, "block");
+	hal_device_property_set_int (d, "block.major", major(dev));
+	hal_device_property_set_int (d, "block.minor", minor(dev));
+	hal_device_property_set_string (d, "block.device", devlink);
+	raw = dsk_to_rdsk (devlink);
+	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
+	free (raw);
+	hal_device_property_set_bool (d, "block.is_volume", FALSE);
+
+	devinfo_add_enqueue (d, minor_path, &devinfo_storage_handler);
+
+	/* add volumes: one on main device and a few pcfs candidates */
+	m = devinfo_storage_new_minor(minor_path, WHOLE_DISK, devlink, dev, -1);
+	devinfo_volume_add (d, node, m);
+	devinfo_storage_free_minor (m);
+
+	doslink = (char *)calloc (1, strlen (devlink) + sizeof (":NNN") + 1);
+	if (doslink != NULL) {
+		for (i = 1; i < 16; i++) {
+			snprintf(dospath, sizeof (dospath), WHOLE_DISK":%d", i);
+			sprintf(doslink, "%s:%d", devlink, i);
+			m = devinfo_storage_new_minor(minor_path, dospath, doslink, dev, i);
+			devinfo_volume_add (d, node, m);
+			devinfo_storage_free_minor (m);
+		}
+		free (doslink);
+	}
+}
+
+void
+devinfo_lofi_remove_minor(char *parent_devfs_path, char *name)
+{
+	GSList *i;
+	GSList *devices;
+	HalDevice *d = NULL;
+	const char *devfs_path;
+
+	devices = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
+		"block.solaris.raw_device", name);
+        for (i = devices; i != NULL; i = g_slist_next (i)) {
+		if (hal_device_has_capability (HAL_DEVICE (i->data), "storage")) {
+			d = HAL_DEVICE (i->data);
+			break;
+		}
+	}
+	g_slist_free (devices);
+
+	if (d == NULL) {
+		HAL_INFO (("device not found %s", name));
+		return;
+	}
+
+	if ((devfs_path = hal_device_property_get_string (d,
+	    "solaris.devfs_path")) == NULL) {
+		HAL_INFO (("devfs_path not found %s", hal_device_get_udi (d)));
+		return;
+	}
+
+	if (d != NULL) {
+		devinfo_remove_branch ((char *)devfs_path, d);
+	}
+}
+
+/* common storage */
+
+static int
+walk_devlinks(di_devlink_t devlink, void *arg)
+{
+        char    **path= (char **)arg;
+
+        *path = strdup(di_devlink_path(devlink));
+
+        return (DI_WALK_TERMINATE);
+}
+
+static char *
+get_devlink(di_devlink_handle_t devlink_hdl, char *path)
+{
+        char    *devlink_path = NULL;
+
+        (void) di_devlink_walk(devlink_hdl, NULL, path,
+            DI_PRIMARY_LINK, &devlink_path, walk_devlinks);
+
+        return (devlink_path);
+}
+
+static void
+devinfo_storage_free_minor(struct devinfo_storage_minor *m)
+{
+	if (m != NULL) {
+		free (m->slice);
+		free (m->devlink);
+		free (m->devpath);
+		free (m);
+	}
+}
+
+static struct devinfo_storage_minor *
+devinfo_storage_new_minor(char *maindev_path, char *slice, char *devlink, dev_t dev, int dosnum)
+{
+	struct devinfo_storage_minor *m;
+	int pathlen;
+	char *devpath;
+
+	m = (struct devinfo_storage_minor *)calloc (sizeof (struct devinfo_storage_minor), 1);
+	if (m != NULL) {
+		/*
+		 * For volume's devfs_path we'll use minor_path/slice instead of
+		 * minor_path which we use for parent storage device.
+		 */
+		pathlen = strlen (maindev_path) + strlen (slice) + 2;
+		devpath = (char *)calloc (1, pathlen);
+		snprintf(devpath, pathlen, "%s/%s", maindev_path, slice);
+
+		m->devpath = devpath;
+		m->devlink = strdup (devlink);
+		m->slice = strdup (slice);
+		m->dev = dev;
+		m->dosnum = dosnum;
+		if ((m->devpath == NULL) || (m->devlink == NULL)) {
+			devinfo_storage_free_minor (m);
+			m = NULL;
+		}
+	}
+	return (m);
+}
+
+/*
+ * Storage minor nodes are potential "volume" objects.
+ * This function also completes building the parent object (main storage device).
+ */
+static void
+devinfo_storage_minors(HalDevice *parent, di_node_t node, gchar *devfs_path, gboolean rescan)
+{
+	di_devlink_handle_t devlink_hdl;
+	gboolean is_cdrom;
+	const char *whole_disk;
+	int     major;
+	di_minor_t minor;
+	dev_t   dev;
+	char    *minor_path = NULL;
+	char    *maindev_path = NULL;
+	char    *devpath, *devlink;
+	int	doslink_len;
+	char	*doslink;
+	char	dospath[64];
+	char    *slice;
+	int	pathlen;
+	int	i;
+	char	*raw;
+	boolean_t maindev_is_d0;
+	GQueue	*mq;
+	HalDevice *volume;
+	struct devinfo_storage_minor *m;
+	struct devinfo_storage_minor *maindev = NULL;
+
+	/* for cdroms whole disk is always s2 */
+	is_cdrom = hal_device_has_capability (parent, "storage.cdrom");
+	whole_disk = is_cdrom ? "s2" : WHOLE_DISK;
+
+	major = di_driver_major(node);
+
+	/* the "whole disk" p0/s2/d0 node must come first in the hotplug queue
+	 * so we put other minor nodes on the local queue and move to the
+	 * hotplug queue up in the end
+	 */
+	if ((mq = g_queue_new()) == NULL) {
+		goto err;
+	}
+	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
+		g_queue_free (mq);
+		goto err;
+	}
+	minor = DI_MINOR_NIL;
+	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+		dev = di_minor_devt(minor);
+		if ((major != major(dev)) ||
+		    (di_minor_type(minor) != DDM_MINOR) ||
+		    (di_minor_spectype(minor) != S_IFBLK) ||
+		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
+			continue;
+		}
+		if ((devlink = get_devlink(devlink_hdl, minor_path)) == NULL) {
+			di_devfs_path_free (minor_path);
+        		continue;
+		}
+
+		slice = devinfo_volume_get_slice_name (devlink);
+		if (strlen (slice) < 2) {
+			free (devlink);
+			di_devfs_path_free (minor_path);
+			continue;
+		}
+
+		/* ignore p1..N - we'll use p0:N instead */
+		if ((strlen (slice) > 1) && (slice[0] == 'p') && isdigit(slice[1]) &&
+		    ((atol(&slice[1])) > 0)) {
+			free (devlink);
+			di_devfs_path_free (minor_path);
+			continue;
+		}
+
+		m = devinfo_storage_new_minor(minor_path, slice, devlink, dev, -1);
+		if (m == NULL) {
+			free (devlink);
+			di_devfs_path_free (minor_path);
+			continue;
+		}
+
+		/* main device is either s2/p0 or d0, the latter taking precedence */
+		if ((strcmp (slice, "d0") == 0) ||
+		    (((strcmp (slice, whole_disk) == 0) && (maindev == NULL)))) {
+			if (maindev_path != NULL) {
+				di_devfs_path_free (maindev_path);
+			}
+			maindev_path = minor_path;
+			maindev = m;
+			g_queue_push_head (mq, maindev);
+		} else {
+			di_devfs_path_free (minor_path);
+			g_queue_push_tail (mq, m);
+		}
+
+		free (devlink);
+	}
+	di_devlink_fini (&devlink_hdl);
+
+	if (maindev == NULL) {
+		/* shouldn't typically happen */
+		while (!g_queue_is_empty (mq)) {
+			devinfo_storage_free_minor (g_queue_pop_head (mq));
+		}
+		goto err;
+	}
+
+	/* first enqueue main storage device */
+	if (!rescan) {
+		hal_device_property_set_int (parent, "block.major", major);
+		hal_device_property_set_int (parent, "block.minor", minor(maindev->dev));
+		hal_device_property_set_string (parent, "block.device", maindev->devlink);
+		raw = dsk_to_rdsk (maindev->devlink);
+		hal_device_property_set_string (parent, "block.solaris.raw_device", raw);
+		free (raw);
+		hal_device_property_set_bool (parent, "block.is_volume", FALSE);
+		hal_device_property_set_string (parent, "solaris.devfs_path", maindev_path);
+		devinfo_add_enqueue (parent, maindev_path, &devinfo_storage_handler);
+	}
+
+	/* add virtual dos volumes to enable pcfs probing */
+	if (!is_cdrom) {
+		doslink_len = strlen (maindev->devlink) + sizeof (":NNN") + 1;
+		if ((doslink = (char *)calloc (1, doslink_len)) != NULL) {
+			for (i = 1; i < 16; i++) {
+				snprintf(dospath, sizeof (dospath), "%s:%d", maindev->slice, i);
+				snprintf(doslink, doslink_len, "%s:%d", maindev->devlink, i);
+				m = devinfo_storage_new_minor(maindev_path, dospath, doslink, maindev->dev, i);
+				g_queue_push_tail (mq, m);
+			}
+			free (doslink);
+		}
+	}
+
+	maindev_is_d0 = (strcmp (maindev->slice, "d0") == 0);
+
+	/* enqueue all volumes */
+	while (!g_queue_is_empty (mq)) {
+		m = g_queue_pop_head (mq);
+
+		/* if main device is d0, we'll throw away s2/p0 */
+		if (maindev_is_d0 && (strcmp (m->slice, whole_disk) == 0)) {
+			devinfo_storage_free_minor (m);
+			continue;
+		}
+		/* don't do p0 on cdrom */
+		if (is_cdrom && (strcmp (m->slice, "p0") == 0)) {
+			devinfo_storage_free_minor (m);
+			continue;
+		}
+		if (rescan) {
+			/* in rescan mode, don't reprobe existing volumes */
+			/* XXX detect volume removal? */
+			volume = hal_device_store_match_key_value_string (hald_get_gdl (),
+			    "solaris.devfs_path", m->devpath);
+			if ((volume == NULL) || !hal_device_has_capability(volume, "volume")) {
+				devinfo_volume_add (parent, node, m);
+			} else {
+				HAL_INFO(("rescan volume exists %s", m->devpath));
+			}
+		} else {
+			devinfo_volume_add (parent, node, m);
+		}
+		devinfo_storage_free_minor (m);
+	}
+
+	if (maindev_path != NULL) {
+		di_devfs_path_free (maindev_path);
+	}
+
+	return;
+
+err:
+	if (maindev_path != NULL) {
+		di_devfs_path_free (maindev_path);
+	}
+	if (!rescan) {
+		devinfo_add_enqueue (parent, devfs_path, &devinfo_storage_handler);
+	}
+}
+
+HalDevice *
+devinfo_volume_add(HalDevice *parent, di_node_t node, devinfo_storage_minor_t *m)
+{
+	HalDevice *d;
+	char	*raw;
+        char    udi[HAL_PATH_MAX];
+	char	*devfs_path = m->devpath;
+	char	*devlink = m->devlink;
+	dev_t	dev = m->dev;
+	int	dosnum = m->dosnum;
+	char	*slice = m->slice;
+
+	HAL_INFO (("volume_add: devfs_path=%s devlink=%s", devfs_path, devlink));
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+        hal_device_property_set_string (d, "info.category", "volume");
+
+       	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+		"%s/%s", hal_device_get_udi (parent), slice);
+        hal_device_set_udi (d, udi);
+        hal_device_property_set_string (d, "info.udi", udi);
+        hal_device_property_set_string (d, "info.product", slice);
+
+       	hal_device_add_capability (d, "volume");
+       	hal_device_add_capability (d, "block");
+	hal_device_property_set_int (d, "block.major", major (dev));
+	hal_device_property_set_int (d, "block.minor", minor (dev));
+	hal_device_property_set_string (d, "block.device", devlink);
+	raw = dsk_to_rdsk (devlink);
+	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
+	free (raw);
+	hal_device_property_set_string (d, "block.solaris.slice", slice);
+	hal_device_property_set_bool (d, "block.is_volume", TRUE); /* XXX */
+
+	hal_device_property_set_string (d, "block.storage_device", hal_device_get_udi (parent));
+
+	/* set volume defaults */
+	hal_device_property_set_string (d, "volume.fstype", "");
+	hal_device_property_set_string (d, "volume.fsusage", "");
+	hal_device_property_set_string (d, "volume.fsversion", "");
+	hal_device_property_set_string (d, "volume.uuid", "");
+	hal_device_property_set_string (d, "volume.label", "");
+	hal_device_property_set_string (d, "volume.mount_point", "");
+	hal_device_property_set_bool (d, "volume.is_mounted", FALSE);
+	if (strcmp (hal_device_property_get_string (parent, "storage.drive_type"), "cdrom") == 0) {
+		hal_device_property_set_bool (d, "volume.is_disc", TRUE);
+		hal_device_add_capability (d, "volume.disc");
+	} else {
+		hal_device_property_set_bool (d, "volume.is_disc", FALSE);
+	}
+
+	if (dosnum > 0) {
+		hal_device_property_set_bool (d, "volume.is_partition", TRUE);
+		hal_device_property_set_int (d, "volume.partition.number", dosnum);
+	} else {
+		hal_device_property_set_bool (d, "volume.is_partition", FALSE);
+	}
+
+	/* prober may override these */
+        hal_device_property_set_int (d, "volume.block_size", 512);
+
+	devinfo_add_enqueue (d, devfs_path, &devinfo_volume_handler);
+
+	return (d);
+}
+
+static void
+devinfo_volume_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
+{
+	void *end_token = (void *) userdata1;
+	char *whole_disk;
+	char *block_device;
+	const char *storage_udi;
+	HalDevice *storage_d;
+	const char *slice;
+	int dos_num;
+
+	if (hal_device_property_get_bool (d, "info.ignore")) {
+		HAL_INFO (("Preprobing merged info.ignore==TRUE %s", hal_device_get_udi (d)));
+		goto skip;
+	}
+
+	/*
+	 * Optimizations: only probe if there's a chance to find something
+	 */
+	block_device = (char *)hal_device_property_get_string (d, "block.device");
+	storage_udi = hal_device_property_get_string (d, "block.storage_device");
+	slice = hal_device_property_get_string(d, "block.solaris.slice");
+	if ((block_device == NULL) || (storage_udi == NULL) ||
+	    (slice == NULL) || (strlen (slice) < 2)) {
+		HAL_INFO (("Malformed volume properties %s", hal_device_get_udi (d)));
+		goto skip;
+	}
+	storage_d = hal_device_store_match_key_value_string (hald_get_gdl (), "info.udi", storage_udi);
+	if (storage_d == NULL) {
+		HAL_INFO (("Storage device not found %s", hal_device_get_udi (d)));
+		goto skip;
+	}
+
+	whole_disk = hal_device_has_capability (storage_d,
+	    "storage.cdrom") ? "s2" : WHOLE_DISK;
+
+	if (is_dos_path(block_device, &dos_num)) {
+		/* don't probe more dos volumes than probe-storage found */
+		if ((hal_device_property_get_bool (storage_d, "storage.no_partitions_hint") ||
+		    (dos_num > hal_device_property_get_int (storage_d, "storage.solaris.num_dos_partitions")))) {
+			    HAL_INFO (("%d > %d %s", dos_num, hal_device_property_get_int (storage_d,
+				"storage.solaris.num_dos_partitions"), hal_device_get_udi (storage_d)));
+			goto skip;
+		}
+	} else {
+		/* if no VTOC slices found, don't probe slices except s2 */
+		if ((slice[0] == 's') && (isdigit(slice[1])) && ((strcmp (slice, whole_disk)) != 0) &&
+		    !hal_device_property_get_bool (storage_d, "storage.solaris.vtoc_slices")) {
+			HAL_INFO (("Not probing slice %s", hal_device_get_udi (d)));
+			goto skip;
+		}
+	}
+
+	HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
+	hald_runner_run (d,
+			"hald-probe-volume", NULL,
+			DEVINFO_PROBE_VOLUME_TIMEOUT,
+			devinfo_callouts_probing_done,
+			(gpointer) end_token, userdata2);
+
+	return;
+
+skip:
+	hal_device_store_remove (hald_get_tdl (), d);
+	g_object_unref (d);
+	hotplug_event_end (end_token);
+}
+
+static void
+devinfo_volume_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
+{
+	HAL_INFO(("Preprobing volume udi=%s", hal_device_get_udi (d)));
+
+	if (hal_device_property_get_bool (parent, "info.ignore")) {
+		HAL_INFO (("Ignoring volume: parent's info.ignore is TRUE"));
+		goto skip;
+	}
+
+        /* add to TDL so preprobing callouts and prober can access it */
+        hal_device_store_add (hald_get_tdl (), d);
+
+        /* Process preprobe fdi files */
+        di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
+
+        /* Run preprobe callouts */
+        hal_util_callout_device_preprobe (d, devinfo_volume_preprobing_done, end_token, handler);
+
+	return;
+
+skip:
+	g_object_unref (d);
+	hotplug_event_end (end_token);
+}
+
+void
+devinfo_storage_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
+{
+	const char *drive_type;
+	const char *p_udi;
+	HalDevice *p_d;
+	HalDevice *phys_d = NULL;
+	const char *phys_bus;
+	const char *bus;
+	static const char *busses[] = { "usb", "ide", "scsi", "ieee1394",
+					"pseudo" };
+	int i;
+
+	HAL_INFO (("Preprobing udi=%s", hal_device_get_udi (d)));
+
+	if (parent == NULL) {
+		HAL_INFO (("no parent %s", hal_device_get_udi (d)));
+		goto error;
+	}
+
+	/*
+	 * figure out physical device and bus, except for floppy
+	 */
+	drive_type = hal_device_property_get_string (d, "storage.drive_type");
+	if ((drive_type != NULL) && (strcmp (drive_type, "floppy") == 0)) {
+		goto skip_bus;
+	}
+
+	p_d = parent;
+	for (;;) {
+		bus = hal_device_property_get_string (p_d, "info.bus");
+		if (bus != NULL) {
+			for (i = 0; i < NELEM(busses); i++) {
+				if (strcmp(bus, busses[i]) == 0) {
+					phys_d = p_d;
+					phys_bus = busses[i];
+					break;
+				}
+			}
+		}
+		/* up the tree */
+		p_udi = hal_device_property_get_string (p_d, "info.parent");
+		if (p_udi == NULL) {
+			break;
+		}
+		p_d = hal_device_store_find (hald_get_gdl (), p_udi);
+	}
+	if (phys_d == NULL) {
+		HAL_INFO (("no physical device %s", hal_device_get_udi (d)));
+		goto error;
+	}
+	hal_device_property_set_string (d, "storage.physical_device", hal_device_get_udi (phys_d));
+	hal_device_property_set_string (d, "storage.bus", phys_bus);
+
+skip_bus:
+
+	/* add to TDL so preprobing callouts and prober can access it */
+	hal_device_store_add (hald_get_tdl (), d);
+
+	/* Process preprobe fdi files */
+	di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
+
+	/* Run preprobe callouts */
+	hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
+
+	return;
+
+error:
+	g_object_unref (d);
+	hotplug_event_end (end_token);
+}
+
+static void
+devinfo_storage_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
+{
+        void *end_token = (void *) userdata1;
+
+	HAL_INFO (("devinfo_storage_probing_done %s", hal_device_get_udi (d)));
+
+        /* Discard device if probing reports failure */
+        if (exit_type != HALD_RUN_SUCCESS || return_code != 0) {
+		HAL_INFO (("devinfo_storage_probing_done returning exit_type=%d return_code=%d", exit_type, return_code));
+                hal_device_store_remove (hald_get_tdl (), d);
+                g_object_unref (d);
+                hotplug_event_end (end_token);
+		return;
+        }
+
+	devinfo_storage_set_nicknames (d);
+
+        /* Merge properties from .fdi files */
+        di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
+        di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
+
+	hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
+}
+
+const gchar *
+devinfo_storage_get_prober (HalDevice *d, int *timeout)
+{
+	*timeout = DEVINFO_PROBE_STORAGE_TIMEOUT;
+	return "hald-probe-storage";
+}
+
+const gchar *
+devinfo_volume_get_prober (HalDevice *d, int *timeout)
+{
+	*timeout = DEVINFO_PROBE_VOLUME_TIMEOUT;
+	return "hald-probe-volume";
+}
+
+/*
+ * After reprobing storage, reprobe its volumes.
+ */
+static void
+devinfo_storage_rescan_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
+{
+        void *end_token = (void *) userdata1;
+	const char *devfs_path_orig = NULL;
+	char *devfs_path = NULL;
+	char *p;
+	di_node_t node;
+
+	HAL_INFO (("devinfo_storage_rescan_probing_done %s", hal_device_get_udi (d)));
+
+	devfs_path_orig = hal_device_property_get_string (d, "solaris.devfs_path");
+	if (devfs_path_orig == NULL) {
+		HAL_INFO (("device has no solaris.devfs_path"));
+		hotplug_event_process_queue ();
+		return;
+	}
+
+	/* strip trailing minor part if any */
+	if (strrchr(devfs_path_orig, ':') != NULL) {
+		if ((devfs_path = strdup (devfs_path_orig)) != NULL) {
+			p = strrchr(devfs_path, ':');
+			*p = '\0';
+		}
+	} else {
+		devfs_path = (char *)devfs_path_orig;
+	}
+
+	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
+		HAL_INFO (("di_init %s failed %d %s", devfs_path, errno, hal_device_get_udi (d)));
+		hotplug_event_process_queue ();
+		return;
+	} else {
+		devinfo_storage_minors (d, node, (char *)devfs_path, TRUE);
+		di_fini (node);
+	}
+
+	if (devfs_path != devfs_path_orig) {
+		free (devfs_path);
+	}
+
+	hotplug_event_process_queue ();
+}
+
+/*
+ * For removable media devices, check for "storage.removable.media_available".
+ * For non-removable media devices, assume media is always there.
+ *
+ * If media is gone, enqueue remove events for all children volumes.
+ * If media is there, first reprobe storage, then probe for new volumes (but leave existing volumes alone).
+ */
+gboolean
+devinfo_storage_device_rescan (HalDevice *d)
+{
+	GSList *i;
+	GSList *volumes;
+	HalDevice *v;
+	gchar *v_devfs_path;
+	const char *drive_type;
+	gboolean is_floppy;
+	gboolean media_available;
+
+	HAL_INFO (("devinfo_storage_device_rescan udi=%s", hal_device_get_udi (d)));
+
+	if (hal_device_property_get_bool (d, "block.is_volume")) {
+		HAL_INFO (("nothing to do for volume"));
+		return (FALSE);
+	}
+
+	drive_type = hal_device_property_get_string (d, "storage.drive_type");
+	is_floppy = (drive_type != NULL) && (strcmp (drive_type, "floppy") == 0);
+		
+	media_available = !hal_device_property_get_bool (d, "storage.removable") ||
+	    hal_device_property_get_bool (d, "storage.removable.media_available");
+
+	if (!media_available && !is_floppy) {
+		HAL_INFO (("media gone %s", hal_device_get_udi (d)));
+
+		volumes = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
+        	    "block.storage_device", hal_device_get_udi (d));
+		for (i = volumes; i != NULL; i = g_slist_next (i)) {
+        		v = HAL_DEVICE (i->data);
+			v_devfs_path = (gchar *)hal_device_property_get_string (v, "solaris.devfs_path");
+			HAL_INFO (("child volume %s", hal_device_get_udi (v)));
+			if ((v_devfs_path != NULL) && hal_device_has_capability (v, "volume")) {
+				HAL_INFO (("removing volume %s", hal_device_get_udi (v)));
+				devinfo_remove_enqueue (v_devfs_path, NULL);
+			} else {
+				HAL_INFO (("not a volume %s", hal_device_get_udi (v)));
+			}
+		}
+		g_slist_free (volumes);
+
+		hotplug_event_process_queue ();
+	} else if (is_floppy) {
+		HAL_INFO (("rescanning floppy %s", hal_device_get_udi (d)));
+		
+		hald_runner_run (d,
+				 "hald-probe-storage --only-check-for-media", NULL,
+				 DEVINFO_PROBE_STORAGE_TIMEOUT,
+				 devinfo_floppy_rescan_probing_done,
+				 NULL, NULL);
+	} else {
+		HAL_INFO (("media available %s", hal_device_get_udi (d)));
+
+		hald_runner_run (d,
+				 "hald-probe-storage --only-check-for-media", NULL,
+				 DEVINFO_PROBE_STORAGE_TIMEOUT,
+				 devinfo_storage_rescan_probing_done,
+				 NULL, NULL);
+	}
+
+	return TRUE;
+}
+
+static char *
+devinfo_volume_get_slice_name (char *devlink)
+{
+	char	*part, *slice, *disk;
+	char	*s = NULL;
+	char	*p;
+
+	if ((p = strstr(devlink, "/lofi/")) != 0) {
+		return (p + sizeof ("/lofi/") - 1);
+	}
+
+	part = strrchr(devlink, 'p');
+	slice = strrchr(devlink, 's');
+	disk = strrchr(devlink, 'd');
+
+	if ((part != NULL) && (part > slice) && (part > disk)) {
+		s = part;
+	} else if ((slice != NULL) && (slice > disk)) {
+		s = slice;
+	} else {
+		s = disk;
+	}
+	if ((s != NULL) && isdigit(s[1])) {
+		return (s);
+	} else {
+		return ("");
+	}
+}
+
+static gboolean
+is_dos_path(char *path, int *partnum)
+{
+	char *p;
+
+	if ((p = strrchr (path, ':')) == NULL) {
+		return (FALSE);
+	}
+	return ((*partnum = atoi(p + 1)) != 0);
+}
+
+static gboolean
+dos_to_dev(char *path, char **devpath, int *partnum)
+{
+	char *p;
+
+	if ((p = strrchr (path, ':')) == NULL) {
+		return (FALSE);
+	}
+	if ((*partnum = atoi(p + 1)) == 0) {
+		return (FALSE);
+	}
+	p[0] = '\0';
+	*devpath = strdup(path);
+	p[0] = ':';
+	return (*devpath != NULL);
+}
+
+static void
+devinfo_storage_cleanup_mountpoint_cb (HalDevice *d, guint32 exit_type, 
+		       gint return_code, gchar **error,
+		       gpointer data1, gpointer data2)
+{
+	char *mount_point = (char *) data1;
+
+	HAL_INFO (("Cleaned up mount point '%s'", mount_point));
+	g_free (mount_point);
+}
+
+
+void
+devinfo_storage_mnttab_event (HalDevice *hal_volume)
+{
+	FILE *fp = NULL;
+        struct extmnttab m;
+	HalDevice *d;
+	unsigned int major;
+	unsigned int minor;
+	GSList *volumes = NULL;
+	GSList *v;
+	char *mount_point;
+	dbus_bool_t is_partition;
+	const char *fstype;
+	int partition_number;
+
+	if (hal_volume != NULL) {
+		volumes = g_slist_append (NULL, hal_volume);
+	} else {
+		volumes = hal_device_store_match_multiple_key_value_string (hald_get_gdl (), "info.category", "volume");
+	}
+	if (volumes == NULL) {
+		return;
+	}
+
+	if ((fp = fopen(MNTTAB, "r")) == NULL) {
+		HAL_ERROR (("Open failed %s errno %d", MNTTAB, errno));
+		return;
+	}
+
+	while (getextmntent(fp, &m, 1) == 0) {
+		for (v = volumes; v != NULL; v = g_slist_next (v)) {
+			d = HAL_DEVICE (v->data);
+			major = hal_device_property_get_int (d, "block.major");
+			minor = hal_device_property_get_int (d, "block.minor");
+
+			/*
+			 * special handling for pcfs, which encodes logical
+			 * drive number into the 6 upper bits of the minor
+			 */
+			is_partition = hal_device_property_get_bool (d, "volume.is_partition");
+			partition_number = hal_device_property_get_int (d, "volume.partition.number");
+			fstype = hal_device_property_get_string (d, "volume.fstype");
+
+			if (is_partition && (partition_number > 0) && (strcmp (fstype, "pcfs") == 0)) {
+				minor |= partition_number << 12;
+			}
+
+			if (m.mnt_major != major || m.mnt_minor != minor) {
+				continue;
+			}
+
+			/* this volume matches the mnttab entry */
+			device_property_atomic_update_begin ();
+			hal_device_property_set_bool (d, "volume.is_mounted", TRUE);
+			hal_device_property_set_bool (d, "volume.is_mounted_read_only",
+						      hasmntopt ((struct mnttab *)&m, "ro") ? TRUE : FALSE);
+			hal_device_property_set_string (d, "volume.mount_point", m.mnt_mountp);
+			device_property_atomic_update_end ();
+
+			HAL_INFO (("set %s to be mounted at %s",
+				   hal_device_get_udi (d), m.mnt_mountp));
+			volumes = g_slist_delete_link (volumes, v);
+		}
+	}
+
+	/* all remaining volumes are not mounted */
+	for (v = volumes; v != NULL; v = g_slist_next (v)) {
+		d = HAL_DEVICE (v->data);
+		mount_point = g_strdup (hal_device_property_get_string (d, "volume.mount_point"));
+		if (mount_point == NULL || strlen (mount_point) == 0) {
+			g_free (mount_point);
+			continue;
+		}
+
+		device_property_atomic_update_begin ();
+		hal_device_property_set_bool (d, "volume.is_mounted", FALSE);
+		hal_device_property_set_bool (d, "volume.is_mounted_read_only", FALSE);
+		hal_device_property_set_string (d, "volume.mount_point", "");
+		device_property_atomic_update_end ();
+
+		HAL_INFO (("set %s to unmounted", hal_device_get_udi (d)));
+
+		/* cleanup if was mounted by us */
+		if (hal_util_is_mounted_by_hald (mount_point)) {
+			char *cleanup_stdin;
+			char *extra_env[2];
+
+			HAL_INFO (("Cleaning up '%s'", mount_point));
+
+			extra_env[0] = g_strdup_printf ("HALD_CLEANUP=%s", mount_point);
+			extra_env[1] = NULL;
+			cleanup_stdin = "\n";
+
+			hald_runner_run_method (d, 
+						"hal-storage-cleanup-mountpoint", 
+						extra_env, 
+						cleanup_stdin, TRUE,
+						0,
+						devinfo_storage_cleanup_mountpoint_cb,
+						g_strdup (mount_point), NULL);
+
+			g_free (extra_env[0]);
+		}
+
+		g_free (mount_point);
+	}
+	g_slist_free (volumes);
+
+	(void) fclose (fp);
+}
+
+static void
+devinfo_volume_force_unmount_cb (HalDevice *d, guint32 exit_type, 
+		  gint return_code, gchar **error,
+		  gpointer data1, gpointer data2)
+{
+	void *end_token = (void *) data1;
+
+	HAL_INFO (("devinfo_volume_force_unmount_cb for udi='%s', exit_type=%d, return_code=%d", hal_device_get_udi (d), exit_type, return_code));
+
+	if (exit_type == HALD_RUN_SUCCESS && error != NULL && 
+	    error[0] != NULL && error[1] != NULL) {
+		char *exp_name = NULL;
+		char *exp_detail = NULL;
+
+		exp_name = error[0];
+		if (error[0] != NULL) {
+			exp_detail = error[1];
+		}
+		HAL_INFO (("failed with '%s' '%s'", exp_name, exp_detail));
+	}
+
+	hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
+}
+
+static void
+devinfo_volume_force_unmount (HalDevice *d, void *end_token)
+{
+	const char *device_file;
+	const char *mount_point;
+	char *unmount_stdin;
+	char *extra_env[2];
+	extra_env[0] = "HAL_METHOD_INVOKED_BY_UID=0";
+	extra_env[1] = NULL;
+
+	device_file = hal_device_property_get_string (d, "block.device");
+	mount_point = hal_device_property_get_string (d, "volume.mount_point");
+
+	if (mount_point == NULL || strlen (mount_point) == 0 || !hal_util_is_mounted_by_hald (mount_point)) {
+		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
+		return;
+	}
+
+	HAL_INFO (("devinfo_volume_force_unmount for udi='%s'", hal_device_get_udi (d)));
+		
+	unmount_stdin = "\n";
+		
+	hald_runner_run_method (d, 
+				"hal-storage-unmount", 
+				extra_env, 
+				unmount_stdin, TRUE,
+				0,
+				devinfo_volume_force_unmount_cb,
+				end_token, NULL);
+}
+
+void
+devinfo_volume_hotplug_begin_remove (HalDevice *d, char *devfs_path, void *end_token)
+{
+	if (hal_device_property_get_bool (d, "volume.is_mounted")) {
+		devinfo_volume_force_unmount (d, end_token);
+	} else {
+		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
+	}
+}
+
+
+enum {
+	LEGACY_CDROM,
+	LEGACY_FLOPPY,
+	LEGACY_RMDISK
+};
+
+static const char *legacy_media_str[] = {
+	"cdrom",
+	"floppy",
+	"rmdisk"
+};
+
+struct enum_nick {
+	const char *type;
+	GSList	*nums;
+};
+
+static int
+devinfo_storage_get_legacy_media(HalDevice *d)
+{
+	const char *drive_type;
+
+	if (hal_device_has_capability (d, "storage.cdrom")) {
+		return (LEGACY_CDROM);
+	} else if (((drive_type = hal_device_property_get_string (d,
+	    "storage.drive_type")) != NULL) && (strcmp (drive_type, "floppy") == 0)) {
+		return (LEGACY_FLOPPY);
+	} else if (hal_device_property_get_bool (d, "storage.removable") ||
+	           hal_device_property_get_bool (d, "storage.hotpluggable")) {
+		return (LEGACY_RMDISK);
+	} else {
+		return (-1);
+	}
+}
+
+static gboolean
+devinfo_storage_foreach_nick (HalDeviceStore *store, HalDevice *d, gpointer user_data)
+{
+	struct enum_nick *en = (struct enum_nick *) user_data;
+	const char *media_type;
+	int media_num;
+
+	media_type = hal_device_property_get_string (d, "storage.solaris.legacy.media_type");
+	media_num = hal_device_property_get_int (d, "storage.solaris.legacy.media_num");
+	if ((media_type != NULL) && (strcmp (media_type, en->type) == 0) &&
+	    (media_num >= 0)) {
+		en->nums = g_slist_prepend (en->nums, GINT_TO_POINTER(media_num));
+	}
+	return TRUE;
+}
+
+static void
+devinfo_storage_append_nickname (HalDevice *d, const char *media_type, int media_num)
+{
+	char buf[64];
+
+	if (media_num == 0) {
+		hal_device_property_strlist_append (d, "storage.solaris.nicknames", media_type);
+	}
+	snprintf(buf, sizeof (buf), "%s%d", media_type, media_num);
+	hal_device_property_strlist_append (d, "storage.solaris.nicknames", buf);
+}
+
+static void
+devinfo_storage_set_nicknames (HalDevice *d)
+{
+	int media;
+	const char *media_type;
+	int media_num;
+	GSList *i;
+	struct enum_nick en;
+	char buf[64];
+
+	if ((media = devinfo_storage_get_legacy_media (d)) < 0) {
+		return;
+	}
+	media_type = legacy_media_str[media];
+
+	/* enumerate all storage devices of this media type */
+	en.type = media_type;
+	en.nums = NULL;
+	hal_device_store_foreach (hald_get_gdl (), devinfo_storage_foreach_nick, &en);
+
+	/* find a free number */
+	for (media_num = 0; ; media_num++) {
+		for (i = en.nums; i != NULL; i = g_slist_next (i)) {
+        		if (GPOINTER_TO_INT (i->data) == media_num) {
+				break;
+			}
+		}
+		if (i == NULL) {
+			break;
+		}
+	}
+	g_slist_free (en.nums);
+
+	hal_device_property_set_string (d, "storage.solaris.legacy.media_type", media_type);
+	hal_device_property_set_int (d, "storage.solaris.legacy.media_num", media_num);
+
+	/* primary nickname, and also vold-style symdev */
+	snprintf(buf, sizeof (buf), "%s%d", media_type, media_num);
+	hal_device_property_set_string (d, "storage.solaris.legacy.symdev", buf);
+	devinfo_storage_append_nickname(d, media_type, media_num);
+
+	/* additional nicknames */
+	if (media == LEGACY_CDROM) {
+		devinfo_storage_append_nickname(d, "cd", media_num);
+		devinfo_storage_append_nickname(d, "sr", media_num);
+	} else if (media == LEGACY_FLOPPY) {
+		devinfo_storage_append_nickname(d, "fd", media_num);
+		devinfo_storage_append_nickname(d, "diskette", media_num);
+		devinfo_storage_append_nickname(d, "rdiskette", media_num);
+	}
+}
diff --git a/hald/solaris/devinfo_storage.h b/hald/solaris/devinfo_storage.h
new file mode 100644
index 0000000..ad6bb6d
--- /dev/null
+++ b/hald/solaris/devinfo_storage.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ *
+ * devinfo_storage.h : definitions for storage devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_storage.h	1.2	06/10/13 SMI"
+
+#ifndef DEVINFO_STORAGE_H
+#define DEVINFO_STORAGE_H
+
+#include "devinfo.h"
+
+extern DevinfoDevHandler devinfo_ide_handler;
+extern DevinfoDevHandler devinfo_scsi_handler;
+extern DevinfoDevHandler devinfo_floppy_handler;
+extern DevinfoDevHandler devinfo_lofi_handler;
+
+gboolean devinfo_storage_device_rescan (HalDevice *d);
+HalDevice *devinfo_lofi_add_major(HalDevice *parent, di_node_t node, char *devfs_path,
+    char *device_type, gboolean rescan, HalDevice *lofi_d);
+void devinfo_lofi_remove_minor(char *parent_devfs_path, char *name);
+void devinfo_storage_mnttab_event (HalDevice *hal_volume);
+void devinfo_volume_hotplug_begin_remove (HalDevice *d, char *devfs_path, void *end_token);
+
+#endif /* DEVINFO_STORAGE_H */
diff --git a/hald/solaris/devinfo_usb.c b/hald/solaris/devinfo_usb.c
new file mode 100644
index 0000000..b3359a2
--- /dev/null
+++ b/hald/solaris/devinfo_usb.c
@@ -0,0 +1,231 @@
+/***************************************************************************
+ *
+ * devinfo_usb.h : USB devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_usb.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <libdevinfo.h>
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "../ids.h"
+#include "hotplug.h"
+#include "devinfo.h"
+#include "devinfo_usb.h"
+
+HalDevice *devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
+static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path, int ifnum);
+static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node, gchar *devfs_path);
+
+DevinfoDevHandler devinfo_usb_handler = {
+        devinfo_usb_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+        NULL
+};
+
+HalDevice *
+devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
+{
+	HalDevice *d, *nd = NULL;
+	char	*s;
+	int	*i, *vid;
+	char	*driver_name, *binding_name;
+        char    if_devfs_path[HAL_PATH_MAX];
+
+	/*
+	 * we distinguish USB devices by presence of "usb-vendor-id"
+	 * property. should USB devices have "device_type"?
+	 */
+        if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-vendor-id", &vid) <= 0) {
+		return (NULL);
+	}
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+	hal_device_property_set_string (d, "info.bus", "usb_device");
+	PROP_STR(d, node, s, "usb-product-name", "info.product");
+	PROP_STR(d, node, s, "usb-product-name", "usb_device.product");
+	PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor");
+	PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id");
+	PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id");
+	PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd");
+	PROP_INT(d, node, i, "usb-release-id", "usb_device.version_bcd");
+	PROP_STR(d, node, s, "usb-serialno", "usb_device.serial");
+
+	/* class, subclass */
+	/* hal_device_property_set_int (d, "usb_device.device_class", 8); */
+
+	/* binding name tells us if driver is bound to interface or device */
+	if (((binding_name = di_binding_name(node)) != NULL) &&
+	    (strncmp(binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) {
+		snprintf(if_devfs_path, sizeof (if_devfs_path), "%s:if%d", devfs_path, 0);
+		if ((nd = devinfo_usb_if_add(d, node, if_devfs_path, 0)) != NULL) {
+			d = nd;
+			nd = NULL;
+			devfs_path = if_devfs_path;
+		}
+	}
+
+	/* driver specific */
+	driver_name = di_driver_name (node);
+	if ((driver_name != NULL) && (strcmp (driver_name, "scsa2usb") == 0)) {
+		nd = devinfo_usb_scsa2usb_add (d, node, devfs_path);
+	} else {
+		devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
+	}
+
+out:
+	if (nd != NULL) {
+		return (nd);
+	} else {
+		return (d);
+	}
+}
+
+static HalDevice *
+devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path, int ifnum)
+{
+	HalDevice *d = NULL;
+        char    udi[HAL_PATH_MAX];
+
+	devinfo_add_enqueue (parent, devfs_path, &devinfo_usb_handler);
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, parent, node, devfs_path);
+        hal_device_property_set_string (d, "info.bus", "usb");
+
+        hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+		"%s_if%d", hal_device_get_udi (parent), ifnum);
+        hal_device_set_udi (d, udi);
+        hal_device_property_set_string (d, "info.udi", udi);
+        hal_device_property_set_string (d, "info.product", "USB Device Interface");
+
+	/* copy parent's usb_device.* properties */
+	hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");
+
+	return (d);
+}
+
+static int
+walk_devlinks(di_devlink_t devlink, void *arg)
+{
+        char **path = (char **)arg;
+
+        *path = strdup(di_devlink_path(devlink));
+
+        return (DI_WALK_TERMINATE);
+}
+
+static char *
+get_devlink(di_devlink_handle_t devlink_hdl, char *path)
+{
+	char *devlink = NULL;
+
+        (void) di_devlink_walk(devlink_hdl, NULL, path,
+            DI_PRIMARY_LINK, &devlink, walk_devlinks);
+
+        return (devlink);
+}
+
+static HalDevice *
+devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node, gchar *devfs_path)
+{
+	HalDevice *d = NULL;
+	di_devlink_handle_t devlink_hdl;
+        int     major;
+        di_minor_t minor;
+        dev_t   devt;
+        char    *minor_path = NULL;
+	char	*devlink = NULL;
+        char    udi[HAL_PATH_MAX];
+
+	devinfo_add_enqueue (usbd, devfs_path, &devinfo_usb_handler);
+
+        if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
+                printf("di_devlink_init() failed\n");
+                return (NULL);
+        }
+
+        major = di_driver_major(node);
+        minor = DI_MINOR_NIL;
+        while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+                devt = di_minor_devt(minor);
+                if (major != major(devt)) {
+                        continue;
+                }
+                if ((minor_path = di_devfs_minor_path(minor)) == NULL) {
+                        continue;
+                }
+                if (di_minor_type(minor) != DDM_MINOR) {
+                        continue;
+                }
+		if (strcmp (di_minor_nodetype(minor),
+		    "ddi_ctl:devctl:scsi") == 0) {
+                	devlink = get_devlink(devlink_hdl, minor_path);
+                	if (devlink == NULL) {
+				devlink = strdup("");
+			}
+			break;
+		}
+		di_devfs_path_free (minor_path);
+		minor_path = NULL;
+        }
+
+	di_devlink_fini (&devlink_hdl);
+
+	if (devlink == NULL) {
+		goto out;
+	}
+
+	d = hal_device_new ();
+
+	devinfo_set_default_properties (d, usbd, node, minor_path);
+       	hal_device_property_set_string (d, "scsi_host.solaris.device", devlink);
+        hal_device_property_set_string (d, "info.category", "scsi_host");
+        hal_device_property_set_int (d, "scsi_host.host", 0);
+
+        hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
+		"%s/scsi_host%d", hal_device_get_udi (usbd),
+		hal_device_property_get_int (d, "scsi_host.host"));
+        hal_device_set_udi (d, udi);
+        hal_device_property_set_string (d, "info.udi", udi);
+        hal_device_property_set_string (d, "info.product", "SCSI Host Adapter");
+
+	devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler);
+
+out:
+	if (devlink) {
+		free(devlink);
+	}
+	if (minor_path) {
+		di_devfs_path_free (minor_path);
+	}
+
+	return (d);
+}
+
diff --git a/hald/solaris/devinfo_usb.h b/hald/solaris/devinfo_usb.h
new file mode 100644
index 0000000..8eed9f1
--- /dev/null
+++ b/hald/solaris/devinfo_usb.h
@@ -0,0 +1,21 @@
+/***************************************************************************
+ *
+ * devinfo_usb.h : definitions for USB devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)devinfo_usb.h	1.2	06/10/13 SMI"
+
+#ifndef DEVINFO_USB_H
+#define DEVINFO_USB_H
+
+#include "devinfo.h"
+
+extern DevinfoDevHandler devinfo_usb_handler;
+
+#endif /* DEVINFO_USB_H */
diff --git a/hald/solaris/hal.xml b/hald/solaris/hal.xml
new file mode 100644
index 0000000..4221eb5
--- /dev/null
+++ b/hald/solaris/hal.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+	Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+	Use is subject to license terms.
+
+        Licensed under the Academic Free License version 2.1
+
+	ident	"@(#)hal.xml	1.1	06/10/10 SMI"
+
+	NOTE:  This service manifest is not editable; its contents will
+	be overwritten by package or patch operations, including
+	operating system upgrade.  Make customizations in a different
+	file.
+
+	Service manifest for HAL.
+-->
+
+<service_bundle type='manifest' name='SUNWhalr:hal'>
+
+<service
+	name='system/hal'
+	type='service'
+	version='1'>
+
+	<create_default_instance enabled='false' />
+
+	<single_instance />
+
+	<dependency name='usr'
+		type='service'
+		grouping='require_all'
+		restart_on='none'>
+		<service_fmri value='svc:/system/filesystem/local' />
+	</dependency>
+
+	<dependency
+		name='devices'
+		grouping='require_all'
+		restart_on='none'
+		type='service'>
+		<service_fmri value='svc:/system/device/local' />
+	</dependency>
+
+	<dependency name='dbus'
+		type='service'
+		grouping='require_all'
+		restart_on='none'>
+		<service_fmri value='svc:/system/dbus' />
+	</dependency>
+
+	<exec_method
+		type='method'
+		name='start'
+		exec='/lib/svc/method/svc-hal start'
+		timeout_seconds='600'>
+		<method_context>
+			<method_credential user='root' group='root' />
+		</method_context>
+	</exec_method>
+
+	<exec_method
+		type='method'
+		name='stop'
+		exec=':kill'
+		timeout_seconds='30' />
+
+	<property_group name='startd' type='framework'>
+		<!-- sub-process core dumps shouldn't restart session -->
+		<propval name='ignore_error' type='astring'
+		    value='core,signal' />
+	</property_group>
+
+	<stability value='Unstable' />
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'>
+			Hardware Abstraction Layer daemon
+			</loctext>
+		</common_name>
+		<documentation>
+			<manpage title='hal' section='1M' manpath='/usr/man' />
+		</documentation>
+	</template>
+
+</service>
+
+</service_bundle>
diff --git a/hald/solaris/hotplug.c b/hald/solaris/hotplug.c
new file mode 100644
index 0000000..edb1e18
--- /dev/null
+++ b/hald/solaris/hotplug.c
@@ -0,0 +1,193 @@
+/***************************************************************************
+ *
+ * hotplug.c : HAL-internal hotplug events
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)hotplug.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.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 "../device_info.h"
+
+#include "osspec_solaris.h"
+#include "hotplug.h"
+#include "devinfo.h"
+
+/** Queue of ordered hotplug events */
+GQueue *hotplug_event_queue;
+
+/** List of HotplugEvent objects we are currently processing */
+GSList *hotplug_events_in_progress = NULL;
+
+static void hotplug_event_begin (HotplugEvent *hotplug_event);
+
+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_devfs_add (HotplugEvent *hotplug_event, HalDevice *d)
+{
+	HalDevice *parent;
+	const gchar *parent_udi;
+	void (*begin_add_func) (HalDevice *, HalDevice *, DevinfoDevHandler *, void *);
+
+	if (d != NULL) {
+		/* XXX */
+		HAL_ERROR (("devpath %s already present in store, ignore event", hotplug_event->un.devfs.devfs_path));
+		hotplug_event_end ((void *) hotplug_event);
+		return;
+	}
+
+	/* find parent */
+	parent_udi = hal_device_property_get_string (hotplug_event->d, "info.parent");
+	if (parent_udi == NULL || strlen(parent_udi) == 0) {
+		parent = NULL;
+	} else {
+		parent = hal_device_store_match_key_value_string (hald_get_gdl (), "info.udi", parent_udi);
+	}
+	/* only root node is allowed to be orphan */
+	if (parent == NULL) {
+		if (strcmp(hotplug_event->un.devfs.devfs_path, "/") != 0) {
+			HAL_ERROR (("Parent is NULL devfs_path=%s parent_udi=%s", hotplug_event->un.devfs.devfs_path, parent_udi ? parent_udi : "<null>"));
+			hotplug_event_end ((void *) hotplug_event);
+			return;
+		}
+	}
+
+	/* children of ignored parent should be ignored */
+	if (hal_device_property_get_bool (parent, "info.ignore")) {
+		HAL_INFO (("parent ignored %s", parent_udi));
+			hotplug_event_end ((void *) hotplug_event);
+			return;
+	}
+
+	/* custom or generic add function */
+	begin_add_func = hotplug_event->un.devfs.handler->hotplug_begin_add;
+	if (begin_add_func == NULL) {
+		begin_add_func = hotplug_event_begin_add_devinfo;
+	}
+	begin_add_func (hotplug_event->d, 
+			 parent,
+			 hotplug_event->un.devfs.handler, 
+			 (void *) hotplug_event);
+}
+
+static void
+hotplug_event_begin_devfs_remove (HotplugEvent *hotplug_event, HalDevice *d)
+{
+	if (d == NULL) {
+		HAL_ERROR (("devpath %s not present in store, ignore event", hotplug_event->un.devfs.devfs_path));
+		hotplug_event_end ((void *) hotplug_event);
+		return;
+	}
+	HAL_INFO (("hotplug_event_begin_devfs_remove %s", hal_device_get_udi (d)));
+
+	hotplug_event_begin_remove_devinfo(d, 
+			 hotplug_event->un.devfs.devfs_path, 
+			 (void *) hotplug_event);
+}
+
+static void
+hotplug_event_begin_devfs (HotplugEvent *hotplug_event)
+{
+	HalDevice *d;
+
+	HAL_INFO (("hotplug_event_begin_devfs: %s", hotplug_event->un.devfs.devfs_path));
+	d = hal_device_store_match_key_value_string (hald_get_gdl (),
+						"solaris.devfs_path",
+						hotplug_event->un.devfs.devfs_path);
+
+	if (hotplug_event->action == HOTPLUG_ACTION_ADD) {
+		hotplug_event_begin_devfs_add (hotplug_event, d);
+	} else if (hotplug_event->action == HOTPLUG_ACTION_REMOVE) {
+		hotplug_event_begin_devfs_remove (hotplug_event, d);
+	} else {
+		hotplug_event_end ((void *) hotplug_event);
+	}
+}
+
+static void
+hotplug_event_begin (HotplugEvent *hotplug_event)
+{
+	switch (hotplug_event->type) {
+
+	case HOTPLUG_EVENT_DEVFS:
+		hotplug_event_begin_devfs (hotplug_event);
+		break;
+
+	default:
+		HAL_ERROR (("Unknown hotplug event type %d", hotplug_event->type));
+		hotplug_event_end ((void *) hotplug_event);
+		break;
+	}
+}
+
+void 
+hotplug_event_enqueue (HotplugEvent *hotplug_event, int front)
+{
+	if (hotplug_event_queue == NULL)
+		hotplug_event_queue = g_queue_new ();
+
+	if (front) {
+		g_queue_push_head (hotplug_event_queue, hotplug_event);
+	} else {
+		g_queue_push_tail (hotplug_event_queue, hotplug_event);
+	}
+}
+
+void 
+hotplug_event_process_queue (void)
+{
+	HotplugEvent *hotplug_event;
+
+	if (hotplug_events_in_progress == NULL && 
+	    (hotplug_event_queue == NULL || g_queue_is_empty (hotplug_event_queue))) {
+		hotplug_queue_now_empty ();
+		goto out;
+	}
+
+	/* do not process events if some other event is in progress */
+	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:
+	;	
+}
diff --git a/hald/solaris/hotplug.h b/hald/solaris/hotplug.h
new file mode 100644
index 0000000..724735e
--- /dev/null
+++ b/hald/solaris/hotplug.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ *
+ * hotplug.h : definitions for HAL-internal hotplug events
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)hotplug.h	1.2	06/10/13 SMI"
+
+#ifndef HOTPLUG_H
+#define HOTPLUG_H
+
+#include <glib.h>
+
+#include "../device.h"
+#include "../util.h"
+
+#include "devinfo.h"
+
+typedef enum {
+	HOTPLUG_ACTION_ADD,
+	HOTPLUG_ACTION_REMOVE,
+} HotplugActionType;
+
+typedef enum {
+	HOTPLUG_EVENT_DEVFS       = 0,
+} HotplugEventType;
+
+/** Data structure representing a hotplug event; also used for
+ *  coldplugging.
+ */
+typedef struct
+{
+	HotplugActionType action;               /**< Whether the event is add or remove */
+	HotplugEventType type;                  /**< Type of hotplug event */
+
+	HalDevice *d;
+
+	union {
+		struct {
+			char devfs_path[HAL_PATH_MAX];
+			DevinfoDevHandler *handler;
+		} devfs;
+	} un;
+
+} HotplugEvent;
+
+void hotplug_event_enqueue (HotplugEvent *event, int front);
+
+void hotplug_event_process_queue (void);
+
+void hotplug_event_end (void *end_token);
+
+void hotplug_queue_now_empty (void);
+
+#endif /* HOTPLUG_H */
diff --git a/hald/solaris/osspec.c b/hald/solaris/osspec.c
index d694667..f5903f3 100644
--- a/hald/solaris/osspec.c
+++ b/hald/solaris/osspec.c
@@ -1,53 +1,76 @@
 /***************************************************************************
- * CVSID: $Id$
  *
- * osspec.c : HAL backend for Solaris
+ * osspec.c : Solaris HAL backend entry points
  *
- * Copyright (C) 2005 Sun Microsystems
- * Author: Alvaro Lopez Ortega <alvaro at sun.com>
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * Licensed under the Academic Free License version 2.1
  *
  **************************************************************************/
 
+#pragma ident	"@(#)osspec.c	1.2	06/10/13 SMI"
+
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
 
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <port.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+
 #include "../osspec.h"
 #include "../logger.h"
 #include "../hald.h"
 #include "../hald_dbus.h"
 #include "../device_info.h"
 #include "../util.h"
+#include "../ids.h"
+#include "osspec_solaris.h"
+#include "hotplug.h"
+#include "sysevent.h"
+#include "devinfo.h"
+#include "devinfo_storage.h"
 
+static void mnttab_event_init ();
+static gboolean mnttab_event (GIOChannel *channel, GIOCondition cond, gpointer user_data);
 
 void
 osspec_init (void)
 {
+	ids_init ();
+	sysevent_init ();
+	mnttab_event_init ();
+}
+
+void
+hotplug_queue_now_empty (void)
+{
+        if (hald_is_initialising) {
+                osspec_probe_done ();
+	}
 }
 
 void 
 osspec_probe (void)
 {
+	/* add entire device tree */
+	devinfo_add (NULL, "/");
+
+	/* start processing events */
+	hotplug_event_process_queue ();
 }
 
 gboolean
 osspec_device_rescan (HalDevice *d)
 {
-	   return FALSE;
+	   return (devinfo_device_rescan (d));
 }
 
 gboolean
@@ -62,3 +85,151 @@ osspec_filter_function (DBusConnection *
 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
+/** Find the closest ancestor by looking at devfs paths
+ *
+ *  @param  devfs_path           Path into devfs, e.g. /pci at 0,0/pci1025,57 at 10,2/storage at 1
+ *  @return                      Parent Hal Device Object or #NULL if there is none
+ */
+HalDevice *
+hal_util_find_closest_ancestor (const gchar *devfs_path, gchar **ancestor_devfs_path, gchar **hotplug_devfs_path)
+{
+        gchar buf[512];
+	gchar c;
+        HalDevice *parent;
+
+        parent = NULL;
+
+        strncpy (buf, devfs_path, sizeof (buf));
+        do {
+                char *p;
+
+                p = strrchr (buf, '/');
+                if (p == NULL)
+                        break;
+		c = *p;
+                *p = '\0';
+
+                parent = hal_device_store_match_key_value_string (hald_get_gdl (),
+                                                                  "solaris.devfs_path",
+                                                                  buf);
+                if (parent != NULL) {
+			if (ancestor_devfs_path != NULL) {
+				*ancestor_devfs_path = g_strdup (buf);
+			}
+			if (hotplug_devfs_path != NULL) {
+				*p = c;
+				*hotplug_devfs_path = g_strdup (buf);
+			}
+                        break;
+		}
+
+        } while (TRUE);
+
+        return parent;
+}
+
+char *
+dsk_to_rdsk(char *dsk)
+{
+        int     len, pos;
+        char    *p;
+        char    *rdsk;
+
+	if ((len = strlen (dsk)) < sizeof ("/dev/dsk/cN") - 1) {
+		return (strdup(""));
+	}
+	if ((p = strstr (dsk, "/dsk/")) == NULL) {
+		if ((p = strstr (dsk, "/lofi/")) == NULL) {
+			p = strstr (dsk, "/diskette");
+		}
+	}
+	if (p == NULL) {
+		return (strdup(""));
+	}
+
+	pos = (uintptr_t)p - (uintptr_t)dsk;
+	if ((rdsk = (char *)calloc (len + 2, 1)) != NULL) {
+        	strncpy (rdsk, dsk, pos + 1);
+        	rdsk[pos + 1] = 'r';
+        	strcpy (rdsk + pos + 2, dsk + pos + 1);
+	}
+
+        return (rdsk);
+}
+
+/*
+ * Setup to watch mnttab changes
+ *
+ * When mnttab changes, POLLRDBAND is set. However, glib does not
+ * support POLLRDBAND, so we use Solaris ports (see port_create(3C))
+ * to "map" POLLRDBAND to POLLIN:
+ *
+ * - create a port
+ * - associate the port with mnttab file descriptor and POLLRDBAND
+ * - now polling for POLLIN on the port descriptor will unblock when
+ *   the associated file descriptor receives POLLRDBAND
+ */
+static int	mnttab_fd;
+static int	mnttab_port;
+static GIOChannel *mnttab_channel;
+
+static void
+mnttab_event_init ()
+{
+	char	buf[81];
+
+	if ((mnttab_fd = open (MNTTAB, O_RDONLY)) < 0) {
+		return;
+	}
+	if ((mnttab_port = port_create ()) < 0) {
+		(void) close (mnttab_fd);
+		return;
+	}
+	if (port_associate (mnttab_port, PORT_SOURCE_FD, mnttab_fd, POLLRDBAND,
+	    NULL) != 0) {
+		(void) close (mnttab_port);
+		(void) close (mnttab_fd);
+		return;
+	}
+
+	/* suppress initial event */
+	(void) read(mnttab_fd, buf, (size_t)(sizeof (buf) - 1));
+	(void) lseek(mnttab_fd, 0, SEEK_SET);
+
+	mnttab_channel = g_io_channel_unix_new (mnttab_port);
+	g_io_add_watch (mnttab_channel, G_IO_IN, mnttab_event, NULL);
+}
+
+static gboolean
+mnttab_event (GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+	port_event_t pe;
+	timespec_t timeout;
+	char	buf[81];
+
+	/* if (cond & ~G_IO_ERR)
+		return TRUE;
+	 */
+	HAL_INFO (("mnttab event"));
+
+	/* we have to re-associate port with fd every time */
+	timeout.tv_sec = timeout.tv_nsec = 0;
+	(void) port_get(mnttab_port, &pe, &timeout);
+	(void) port_associate(mnttab_port, PORT_SOURCE_FD,
+	    mnttab_fd, POLLRDBAND, NULL);
+
+	if (!hald_is_initialising) {
+		devinfo_storage_mnttab_event (NULL);
+	}
+
+	(void) lseek(mnttab_fd, 0, SEEK_SET);
+	(void) read(mnttab_fd, buf, (size_t)(sizeof (buf) - 1));
+
+	return TRUE;
+}
+
+void
+osspec_refresh_mount_state_for_block_device (HalDevice *d)
+{
+	devinfo_storage_mnttab_event (d);
+}
diff --git a/hald/solaris/osspec_solaris.h b/hald/solaris/osspec_solaris.h
new file mode 100644
index 0000000..ce7436c
--- /dev/null
+++ b/hald/solaris/osspec_solaris.h
@@ -0,0 +1,23 @@
+/***************************************************************************
+ *
+ * osspec_solaris.h : definitions for Solaris HAL backend
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)osspec_solaris.h	1.2	06/10/13 SMI"
+
+#ifndef OSSPEC_SOLARIS_H
+#define OSSPEC_SOLARIS_H
+
+#include <glib.h>
+
+void hotplug_queue_now_empty (void);
+HalDevice *hal_util_find_closest_ancestor (const gchar *devfs_path, gchar **ancestor_devfs_path, gchar **hotplug_devfs_path);
+char *dsk_to_rdsk(char *);
+
+#endif /* OSSPEC_SOLARIS_H */
diff --git a/hald/solaris/probing/Makefile.am b/hald/solaris/probing/Makefile.am
new file mode 100644
index 0000000..b3c94ff
--- /dev/null
+++ b/hald/solaris/probing/Makefile.am
@@ -0,0 +1,22 @@
+
+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$(top_srcdir)/hald -I$(top_srcdir)/libhal -I$(top_srcdir)/libhal-storage \
+	@GLIB_CFLAGS@ @DBUS_CFLAGS@
+
+if HALD_COMPILE_SOLARIS
+libexec_PROGRAMS = hald-probe-storage hald-probe-volume
+endif
+
+hald_probe_storage_SOURCES = probe-storage.c cdutils.c cdutils.h fsutils.c fsutils.h ../../logger.c
+hald_probe_storage_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@ -ladm -lefi
+hald_probe_storage_CFLAGS = -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+
+hald_probe_volume_SOURCES = probe-volume.c cdutils.c cdutils.h fsutils.c fsutils.h ../../logger.c 
+hald_probe_volume_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@ -lfstyp -lnvpair -ladm -lefi
+hald_probe_volume_CFLAGS = -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+
diff --git a/hald/solaris/probing/cdutils.c b/hald/solaris/probing/cdutils.c
new file mode 100644
index 0000000..8ea0c45
--- /dev/null
+++ b/hald/solaris/probing/cdutils.c
@@ -0,0 +1,484 @@
+/***************************************************************************
+ *
+ * cdutils.h : CD/DVD utilities
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)cdutils.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/dkio.h>
+#include <libintl.h>
+
+#include <logger.h>
+
+#include "cdutils.h"
+
+#define	RQLEN	32
+#define SENSE_KEY(rqbuf)        (rqbuf[2])      /* scsi error category */
+#define ASC(rqbuf)              (rqbuf[12])     /* additional sense code */
+#define ASCQ(rqbuf)             (rqbuf[13])     /* ASC qualifier */
+
+#define	GET16(a) (((a)[0] << 8) | (a)[1])
+#define	GET32(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
+
+#define	CD_USCSI_TIMEOUT	60
+
+void
+uscsi_cmd_init(struct uscsi_cmd *scmd, char *cdb, int cdblen)
+{
+	bzero(scmd, sizeof (*scmd));
+	bzero(cdb, cdblen);
+	scmd->uscsi_cdb = cdb;
+}
+
+int
+uscsi(int fd, struct uscsi_cmd *scmd)
+{
+	char		rqbuf[RQLEN];
+	int		ret;
+	int		i, retries, total_retries;
+	int		max_retries = 20;
+
+	scmd->uscsi_flags |= USCSI_RQENABLE;
+	scmd->uscsi_rqlen = RQLEN;
+	scmd->uscsi_rqbuf = rqbuf;
+
+	for (retries = 0; retries < max_retries; retries++) {
+		scmd->uscsi_status = 0;
+		memset(rqbuf, 0, RQLEN);
+
+		ret = ioctl(fd, USCSICMD, scmd);
+
+		if ((ret == 0) && (scmd->uscsi_status == 2)) {
+			ret = -1;
+			errno = EIO;
+		}
+		if ((ret < 0) && (scmd->uscsi_status == 2)) {
+			/*
+			 * The drive is not ready to recieve commands but
+			 * may be in the process of becoming ready.
+			 * sleep for a short time then retry command.
+			 * SENSE/ASC = 2/4 : not ready
+			 * ASCQ = 0  Not Reportable.
+			 * ASCQ = 1  Becoming ready.
+			 * ASCQ = 4  FORMAT in progress.
+			 * ASCQ = 7  Operation in progress.
+			 */
+			if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
+			    ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
+			    (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) {
+				total_retries++;
+				sleep(1);
+				continue;
+			}
+
+			/*
+			 * Device is not ready to transmit or a device reset
+			 * has occurred. wait for a short period of time then
+			 * retry the command.
+			 */
+			if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
+			    (ASC(rqbuf) == 0x29))) {
+				sleep(1);
+				total_retries++;
+				continue;
+			}
+			/*
+			 * Blank Sense, we don't know what the error is or if
+			 * the command succeeded, Hope for the best. Some
+			 * drives return blank sense periodically and will
+			 * fail if this is removed.
+			 */
+			if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) &&
+			    (ASCQ(rqbuf) == 0)) {
+				ret = 0;
+				break;
+			}
+
+			HAL_DEBUG (("cmd: 0x%02x ret:%i status:%02x "
+			    " sense: %02x ASC: %02x ASCQ:%02x\n",
+			    (uchar_t)scmd->uscsi_cdb[0], ret,
+			    scmd->uscsi_status,
+			    (uchar_t)SENSE_KEY(rqbuf),
+			    (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf)));
+		}
+
+		break;
+	}
+
+	if (retries) {
+		HAL_DEBUG (("total retries: %d\n", total_retries));
+	}
+
+	return (ret);
+}
+
+int
+mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
+{
+	struct uscsi_cmd scmd;
+	char cdb[16];
+
+	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
+	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
+	scmd.uscsi_buflen = page_len;
+	scmd.uscsi_bufaddr = (char *)buffer;
+	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
+	scmd.uscsi_cdblen = 0xa;
+	scmd.uscsi_cdb[0] = 0x5a; /* MODE SENSE 10 */
+	if (dbd) {
+		scmd.uscsi_cdb[1] = 0x8; /* no block descriptors */
+	}
+	scmd.uscsi_cdb[2] = pc;
+	scmd.uscsi_cdb[7] = (page_len >> 8) & 0xff;
+	scmd.uscsi_cdb[8] = page_len & 0xff;
+
+	return (uscsi(fd, &scmd) == 0);
+}
+
+/*
+ * will get the mode page only i.e. will strip off the header.
+ */
+int
+get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer, int *plen)
+{
+	int ret;
+	uchar_t byte2;
+	uchar_t buf[256];
+	uint_t header_len, page_len, copy_cnt;
+
+	byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
+
+	/* Ask 254 bytes only to make our IDE driver happy */
+	if ((ret = mode_sense(fd, byte2, 1, 254, buf)) == 0) {
+		return (0);
+	}
+
+	header_len = 8 + GET16(&buf[6]);
+	page_len = buf[header_len + 1] + 2;
+
+	copy_cnt = (page_len > buf_len) ? buf_len : page_len;
+	(void) memcpy(buffer, &buf[header_len], copy_cnt);
+
+	if (plen) {
+		*plen = page_len;
+	}
+
+	return (1);
+}
+
+/* Get information about the Logical Unit's capabilities */
+int
+get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf)
+{
+	struct uscsi_cmd scmd;
+	char cdb[16];
+
+	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
+	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
+	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
+	scmd.uscsi_cdb[0] = 0x46; /* GET CONFIGURATION */
+	scmd.uscsi_cdb[1] = 0x2; /* request type */
+	scmd.uscsi_cdb[2] = (feature >> 8) & 0xff; /* starting feature # */
+	scmd.uscsi_cdb[3] = feature & 0xff;
+	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
+	scmd.uscsi_cdb[8] = bufsize & 0xff;
+	scmd.uscsi_cdblen = 10;
+	scmd.uscsi_bufaddr = (char *)buf;
+	scmd.uscsi_buflen = bufsize;
+
+	return (uscsi(fd, &scmd) == 0);
+}
+
+boolean_t
+get_current_profile(int fd, int *profile)
+{
+	size_t i;
+	uchar_t smallbuf[4];
+	size_t buflen;
+	uchar_t *bufp;
+	int ret = B_FALSE;
+
+	/* first determine amount of memory needed to hold all profiles */
+	if (get_configuration(fd, 0, 4, &smallbuf[0])) {
+		buflen = GET32(smallbuf) + 4;
+		bufp = (uchar_t *)malloc(buflen);
+
+	 	/* now get all profiles */
+		if (get_configuration(fd, 0, buflen, bufp)) {
+			*profile = GET16(&bufp[6]);
+			ret = B_TRUE;
+		}
+		free(bufp);
+	}
+
+	return (ret);
+}
+
+void
+walk_profiles(int fd, int (*f)(void *, int, boolean_t), void *arg)
+{
+	size_t i;
+	uint16_t profile, current_profile;
+	uchar_t smallbuf[4];
+	size_t buflen;
+	uchar_t *bufp;
+	int ret;
+
+	/* first determine amount of memory needed to hold all profiles */
+	if (get_configuration(fd, 0, 4, &smallbuf[0])) {
+		buflen = GET32(smallbuf) + 4;
+		bufp = (uchar_t *)malloc(buflen);
+
+	 	/* now get all profiles */
+		if (get_configuration(fd, 0, buflen, bufp)) {
+			current_profile = GET16(&bufp[6]);
+			for (i = 8 + 4;  i < buflen; i += 4) {
+				profile = GET16(&bufp[i]);
+				ret = f(arg, profile, (profile == current_profile));
+				if (ret == CDUTIL_WALK_STOP) {
+					break;
+				}
+			}
+		}
+
+		free(bufp);
+	}
+}
+
+/* retrieve speed list from the Write Speed Performance Descriptor Blocks
+ */
+void
+get_write_speeds(uchar_t *page, int n, intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
+{
+	uchar_t	*p = page + 2;
+	int	i;
+	intlist_t **nextp;
+	intlist_t *current;
+	boolean_t skip;
+
+	*n_speeds = 0;
+	*speeds = NULL;
+	*speeds_mem = (intlist_t *)calloc(n, sizeof (intlist_t));
+	if (*speeds_mem == NULL) {
+		return;
+	}
+
+	for (i = 0; i < n; i++, p += 4) {
+		current = &(*speeds_mem)[i];
+		current->val = GET16(p);
+
+		/* keep the list sorted */
+		skip = B_FALSE;
+		for (nextp = speeds; *nextp != NULL; nextp = &((*nextp)->next)) {
+			if (current->val == (*nextp)->val) {
+				skip = B_TRUE; /* skip duplicates */
+				break;
+			} else if (current->val > (*nextp)->val) {
+				break;
+			}
+		}
+		if (!skip) {
+			current->next = *nextp;
+			*nextp = current;
+			*n_speeds++;
+		}
+	}
+}
+
+void
+get_read_write_speeds(int fd, int *read_speed, int *write_speed,
+    intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
+{
+	int page_len;
+	uchar_t	p[254];
+	int n; /* number of write speed performance descriptor blocks */
+
+	*read_speed = *write_speed = 0;
+	*speeds = *speeds_mem = NULL;
+
+	if (!get_mode_page(fd, 0x2A, 0, sizeof (p), p, &page_len)) {
+		return;
+	}
+
+	if (page_len > 8) {
+		*read_speed = GET16(&p[8]);
+	}
+	if (page_len > 18) {
+		*write_speed = GET16(&p[18]);
+	}
+	if (page_len < 28) {
+		printf("MMC-2\n");
+		return;
+	} else {
+		printf("MMC-3\n");
+	}
+
+	*write_speed = GET16(&p[28]);
+
+	if (page_len < 30) {
+		return;
+	}
+
+	/* retrieve speed list */
+	n = GET16(&p[30]);
+	n = min(n, (sizeof (p) - 32) / 4);
+
+	get_write_speeds(&p[32], n, speeds, n_speeds, speeds_mem);
+
+	if (*speeds != NULL) {
+		*write_speed = max(*write_speed, (*speeds)[0].val);
+	}
+}
+
+boolean_t
+get_disc_info(int fd, disc_info_t *di)
+{
+	struct uscsi_cmd scmd;
+	char cdb[16];
+	uint8_t	buf[32];
+	int bufsize = sizeof (buf);
+
+	bzero(buf, bufsize);
+	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
+	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
+	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
+	scmd.uscsi_cdb[0] = 0x51; /* READ DISC INFORMATION */
+	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
+	scmd.uscsi_cdb[8] = bufsize & 0xff;
+	scmd.uscsi_cdblen = 10;
+	scmd.uscsi_bufaddr = (char *)buf;
+	scmd.uscsi_buflen = bufsize;
+
+	if ((uscsi(fd, &scmd)) != 0) {
+		return (B_FALSE);
+	}
+
+	di->disc_status = buf[2] & 0x03;
+	di->erasable = buf[2] & 0x10;
+	if ((buf[21] != 0) && (buf[21] != 0xff)) {
+		di->capacity = ((buf[21] * 60) + buf[22]) * 75;
+	} else {
+		di->capacity = 0;
+        }
+
+	return (B_TRUE);
+}
+
+/*
+ * returns current/maximum format capacity in bytes
+ */
+boolean_t
+read_format_capacity(int fd, uint64_t *capacity)
+{
+	struct uscsi_cmd scmd;
+	char cdb[16];
+	uint8_t	buf[32];
+	int bufsize = sizeof (buf);
+	uint32_t num_blocks;
+	uint32_t block_len;
+
+	bzero(buf, bufsize);
+	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
+	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
+	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
+	scmd.uscsi_cdb[0] = 0x23; /* READ FORMAT CAPACITIRES */
+	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
+	scmd.uscsi_cdb[8] = bufsize & 0xff;
+	scmd.uscsi_cdblen = 12;
+	scmd.uscsi_bufaddr = (char *)buf;
+	scmd.uscsi_buflen = bufsize;
+
+	if ((uscsi(fd, &scmd)) != 0) {
+		return (B_FALSE);
+	}
+
+	num_blocks = (uint32_t)(buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7];
+	block_len = (uint32_t)(buf[9] << 16) + (buf[10] << 8) + buf[11];
+	*capacity = (uint64_t)num_blocks * block_len;
+
+	return (B_TRUE);
+}
+
+boolean_t
+get_media_info(int fd, struct dk_minfo *minfop)
+{
+	return (ioctl(fd, DKIOCGMEDIAINFO, minfop) != -1);
+}
+
+/*
+ * given current profile, use the best method for determining
+ * disc capacity (in bytes)
+ */
+boolean_t
+get_disc_capacity_for_profile(int fd, int profile, uint64_t *capacity)
+{
+	struct dk_minfo	mi;
+	disc_info_t	di;
+	boolean_t	ret = B_FALSE;
+
+	switch (profile) {
+	case 0x08: /* CD-ROM */
+	case 0x10: /* DVD-ROM */
+		if (get_media_info(fd, &mi) && (mi.dki_capacity > 1)) {
+			*capacity = mi.dki_capacity * mi.dki_lbsize;
+			ret = B_TRUE;
+		}
+		break;
+	default:
+		if (read_format_capacity(fd, capacity) && (*capacity > 0)) {
+			ret = B_TRUE;
+		} else if (get_disc_info(fd, &di) && (di.capacity > 0)) {
+			if (get_media_info(fd, &mi)) {
+				*capacity = di.capacity * mi.dki_lbsize;
+				ret = B_TRUE;
+			}
+		}
+	}
+
+	return (ret);
+}
+
+boolean_t
+read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf)
+{
+	struct uscsi_cmd scmd;
+	char cdb[16];
+
+	bzero(buf, buflen);
+	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
+	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
+	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
+	scmd.uscsi_cdb[0] = 0x43 /* READ_TOC_CMD */;
+	scmd.uscsi_cdb[2] = format & 0xf;
+	scmd.uscsi_cdb[6] = trackno;
+	scmd.uscsi_cdb[8] = buflen & 0xff;
+	scmd.uscsi_cdb[7] = (buflen >> 8) & 0xff;
+	scmd.uscsi_cdblen = 10;
+	scmd.uscsi_bufaddr = (char *)buf;
+	scmd.uscsi_buflen = buflen;
+
+	if ((uscsi(fd, &scmd)) != 0) {
+        	return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
diff --git a/hald/solaris/probing/cdutils.h b/hald/solaris/probing/cdutils.h
new file mode 100644
index 0000000..678e26d
--- /dev/null
+++ b/hald/solaris/probing/cdutils.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *
+ * cdutils.h : definitions for CD/DVD utilities
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)cdutils.h	1.2	06/10/13 SMI"
+
+#ifndef CDUTILS_H
+#define CDUTILS_H
+
+#include <sys/types.h>
+#include <sys/dkio.h>
+#include <sys/cdio.h>
+#include <sys/scsi/impl/uscsi.h>
+
+enum {
+	CDUTIL_WALK_CONTINUE,
+	CDUTIL_WALK_STOP
+};
+
+typedef struct intlist {
+	int	val;
+	struct intlist *next;
+} intlist_t;
+
+typedef struct disc_info {
+	int	disc_status;
+	int	erasable;
+	uint_t	capacity;
+} disc_info_t;
+
+#define	min(a, b)	((a) < (b) ? (a) : (b))
+#define	max(a, b)	((a) > (b) ? (a) : (b))
+
+void		uscsi_cmd_init(struct uscsi_cmd *scmd, char *cdb, int cdblen);
+int		uscsi(int fd, struct uscsi_cmd *scmd);
+int		mode_sense(int fd, uchar_t pc, int dbd, int page_len,
+		uchar_t *buffer);
+int		get_mode_page(int fd, int page_no, int pc, int buf_len,
+		uchar_t *buffer, int *plen);
+int		get_configuration(int fd, uint16_t feature, int bufsize,
+		uchar_t *buf);
+boolean_t	get_current_profile(int fd, int *profile);
+void		walk_profiles(int fd, int (*f)(void *, int, boolean_t), void *);
+void		get_read_write_speeds(int fd, int *read_speed, int *write_speed,
+		intlist_t **wspeeds, int *n_wspeeds, intlist_t **wspeeds_mem);
+boolean_t	get_disc_info(int fd, disc_info_t *);
+boolean_t	read_format_capacity(int fd, uint64_t *capacity);
+boolean_t	get_media_info(int fd, struct dk_minfo *minfop);
+boolean_t	get_disc_capacity_for_profile(int fd, int profile,
+		uint64_t *capacity);
+boolean_t	read_toc(int fd, int format, int trackno, int buflen,
+		uchar_t *buf);
+
+#endif /* CDUTILS_H */
diff --git a/hald/solaris/probing/fsutils.c b/hald/solaris/probing/fsutils.c
new file mode 100644
index 0000000..45386d3
--- /dev/null
+++ b/hald/solaris/probing/fsutils.c
@@ -0,0 +1,250 @@
+/***************************************************************************
+ *
+ * fsutils.c : filesystem utilities
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)fsutils.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/dkio.h>
+#include <libintl.h>
+#include <sys/dktp/fdisk.h>
+#include <sys/fs/pc_label.h>
+
+#include <libhal.h>
+#include "fsutils.h"
+
+/*
+ * Separates dos notation device spec into device and drive number
+ */
+boolean_t
+dos_to_dev(char *path, char **devpath, int *num)
+{
+	char *p;
+
+	if ((p = strrchr(path, ':')) == NULL) {
+		return (B_FALSE);
+	}
+	if ((*num = atoi(p + 1)) == 0) {
+		return (B_FALSE);
+	}
+	p[0] = '\0';
+	*devpath = strdup(path);
+	p[0] = ':';
+	return (*devpath != NULL);
+}
+
+char *
+get_slice_name (char *devlink)
+{
+	char	*part, *slice, *disk;
+	char	*s = NULL;
+	char	*p;
+
+	if ((p = strstr(devlink, "/lofi/")) != 0) {
+		return (p + sizeof ("/lofi/") - 1);
+	}
+
+	part = strrchr(devlink, 'p');
+	slice = strrchr(devlink, 's');
+	disk = strrchr(devlink, 'd');
+
+	if ((part != NULL) && (part > slice) && (part > disk)) {
+		s = part;
+	} else if ((slice != NULL) && (slice > disk)) {
+		s = slice;
+	} else {
+		s = disk;
+	}
+	if ((s != NULL) && isdigit(s[1])) {
+		return (s);
+	} else {
+		return ("");
+	}
+}
+
+boolean_t
+is_dos_drive(uchar_t type)
+{
+	return ((type == 1) || (type == 4) || (type == 5) || (type == 6) ||
+	    ((type >= 8) && (type <= 0xf)));
+}
+
+boolean_t
+is_dos_extended(uchar_t id)
+{
+	return ((id == EXTDOS) || (id == FDISK_EXTLBA));
+}
+
+struct part_find_s {
+	int	num;
+	int	count;
+	int	systid;
+	int	r_systid;
+	int	r_relsect;
+	int	r_numsect;
+};
+
+enum { WALK_CONTINUE, WALK_TERMINATE };
+
+/*
+ * Walk partition tables and invoke a callback for each.
+ */
+static void
+walk_partitions(int fd, int startsec, int (*f)(void *, int, int, int),
+    void *arg)
+{
+	uint32_t buf[1024/4];
+	int bufsize = 1024;
+	struct mboot *mboot = (struct mboot *)&buf[0];
+	struct ipart ipart[FD_NUMPART];
+	int sec = startsec;
+	int lastsec = sec + 1;
+	int relsect;
+	int ext = 0;
+	int systid;
+	boolean_t valid;
+	int i;
+
+	while (sec != lastsec) {
+		if (pread(fd, buf, bufsize, (off_t)sec * 512) != bufsize) {
+			break;
+		}
+		lastsec = sec;
+		if (ltohs(mboot->signature) != MBB_MAGIC) {
+			break;
+		}
+		bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart));
+
+		for (i = 0; i < FD_NUMPART; i++) {
+			systid = ipart[i].systid;
+			relsect = sec + ltohi(ipart[i].relsect);
+			if (systid == 0) {
+				continue;
+			}
+			valid = B_TRUE;
+			if (is_dos_extended(systid) && (sec == lastsec)) {
+				sec = startsec + ltohi(ipart[i].relsect);
+				if (ext++ == 0) {
+					relsect = startsec = sec;
+				} else {
+					valid = B_FALSE;
+				}
+			}
+			if (valid && f(arg, ipart[i].systid, relsect,
+			    ltohi(ipart[i].numsect)) == WALK_TERMINATE) {
+				return;
+			}
+		}
+	}
+}
+
+static int
+find_dos_drive_cb(void *arg, int systid, int relsect, int numsect)
+{
+	struct part_find_s *p = arg;
+
+	if (is_dos_drive(systid)) {
+		if (++p->count == p->num) {
+			p->r_relsect = relsect;
+			p->r_numsect = numsect;
+			p->r_systid = systid;
+			return (WALK_TERMINATE);
+		}
+	}
+
+	return (WALK_CONTINUE);
+}
+
+/*
+ * Given a dos drive number, return its relative sector number,
+ * number of sectors in partition and the system id.
+ */
+boolean_t
+find_dos_drive(int fd, int num, int *relsect, int *numsect, int *systid)
+{
+	struct part_find_s p = { 0, 0, 0, 0, 0, 0 };
+
+	p.num = num;
+
+	if (num > 0) {
+		walk_partitions(fd, 0, find_dos_drive_cb, &p);
+		if (p.count == num) {
+			*relsect = p.r_relsect;
+			*numsect = p.r_numsect;
+			*systid = p.r_systid;
+			return (B_TRUE);
+		}
+	}
+
+	return (B_FALSE);
+}
+
+static int
+get_num_dos_drives_cb(void *arg, int systid, int relsect, int numsect)
+{
+	if (is_dos_drive(systid)) {
+		(*(int *)arg)++;
+	}
+	return (WALK_CONTINUE);
+}
+
+int
+get_num_dos_drives(int fd)
+{
+	int count = 0;
+
+	walk_partitions(fd, 0, get_num_dos_drives_cb, &count);
+
+	return (count);
+}
+
+/*
+ * Return true if all non-empty slices in vtoc have identical start/size and
+ * are tagged backup/entire disk.
+ */
+boolean_t
+vtoc_one_slice_entire_disk(struct vtoc *vtoc)
+{
+	int		i;
+	struct partition *p;
+	daddr_t		prev_start;
+	long		prev_size;
+
+	for (i = 0; i < vtoc->v_nparts; i++) {
+		p = &vtoc->v_part[i];
+		if (p->p_size == 0) {
+			continue;
+		}
+		if ((p->p_tag != V_BACKUP) && ((p->p_tag != V_UNASSIGNED))) {
+			return (B_FALSE);
+		}
+		if ((i > 0) &&
+		    ((p->p_start != prev_start) || (p->p_size != prev_size))) {
+			return (B_FALSE);
+		}
+		prev_start = p->p_start;
+		prev_size = p->p_size;
+	}
+
+	return (B_TRUE);
+}
diff --git a/hald/solaris/probing/fsutils.h b/hald/solaris/probing/fsutils.h
new file mode 100644
index 0000000..73fa823
--- /dev/null
+++ b/hald/solaris/probing/fsutils.h
@@ -0,0 +1,28 @@
+/***************************************************************************
+ *
+ * fsutils.h : definitions for filesystem utilities
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)fsutils.h	1.2	06/10/13 SMI"
+
+#ifndef FSUTILS_H
+#define FSUTILS_H
+
+#include <sys/types.h>
+#include <sys/vtoc.h>
+
+boolean_t dos_to_dev(char *path, char **devpath, int *num);
+char *get_slice_name (char *devlink);
+boolean_t is_dos_drive(uchar_t id);
+boolean_t is_dos_extended(uchar_t id);
+boolean_t find_dos_drive(int fd, int num, int *relsect, int *numsect, int *systid);
+int get_num_dos_drives(int fd);
+boolean_t vtoc_one_slice_entire_disk(struct vtoc *vtoc);
+
+#endif /* FSUTILS_H */
diff --git a/hald/solaris/probing/probe-storage.c b/hald/solaris/probing/probe-storage.c
new file mode 100644
index 0000000..61ec991
--- /dev/null
+++ b/hald/solaris/probing/probe-storage.c
@@ -0,0 +1,488 @@
+/***************************************************************************
+ *
+ * probe-storage.c : Probe for storage devices
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)probe-storage.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mnttab.h>
+#include <sys/fdio.h>
+#include <sys/scsi/scsi.h>
+#include <sys/vtoc.h>
+#include <sys/efi_partition.h>
+#include <priv.h>
+
+#include <libhal.h>
+#include <cdutils.h>
+#include <fsutils.h>
+#include <logger.h>
+
+/** Check if a filesystem on a special device file is mounted
+ *
+ *  @param  device_file         Special device file, e.g. /dev/cdrom
+ *  @return                     TRUE iff there is a filesystem system mounted
+ *                              on the special device file
+ */
+static dbus_bool_t
+is_mounted (const char *device_file)
+{
+	FILE *f;
+	dbus_bool_t rc = FALSE;
+	struct mnttab mp;
+	struct mnttab mpref;
+
+	if ((f = fopen ("/etc/mnttab", "r")) == NULL)
+		return rc;
+
+	bzero(&mp, sizeof (mp));
+	bzero(&mpref, sizeof (mpref));
+	mpref.mnt_special = (char *)device_file;
+	if (getmntany(f, &mp, &mpref) == 0) {
+		rc = TRUE;
+	}
+
+	fclose (f);
+	return rc;
+}
+
+static int
+get_cdrom_properties_walker (void *arg, int profile, boolean_t is_current)
+{
+	LibHalChangeSet	*cs = (LibHalChangeSet *)arg;
+
+	switch (profile) {
+	case 0x09:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", TRUE);
+		break;
+	case 0x0a:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", TRUE);
+		break;
+	case 0x10:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", TRUE);
+		break;
+	case 0x11:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", TRUE);
+		break;
+	case 0x12:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", TRUE);
+		break;
+	case 0x13:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE);
+		break;
+	case 0x14:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE);
+		break;
+	case 0x1a:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", TRUE);
+		break;
+	case 0x1b:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", TRUE);
+		break;
+	case 0x2b:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", TRUE);
+		break;
+	case 0x40:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", TRUE);
+		break;
+	case 0x41:
+	case 0x42:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", TRUE);
+		break;
+	case 0x43:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", TRUE);
+		break;
+	case 0x50:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", TRUE);
+		break;
+	case 0x51:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", TRUE);
+		break;
+	case 0x52:
+		libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", TRUE);
+		break;
+	}
+
+	return CDUTIL_WALK_CONTINUE;
+}
+
+#define	WSPLEN	64
+
+static void
+get_cdrom_properties (int fd, LibHalChangeSet *cs)
+{
+	DBusError error;
+	int capabilities;
+	int read_speed, write_speed;
+	intlist_t *write_speeds, *write_speeds_mem, *sp;
+	int n_wspeeds;
+	char **wspeeds;
+	char *wspeeds_mem;
+	int i;
+			
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", FALSE);
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", FALSE);
+
+	walk_profiles(fd, get_cdrom_properties_walker, cs);
+
+	/* XXX */		
+	libhal_changeset_set_property_bool (cs, "storage.cdrom.support_media_changed", TRUE);
+			
+	get_read_write_speeds(fd, &read_speed, &write_speed, &write_speeds, &n_wspeeds, &write_speeds_mem);
+
+	libhal_changeset_set_property_int (cs, "storage.cdrom.read_speed", read_speed);
+	libhal_changeset_set_property_int (cs, "storage.cdrom.write_speed", write_speed);
+
+	if (n_wspeeds <= 0) {
+		wspeeds_mem = NULL;
+ 		libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)&wspeeds_mem);
+		return;
+	}
+	if ((wspeeds = (char **)calloc(n_wspeeds + 1, sizeof (char *))) == NULL) {
+		free (write_speeds_mem);
+		return;
+	}
+	if ((wspeeds_mem = (char *)calloc(n_wspeeds, WSPLEN)) == NULL) {
+		free (wspeeds);
+		free (write_speeds_mem);
+		return;
+	}
+	for (i = 0; i < n_wspeeds; i++) {
+		wspeeds[i] = &wspeeds_mem[i * WSPLEN];
+	}
+
+	for (sp = write_speeds, i = 0; sp != NULL; sp = sp->next, i++) {
+		snprintf (wspeeds[i], WSPLEN, "%d", sp->val);
+	}
+	libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)wspeeds);
+
+	free (wspeeds);
+	free (wspeeds_mem);
+	free (write_speeds_mem);
+}
+
+/*
+ * Return a copy of a string without trailing spaces. If 'len' is non-zero,
+ * it specifies max length, otherwise the string must be null-terminated.
+ */
+char *
+rtrim_copy(char *src, int len)
+{
+	char	*dst, *p;
+
+	if (len == 0) {
+		len = strlen(src);
+	}
+	if ((dst = calloc(1, len + 1)) != NULL) {
+		strncpy(dst, src, len);
+		p = dst + len - 1;
+		while ((p >= dst) && (isspace(*p))) {
+			*p-- = '\0';
+		}
+	}
+	return (dst);
+}
+
+static void
+get_disk_properties (int fd, LibHalChangeSet *cs)
+{
+	struct scsi_inquiry inq;
+	struct uscsi_cmd ucmd;
+	union scsi_cdb  cdb;
+	int		status;
+	char		*s;
+
+	/* INQUIRY */
+	(void) memset((void *) &inq, 0, sizeof (inq));
+	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
+	(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));
+	cdb.scc_cmd = SCMD_INQUIRY;
+	FORMG0COUNT(&cdb, sizeof (inq));
+	ucmd.uscsi_cdb = (caddr_t) & cdb;
+	ucmd.uscsi_cdblen = CDB_GROUP0;
+	ucmd.uscsi_bufaddr = (caddr_t) & inq;
+	ucmd.uscsi_buflen = sizeof (inq);
+	ucmd.uscsi_timeout = 30;
+	ucmd.uscsi_flags = USCSI_READ;
+	status = ioctl(fd, USCSICMD, &ucmd);
+	if (status || ucmd.uscsi_status) {
+		return;
+	}
+
+	if ((s = rtrim_copy(inq.inq_vid, sizeof (inq.inq_vid))) != NULL) {
+		libhal_changeset_set_property_string (cs, "storage.vendor", s);
+		free(s);
+	}
+	if ((s = rtrim_copy(inq.inq_pid, sizeof (inq.inq_pid))) != NULL) {
+		libhal_changeset_set_property_string (cs, "storage.model", s);
+		free(s);
+	}
+	if ((s = rtrim_copy(inq.inq_revision, sizeof (inq.inq_revision))) != NULL) {
+		libhal_changeset_set_property_string (cs, "storage.firmware_revision", s);
+		free(s);
+	}
+	if ((s = rtrim_copy(inq.inq_serial, sizeof (inq.inq_serial))) != NULL) {
+		libhal_changeset_set_property_string (cs, "storage.serial", s);
+		free(s);
+	}
+}
+
+/*
+ * returns TRUE if diskette is inserted.
+ * also returns write protection status.
+ */
+static dbus_bool_t
+check_floppy(int fd, dbus_bool_t *wprot)
+{
+	int	chg;
+
+	if ((ioctl(fd, FDGETCHANGE, &chg) == 0) && !(chg & FDGC_CURRENT)) {
+		*wprot = ((chg & FDGC_CURWPROT) != NULL);
+		return (TRUE);
+	} else {
+		return (FALSE);
+	}
+}
+
+void
+drop_privileges ()
+{
+	priv_set_t *pPrivSet = NULL;
+	priv_set_t *lPrivSet = NULL;
+
+	/*
+	 * Start with the 'basic' privilege set and then remove any
+	 * of the 'basic' privileges that will not be needed.
+	 */
+	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
+		return;
+	}
+
+	/* Clear privileges we will not need from the 'basic' set */
+	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
+	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
+	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
+	(void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
+	(void) priv_delset(pPrivSet, PRIV_PROC_FORK);
+
+	/* for uscsi */
+	(void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
+
+	/* to open logindevperm'd devices */
+	(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
+
+	/* Set the permitted privilege set. */
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
+		return;
+	}
+
+	/* Clear the limit set. */
+	if ((lPrivSet = priv_allocset()) == NULL) {
+		return;
+	}
+
+	priv_emptyset(lPrivSet);
+
+	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
+		return;
+	}
+
+	priv_freeset(lPrivSet);
+}
+
+int 
+main (int argc, char *argv[])
+{
+	int ret = 1;
+	int fd = -1;
+	int rfd = -1;
+	char *udi;
+	char *device_file;
+	char *raw_device_file;
+	LibHalContext *ctx = NULL;
+	DBusError error;
+	char *bus;
+	char *drive_type;
+	dbus_bool_t is_cdrom;
+	dbus_bool_t is_floppy;
+	struct dk_minfo minfo;
+	dbus_bool_t only_check_for_media;
+	int got_media = FALSE;
+	dbus_bool_t is_write_protected = FALSE;
+	dbus_bool_t is_mbr = FALSE;
+	dbus_bool_t is_smi = FALSE;
+	dbus_bool_t is_gpt = FALSE;
+	dbus_bool_t is_partitioned = FALSE;
+	dbus_bool_t vtoc_slices = FALSE;
+	int dos_cnt = 0;
+	const char *scheme = "";
+	struct vtoc vtoc;
+	dk_gpt_t *gpt;
+	LibHalChangeSet *cs = NULL;
+
+	if ((udi = getenv ("UDI")) == NULL)
+		goto out;
+	if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL)
+		goto out;
+	if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL)
+		goto out;
+	if ((bus = getenv ("HAL_PROP_STORAGE_BUS")) == NULL)
+		goto out;
+	if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL)
+		goto out;
+
+	drop_privileges ();
+
+	setup_logger ();
+
+	if (argc == 2 && strcmp (argv[1], "--only-check-for-media") == 0)
+		only_check_for_media = TRUE;
+	else
+		only_check_for_media = FALSE;
+
+	is_cdrom = (strcmp (drive_type, "cdrom") == 0);
+	is_floppy = (strcmp (drive_type, "floppy") == 0);
+
+	dbus_error_init (&error);
+	if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
+		goto out;
+
+	if ((cs = libhal_device_new_changeset (udi)) == NULL) {
+		HAL_DEBUG (("Cannot allocate changeset"));
+		goto out;
+	}
+
+	HAL_DEBUG (("Doing probe-storage for %s (bus %s) (drive_type %s) (udi=%s) (--only-check-for-media==%d)", 
+	     device_file, bus, drive_type, udi, only_check_for_media));
+
+	if ((rfd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0) {
+		HAL_DEBUG (("Cannot open %s: %s", raw_device_file, strerror (errno)));
+		goto out;
+	}
+
+	if (!only_check_for_media) {
+		if (strcmp (drive_type, "cdrom") == 0) {
+			get_cdrom_properties (rfd, cs);
+		} else if (strcmp (drive_type, "disk") == 0) {
+			get_disk_properties (rfd, cs);
+		}
+	}
+
+	ret = 0;
+
+	if (is_cdrom) {
+		HAL_DEBUG (("Checking for optical disc on %s", raw_device_file));
+		got_media = get_media_info(rfd, &minfo);
+		if (!got_media) {
+			goto out_cs;
+		}
+		/* XXX */
+		is_write_protected = TRUE;
+	} else if (is_floppy) {
+		HAL_DEBUG (("Checking for floppy on %s", raw_device_file));
+		if (check_floppy(rfd, &is_write_protected)) {
+			got_media = TRUE;
+		}
+		/* don't look for partitions on floppy */
+		goto out_cs;
+	} else {
+		got_media = TRUE;
+	}
+
+	HAL_DEBUG (("Checking for partitions on %s", device_file));
+
+	if ((fd = open (device_file, O_RDONLY | O_NONBLOCK)) < 0) {
+		HAL_DEBUG (("Cannot open %s: %s", device_file, strerror (errno)));
+		goto out_cs;
+	}
+
+	dos_cnt = get_num_dos_drives(fd);
+	is_mbr = (dos_cnt > 0);
+	if (is_mbr) {
+		scheme = "mbr";
+	}
+	if (read_vtoc(rfd, &vtoc) >= 0) {
+		if (!vtoc_one_slice_entire_disk(&vtoc)) {
+			is_smi = TRUE;
+			if (!is_mbr) {
+				/* smi within mbr partition is okay */
+				scheme = "smi";
+			}
+			vtoc_slices = TRUE;
+		}
+	} else if (!is_cdrom && (efi_alloc_and_read(rfd, &gpt) >= 0)) {
+		/*
+		 * Note: for some reason efi_read takes very long on cdroms.
+		 * Needs more investigation, skip gpt on cdrom for now.
+		 */
+		is_gpt = TRUE;
+		scheme = "gpt";
+		efi_free(gpt);
+	}
+
+out_cs:
+	is_partitioned = is_mbr || is_smi || is_gpt;
+	libhal_changeset_set_property_bool (cs, "storage.no_partitions_hint", !is_partitioned);
+	libhal_changeset_set_property_bool (cs, "block.no_partitions", !is_partitioned);
+	libhal_changeset_set_property_string (cs, "storage.partitioning_scheme", scheme);
+	libhal_changeset_set_property_bool (cs, "storage.solaris.vtoc_slices", vtoc_slices);
+	libhal_changeset_set_property_int (cs, "storage.solaris.num_dos_partitions", dos_cnt);
+	/* XXX should only set for removable drives */
+	libhal_changeset_set_property_bool (cs, "storage.removable.media_available", got_media);
+	libhal_changeset_set_property_bool (cs, "storage.removable.solaris.read_only", is_write_protected);
+
+	libhal_device_commit_changeset (ctx, cs, &error);
+
+out:
+	if (cs != NULL) {
+		libhal_device_free_changeset (cs);
+	}
+	if (fd >= 0) {
+		close (fd);
+	}
+	if (rfd >= 0) {
+		close (rfd);
+	}
+	if (ctx != NULL) {
+		if (dbus_error_is_set(&error)) {
+			dbus_error_free (&error);
+		}
+		libhal_ctx_shutdown (ctx, &error);
+		libhal_ctx_free (ctx);
+	}
+
+	return ret;
+}
diff --git a/hald/solaris/probing/probe-volume.c b/hald/solaris/probing/probe-volume.c
new file mode 100644
index 0000000..621022e
--- /dev/null
+++ b/hald/solaris/probing/probe-volume.c
@@ -0,0 +1,673 @@
+/***************************************************************************
+ *
+ * probe-volume.c : probe volumes
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)probe-volume.c	1.4	06/10/27 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/dkio.h>
+#include <sys/cdio.h>
+#include <sys/fdio.h>
+#include <libnvpair.h>
+#include <libfstyp.h>
+#include <sys/vtoc.h>
+#include <sys/efi_partition.h>
+#include <sys/fs/hsfs_spec.h>
+#include <sys/fs/hsfs_isospec.h>
+#include <priv.h>
+
+#include <libhal.h>
+#include <cdutils.h>
+#include <fsutils.h>
+#include <logger.h>
+
+static void
+my_dbus_error_free(DBusError *error)
+{
+	if (dbus_error_is_set(error)) {
+		dbus_error_free(error);
+	}
+}
+
+/*
+ * Return a copy of a string without trailing spaces. If 'len' is non-zero,
+ * it specifies max length, otherwise the string must be null-terminated.
+ */
+static char *
+rtrim_copy(char *src, int len)
+{
+	char	*dst, *p;
+
+	if (len == 0) {
+		len = strlen(src);
+	}
+	if ((dst = calloc(1, len + 1)) != NULL) {
+		strncpy(dst, src, len);
+		p = dst + len - 1;
+		while ((p >= dst) && (isspace(*p))) {
+			*p-- = '\0';
+		}
+	}
+	return (dst);
+}
+
+static void
+set_fstyp_properties (LibHalContext *ctx, const char *udi, const char *fstype, nvlist_t *fsattr)
+{
+	char buf[256];
+	DBusError error;
+	char *uuid = NULL;
+	char *label_orig = NULL;
+	char *label = NULL;
+	LibHalChangeSet *cs;
+
+	dbus_error_init (&error);
+
+	if ((cs = libhal_device_new_changeset (udi)) == NULL) {
+		return;
+	}
+
+	libhal_changeset_set_property_string (cs, "volume.fsusage", "filesystem");
+	libhal_changeset_set_property_string (cs, "volume.fstype", fstype);
+
+	/* label */
+	(void) nvlist_lookup_string(fsattr, "gen_volume_label", &label_orig);
+	if (label_orig != NULL) {
+		label = rtrim_copy(label_orig, 0);
+	}
+	if ((label != NULL) && (label[0] != '\0')) {
+		libhal_changeset_set_property_string (cs, "volume.label", label);
+		libhal_changeset_set_property_string (cs, "info.product", label);
+	} else {
+		libhal_changeset_set_property_string (cs, "volume.label", "");
+		snprintf (buf, sizeof (buf), "Volume (%s)", fstype);
+		libhal_changeset_set_property_string (cs, "info.product", buf);
+	}
+	free(label);
+
+	/* uuid */
+	if (nvlist_lookup_string(fsattr, "gen_uuid", &uuid) == 0) {
+		libhal_changeset_set_property_string (cs, "volume.uuid", uuid);
+	} else {
+		libhal_changeset_set_property_string (cs, "volume.uuid", "");
+	}
+
+	libhal_device_commit_changeset (ctx, cs, &error);
+	libhal_device_free_changeset (cs);
+
+	my_dbus_error_free (&error);
+}
+
+/*
+ * hsfs/iso9660 contents detection: Video DVD, Video CD, etc.
+ */
+static void
+hsfs_contents(int fd, off_t probe_offset, LibHalContext *ctx, const char *udi)
+{
+	size_t	secsz = ISO_SECTOR_SIZE;
+	uchar_t	buf[ISO_SECTOR_SIZE];
+	int	ptbl_lbn, ptbl_size;
+	int	off, reloff, readoff;
+	uchar_t	*p;
+	char	*name;
+	int	name_len;
+	int	ipe_len;
+	DBusError error;
+
+	/*
+	 * find 1st Primary Volume Descriptor
+	 */
+	readoff = probe_offset + ISO_VOLDESC_SEC * secsz;
+	if (pread (fd, buf, secsz, readoff) != secsz) {
+		return;
+	}
+	while (ISO_DESC_TYPE (buf) != ISO_VD_PVD) {
+		if (ISO_DESC_TYPE (buf) == ISO_VD_EOV) {
+			return;
+		}
+		readoff += secsz;
+		if (pread (fd, buf, secsz, readoff) != secsz) {
+			return;
+		}
+	}
+
+	/*
+	 * PVD contains size and offset of the LSB/MSB path table
+	 */
+	ptbl_size = ISO_PTBL_SIZE (buf);
+#if defined(_LITTLE_ENDIAN)
+        ptbl_lbn = ISO_PTBL_MAN_LS (buf);
+#else
+        ptbl_lbn = ISO_PTBL_MAN_MS (buf);
+#endif
+
+	/*
+	 * Look through path table entries
+	 */
+	readoff = probe_offset + ptbl_lbn * secsz;
+	if (pread (fd, buf, secsz, readoff) != secsz) {
+		return;
+	}
+	dbus_error_init (&error);
+
+	for (off = reloff = 0;
+	    off < ptbl_size;
+	    off += ipe_len, reloff += ipe_len) {
+
+		/* load sectors on demand */
+		if (reloff >= secsz) {
+			readoff += secsz;
+			if (pread (fd, buf, secsz, readoff) != secsz) {
+				break;
+			}
+			reloff -= secsz;
+		}
+
+		p = buf + reloff;
+		name_len = IPE_NAME_LEN(p);
+		ipe_len = IPE_FPESIZE + name_len + (name_len % 2);
+
+		/* only interested in root directories */
+		if (IPE_PARENT_NO (p) != 1) {
+			continue;
+		}
+		if ((name_len < 2) || (name_len > IDE_MAX_NAME_LEN)) {
+			continue;
+		}
+
+		name = (char *)IPE_NAME (p);
+		if (strncasecmp (name, "VIDEO_TS", min (8, name_len)) == 0) {
+			libhal_device_set_property_bool (ctx, udi,
+			    "volume.disc.is_videodvd", TRUE, &error);
+		} else if (strncasecmp (name, "VCD", min (3, name_len)) == 0) {
+			libhal_device_set_property_bool (ctx, udi,
+			    "volume.disc.is_vcd", TRUE, &error);
+		} else if (strncasecmp (name, "SVCD", min (4, name_len)) == 0) {
+			libhal_device_set_property_bool (ctx, udi,
+			    "volume.disc.is_svcd", TRUE, &error);
+		}
+	}
+
+	my_dbus_error_free (&error);
+}
+
+static dbus_bool_t
+probe_disc (int fd, LibHalContext *ctx, const char *udi, dbus_bool_t *should_probe_for_fs)
+{
+	DBusError error;
+	disc_info_t di;
+	int profile;
+	dbus_bool_t has_audio, has_data, is_blank, is_appendable, is_rewritable;
+	char *disc_type = "cd_rom";
+	uint64_t capacity = 0;
+	int i;
+	LibHalChangeSet *cs;
+
+	dbus_error_init (&error);
+
+	if (get_disc_info (fd, &di)) {
+		is_blank = (di.disc_status == 0);
+		is_appendable = (di.disc_status == 1);
+		is_rewritable = (di.erasable != 0);
+	} else {
+		is_blank = is_appendable = is_rewritable = FALSE;
+	}
+
+	if (get_current_profile (fd, &profile)) {
+		switch (profile) {
+		case 0x08: /* CD-ROM */
+			disc_type = "cd_rom";
+			break;
+		case 0x09: /* CD-R */
+			disc_type = "cd_r";
+			break;
+		case 0x0A: /* CD-RW */
+			disc_type = "cd_rw";
+			is_rewritable = TRUE;
+			break;
+		case 0x10: /* DVD-ROM */
+			disc_type = "dvd_rom";
+			break;
+		case 0x11: /* DVD-R Sequential */
+			disc_type = "dvd_r";
+			break;
+		case 0x12: /* DVD-RAM */
+			disc_type = "dvd_ram";
+			is_rewritable = TRUE;
+			break;
+		case 0x13: /* DVD-RW Restricted Overwrite */
+			disc_type = "dvd_rw";
+			is_rewritable = TRUE;
+			break;
+		case 0x14: /* DVD-RW Sequential */
+			disc_type = "dvd_rw";
+			is_rewritable = TRUE;
+			break;
+		case 0x1A: /* DVD+RW */
+			disc_type = "dvd_plus_rw";
+			is_rewritable = TRUE;
+			break;
+		case 0x1B: /* DVD+R */
+			disc_type = "dvd_plus_r";
+			break;
+		case 0x2B: /* DVD+R Double Layer */
+                        disc_type = "dvd_plus_r_dl";
+			break;
+		case 0x40: /* BD-ROM */
+                        disc_type = "bd_rom";
+			break;
+		case 0x41: /* BD-R Sequential */
+                        disc_type = "bd_r";
+			break;
+		case 0x42: /* BD-R Random */
+                        disc_type = "bd_r";
+			break;
+		case 0x43: /* BD-RE */
+                        disc_type = "bd_re";
+			is_rewritable = TRUE;
+			break;
+		case 0x50: /* HD DVD-ROM */
+                        disc_type = "hddvd_rom";
+			break;
+		case 0x51: /* HD DVD-R */
+                        disc_type = "hddvd_r";
+			break;
+		case 0x52: /* HD DVD-Rewritable */
+                        disc_type = "hddvd_rw";
+			is_rewritable = TRUE;
+			break;
+		}
+
+		(void) get_disc_capacity_for_profile(fd, profile, &capacity);
+	}
+
+	has_audio = has_data = FALSE;
+	if (!is_blank) {
+		uchar_t	smalltoc[12];
+		size_t	toc_size;
+		uchar_t	*toc, *p;
+
+		/*
+		 * XXX for some reason CDROMREADTOCENTRY fails on video DVDs,
+		 * but extracting the toc directly works okay.
+		 */
+        	if (!read_toc(fd, 0, 1, 4, smalltoc)) {
+                	HAL_DEBUG(("read_toc failed"));
+			has_data = B_TRUE; /* probe for fs anyway */
+        	} else {
+        		toc_size = smalltoc[0] * 256 + smalltoc[1] + 2;
+        		toc = (uchar_t *)calloc(1, toc_size);
+        		if (toc == NULL || !read_toc(fd, 0, 1, toc_size, toc)) {
+                		HAL_DEBUG (("read_toc again failed"));
+        		} else {
+        			for (p = &toc[4]; p < (toc + toc_size); p += 8) {
+					/* skip leadout */
+                			if (p[2] == 0xAA) {
+						continue;
+					}
+					if (p[1] & 4) {
+						has_data = B_TRUE;
+					} else {
+						has_audio = B_TRUE;
+					}
+        			}
+			}
+			free(toc);
+		}
+	}
+
+	if ((cs = libhal_device_new_changeset (udi)) == NULL) {
+		return (FALSE);
+	}
+	libhal_changeset_set_property_string (cs, "volume.disc.type", disc_type);
+	libhal_changeset_set_property_bool (cs, "volume.disc.is_blank", is_blank);
+	libhal_changeset_set_property_bool (cs, "volume.disc.has_audio", has_audio);
+	libhal_changeset_set_property_bool (cs, "volume.disc.has_data", has_data);
+	libhal_changeset_set_property_bool (cs, "volume.disc.is_appendable", is_appendable);
+	libhal_changeset_set_property_bool (cs, "volume.disc.is_rewritable", is_rewritable);
+	libhal_changeset_set_property_uint64 (cs, "volume.disc.capacity", capacity);
+
+	libhal_changeset_set_property_bool (cs, "volume.disc.is_videodvd", FALSE);
+	libhal_changeset_set_property_bool (cs, "volume.disc.is_vcd", FALSE);
+	libhal_changeset_set_property_bool (cs, "volume.disc.is_svcd", FALSE);
+
+	libhal_device_commit_changeset (ctx, cs, &error);
+	libhal_device_free_changeset (cs);
+
+out:
+
+	*should_probe_for_fs = has_data;
+
+	my_dbus_error_free (&error);
+
+	return (TRUE);
+}
+
+static void
+drop_privileges ()
+{
+	priv_set_t *pPrivSet = NULL;
+	priv_set_t *lPrivSet = NULL;
+
+	/*
+	 * Start with the 'basic' privilege set and then remove any
+	 * of the 'basic' privileges that will not be needed.
+	 */
+	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
+		return;
+	}
+
+	/* Clear privileges we will not need from the 'basic' set */
+	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
+	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
+	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
+	(void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
+	(void) priv_delset(pPrivSet, PRIV_PROC_FORK);
+
+	/* for uscsi */
+	(void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
+
+
+	/* to open logindevperm'd devices */
+	(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
+
+	/* Set the permitted privilege set. */
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
+		return;
+	}
+
+	/* Clear the limit set. */
+	if ((lPrivSet = priv_allocset()) == NULL) {
+		return;
+	}
+
+	priv_emptyset(lPrivSet);
+
+	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
+		return;
+	}
+
+	priv_freeset(lPrivSet);
+}
+
+int 
+main (int argc, char *argv[])
+{
+	int fd, rfd;
+	int ret;
+	char *udi;
+	char *device_file, *raw_device_file;
+	char *devpath, *rdevpath;
+	boolean_t is_dos;
+	int dos_num;
+	LibHalContext *ctx = NULL;
+	DBusError error;
+	DBusConnection *conn;
+	char *parent_udi;
+	char *storage_device;
+	char *is_disc_str;
+	int fdc;
+	dbus_bool_t is_disc = FALSE;
+	dbus_bool_t is_floppy = FALSE;
+	unsigned int block_size;
+	dbus_uint64_t vol_size;
+	dbus_bool_t should_probe_for_fs;
+	char *partition_scheme = NULL;
+	dbus_uint64_t partition_start = 0;
+	int partition_number = 0;
+	struct vtoc vtoc;
+	dk_gpt_t *gpt;
+	struct dk_minfo mi;
+	int i, dos_cnt;
+	fstyp_handle_t fstyp_handle;
+	int systid, relsect, numsect;
+	off_t probe_offset = 0;
+	int num_volumes;
+	char **volumes;
+	dbus_uint64_t v_start;
+	const char *fstype;
+	nvlist_t *fsattr;
+
+	fd = rfd = -1;
+
+	ret = 1;
+
+	if ((udi = getenv ("UDI")) == NULL) {
+		goto out;
+	}
+	if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL) {
+		goto out;
+	}
+	if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL) {
+		goto out;
+	}
+	if (!dos_to_dev(device_file, &rdevpath, &dos_num)) {
+		rdevpath = raw_device_file;
+	}
+	if (!(is_dos = dos_to_dev(device_file, &devpath, &dos_num))) {
+		devpath = device_file;
+	}
+	if ((parent_udi = getenv ("HAL_PROP_INFO_PARENT")) == NULL) {
+		goto out;
+	}
+	if ((storage_device = getenv ("HAL_PROP_BLOCK_STORAGE_DEVICE")) == NULL) {
+		goto out;
+	}
+
+	is_disc_str = getenv ("HAL_PROP_VOLUME_IS_DISC");
+	if (is_disc_str != NULL && strcmp (is_disc_str, "true") == 0) {
+		is_disc = TRUE;
+	} else {
+		is_disc = FALSE;
+	}
+
+	drop_privileges ();
+
+	setup_logger ();
+
+	dbus_error_init (&error);
+	if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
+		goto out;
+
+	HAL_DEBUG (("Doing probe-volume for %s\n", device_file));
+
+	fd = open (devpath, O_RDONLY | O_NONBLOCK);
+	if (fd < 0) {
+		goto out;
+	}
+	rfd = open (rdevpath, O_RDONLY | O_NONBLOCK);
+	if (rfd < 0) {
+		goto out;
+	}
+
+	/* if it's a floppy with no media, bail out */
+	if (ioctl(rfd, FDGETCHANGE, &fdc) == 0) {
+		is_floppy = TRUE;
+		if (fdc & FDGC_CURRENT) {
+			goto out;
+		}
+	}
+
+	/* block size and total size */
+	if (ioctl(rfd, DKIOCGMEDIAINFO, &mi) != -1) {
+		block_size = mi.dki_lbsize;
+		vol_size = mi.dki_capacity * block_size;
+	} else {
+		block_size = 512;
+		vol_size = 0;
+	}
+	libhal_device_set_property_int (ctx, udi, "volume.block_size", block_size, &error);
+	my_dbus_error_free (&error);
+	libhal_device_set_property_uint64 (ctx, udi, "volume.size", vol_size, &error);
+	my_dbus_error_free (&error);
+
+	should_probe_for_fs = TRUE;
+
+	if (is_disc) {
+		if (!probe_disc (rfd, ctx, udi, &should_probe_for_fs)) {
+			HAL_DEBUG (("probe_disc failed, skipping fstyp"));
+			goto out;
+		}
+		/* XXX vol_probe_offset for multisession discs? */
+	}
+
+	if (!should_probe_for_fs) {
+		goto skip_fs;
+	}
+
+	/* don't support partitioned floppy */
+	if (is_floppy) {
+		goto skip_part;
+	}
+
+	/*
+	 * first get partitioning info
+	 */
+	if (is_dos) {
+		/* for a dos drive find partition offset */
+		if (!find_dos_drive(fd, dos_num, &relsect, &numsect, &systid)) {
+			goto out;
+		}
+		partition_scheme = "mbr";
+		partition_start = (dbus_uint64_t)relsect * 512;
+		partition_number = dos_num;
+		probe_offset = (off_t)relsect * 512;
+	} else {
+		if ((partition_number = read_vtoc(rfd, &vtoc)) >= 0) {
+			if (!vtoc_one_slice_entire_disk(&vtoc)) {
+				partition_scheme = "smi";
+				if (partition_number < vtoc.v_nparts) {
+					if (vtoc.v_part[partition_number].p_size == 0) {
+						HAL_DEBUG (("zero size partition"));
+					}
+					partition_start = vtoc.v_part[partition_number].p_start * block_size;
+				}
+			}
+		} else if ((partition_number = efi_alloc_and_read(rfd, &gpt)) >= 0) {
+			partition_scheme = "gpt";
+			if (partition_number < gpt->efi_nparts) {
+				if (gpt->efi_parts[partition_number].p_size == 0) {
+					HAL_DEBUG (("zero size partition"));
+				}
+				partition_start = gpt->efi_parts[partition_number].p_start * block_size;
+			}
+			efi_free(gpt);
+		}
+		probe_offset = 0;
+	}
+
+	if (partition_scheme != NULL) {
+		libhal_device_set_property_string (ctx, udi, "volume.partition.scheme", partition_scheme, &error);
+		my_dbus_error_free (&error);
+		libhal_device_set_property_int (ctx, udi, "volume.partition.number", partition_number, &error);
+		my_dbus_error_free (&error);
+		libhal_device_set_property_uint64 (ctx, udi, "volume.partition.start", partition_start, &error);
+		my_dbus_error_free (&error);
+		libhal_device_set_property_bool (ctx, udi, "volume.is_partition", TRUE, &error);
+		my_dbus_error_free (&error);
+	} else {
+		libhal_device_set_property_bool (ctx, udi, "volume.is_partition", FALSE, &error);
+		my_dbus_error_free (&error);
+	}
+
+	/*
+	 * ignore duplicate partitions
+	 */
+	if ((volumes = libhal_manager_find_device_string_match (
+	    ctx, "block.storage_device", storage_device, &num_volumes, &error)) != NULL) {
+		my_dbus_error_free (&error);
+		for (i = 0; i < num_volumes; i++) {
+			if (strcmp (udi, volumes[i]) == 0) {
+				continue; /* skip self */
+			}
+			v_start = libhal_device_get_property_uint64 (ctx, volumes[i], "volume.partition.start", &error);
+			if (dbus_error_is_set(&error)) {
+				dbus_error_free(&error);
+				continue;
+			}
+			if (v_start == partition_start) {
+				HAL_DEBUG (("duplicate partition"));
+				goto out;
+			}
+		}
+		libhal_free_string_array (volumes);
+	}
+
+skip_part:
+
+	/*
+	 * now determine fs type
+	 */
+	if (fstyp_init(fd, probe_offset, NULL, &fstyp_handle) != 0) {
+		HAL_DEBUG (("fstyp_init failed"));
+		goto out;
+	}
+	if ((fstyp_ident(fstyp_handle, NULL, &fstype) != 0) ||
+	    (fstyp_get_attr(fstyp_handle, &fsattr) != 0)) {
+		HAL_DEBUG (("fstyp ident or get_attr failed"));
+
+		/*
+		 * XXX fstyp_udfs has a bug that it only works on raw,
+		 * but we don't want to slow down the fast path above.
+		 * Try raw for just udfs here until the bug is fixed.
+		 */
+		HAL_DEBUG (("trying udfs workaround"));
+		fstyp_fini(fstyp_handle);
+		if (fstyp_init(rfd, probe_offset, NULL, &fstyp_handle) != 0) {
+			goto out;
+		}
+		if ((fstyp_ident(fstyp_handle, "udfs", &fstype) != 0) ||
+		    (fstyp_get_attr(fstyp_handle, &fsattr) != 0)) {
+			fstyp_fini(fstyp_handle);
+			goto out;
+		}
+	}
+	set_fstyp_properties (ctx, udi, fstype, fsattr);
+
+	if (strcmp (fstype, "hsfs") == 0) {
+		hsfs_contents (fd, probe_offset, ctx, udi);
+	}
+
+	fstyp_fini(fstyp_handle);
+
+skip_fs:
+
+	ret = 0;
+
+out:
+	if (fd >= 0)
+		close (fd);
+	if (rfd >= 0)
+		close (rfd);
+
+	if (ctx != NULL) {
+		my_dbus_error_free (&error);
+		libhal_ctx_shutdown (ctx, &error);
+		libhal_ctx_free (ctx);
+	}
+
+	return ret;
+
+}
diff --git a/hald/solaris/svc-hal b/hald/solaris/svc-hal
new file mode 100755
index 0000000..f8198d2
--- /dev/null
+++ b/hald/solaris/svc-hal
@@ -0,0 +1,39 @@
+#!/sbin/sh
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# Licensed under the Academic Free License version 2.1
+#
+# ident	"@(#)svc-hal	1.1	06/10/10 SMI"
+
+. /lib/svc/share/smf_include.sh
+
+case "$1" in
+'start')
+        if smf_is_nonglobalzone; then
+                /usr/sbin/svcadm disable $SMF_FMRI
+                echo "$SMF_FMRI is not supported in a local zone"
+                sleep 5 &
+                exit $SMF_EXIT_OK
+        fi
+
+	[ ! -x /usr/lib/hal/hald ] && exit $SMF_EXIT_ERR_CONFIG
+
+	[ ! -d /var/run/hald ] && /usr/bin/mkdir -m 755 /var/run/hald
+	[ ! -d /media ] && /usr/bin/mkdir -m 755 /media
+
+	/usr/lib/hal/hald --daemon=yes
+	err=$?
+	if [ $err -ne 0 ]; then
+		echo "hal failed to start: error $err"
+		exit $SMF_EXIT_ERR_FATAL
+	fi
+	;;
+*)
+        echo "Usage: $0 { start }"
+	exit $SMF_EXIT_ERR_FATAL 
+        ;;
+esac
+
+exit $SMF_EXIT_OK
diff --git a/hald/solaris/sysevent.c b/hald/solaris/sysevent.c
new file mode 100644
index 0000000..9ae7c13
--- /dev/null
+++ b/hald/solaris/sysevent.c
@@ -0,0 +1,295 @@
+/***************************************************************************
+ *
+ * sysevent.c : Solaris sysevents
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)sysevent.c	1.2	06/10/13 SMI"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/dkio.h>
+#include <sys/stat.h>
+#include <libdevinfo.h>
+#include <libsysevent.h>
+#include <sys/sysevent/dev.h>
+#include <glib.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "osspec_solaris.h"
+#include "hotplug.h"
+#include "devinfo.h"
+#include "devinfo_storage.h"
+#include "sysevent.h"
+
+#ifndef ESC_LOFI
+#define ESC_LOFI "lofi"
+#endif
+
+static void	sysevent_dev_handler(sysevent_t *);
+static gboolean sysevent_iochannel_data(GIOChannel *, GIOCondition, gpointer);
+static void	sysevent_dev_add(gchar *, gchar *);
+static void	sysevent_dev_remove(gchar *, gchar *);
+static void	sysevent_dev_branch(gchar *);
+static void	sysevent_lofi_add(gchar *, gchar *);
+static void	sysevent_lofi_remove(gchar *, gchar *);
+
+static sysevent_handle_t	*shp;
+
+static int sysevent_pipe_fds[2];
+static GIOChannel *sysevent_iochannel;
+static guint sysevent_source_id;
+
+gboolean
+sysevent_init(void)
+{
+	GError *err = NULL;
+	const char	*subcl[2];
+
+        /*
+	 * pipe used to serialize sysevents through the main loop
+ 	 */
+        if (pipe (sysevent_pipe_fds) != 0) {
+                HAL_INFO (("pipe() failed errno=%d", errno));
+		return (FALSE);
+        }
+        sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]);
+	if (sysevent_iochannel == NULL) {
+                HAL_INFO (("g_io_channel_unix_new failed"));
+		return (FALSE);
+	}
+	g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err);
+        sysevent_source_id = g_io_add_watch (
+                sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL);
+
+	shp = sysevent_bind_handle(sysevent_dev_handler);
+	if (shp == NULL) {
+		HAL_INFO (("sysevent_bind_handle failed %d", errno));
+		return (FALSE);
+	}
+
+	subcl[0] = ESC_DISK;
+	subcl[1] = ESC_LOFI;
+	if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 2) != 0) {
+		HAL_INFO (("subscribe(dev_add) failed %d", errno));
+		sysevent_unbind_handle(shp);
+		return (FALSE);
+	}
+	if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 2) != 0) {
+		HAL_INFO (("subscribe(dev_remove) failed %d", errno));
+		sysevent_unbind_handle(shp);
+		return (FALSE);
+	}
+
+	subcl[0] = ESC_DEV_BRANCH_REMOVE;
+	if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) {
+		HAL_INFO (("subscribe(dev_branch) failed %d", errno));
+		sysevent_unbind_handle(shp);
+		return (FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+void
+sysevent_fini(void)
+{
+	sysevent_unbind_handle(shp);
+	shp = NULL;
+}
+
+static void
+sysevent_dev_handler(sysevent_t *ev)
+{
+	char		*class;
+	char		*subclass;
+	nvlist_t	*attr_list;
+	char		*phys_path;
+	char		*dev_name;
+	char		s[1024];
+	ssize_t		nwritten;
+
+	if ((class = sysevent_get_class_name(ev)) == NULL)
+		return;
+
+	if ((subclass = sysevent_get_subclass_name(ev)) == NULL)
+		return;
+
+	if (sysevent_get_attr_list(ev, &attr_list) != 0)
+		return;
+
+	if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path) != 0)
+		goto out;
+
+	if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0)
+		dev_name = "";
+
+	snprintf(s, sizeof (s), "%s %s %s %s\n",
+		class, subclass, phys_path, dev_name);
+	nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1);
+
+	HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten));
+
+out:
+	nvlist_free(attr_list);
+}
+
+static gboolean
+sysevent_iochannel_data (GIOChannel *source,
+                    GIOCondition condition,
+                    gpointer user_data)
+{
+        GError *err = NULL;
+	gchar *s = NULL;
+	gsize len;
+	int matches;
+	gchar class[1024];
+	gchar subclass[1024];
+	gchar phys_path[1024];
+	gchar dev_name[1024];
+
+	HAL_INFO (("sysevent_iochannel_data"));
+
+	while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL,
+					&err) == G_IO_STATUS_NORMAL) {
+		if (len == 0) {
+			break;
+		}
+
+		class[0] = subclass[0] = phys_path[0] = dev_name[0] = '\0';
+		matches = sscanf(s, "%s %s %s %s", class, subclass, phys_path, dev_name);
+		g_free (s);
+		s = NULL;
+		if (matches < 3) {
+			continue;
+		}
+		HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass));
+
+		if (strcmp(class, EC_DEV_ADD) == 0) {
+			if (strcmp(subclass, ESC_DISK) == 0) {
+				sysevent_dev_add(phys_path, dev_name);
+			} else if (strcmp(subclass, ESC_LOFI) == 0) {
+				sysevent_lofi_add(phys_path, dev_name);
+			}
+		} else if (strcmp(class, EC_DEV_REMOVE) == 0) {
+			if (strcmp(subclass, ESC_DISK) == 0) {
+				sysevent_dev_remove(phys_path, dev_name);
+			} else if (strcmp(subclass, ESC_LOFI) == 0) {
+				sysevent_lofi_remove(phys_path, dev_name);
+			}
+		} else if (strcmp(class, EC_DEV_BRANCH) == 0) {
+			sysevent_dev_branch(phys_path);
+		}
+	}
+
+	if (err) {
+		g_error_free (err);
+	}
+
+	return (TRUE);
+}
+
+static void
+sysevent_dev_add(gchar *devfs_path, gchar *name)
+{
+	gchar	*parent_devfs_path, *hotplug_devfs_path;
+	HalDevice *parent;
+
+	HAL_INFO (("dev_add: %s %s", name, devfs_path));
+
+        parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path);
+	if (parent == NULL) {
+		return;
+	}
+
+	HAL_INFO (("dev_add: parent=%s", parent_devfs_path));
+	HAL_INFO (("dev_add: real=%s", hotplug_devfs_path));
+
+	devinfo_add (parent, hotplug_devfs_path);
+
+	g_free (parent_devfs_path);
+	g_free (hotplug_devfs_path);
+
+	hotplug_event_process_queue ();
+}
+
+static void
+sysevent_dev_remove(gchar *devfs_path, gchar *name)
+{
+	HAL_INFO (("dev_remove: %s %s", name, devfs_path));
+
+	devinfo_remove_branch (devfs_path, NULL);
+	hotplug_event_process_queue ();
+}
+
+static void
+sysevent_dev_branch(gchar *devfs_path)
+{
+	HAL_INFO (("branch_remove: %s", devfs_path));
+
+	devinfo_remove_branch (devfs_path, NULL);
+	hotplug_event_process_queue ();
+}
+
+static void
+sysevent_lofi_add(gchar *devfs_path, gchar *name)
+{
+	di_node_t node;
+	const char *parent_udi;
+	HalDevice *d, *parent;
+
+	HAL_INFO (("lofi_add: %s %s", name, devfs_path));
+
+	if ((d = hal_device_store_match_key_value_string (hald_get_gdl (),
+	    "solaris.devfs_path", devfs_path)) == NULL) {
+		HAL_INFO (("device not found in GDL %s", devfs_path));
+		return;
+	}
+	parent_udi = hal_device_property_get_string (d, "info.parent");
+	if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) {
+		HAL_INFO (("parent not found in GDL %s", parent_udi));
+		return;
+	}
+	if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (),
+	    "info.udi", parent_udi)) == NULL) {
+		HAL_INFO (("parent not found in GDL %s", parent_udi));
+		return;
+	}
+
+	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
+		HAL_INFO (("device not found in devinfo %s", devfs_path));
+		return;
+	}
+
+	HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi));
+	devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d);
+
+	di_fini (node);
+
+	hotplug_event_process_queue ();
+}
+
+static void
+sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name)
+{
+	devinfo_lofi_remove_minor(parent_devfs_path, name);
+	hotplug_event_process_queue ();
+}
diff --git a/hald/solaris/sysevent.h b/hald/solaris/sysevent.h
new file mode 100644
index 0000000..4acfcbf
--- /dev/null
+++ b/hald/solaris/sysevent.h
@@ -0,0 +1,22 @@
+/***************************************************************************
+ *
+ * sysevent.h : definitions for Solaris sysevents
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"@(#)sysevent.h	1.2	06/10/13 SMI"
+
+#ifndef SYSEVENT_H
+#define SYSEVENT_H
+
+#include <glib.h>
+
+gboolean sysevent_init(void);
+void sysevent_fini(void);
+
+#endif /* SYSEVENT_H */
diff --git a/hald/util_helper.c b/hald/util_helper.c
index 5a83d8b..ee97026 100644
--- a/hald/util_helper.c
+++ b/hald/util_helper.c
@@ -46,6 +46,50 @@ extern char **environ;
 static char **argv_buffer = NULL;
 static size_t argv_size = 0;
 
+#ifdef sun
+#include <priv.h>
+void
+drop_privileges (int keep_auxgroups)
+{
+	priv_set_t *pPrivSet = NULL;
+	priv_set_t *lPrivSet = NULL;
+
+	/*
+	 * Start with the 'basic' privilege set and then remove any
+	 * of the 'basic' privileges that will not be needed.
+	 */
+	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
+		return;
+	}
+
+	/* Clear privileges we will not need from the 'basic' set */
+	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
+	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
+	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
+
+	/* for sysevent need to be root and have this privilege */
+	(void) priv_addset(pPrivSet, PRIV_SYS_CONFIG);
+
+	/* Set the permitted privilege set. */
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
+		return;
+	}
+
+	/* Clear the limit set. */
+	if ((lPrivSet = priv_allocset()) == NULL) {
+		return;
+	}
+
+	priv_emptyset(lPrivSet);
+
+	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
+		return;
+	}
+
+	priv_freeset(lPrivSet);
+}
+#else /* !sun */
+
 /** Drop root privileges: Set the running user id to HAL_USER and
  *  group to HAL_GROUP, and optionally retain auxiliary groups of HAL_USER.
  */
@@ -86,6 +130,7 @@ drop_privileges (int keep_auxgroups)
 		exit (-1);
 	}
 }
+#endif /* !sun */
 
 void
 hal_set_proc_title_init (int argc, char *argv[])
diff --git a/partutil/Makefile.am b/partutil/Makefile.am
index 5d409bb..67b369b 100644
--- a/partutil/Makefile.am
+++ b/partutil/Makefile.am
@@ -1,3 +1,4 @@
+if !HALD_COMPILE_SOLARIS
 
 INCLUDES = @GLIB_CFLAGS@
 
@@ -9,3 +10,5 @@ libpartutil_la_LIBADD = @GLIB_LIBS@ @PAR
 
 clean-local :
 	rm -f *~
+
+endif
diff --git a/tools/hal-storage-shared.c b/tools/hal-storage-shared.c
index 0b95a35..74bd89e 100644
--- a/tools/hal-storage-shared.c
+++ b/tools/hal-storage-shared.c
@@ -103,7 +103,7 @@ mtab_next (gpointer handle, char **mount
 
 	if (getmntent (handle, &mnt) == 0) {
 		if (mount_point != NULL) {
-			*mount_point = g_strdup (mnt->mnt_mountp);
+			*mount_point = g_strdup (mnt.mnt_mountp);
 		}
 		return mnt.mnt_special;
 	} else {


More information about the hal-commit mailing list