/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rmm_common.h" #define RMM_PRINT_DEVICE_WIDTH 20 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, LibHalDeviceCondition cond_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 (cond_cb != NULL) { libhal_ctx_set_device_condition(ctx, cond_cb); } 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_unref(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; int i_drive = -1; *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 and drives 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); } } else if ((*volumes == NULL) && libhal_device_query_capability(hal_ctx, udis[i], "storage", &error)) { i_drive = i; } } if (*volumes != NULL) { /* used prepend, preserve original order */ *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; } } else if (i_drive >= 0) { drive = libhal_drive_from_udi(hal_ctx, udis[i_drive]); } libhal_free_string_array(udis); rmm_dbus_error_free(&error); return (drive); } static void rmm_print_nicknames_one(LibHalDrive *d, LibHalVolume *v, const char *device, char **drive_nicknames) { const char *volume_label = NULL; const char *mount_point = NULL; boolean_t comma; int i; (void) printf("%-*s ", RMM_PRINT_DEVICE_WIDTH, device); comma = B_FALSE; if (drive_nicknames != NULL) { for (i = 0; drive_nicknames[i] != NULL; i++) { (void) printf("%s%s", comma ? "," : "", drive_nicknames[i]); comma = B_TRUE; } } if ((v != NULL) && ((volume_label = libhal_volume_get_label(v)) != NULL) && (strlen(volume_label) > 0)) { (void) printf("%s%s", comma ? "," : "", volume_label); comma = B_TRUE; } if ((v != NULL) && ((mount_point = libhal_volume_get_mount_point(v)) != NULL) && (strlen(mount_point) > 0)) { (void) printf("%s%s", comma ? "," : "", mount_point); comma = B_TRUE; } (void) printf("\n"); } /* * print nicknames for each available volume * * print_mask: * RMM_PRINT_MOUNTABLE print only mountable volumes * RMM_PRINT_EJECTABLE print volume-less ejectable drives */ void rmm_print_volume_nicknames(LibHalContext *hal_ctx, DBusError *error, int print_mask) { char **udis; int num_udis; GSList *volumes = NULL; LibHalDrive *d, *d_tmp; LibHalVolume *v; const char *device; char **nicknames; int i; GSList *j; int nprinted; dbus_error_init(error); if ((udis = libhal_find_device_by_capability(hal_ctx, "storage", &num_udis, error)) == NULL) { rmm_dbus_error_free(error); return; } for (i = 0; i < num_udis; i++) { if ((d = libhal_drive_from_udi(hal_ctx, udis[i])) == NULL) { continue; } /* find volumes belonging to this drive */ if ((d_tmp = rmm_hal_volume_findby(hal_ctx, "block.storage_device", udis[i], &volumes)) != NULL) { libhal_drive_free(d_tmp); } nicknames = libhal_device_get_property_strlist(hal_ctx, udis[i], "storage.solaris.nicknames", NULL); nprinted = 0; for (j = volumes; j != NULL; j = g_slist_next(j)) { v = (LibHalVolume *)(j->data); if ((device = libhal_volume_get_device_file(v)) == NULL) { continue; } if ((print_mask & RMM_PRINT_MOUNTABLE) && (libhal_volume_get_fsusage(v) != LIBHAL_VOLUME_USAGE_MOUNTABLE_FILESYSTEM)) { continue; } rmm_print_nicknames_one(d, v, device, nicknames); nprinted++; } if ((nprinted == 0) && (print_mask & RMM_PRINT_EJECTABLE) && libhal_drive_requires_eject(d) && ((device = libhal_drive_get_device_file(d)) != NULL)) { rmm_print_nicknames_one(d, NULL, device, nicknames); } libhal_free_string_array(nicknames); libhal_drive_free(d); rmm_volumes_free(volumes); volumes = NULL; } 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, RMM_MOUNT_TIMEOUT, 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, RMM_UNMOUNT_TIMEOUT, 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, RMM_EJECT_TIMEOUT, 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, RMM_CLOSETRAY_TIMEOUT, 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) { bzero(&aa_local, sizeof (aa_local)); 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); } if (aap == &aa_local) { rmm_volume_aa_free(aap); } } out: rmm_volumes_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; boolean_t ret = B_FALSE; 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) { ret = libhal_device_get_property_bool(hal_ctx, udis[i], "storage.removable.media_available", NULL); if (ret) { printf(gettext("%s is available\n"), name ? name : nickname); } else { printf(gettext("%s is not available\n"), name ? name : nickname); } } 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_ */ 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); }