summaryrefslogtreecommitdiff
path: root/usr/src/cmd/rmvolmgr/rmm_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/rmvolmgr/rmm_common.c')
-rw-r--r--usr/src/cmd/rmvolmgr/rmm_common.c1355
1 files changed, 1355 insertions, 0 deletions
diff --git a/usr/src/cmd/rmvolmgr/rmm_common.c b/usr/src/cmd/rmvolmgr/rmm_common.c
new file mode 100644
index 0000000000..4859e8e2c2
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/rmm_common.c
@@ -0,0 +1,1355 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mnttab.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <libhal.h>
+#include <libhal-storage.h>
+
+#include "rmm_common.h"
+
+extern int rmm_debug;
+
+static const char *action_strings[] = {
+ "eject",
+ "mount",
+ "remount",
+ "unmount",
+ "clear_mounts",
+ "closetray"
+};
+
+
+LibHalContext *
+rmm_hal_init(LibHalDeviceAdded devadd_cb, LibHalDeviceRemoved devrem_cb,
+ LibHalDevicePropertyModified propmod_cb,
+ DBusError *error, rmm_error_t *rmm_error)
+{
+ DBusConnection *dbus_conn;
+ LibHalContext *ctx;
+ char **devices;
+ int nr;
+
+ dbus_error_init(error);
+
+ /*
+ * setup D-Bus connection
+ */
+ if (!(dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, error))) {
+ dprintf("cannot get system bus: %s\n", rmm_strerror(error, -1));
+ *rmm_error = RMM_EDBUS_CONNECT;
+ return (NULL);
+ }
+ rmm_dbus_error_free(error);
+
+ dbus_connection_setup_with_g_main(dbus_conn, NULL);
+ dbus_connection_set_exit_on_disconnect(dbus_conn, B_TRUE);
+
+ if ((ctx = libhal_ctx_new()) == NULL) {
+ dprintf("libhal_ctx_new failed");
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ }
+
+ libhal_ctx_set_dbus_connection(ctx, dbus_conn);
+
+ /*
+ * register callbacks
+ */
+ if (devadd_cb != NULL) {
+ libhal_ctx_set_device_added(ctx, devadd_cb);
+ }
+ if (devrem_cb != NULL) {
+ libhal_ctx_set_device_removed(ctx, devrem_cb);
+ }
+ if (propmod_cb != NULL) {
+ libhal_ctx_set_device_property_modified(ctx, propmod_cb);
+ if (!libhal_device_property_watch_all(ctx, error)) {
+ dprintf("property_watch_all failed %s",
+ rmm_strerror(error, -1));
+ libhal_ctx_free(ctx);
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ }
+ }
+
+ if (!libhal_ctx_init(ctx, error)) {
+ dprintf("libhal_ctx_init failed: %s", rmm_strerror(error, -1));
+ libhal_ctx_free(ctx);
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ }
+ rmm_dbus_error_free(error);
+
+ /*
+ * The above functions do not guarantee that HAL is actually running.
+ * Check by invoking a method.
+ */
+ if (!(devices = libhal_get_all_devices(ctx, &nr, error))) {
+ dprintf("HAL is not running: %s", rmm_strerror(error, -1));
+ libhal_ctx_shutdown(ctx, NULL);
+ libhal_ctx_free(ctx);
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ } else {
+ rmm_dbus_error_free(error);
+ libhal_free_string_array(devices);
+ }
+
+ return (ctx);
+}
+
+
+void
+rmm_hal_fini(LibHalContext *hal_ctx)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+
+ (void) dbus_connection_close(dbus_conn);
+ (void) libhal_ctx_free(hal_ctx);
+}
+
+
+/*
+ * find volume from any type of name, similar to the old media_findname()
+ * returns the LibHalDrive object and a list of LibHalVolume objects.
+ */
+LibHalDrive *
+rmm_hal_volume_find(LibHalContext *hal_ctx, const char *name, DBusError *error,
+ GSList **volumes)
+{
+ LibHalDrive *drive;
+ char *p;
+ char lastc;
+
+ *volumes = NULL;
+
+ /* temporarily remove trailing slash */
+ p = (char *)name + strlen(name) - 1;
+ if (*p == '/') {
+ lastc = *p;
+ *p = '\0';
+ } else {
+ p = NULL;
+ }
+
+ if (name[0] == '/') {
+ if (((drive = rmm_hal_volume_findby(hal_ctx,
+ "info.udi", name, volumes)) != NULL) ||
+ ((drive = rmm_hal_volume_findby(hal_ctx,
+ "block.device", name, volumes)) != NULL) ||
+ ((drive = rmm_hal_volume_findby(hal_ctx,
+ "block.solaris.raw_device", name, volumes)) != NULL) ||
+ ((drive = rmm_hal_volume_findby(hal_ctx,
+ "volume.mount_point", name, volumes)) != NULL)) {
+ goto out;
+ } else {
+ goto out;
+ }
+ }
+
+ /* try volume label */
+ if ((drive = rmm_hal_volume_findby(hal_ctx,
+ "volume.label", name, volumes)) != NULL) {
+ goto out;
+ }
+
+ drive = rmm_hal_volume_findby_nickname(hal_ctx, name, volumes);
+
+out:
+ if (p != NULL) {
+ *p = lastc;
+ }
+ return (drive);
+}
+
+/*
+ * find default volume. Returns volume pointer and name in 'name'.
+ */
+LibHalDrive *
+rmm_hal_volume_find_default(LibHalContext *hal_ctx, DBusError *error,
+ const char **name_out, GSList **volumes)
+{
+ LibHalDrive *drive;
+ static const char *names[] = { "floppy", "cdrom", "rmdisk" };
+ int i;
+
+ *volumes = NULL;
+
+ for (i = 0; i < NELEM(names); i++) {
+ if ((drive = rmm_hal_volume_findby_nickname(hal_ctx,
+ names[i], volumes)) != NULL) {
+ /*
+ * Skip floppy if it has no media.
+ * XXX might want to actually check for media
+ * every time instead of relying on volcheck.
+ */
+ if ((strcmp(names[i], "floppy") != 0) ||
+ libhal_device_get_property_bool(hal_ctx,
+ libhal_drive_get_udi(drive),
+ "storage.removable.media_available", NULL)) {
+ *name_out = names[i];
+ break;
+ }
+ }
+ rmm_dbus_error_free(error);
+ }
+
+ return (drive);
+}
+
+/*
+ * find volume by property=value
+ * returns the LibHalDrive object and a list of LibHalVolume objects.
+ * XXX add support for multiple properties, reduce D-Bus traffic
+ */
+LibHalDrive *
+rmm_hal_volume_findby(LibHalContext *hal_ctx, const char *property,
+ const char *value, GSList **volumes)
+{
+ DBusError error;
+ LibHalDrive *drive = NULL;
+ LibHalVolume *v = NULL;
+ char **udis;
+ int num_udis;
+ int i;
+
+ *volumes = NULL;
+
+ dbus_error_init(&error);
+
+ /* get all devices with property=value */
+ if ((udis = libhal_manager_find_device_string_match(hal_ctx, property,
+ value, &num_udis, &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ return (NULL);
+ }
+
+ /* find volumes among these devices */
+ for (i = 0; i < num_udis; i++) {
+ rmm_dbus_error_free(&error);
+ if (libhal_device_query_capability(hal_ctx, udis[i], "volume",
+ &error)) {
+ v = libhal_volume_from_udi(hal_ctx, udis[i]);
+ if (v != NULL) {
+ *volumes = g_slist_prepend(*volumes, v);
+ }
+ }
+ }
+
+ /* used prepend, preserve original order */
+ if (*volumes != NULL) {
+ *volumes = g_slist_reverse(*volumes);
+
+ v = (LibHalVolume *)(*volumes)->data;
+ drive = libhal_drive_from_udi(hal_ctx,
+ libhal_volume_get_storage_device_udi(v));
+ if (drive == NULL) {
+ rmm_volumes_free (*volumes);
+ *volumes = NULL;
+ }
+ }
+
+ libhal_free_string_array(udis);
+ rmm_dbus_error_free(&error);
+
+ return (drive);
+}
+
+
+/*
+ * print nicknames for each available volume
+ */
+void
+rmm_print_volume_nicknames(LibHalContext *hal_ctx, DBusError *error)
+{
+ char **udis;
+ int num_udis;
+ char *block_device;
+ char *drive_udi;
+ char *volume_label;
+ char *mount_point;
+ boolean_t comma;
+ char **nicknames;
+ int i, j;
+
+ dbus_error_init(error);
+
+ if ((udis = libhal_find_device_by_capability(hal_ctx, "volume",
+ &num_udis, error)) == NULL) {
+ return;
+ }
+
+ for (i = 0; i < num_udis; i++) {
+ if ((block_device = libhal_device_get_property_string(hal_ctx,
+ udis[i], "block.device", NULL)) == NULL) {
+ continue;
+ }
+ if ((drive_udi = libhal_device_get_property_string(hal_ctx,
+ udis[i], "block.storage_device", NULL)) == NULL) {
+ libhal_free_string(block_device);
+ continue;
+ }
+ (void) printf("%s\t", block_device);
+ comma = B_FALSE;
+
+ if ((nicknames = libhal_device_get_property_strlist(hal_ctx,
+ drive_udi, "storage.solaris.nicknames", NULL)) != NULL) {
+ for (j = 0; nicknames[j] != NULL; j++) {
+ (void) printf("%s%s", comma ? "," : "",
+ nicknames[j]);
+ comma = B_TRUE;
+ }
+ }
+
+ if (((volume_label = libhal_device_get_property_string(hal_ctx,
+ udis[i], "volume.label", NULL)) != NULL) &&
+ (strlen(volume_label) > 0)) {
+ (void) printf("%s%s", comma ? "," : "", volume_label);
+ comma = B_TRUE;
+ }
+
+ if (((mount_point = libhal_device_get_property_string(hal_ctx,
+ udis[i], "volume.mount_point", NULL)) != NULL) &&
+ (strlen(mount_point) > 0)) {
+ (void) printf("%s%s", comma ? "," : "", mount_point);
+ comma = B_TRUE;
+ }
+
+ (void) printf("\n");
+
+ libhal_free_string_array(nicknames);
+ libhal_free_string(drive_udi);
+ libhal_free_string(volume_label);
+ libhal_free_string(mount_point);
+ libhal_free_string(block_device);
+ }
+ libhal_free_string_array(udis);
+}
+
+
+/*
+ * find volume by nickname
+ * returns the LibHalDrive object and a list of LibHalVolume objects.
+ */
+LibHalDrive *
+rmm_hal_volume_findby_nickname(LibHalContext *hal_ctx, const char *name,
+ GSList **volumes)
+{
+ DBusError error;
+ LibHalDrive *drive = NULL;
+ LibHalDrive *drive_tmp;
+ char **udis;
+ int num_udis;
+ char **nicknames;
+ int i, j;
+
+ *volumes = NULL;
+
+ dbus_error_init(&error);
+
+ if ((udis = libhal_find_device_by_capability(hal_ctx, "storage",
+ &num_udis, &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ return (NULL);
+ }
+
+ /* find a drive by nickname */
+ for (i = 0; (i < num_udis) && (drive == NULL); i++) {
+ if ((nicknames = libhal_device_get_property_strlist(hal_ctx,
+ udis[i], "storage.solaris.nicknames", &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ continue;
+ }
+ for (j = 0; (nicknames[j] != NULL) && (drive == NULL); j++) {
+ if (strcmp(nicknames[j], name) == 0) {
+ drive = libhal_drive_from_udi(hal_ctx, udis[i]);
+ }
+ }
+ libhal_free_string_array(nicknames);
+ }
+ libhal_free_string_array(udis);
+
+ if (drive != NULL) {
+ /* found the drive, now find its volumes */
+ if ((drive_tmp = rmm_hal_volume_findby(hal_ctx,
+ "block.storage_device", libhal_drive_get_udi(drive),
+ volumes)) != NULL) {
+ libhal_drive_free(drive_tmp);
+ }
+ }
+
+ rmm_dbus_error_free(&error);
+
+ return (drive);
+}
+
+void
+rmm_volumes_free(GSList *volumes)
+{
+ GSList *i;
+
+ for (i = volumes; i != NULL; i = g_slist_next(i)) {
+ libhal_volume_free((LibHalVolume *)(i->data));
+ }
+ g_slist_free(volumes);
+}
+
+/*
+ * Call HAL's Mount() method on the given device
+ */
+boolean_t
+rmm_hal_mount(LibHalContext *hal_ctx, const char *udi,
+ char **opts, int num_opts, char *mountpoint, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char *fstype;
+
+ dprintf("mounting %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Volume", "Mount"))) {
+ dprintf(
+ "mount failed for %s: cannot create dbus message\n", udi);
+ return (B_FALSE);
+ }
+
+ fstype = "";
+ if (mountpoint == NULL) {
+ mountpoint = "";
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &mountpoint,
+ DBUS_TYPE_STRING, &fstype,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &opts, num_opts,
+ DBUS_TYPE_INVALID)) {
+ dprintf("mount failed for %s: cannot append args\n", udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("mount failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("mounted %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+
+/*
+ * Call HAL's Unmount() method on the given device
+ */
+boolean_t
+rmm_hal_unmount(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char **opts = NULL;
+
+ dprintf("unmounting %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Volume", "Unmount"))) {
+ dprintf(
+ "unmount failed %s: cannot create dbus message\n", udi);
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+ &opts, 0, DBUS_TYPE_INVALID)) {
+ dprintf("unmount failed %s: cannot append args\n", udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("unmount failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("unmounted %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+
+/*
+ * Call HAL's Eject() method on the given device
+ */
+boolean_t
+rmm_hal_eject(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char **options = NULL;
+ uint_t num_options = 0;
+
+ dprintf("ejecting %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Storage", "Eject"))) {
+ dprintf("eject %s: cannot create dbus message\n", udi);
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
+ DBUS_TYPE_INVALID)) {
+ dprintf("eject %s: cannot append args to dbus message ", udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("eject %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("ejected %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+/*
+ * Call HAL's CloseTray() method on the given device
+ */
+boolean_t
+rmm_hal_closetray(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char **options = NULL;
+ uint_t num_options = 0;
+
+ dprintf("closing tray %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Storage", "CloseTray"))) {
+ dprintf(
+ "closetray failed for %s: cannot create dbus message\n",
+ udi);
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
+ DBUS_TYPE_INVALID)) {
+ dprintf("closetray %s: cannot append args to dbus message ",
+ udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("closetray failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("closetray ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+/*
+ * Call HAL's Rescan() method on the given device
+ */
+boolean_t
+rmm_hal_rescan(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+
+ dprintf("rescanning %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device", "Rescan"))) {
+ dprintf("rescan failed for %s: cannot create dbus message\n",
+ udi);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("rescan failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("rescan ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+boolean_t
+rmm_hal_claim_branch(LibHalContext *hal_ctx, const char *udi)
+{
+ DBusError error;
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ const char *claimed_by = "rmvolmgr";
+
+ dprintf("claiming branch %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
+ "ClaimBranch"))) {
+ dprintf("cannot create dbus message\n");
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
+ DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
+ dprintf("cannot append args to dbus message\n");
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(&error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, &error))) {
+ dprintf("cannot send dbus message\n");
+ dbus_message_unref(dmesg);
+ rmm_dbus_error_free(&error);
+ return (B_FALSE);
+ }
+
+ dprintf("claim branch ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ return (B_TRUE);
+}
+
+boolean_t
+rmm_hal_unclaim_branch(LibHalContext *hal_ctx, const char *udi)
+{
+ DBusError error;
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ const char *claimed_by = "rmvolmgr";
+
+ dprintf("unclaiming branch %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
+ "UnclaimBranch"))) {
+ dprintf("cannot create dbus message\n");
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
+ DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
+ dprintf("cannot append args to dbus message\n");
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(&error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, &error))) {
+ dprintf("cannot send dbus message\n");
+ dbus_message_unref(dmesg);
+ rmm_dbus_error_free(&error);
+ return (B_FALSE);
+ }
+
+ dprintf("unclaim branch ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ return (B_TRUE);
+}
+
+static boolean_t
+rmm_action_one(LibHalContext *hal_ctx, const char *name, action_t action,
+ const char *dev, const char *udi, LibHalVolume *v,
+ char **opts, int num_opts, char *mountpoint)
+{
+ char dev_str[MAXPATHLEN];
+ char *mountp;
+ DBusError error;
+ boolean_t ret = B_FALSE;
+
+ if (strcmp(name, dev) == 0) {
+ (void) snprintf(dev_str, sizeof (dev_str), name);
+ } else {
+ (void) snprintf(dev_str, sizeof (dev_str), "%s %s", name, dev);
+ }
+
+ dbus_error_init(&error);
+
+ switch (action) {
+ case EJECT:
+ ret = rmm_hal_eject(hal_ctx, udi, &error);
+ break;
+ case INSERT:
+ case REMOUNT:
+ if (libhal_volume_is_mounted(v)) {
+ goto done;
+ }
+ ret = rmm_hal_mount(hal_ctx, udi,
+ opts, num_opts, mountpoint, &error);
+ break;
+ case UNMOUNT:
+ if (!libhal_volume_is_mounted(v)) {
+ goto done;
+ }
+ ret = rmm_hal_unmount(hal_ctx, udi, &error);
+ break;
+ case CLOSETRAY:
+ ret = rmm_hal_closetray(hal_ctx, udi, &error);
+ break;
+ }
+
+ if (!ret) {
+ (void) fprintf(stderr, gettext("%s of %s failed: %s\n"),
+ action_strings[action], dev_str, rmm_strerror(&error, -1));
+ goto done;
+ }
+
+ switch (action) {
+ case EJECT:
+ (void) printf(gettext("%s ejected\n"), dev_str);
+ break;
+ case INSERT:
+ case REMOUNT:
+ mountp = rmm_get_mnttab_mount_point(dev);
+ if (mountp != NULL) {
+ (void) printf(gettext("%s mounted at %s\n"),
+ dev_str, mountp);
+ free(mountp);
+ }
+ break;
+ case UNMOUNT:
+ (void) printf(gettext("%s unmounted\n"), dev_str);
+ break;
+ case CLOSETRAY:
+ (void) printf(gettext("%s tray closed\n"), dev_str);
+ break;
+ }
+
+done:
+ rmm_dbus_error_free(&error);
+ return (ret);
+}
+
+/*
+ * top level action routine
+ *
+ * If non-null 'aa' is passed, it will be used, otherwise a local copy
+ * will be created.
+ */
+boolean_t
+rmm_action(LibHalContext *hal_ctx, const char *name, action_t action,
+ struct action_arg *aap, char **opts, int num_opts, char *mountpoint)
+{
+ DBusError error;
+ GSList *volumes, *i;
+ LibHalDrive *d;
+ LibHalVolume *v;
+ const char *udi, *d_udi;
+ const char *dev, *d_dev;
+ struct action_arg aa_local;
+ boolean_t ret = B_FALSE;
+
+ dprintf("rmm_action %s %s\n", name, action_strings[action]);
+
+ if (aap == NULL) {
+ aap = &aa_local;
+ }
+
+ dbus_error_init(&error);
+
+ /* find the drive and its volumes */
+ d = rmm_hal_volume_find(hal_ctx, name, &error, &volumes);
+ rmm_dbus_error_free(&error);
+ if (d == NULL) {
+ (void) fprintf(stderr, gettext("cannot find '%s'\n"), name);
+ return (B_FALSE);
+ }
+ d_udi = libhal_drive_get_udi(d);
+ d_dev = libhal_drive_get_device_file(d);
+ if ((d_udi == NULL) || (d_dev == NULL)) {
+ goto out;
+ }
+
+ /*
+ * For those drives that do not require media eject,
+ * EJECT turns into UNMOUNT.
+ */
+ if ((action == EJECT) && !libhal_drive_requires_eject(d)) {
+ action = UNMOUNT;
+ }
+
+ /* per drive action */
+ if ((action == EJECT) || (action == CLOSETRAY)) {
+ ret = rmm_action_one(hal_ctx, name, action, d_dev, d_udi, NULL,
+ opts, num_opts, NULL);
+
+ if (!ret || (action == CLOSETRAY)) {
+ goto out;
+ }
+ }
+
+ /* per volume action */
+ for (i = volumes; i != NULL; i = g_slist_next(i)) {
+ v = (LibHalVolume *)i->data;
+ udi = libhal_volume_get_udi(v);
+ dev = libhal_volume_get_device_file(v);
+
+ if ((udi == NULL) || (dev == NULL)) {
+ continue;
+ }
+ if (aap == &aa_local) {
+ if (!rmm_volume_aa_from_prop(hal_ctx, udi, v, aap)) {
+ dprintf("rmm_volume_aa_from_prop failed %s\n",
+ udi);
+ continue;
+ }
+ }
+ aap->aa_action = action;
+
+ /* ejected above, just need postprocess */
+ if (action != EJECT) {
+ ret = rmm_action_one(hal_ctx, name, action, dev, udi, v,
+ opts, num_opts, mountpoint);
+ }
+ if (ret) {
+ (void) vold_postprocess(hal_ctx, udi, aap);
+ }
+
+ libhal_volume_free(v);
+ if (aap == &aa_local) {
+ rmm_volume_aa_free(aap);
+ }
+ }
+
+out:
+ g_slist_free(volumes);
+ libhal_drive_free(d);
+
+ return (ret);
+}
+
+
+/*
+ * rescan by name
+ * if name is NULL, rescan all drives
+ */
+boolean_t
+rmm_rescan(LibHalContext *hal_ctx, const char *name, boolean_t query)
+{
+ DBusError error;
+ GSList *volumes;
+ LibHalDrive *drive = NULL;
+ const char *drive_udi;
+ char **udis;
+ int num_udis;
+ char *nickname;
+ char **nicks = NULL;
+ boolean_t do_free_udis = FALSE;
+ int i;
+ int ret = 0;
+
+ dprintf("rmm_rescan %s\n", name != NULL ? name : "all");
+
+ dbus_error_init(&error);
+
+ if (name != NULL) {
+ if ((drive = rmm_hal_volume_find(hal_ctx, name, &error,
+ &volumes)) == NULL) {
+ rmm_dbus_error_free(&error);
+ (void) fprintf(stderr,
+ gettext("cannot find '%s'\n"), name);
+ return (B_FALSE);
+ }
+ rmm_dbus_error_free(&error);
+ g_slist_free(volumes);
+
+ drive_udi = libhal_drive_get_udi(drive);
+ udis = (char **)&drive_udi;
+ num_udis = 1;
+ } else {
+ if ((udis = libhal_find_device_by_capability(hal_ctx,
+ "storage", &num_udis, &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ return (B_TRUE);
+ }
+ rmm_dbus_error_free(&error);
+ do_free_udis = TRUE;
+ }
+
+ for (i = 0; i < num_udis; i++) {
+ if (name == NULL) {
+ nicks = libhal_device_get_property_strlist(hal_ctx,
+ udis[i], "storage.solaris.nicknames", NULL);
+ if (nicks != NULL) {
+ nickname = nicks[0];
+ } else {
+ nickname = "";
+ }
+ }
+ if (!(ret = rmm_hal_rescan(hal_ctx, udis[i], &error))) {
+ (void) fprintf(stderr,
+ gettext("rescan of %s failed: %s\n"),
+ name ? name : nickname,
+ rmm_strerror(&error, -1));
+ libhal_free_string_array(nicks);
+ continue;
+ }
+ if (query) {
+ printf(gettext("%s is%s available\n"),
+ name ? name : nickname,
+ libhal_device_get_property_bool(hal_ctx, udis[i],
+ "storage.removable.media_available", NULL) ?
+ "" : " not");
+ }
+ libhal_free_string_array(nicks);
+ }
+
+ if (drive != NULL) {
+ libhal_drive_free(drive);
+ }
+ if (do_free_udis) {
+ libhal_free_string_array(udis);
+ }
+
+ return (ret);
+}
+
+
+/*
+ * set action_arg from volume properties
+ */
+boolean_t
+rmm_volume_aa_from_prop(LibHalContext *hal_ctx, const char *udi_arg,
+ LibHalVolume *volume_arg, struct action_arg *aap)
+{
+ LibHalVolume *volume = volume_arg;
+ const char *udi = udi_arg;
+ const char *drive_udi;
+ char *volume_label;
+ char *mountpoint;
+ int len;
+ int ret = B_FALSE;
+
+ /* at least udi or volume must be supplied */
+ if ((udi == NULL) && (volume == NULL)) {
+ return (B_FALSE);
+ }
+ if (volume == NULL) {
+ if ((volume = libhal_volume_from_udi(hal_ctx, udi)) == NULL) {
+ dprintf("cannot get volume %s\n", udi);
+ goto out;
+ }
+ }
+ if (udi == NULL) {
+ if ((udi = libhal_volume_get_udi(volume)) == NULL) {
+ dprintf("cannot get udi\n");
+ goto out;
+ }
+ }
+ drive_udi = libhal_volume_get_storage_device_udi(volume);
+
+ if (!(aap->aa_symdev = libhal_device_get_property_string(hal_ctx,
+ drive_udi, "storage.solaris.legacy.symdev", NULL))) {
+ dprintf("property %s not found %s\n",
+ "storage.solaris.legacy.symdev", drive_udi);
+ goto out;
+ }
+ if (!(aap->aa_media = libhal_device_get_property_string(hal_ctx,
+ drive_udi, "storage.solaris.legacy.media_type", NULL))) {
+ dprintf("property %s not found %s\n",
+ "storage.solaris.legacy.media_type", drive_udi);
+ goto out;
+ }
+
+ /* name is derived from volume label */
+ aap->aa_name = NULL;
+ if ((volume_label = (char *)libhal_device_get_property_string(hal_ctx,
+ udi, "volume.label", NULL)) != NULL) {
+ if ((len = strlen(volume_label)) > 0) {
+ aap->aa_name = rmm_vold_convert_volume_label(
+ volume_label, len);
+ if (strlen(aap->aa_name) == 0) {
+ free(aap->aa_name);
+ aap->aa_name = NULL;
+ }
+ }
+ libhal_free_string(volume_label);
+ }
+ /* if no label, then unnamed_<mediatype> */
+ if (aap->aa_name == NULL) {
+ aap->aa_name = (char *)calloc(1, sizeof ("unnamed_floppyNNNN"));
+ if (aap->aa_name == NULL) {
+ goto out;
+ }
+ (void) snprintf(aap->aa_name, sizeof ("unnamed_floppyNNNN"),
+ "unnamed_%s", aap->aa_media);
+ }
+
+ if (!(aap->aa_path = libhal_device_get_property_string(hal_ctx, udi,
+ "block.device", NULL))) {
+ dprintf("property %s not found %s\n", "block.device", udi);
+ goto out;
+ }
+ if (!(aap->aa_rawpath = libhal_device_get_property_string(hal_ctx, udi,
+ "block.solaris.raw_device", NULL))) {
+ dprintf("property %s not found %s\n",
+ "block.solaris.raw_device", udi);
+ goto out;
+ }
+ if (!(aap->aa_type = libhal_device_get_property_string(hal_ctx, udi,
+ "volume.fstype", NULL))) {
+ dprintf("property %s not found %s\n", "volume.fstype", udi);
+ goto out;
+ }
+ if (!libhal_device_get_property_bool(hal_ctx, udi,
+ "volume.is_partition", NULL)) {
+ aap->aa_partname = NULL;
+ } else if (!(aap->aa_partname = libhal_device_get_property_string(
+ hal_ctx, udi, "block.solaris.slice", NULL))) {
+ dprintf("property %s not found %s\n",
+ "block.solaris.slice", udi);
+ goto out;
+ }
+ if (!(mountpoint = libhal_device_get_property_string(hal_ctx, udi,
+ "volume.mount_point", NULL))) {
+ dprintf("property %s not found %s\n",
+ "volume.mount_point", udi);
+ goto out;
+ }
+ /*
+ * aa_mountpoint can be reallocated in rmm_volume_aa_update_mountpoint()
+ * won't have to choose between free() or libhal_free_string() later on
+ */
+ aap->aa_mountpoint = strdup(mountpoint);
+ libhal_free_string(mountpoint);
+ if (aap->aa_mountpoint == NULL) {
+ dprintf("mountpoint is NULL %s\n", udi);
+ goto out;
+ }
+
+ ret = B_TRUE;
+
+out:
+ if ((volume != NULL) && (volume != volume_arg)) {
+ libhal_volume_free(volume);
+ }
+ if (!ret) {
+ rmm_volume_aa_free(aap);
+ }
+ return (ret);
+}
+
+/* ARGSUSED */
+void
+rmm_volume_aa_update_mountpoint(LibHalContext *hal_ctx, const char *udi,
+ struct action_arg *aap)
+{
+ if (aap->aa_mountpoint != NULL) {
+ free(aap->aa_mountpoint);
+ }
+ aap->aa_mountpoint = rmm_get_mnttab_mount_point(aap->aa_path);
+}
+
+void
+rmm_volume_aa_free(struct action_arg *aap)
+{
+ if (aap->aa_symdev != NULL) {
+ libhal_free_string(aap->aa_symdev);
+ aap->aa_symdev = NULL;
+ }
+ if (aap->aa_name != NULL) {
+ free(aap->aa_name);
+ aap->aa_name = NULL;
+ }
+ if (aap->aa_path != NULL) {
+ libhal_free_string(aap->aa_path);
+ aap->aa_path = NULL;
+ }
+ if (aap->aa_rawpath != NULL) {
+ libhal_free_string(aap->aa_rawpath);
+ aap->aa_rawpath = NULL;
+ }
+ if (aap->aa_type != NULL) {
+ libhal_free_string(aap->aa_type);
+ aap->aa_type = NULL;
+ }
+ if (aap->aa_media != NULL) {
+ libhal_free_string(aap->aa_media);
+ aap->aa_media = NULL;
+ }
+ if (aap->aa_partname != NULL) {
+ libhal_free_string(aap->aa_partname);
+ aap->aa_partname = NULL;
+ }
+ if (aap->aa_mountpoint != NULL) {
+ free(aap->aa_mountpoint);
+ aap->aa_mountpoint = NULL;
+ }
+}
+
+/*
+ * get device's mount point from mnttab
+ */
+char *
+rmm_get_mnttab_mount_point(const char *special)
+{
+ char *mount_point = NULL;
+ FILE *f;
+ struct mnttab mnt;
+ struct mnttab mpref = { NULL, NULL, NULL, NULL, NULL };
+
+ if ((f = fopen(MNTTAB, "r")) != NULL) {
+ mpref.mnt_special = (char *)special;
+ if (getmntany(f, &mnt, &mpref) == 0) {
+ mount_point = strdup(mnt.mnt_mountp);
+ }
+ fclose(f);
+ }
+
+ return (mount_point);
+}
+
+
+/*
+ * get human readable string from error values
+ */
+const char *
+rmm_strerror(DBusError *dbus_error, int rmm_error)
+{
+ const char *str;
+
+ if ((dbus_error != NULL) && dbus_error_is_set(dbus_error)) {
+ str = dbus_error->message;
+ } else {
+ switch (rmm_error) {
+ case RMM_EOK:
+ str = gettext("success");
+ break;
+ case RMM_EDBUS_CONNECT:
+ str = gettext("cannot connect to D-Bus");
+ break;
+ case RMM_EHAL_CONNECT:
+ str = gettext("cannot connect to HAL");
+ break;
+ default:
+ str = gettext("undefined error");
+ break;
+ }
+ }
+
+ return (str);
+}
+
+void
+rmm_dbus_error_free(DBusError *error)
+{
+ if (error != NULL && dbus_error_is_set(error)) {
+ dbus_error_free(error);
+ }
+}
+
+static int
+rmm_vold_isbadchar(int c)
+{
+ int ret_val = 0;
+
+
+ switch (c) {
+ case '/':
+ case ';':
+ case '|':
+ ret_val = 1;
+ break;
+ default:
+ if (iscntrl(c) || isspace(c)) {
+ ret_val = 1;
+ }
+ }
+
+ return (ret_val);
+}
+
+char *
+rmm_vold_convert_volume_label(const char *name, size_t len)
+{
+ char buf[MAXNAMELEN+1];
+ char *s = buf;
+ int i;
+
+ if (len > MAXNAMELEN) {
+ len = MAXNAMELEN;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (name[i] == '\0') {
+ break;
+ }
+ if (isgraph((int)name[i])) {
+ if (isupper((int)name[i])) {
+ *s++ = tolower((int)name[i]);
+ } else if (rmm_vold_isbadchar((int)name[i])) {
+ *s++ = '_';
+ } else {
+ *s++ = name[i];
+ }
+ }
+ }
+ *s = '\0';
+ s = strdup(buf);
+
+ return (s);
+}
+
+/*
+ * swiped from mkdir.c
+ */
+int
+makepath(char *dir, mode_t mode)
+{
+ int err;
+ char *slash;
+
+
+ if ((mkdir(dir, mode) == 0) || (errno == EEXIST)) {
+ return (0);
+ }
+ if (errno != ENOENT) {
+ return (-1);
+ }
+ if ((slash = strrchr(dir, '/')) == NULL) {
+ return (-1);
+ }
+ *slash = '\0';
+ err = makepath(dir, mode);
+ *slash++ = '/';
+
+ if (err || (*slash == '\0')) {
+ return (err);
+ }
+
+ return (mkdir(dir, mode));
+}
+
+
+void
+dprintf(const char *fmt, ...)
+{
+
+ va_list ap;
+ const char *p;
+ char msg[BUFSIZ];
+ char *errmsg = strerror(errno);
+ char *s;
+
+ if (rmm_debug == 0) {
+ return;
+ }
+
+ (void) memset(msg, 0, BUFSIZ);
+
+ /* scan for %m and replace with errno msg */
+ s = &msg[strlen(msg)];
+ p = fmt;
+
+ while (*p != '\0') {
+ if ((*p == '%') && (*(p+1) == 'm')) {
+ (void) strcat(s, errmsg);
+ p += 2;
+ s += strlen(errmsg);
+ continue;
+ }
+ *s++ = *p++;
+ }
+ *s = '\0'; /* don't forget the null byte */
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, msg, ap);
+ va_end(ap);
+}