1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  *
  21  *
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2014 Andrew Stormont.
  26  */
  27 
  28 #include <stdio.h>
  29 #include <errno.h>
  30 #include <string.h>
  31 #include <strings.h>
  32 #include <stdarg.h>
  33 #include <fcntl.h>
  34 #include <libintl.h>
  35 #include <stdlib.h>
  36 #include <unistd.h>
  37 #include <ctype.h>
  38 #include <sys/param.h>
  39 #include <sys/types.h>
  40 #include <sys/stat.h>
  41 #include <sys/mnttab.h>
  42 
  43 #include <dbus/dbus.h>
  44 #include <dbus/dbus-glib.h>
  45 #include <dbus/dbus-glib-lowlevel.h>
  46 #include <libhal.h>
  47 #include <libhal-storage.h>
  48 
  49 #include "rmm_common.h"
  50 
  51 #define RMM_PRINT_DEVICE_WIDTH  20
  52 
  53 extern int rmm_debug;
  54 
  55 static const char *action_strings[] = {
  56         "eject",
  57         "mount",
  58         "remount",
  59         "unmount",
  60         "clear_mounts",
  61         "closetray"
  62 };
  63 
  64 
  65 LibHalContext *
  66 rmm_hal_init(LibHalDeviceAdded devadd_cb, LibHalDeviceRemoved devrem_cb,
  67     LibHalDevicePropertyModified propmod_cb, LibHalDeviceCondition cond_cb,
  68     DBusError *error, rmm_error_t *rmm_error)
  69 {
  70         DBusConnection  *dbus_conn;
  71         LibHalContext   *ctx;
  72         char            **devices;
  73         int             nr;
  74 
  75         dbus_error_init(error);
  76 
  77         /*
  78          * setup D-Bus connection
  79          */
  80         if (!(dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, error))) {
  81                 dprintf("cannot get system bus: %s\n", rmm_strerror(error, -1));
  82                 *rmm_error = RMM_EDBUS_CONNECT;
  83                 return (NULL);
  84         }
  85         rmm_dbus_error_free(error);
  86 
  87         dbus_connection_setup_with_g_main(dbus_conn, NULL);
  88         dbus_connection_set_exit_on_disconnect(dbus_conn, B_TRUE);
  89 
  90         if ((ctx = libhal_ctx_new()) == NULL) {
  91                 dprintf("libhal_ctx_new failed");
  92                 *rmm_error = RMM_EHAL_CONNECT;
  93                 return (NULL);
  94         }
  95 
  96         libhal_ctx_set_dbus_connection(ctx, dbus_conn);
  97 
  98         /*
  99          * register callbacks
 100          */
 101         if (devadd_cb != NULL) {
 102                 libhal_ctx_set_device_added(ctx, devadd_cb);
 103         }
 104         if (devrem_cb != NULL) {
 105                 libhal_ctx_set_device_removed(ctx, devrem_cb);
 106         }
 107         if (propmod_cb != NULL) {
 108                 libhal_ctx_set_device_property_modified(ctx, propmod_cb);
 109                 if (!libhal_device_property_watch_all(ctx, error)) {
 110                         dprintf("property_watch_all failed %s",
 111                             rmm_strerror(error, -1));
 112                         libhal_ctx_free(ctx);
 113                         *rmm_error = RMM_EHAL_CONNECT;
 114                         return (NULL);
 115                 }
 116         }
 117         if (cond_cb != NULL) {
 118                 libhal_ctx_set_device_condition(ctx, cond_cb);
 119         }
 120 
 121         if (!libhal_ctx_init(ctx, error)) {
 122                 dprintf("libhal_ctx_init failed: %s", rmm_strerror(error, -1));
 123                 libhal_ctx_free(ctx);
 124                 *rmm_error = RMM_EHAL_CONNECT;
 125                 return (NULL);
 126         }
 127         rmm_dbus_error_free(error);
 128 
 129         /*
 130          * The above functions do not guarantee that HAL is actually running.
 131          * Check by invoking a method.
 132          */
 133         if (!(devices = libhal_get_all_devices(ctx, &nr, error))) {
 134                 dprintf("HAL is not running: %s", rmm_strerror(error, -1));
 135                 libhal_ctx_shutdown(ctx, NULL);
 136                 libhal_ctx_free(ctx);
 137                 *rmm_error = RMM_EHAL_CONNECT;
 138                 return (NULL);
 139         } else {
 140                 rmm_dbus_error_free(error);
 141                 libhal_free_string_array(devices);
 142         }
 143 
 144         return (ctx);
 145 }
 146 
 147 
 148 void
 149 rmm_hal_fini(LibHalContext *hal_ctx)
 150 {
 151         DBusConnection  *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 152 
 153         (void) dbus_connection_unref(dbus_conn);
 154         (void) libhal_ctx_free(hal_ctx);
 155 }
 156 
 157 
 158 /*
 159  * find volume from any type of name, similar to the old media_findname()
 160  * returns the LibHalDrive object and a list of LibHalVolume objects.
 161  */
 162 LibHalDrive *
 163 rmm_hal_volume_find(LibHalContext *hal_ctx, const char *name, DBusError *error,
 164     GSList **volumes)
 165 {
 166         LibHalDrive     *drive;
 167         char            *p;
 168         char            lastc;
 169 
 170         *volumes = NULL;
 171 
 172         /* temporarily remove trailing slash */
 173         p = (char *)name + strlen(name) - 1;
 174         if (*p == '/') {
 175                 lastc = *p;
 176                 *p = '\0';
 177         } else {
 178                 p = NULL;
 179         }
 180 
 181         if (name[0] == '/') {
 182                 if (((drive = rmm_hal_volume_findby(hal_ctx,
 183                     "info.udi", name, volumes)) != NULL) ||
 184                     ((drive = rmm_hal_volume_findby(hal_ctx,
 185                     "block.device", name, volumes)) != NULL) ||
 186                     ((drive = rmm_hal_volume_findby(hal_ctx,
 187                     "block.solaris.raw_device", name, volumes)) != NULL) ||
 188                     ((drive = rmm_hal_volume_findby(hal_ctx,
 189                     "volume.mount_point", name, volumes)) != NULL)) {
 190                         goto out;
 191                 } else {
 192                         goto out;
 193                 }
 194         }
 195 
 196         /* try volume label */
 197         if ((drive = rmm_hal_volume_findby(hal_ctx,
 198             "volume.label", name, volumes)) != NULL) {
 199                 goto out;
 200         }
 201 
 202         drive = rmm_hal_volume_findby_nickname(hal_ctx, name, volumes);
 203 
 204 out:
 205         if (p != NULL) {
 206                 *p = lastc;
 207         }
 208         return (drive);
 209 }
 210 
 211 /*
 212  * find default volume. Returns volume pointer and name in 'name'.
 213  */
 214 LibHalDrive *
 215 rmm_hal_volume_find_default(LibHalContext *hal_ctx, DBusError *error,
 216     const char **name_out, GSList **volumes)
 217 {
 218         LibHalDrive     *drive;
 219         static const char *names[] = { "floppy", "cdrom", "rmdisk" };
 220         int             i;
 221 
 222         *volumes = NULL;
 223 
 224         for (i = 0; i < NELEM(names); i++) {
 225                 if ((drive = rmm_hal_volume_findby_nickname(hal_ctx,
 226                     names[i], volumes)) != NULL) {
 227                         /*
 228                          * Skip floppy if it has no media.
 229                          * XXX might want to actually check for media
 230                          * every time instead of relying on volcheck.
 231                          */
 232                         if ((strcmp(names[i], "floppy") != 0) ||
 233                             libhal_device_get_property_bool(hal_ctx,
 234                             libhal_drive_get_udi(drive),
 235                             "storage.removable.media_available", NULL)) {
 236                                 *name_out = names[i];
 237                                 break;
 238                         }
 239                 }
 240                 rmm_dbus_error_free(error);
 241         }
 242 
 243         return (drive);
 244 }
 245 
 246 /*
 247  * find volume by property=value
 248  * returns the LibHalDrive object and a list of LibHalVolume objects.
 249  * XXX add support for multiple properties, reduce D-Bus traffic
 250  */
 251 LibHalDrive *
 252 rmm_hal_volume_findby(LibHalContext *hal_ctx, const char *property,
 253     const char *value, GSList **volumes)
 254 {
 255         DBusError       error;
 256         LibHalDrive     *drive = NULL;
 257         LibHalVolume    *v = NULL;
 258         char            **udis;
 259         int             num_udis;
 260         int             i;
 261         int             i_drive = -1;
 262 
 263         *volumes = NULL;
 264 
 265         dbus_error_init(&error);
 266 
 267         /* get all devices with property=value */
 268         if ((udis = libhal_manager_find_device_string_match(hal_ctx, property,
 269             value, &num_udis, &error)) == NULL) {
 270                 rmm_dbus_error_free(&error);
 271                 return (NULL);
 272         }
 273 
 274         /* find volumes and drives among these devices */
 275         for (i = 0; i < num_udis; i++) {
 276                 rmm_dbus_error_free(&error);
 277                 if (libhal_device_query_capability(hal_ctx, udis[i], "volume",
 278                     &error)) {
 279                         v = libhal_volume_from_udi(hal_ctx, udis[i]);
 280                         if (v != NULL) {
 281                                 *volumes = g_slist_prepend(*volumes, v);
 282                         }
 283                 } else if ((*volumes == NULL) &&
 284                     libhal_device_query_capability(hal_ctx, udis[i], "storage",
 285                     &error)) {
 286                         i_drive = i;
 287                 }
 288         }
 289 
 290         if (*volumes != NULL) {
 291                 /* used prepend, preserve original order */
 292                 *volumes = g_slist_reverse(*volumes);
 293 
 294                 v = (LibHalVolume *)(*volumes)->data;
 295                 drive = libhal_drive_from_udi(hal_ctx,
 296                     libhal_volume_get_storage_device_udi(v));
 297                 if (drive == NULL) {
 298                         rmm_volumes_free (*volumes);
 299                         *volumes = NULL;
 300                 }
 301         } else if (i_drive >= 0) {
 302                 drive = libhal_drive_from_udi(hal_ctx, udis[i_drive]);
 303         }
 304 
 305         libhal_free_string_array(udis);
 306         rmm_dbus_error_free(&error);
 307 
 308         return (drive);
 309 }
 310 
 311 static void
 312 rmm_print_nicknames_one(LibHalDrive *d, LibHalVolume *v,
 313     const char *device, char **drive_nicknames)
 314 {
 315         const char      *volume_label = NULL;
 316         const char      *mount_point = NULL;
 317         boolean_t       comma;
 318         int             i;
 319 
 320         (void) printf("%-*s ", RMM_PRINT_DEVICE_WIDTH, device);
 321         comma = B_FALSE;
 322 
 323         if (drive_nicknames != NULL) {
 324                 for (i = 0; drive_nicknames[i] != NULL; i++) {
 325                         (void) printf("%s%s", comma ? "," : "",
 326                             drive_nicknames[i]);
 327                         comma = B_TRUE;
 328                 }
 329         }
 330 
 331         if ((v != NULL) &&
 332             ((volume_label = libhal_volume_get_label(v)) != NULL) &&
 333             (strlen(volume_label) > 0)) {
 334                 (void) printf("%s%s", comma ? "," : "", volume_label);
 335                 comma = B_TRUE;
 336         }
 337 
 338         if ((v != NULL) &&
 339             ((mount_point = libhal_volume_get_mount_point(v)) != NULL) &&
 340             (strlen(mount_point) > 0)) {
 341                 (void) printf("%s%s", comma ? "," : "", mount_point);
 342                 comma = B_TRUE;
 343         }
 344 
 345         (void) printf("\n");
 346 }
 347 
 348 /*
 349  * print nicknames for each available volume
 350  *
 351  * print_mask:
 352  *   RMM_PRINT_MOUNTABLE        print only mountable volumes
 353  *   RMM_PRINT_EJECTABLE        print volume-less ejectable drives
 354  */
 355 void
 356 rmm_print_volume_nicknames(LibHalContext *hal_ctx, DBusError *error,
 357     int print_mask)
 358 {
 359         char            **udis;
 360         int             num_udis;
 361         GSList          *volumes = NULL;
 362         LibHalDrive     *d, *d_tmp;
 363         LibHalVolume    *v;
 364         const char      *device;
 365         char            **nicknames;
 366         int             i;
 367         GSList          *j;
 368         int             nprinted;
 369 
 370         dbus_error_init(error);
 371 
 372         if ((udis = libhal_find_device_by_capability(hal_ctx, "storage",
 373             &num_udis, error)) == NULL) {
 374                 rmm_dbus_error_free(error);
 375                 return;
 376         }
 377 
 378         for (i = 0; i < num_udis; i++) {
 379                 if ((d = libhal_drive_from_udi(hal_ctx, udis[i])) == NULL) {
 380                         continue;
 381                 }
 382 
 383                 /* find volumes belonging to this drive */
 384                 if ((d_tmp = rmm_hal_volume_findby(hal_ctx,
 385                     "block.storage_device", udis[i], &volumes)) != NULL) {
 386                         libhal_drive_free(d_tmp);
 387                 }
 388 
 389                 nicknames = libhal_device_get_property_strlist(hal_ctx,
 390                     udis[i], "storage.solaris.nicknames", NULL);
 391 
 392                 nprinted = 0;
 393                 for (j = volumes; j != NULL; j = g_slist_next(j)) {
 394                         v = (LibHalVolume *)(j->data);
 395 
 396                         if ((device = libhal_volume_get_device_file(v)) ==
 397                             NULL) {
 398                                 continue;
 399                         }
 400                         if ((print_mask & RMM_PRINT_MOUNTABLE) &&
 401                             (libhal_volume_get_fsusage(v) !=
 402                             LIBHAL_VOLUME_USAGE_MOUNTABLE_FILESYSTEM)) {
 403                                 continue;
 404                         }
 405 
 406                         rmm_print_nicknames_one(d, v, device, nicknames);
 407                         nprinted++;
 408                 }
 409 
 410                 if ((nprinted == 0) &&
 411                     (print_mask & RMM_PRINT_EJECTABLE) &&
 412                     libhal_drive_requires_eject(d) &&
 413                     ((device = libhal_drive_get_device_file(d)) != NULL)) {
 414                         rmm_print_nicknames_one(d, NULL, device, nicknames);
 415                 }
 416 
 417                 libhal_free_string_array(nicknames);
 418                 libhal_drive_free(d);
 419                 rmm_volumes_free(volumes);
 420                 volumes = NULL;
 421         }
 422 
 423         libhal_free_string_array(udis);
 424 }
 425 
 426 /*
 427  * find volume by nickname
 428  * returns the LibHalDrive object and a list of LibHalVolume objects.
 429  */
 430 LibHalDrive *
 431 rmm_hal_volume_findby_nickname(LibHalContext *hal_ctx, const char *name,
 432     GSList **volumes)
 433 {
 434         DBusError       error;
 435         LibHalDrive     *drive = NULL;
 436         LibHalDrive     *drive_tmp;
 437         char            **udis;
 438         int             num_udis;
 439         char            **nicknames;
 440         int             i, j;
 441 
 442         *volumes = NULL;
 443 
 444         dbus_error_init(&error);
 445 
 446         if ((udis = libhal_find_device_by_capability(hal_ctx, "storage",
 447             &num_udis, &error)) == NULL) {
 448                 rmm_dbus_error_free(&error);
 449                 return (NULL);
 450         }
 451 
 452         /* find a drive by nickname */
 453         for (i = 0; (i < num_udis) && (drive == NULL); i++) {
 454                 if ((nicknames = libhal_device_get_property_strlist(hal_ctx,
 455                     udis[i], "storage.solaris.nicknames", &error)) == NULL) {
 456                         rmm_dbus_error_free(&error);
 457                         continue;
 458                 }
 459                 for (j = 0; (nicknames[j] != NULL) && (drive == NULL); j++) {
 460                         if (strcmp(nicknames[j], name) == 0) {
 461                                 drive = libhal_drive_from_udi(hal_ctx, udis[i]);
 462                         }
 463                 }
 464                 libhal_free_string_array(nicknames);
 465         }
 466         libhal_free_string_array(udis);
 467 
 468         if (drive != NULL) {
 469                 /* found the drive, now find its volumes */
 470                 if ((drive_tmp = rmm_hal_volume_findby(hal_ctx,
 471                     "block.storage_device", libhal_drive_get_udi(drive),
 472                     volumes)) != NULL) {
 473                         libhal_drive_free(drive_tmp);
 474                 }
 475         }
 476 
 477         rmm_dbus_error_free(&error);
 478 
 479         return (drive);
 480 }
 481 
 482 void
 483 rmm_volumes_free(GSList *volumes)
 484 {
 485         GSList  *i;
 486 
 487         for (i = volumes; i != NULL; i = g_slist_next(i)) {
 488                 libhal_volume_free((LibHalVolume *)(i->data));
 489         }
 490         g_slist_free(volumes);
 491 }
 492 
 493 /*
 494  * Call HAL's Mount() method on the given device
 495  */
 496 boolean_t
 497 rmm_hal_mount(LibHalContext *hal_ctx, const char *udi,
 498     char **opts, int num_opts, char *mountpoint, DBusError *error)
 499 {
 500         DBusConnection  *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 501         DBusMessage     *dmesg, *reply;
 502         char            *fstype;
 503 
 504         dprintf("mounting %s...\n", udi);
 505 
 506         if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
 507             "org.freedesktop.Hal.Device.Volume", "Mount"))) {
 508                 dprintf(
 509                     "mount failed for %s: cannot create dbus message\n", udi);
 510                 return (B_FALSE);
 511         }
 512 
 513         fstype = "";
 514         if (mountpoint == NULL) {
 515                 mountpoint = "";
 516         }
 517 
 518         if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &mountpoint,
 519             DBUS_TYPE_STRING, &fstype,
 520             DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &opts, num_opts,
 521             DBUS_TYPE_INVALID)) {
 522                 dprintf("mount failed for %s: cannot append args\n", udi);
 523                 dbus_message_unref(dmesg);
 524                 return (B_FALSE);
 525         }
 526 
 527         dbus_error_init(error);
 528         if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
 529             dmesg, RMM_MOUNT_TIMEOUT, error))) {
 530                 dprintf("mount failed for %s: %s\n", udi, error->message);
 531                 dbus_message_unref(dmesg);
 532                 return (B_FALSE);
 533         }
 534 
 535         dprintf("mounted %s\n", udi);
 536 
 537         dbus_message_unref(dmesg);
 538         dbus_message_unref(reply);
 539 
 540         rmm_dbus_error_free(error);
 541 
 542         return (B_TRUE);
 543 }
 544 
 545 
 546 /*
 547  * Call HAL's Unmount() method on the given device
 548  */
 549 boolean_t
 550 rmm_hal_unmount(LibHalContext *hal_ctx, const char *udi, DBusError *error)
 551 {
 552         DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 553         DBusMessage *dmesg, *reply;
 554         char **opts = NULL;
 555 
 556         dprintf("unmounting %s...\n", udi);
 557 
 558         if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
 559             "org.freedesktop.Hal.Device.Volume", "Unmount"))) {
 560                 dprintf(
 561                     "unmount failed %s: cannot create dbus message\n", udi);
 562                 return (B_FALSE);
 563         }
 564 
 565         if (!dbus_message_append_args(dmesg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
 566             &opts, 0, DBUS_TYPE_INVALID)) {
 567                 dprintf("unmount failed %s: cannot append args\n", udi);
 568                 dbus_message_unref(dmesg);
 569                 return (B_FALSE);
 570         }
 571 
 572         dbus_error_init(error);
 573         if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
 574             dmesg, RMM_UNMOUNT_TIMEOUT, error))) {
 575                 dprintf("unmount failed for %s: %s\n", udi, error->message);
 576                 dbus_message_unref(dmesg);
 577                 return (B_FALSE);
 578         }
 579 
 580         dprintf("unmounted %s\n", udi);
 581 
 582         dbus_message_unref(dmesg);
 583         dbus_message_unref(reply);
 584 
 585         rmm_dbus_error_free(error);
 586 
 587         return (B_TRUE);
 588 }
 589 
 590 
 591 /*
 592  * Call HAL's Eject() method on the given device
 593  */
 594 boolean_t
 595 rmm_hal_eject(LibHalContext *hal_ctx, const char *udi, DBusError *error)
 596 {
 597         DBusConnection  *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 598         DBusMessage     *dmesg, *reply;
 599         char            **options = NULL;
 600         uint_t          num_options = 0;
 601 
 602         dprintf("ejecting %s...\n", udi);
 603 
 604         if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
 605             "org.freedesktop.Hal.Device.Storage", "Eject"))) {
 606                 dprintf("eject %s: cannot create dbus message\n", udi);
 607                 return (B_FALSE);
 608         }
 609 
 610         if (!dbus_message_append_args(dmesg,
 611             DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
 612             DBUS_TYPE_INVALID)) {
 613                 dprintf("eject %s: cannot append args to dbus message ", udi);
 614                 dbus_message_unref(dmesg);
 615                 return (B_FALSE);
 616         }
 617 
 618         dbus_error_init(error);
 619         if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
 620             dmesg, RMM_EJECT_TIMEOUT, error))) {
 621                 dprintf("eject %s: %s\n", udi, error->message);
 622                 dbus_message_unref(dmesg);
 623                 return (B_FALSE);
 624         }
 625 
 626         dprintf("ejected %s\n", udi);
 627 
 628         dbus_message_unref(dmesg);
 629         dbus_message_unref(reply);
 630 
 631         rmm_dbus_error_free(error);
 632 
 633         return (B_TRUE);
 634 }
 635 
 636 /*
 637  * Call HAL's CloseTray() method on the given device
 638  */
 639 boolean_t
 640 rmm_hal_closetray(LibHalContext *hal_ctx, const char *udi, DBusError *error)
 641 {
 642         DBusConnection  *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 643         DBusMessage     *dmesg, *reply;
 644         char            **options = NULL;
 645         uint_t          num_options = 0;
 646 
 647         dprintf("closing tray %s...\n", udi);
 648 
 649         if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
 650             "org.freedesktop.Hal.Device.Storage", "CloseTray"))) {
 651                 dprintf(
 652                     "closetray failed for %s: cannot create dbus message\n",
 653                     udi);
 654                 return (B_FALSE);
 655         }
 656 
 657         if (!dbus_message_append_args(dmesg,
 658             DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
 659             DBUS_TYPE_INVALID)) {
 660                 dprintf("closetray %s: cannot append args to dbus message ",
 661                     udi);
 662                 dbus_message_unref(dmesg);
 663                 return (B_FALSE);
 664         }
 665 
 666         dbus_error_init(error);
 667         if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
 668             dmesg, RMM_CLOSETRAY_TIMEOUT, error))) {
 669                 dprintf("closetray failed for %s: %s\n", udi, error->message);
 670                 dbus_message_unref(dmesg);
 671                 return (B_FALSE);
 672         }
 673 
 674         dprintf("closetray ok %s\n", udi);
 675 
 676         dbus_message_unref(dmesg);
 677         dbus_message_unref(reply);
 678 
 679         rmm_dbus_error_free(error);
 680 
 681         return (B_TRUE);
 682 }
 683 
 684 /*
 685  * Call HAL's Rescan() method on the given device
 686  */
 687 boolean_t
 688 rmm_hal_rescan(LibHalContext *hal_ctx, const char *udi, DBusError *error)
 689 {
 690         DBusConnection  *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 691         DBusMessage     *dmesg, *reply;
 692 
 693         dprintf("rescanning %s...\n", udi);
 694 
 695         if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
 696             "org.freedesktop.Hal.Device", "Rescan"))) {
 697                 dprintf("rescan failed for %s: cannot create dbus message\n",
 698                     udi);
 699                 return (B_FALSE);
 700         }
 701 
 702         dbus_error_init(error);
 703         if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
 704             dmesg, -1, error))) {
 705                 dprintf("rescan failed for %s: %s\n", udi, error->message);
 706                 dbus_message_unref(dmesg);
 707                 return (B_FALSE);
 708         }
 709 
 710         dprintf("rescan ok %s\n", udi);
 711 
 712         dbus_message_unref(dmesg);
 713         dbus_message_unref(reply);
 714 
 715         rmm_dbus_error_free(error);
 716 
 717         return (B_TRUE);
 718 }
 719 
 720 boolean_t
 721 rmm_hal_claim_branch(LibHalContext *hal_ctx, const char *udi)
 722 {
 723         DBusError error;
 724         DBusConnection  *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 725         DBusMessage *dmesg, *reply;
 726         const char *claimed_by = "rmvolmgr";
 727 
 728         dprintf("claiming branch %s...\n", udi);
 729 
 730         if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
 731             "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
 732             "ClaimBranch"))) {
 733                 dprintf("cannot create dbus message\n");
 734                 return (B_FALSE);
 735         }
 736 
 737         if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
 738             DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
 739                 dprintf("cannot append args to dbus message\n");
 740                 dbus_message_unref(dmesg);
 741                 return (B_FALSE);
 742         }
 743 
 744         dbus_error_init(&error);
 745         if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
 746             dmesg, -1, &error))) {
 747                 dprintf("cannot send dbus message\n");
 748                 dbus_message_unref(dmesg);
 749                 rmm_dbus_error_free(&error);
 750                 return (B_FALSE);
 751         }
 752 
 753         dprintf("claim branch ok %s\n", udi);
 754 
 755         dbus_message_unref(dmesg);
 756         dbus_message_unref(reply);
 757 
 758         return (B_TRUE);
 759 }
 760 
 761 boolean_t
 762 rmm_hal_unclaim_branch(LibHalContext *hal_ctx, const char *udi)
 763 {
 764         DBusError error;
 765         DBusConnection  *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
 766         DBusMessage *dmesg, *reply;
 767         const char *claimed_by = "rmvolmgr";
 768 
 769         dprintf("unclaiming branch %s...\n", udi);
 770 
 771         if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
 772             "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
 773             "UnclaimBranch"))) {
 774                 dprintf("cannot create dbus message\n");
 775                 return (B_FALSE);
 776         }
 777 
 778         if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
 779             DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
 780                 dprintf("cannot append args to dbus message\n");
 781                 dbus_message_unref(dmesg);
 782                 return (B_FALSE);
 783         }
 784 
 785         dbus_error_init(&error);
 786         if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
 787             dmesg, -1, &error))) {
 788                 dprintf("cannot send dbus message\n");
 789                 dbus_message_unref(dmesg);
 790                 rmm_dbus_error_free(&error);
 791                 return (B_FALSE);
 792         }
 793 
 794         dprintf("unclaim branch ok %s\n", udi);
 795 
 796         dbus_message_unref(dmesg);
 797         dbus_message_unref(reply);
 798 
 799         return (B_TRUE);
 800 }
 801 
 802 static boolean_t
 803 rmm_action_one(LibHalContext *hal_ctx, const char *name, action_t action,
 804     const char *dev, const char *udi, LibHalVolume *v,
 805     char **opts, int num_opts, char *mountpoint)
 806 {
 807         char            dev_str[MAXPATHLEN];
 808         char            *mountp;
 809         DBusError       error;
 810         boolean_t       ret = B_FALSE;
 811 
 812         dprintf("rmm_action_one %s %s\n", name, action_strings[action]);
 813 
 814         if (strcmp(name, dev) == 0) {
 815                 (void) snprintf(dev_str, sizeof (dev_str), name);
 816         } else {
 817                 (void) snprintf(dev_str, sizeof (dev_str), "%s %s", name, dev);
 818         }
 819 
 820         dbus_error_init(&error);
 821 
 822         switch (action) {
 823         case EJECT:
 824                 ret = rmm_hal_eject(hal_ctx, udi, &error);
 825                 break;
 826         case INSERT:
 827         case REMOUNT:
 828                 ret = rmm_hal_mount(hal_ctx, udi,
 829                     opts, num_opts, mountpoint, &error);
 830                 break;
 831         case UNMOUNT:
 832                 ret = rmm_hal_unmount(hal_ctx, udi, &error);
 833                 break;
 834         case CLOSETRAY:
 835                 ret = rmm_hal_closetray(hal_ctx, udi, &error);
 836                 break;
 837         }
 838 
 839         if (!ret) {
 840                 (void) fprintf(stderr, gettext("%s of %s failed: %s\n"),
 841                     action_strings[action], dev_str, rmm_strerror(&error, -1));
 842                 goto done;
 843         }
 844 
 845         switch (action) {
 846         case EJECT:
 847                 (void) printf(gettext("%s ejected\n"), dev_str);
 848                 break;
 849         case INSERT:
 850         case REMOUNT:
 851                 mountp = rmm_get_mnttab_mount_point(dev);
 852                 if (mountp != NULL) {
 853                         (void) printf(gettext("%s mounted at %s\n"),
 854                             dev_str, mountp);
 855                         free(mountp);
 856                 }
 857                 break;
 858         case UNMOUNT:
 859                 (void) printf(gettext("%s unmounted\n"), dev_str);
 860                 break;
 861         case CLOSETRAY:
 862                 (void) printf(gettext("%s tray closed\n"), dev_str);
 863                 break;
 864         }
 865 
 866 done:
 867         rmm_dbus_error_free(&error);
 868         return (ret);
 869 }
 870 
 871 /*
 872  * top level action routine
 873  *
 874  * If non-null 'aa' is passed, it will be used, otherwise a local copy
 875  * will be created.
 876  */
 877 boolean_t
 878 rmm_action(LibHalContext *hal_ctx, const char *name, action_t action,
 879     struct action_arg *aap, char **opts, int num_opts, char *mountpoint)
 880 {
 881         DBusError       error;
 882         GSList          *volumes, *i;
 883         LibHalDrive     *d;
 884         LibHalVolume    *v;
 885         const char      *udi, *d_udi;
 886         const char      *dev, *d_dev;
 887         struct action_arg aa_local;
 888         boolean_t       ret = B_FALSE;
 889 
 890         dprintf("rmm_action %s %s\n", name, action_strings[action]);
 891 
 892         if (aap == NULL) {
 893                 bzero(&aa_local, sizeof (aa_local));
 894                 aap = &aa_local;
 895         }
 896 
 897         dbus_error_init(&error);
 898 
 899         /* find the drive and its volumes */
 900         d = rmm_hal_volume_find(hal_ctx, name, &error, &volumes);
 901         rmm_dbus_error_free(&error);
 902         if (d == NULL) {
 903                 (void) fprintf(stderr, gettext("cannot find '%s'\n"), name);
 904                 return (B_FALSE);
 905         }
 906         d_udi = libhal_drive_get_udi(d);
 907         d_dev = libhal_drive_get_device_file(d);
 908         if ((d_udi == NULL) || (d_dev == NULL)) {
 909                 goto out;
 910         }
 911 
 912         /*
 913          * For those drives that do not require media eject,
 914          * EJECT turns into UNMOUNT.
 915          */
 916         if ((action == EJECT) && !libhal_drive_requires_eject(d)) {
 917                 action = UNMOUNT;
 918         }
 919 
 920         /*
 921          * We can't mount or unmount a drive that has no volumes.
 922          * Either the media isn't inserted or it's not formatted
 923          */
 924         if (volumes == NULL && (action != EJECT && action != CLOSETRAY)) {
 925                 (void) fprintf(stderr, libhal_drive_requires_eject(d) ? 
 926                     gettext("no volumes in '%s' to %s\n") :
 927                     gettext("no volumes on '%s' to %s\n"),
 928                     name, action_strings[action]);
 929                 goto out;
 930         }
 931 
 932         /* per drive action */
 933         if ((action == EJECT) || (action == CLOSETRAY)) {
 934                 ret = rmm_action_one(hal_ctx, name, action, d_dev, d_udi, NULL,
 935                     opts, num_opts, NULL);
 936 
 937                 if (!ret || (action == CLOSETRAY)) {
 938                         goto out;
 939                 }
 940         }
 941 
 942         /* per volume action */
 943         for (i = volumes; i != NULL; i = g_slist_next(i)) {
 944                 v = (LibHalVolume *)i->data;
 945                 udi = libhal_volume_get_udi(v);
 946                 dev = libhal_volume_get_device_file(v);
 947 
 948                 if ((udi == NULL) || (dev == NULL)) {
 949                         continue;
 950                 }
 951                 if (aap == &aa_local) {
 952                         if (!rmm_volume_aa_from_prop(hal_ctx, udi, v, aap)) {
 953                                 dprintf("rmm_volume_aa_from_prop failed %s\n",
 954                                     udi);
 955                                 continue;
 956                         }
 957                 }
 958                 aap->aa_action = action;
 959 
 960                 /* ejected above, just need postprocess */
 961                 if (action != EJECT) {
 962                         ret = rmm_action_one(hal_ctx, name, action, dev, udi, v,
 963                             opts, num_opts, mountpoint);
 964                 }
 965                 if (ret) {
 966                         (void) vold_postprocess(hal_ctx, udi, aap);
 967                 }
 968 
 969                 if (aap == &aa_local) {
 970                         rmm_volume_aa_free(aap);
 971                 }
 972         }
 973 
 974 out:
 975         if (volumes != NULL)
 976                 rmm_volumes_free(volumes);
 977         if (d != NULL)
 978                 libhal_drive_free(d);
 979 
 980         return (ret);
 981 }
 982 
 983 
 984 /*
 985  * rescan by name
 986  * if name is NULL, rescan all drives
 987  */
 988 boolean_t
 989 rmm_rescan(LibHalContext *hal_ctx, const char *name, boolean_t query)
 990 {
 991         DBusError       error;
 992         GSList          *volumes;
 993         LibHalDrive     *drive = NULL;
 994         const char      *drive_udi;
 995         char            **udis;
 996         int             num_udis;
 997         char            *nickname;
 998         char            **nicks = NULL;
 999         boolean_t       do_free_udis = FALSE;
1000         int             i;
1001         boolean_t       ret = B_FALSE;
1002 
1003         dprintf("rmm_rescan %s\n", name != NULL ? name : "all");
1004 
1005         dbus_error_init(&error);
1006 
1007         if (name != NULL) {
1008                 if ((drive = rmm_hal_volume_find(hal_ctx, name, &error,
1009                     &volumes)) == NULL) {
1010                         rmm_dbus_error_free(&error);
1011                         (void) fprintf(stderr,
1012                             gettext("cannot find '%s'\n"), name);
1013                         return (B_FALSE);
1014                 }
1015                 rmm_dbus_error_free(&error);
1016                 g_slist_free(volumes);
1017 
1018                 drive_udi = libhal_drive_get_udi(drive);
1019                 udis = (char **)&drive_udi;
1020                 num_udis = 1;
1021         } else {
1022                 if ((udis = libhal_find_device_by_capability(hal_ctx,
1023                     "storage", &num_udis, &error)) == NULL) {
1024                         rmm_dbus_error_free(&error);
1025                         return (B_TRUE);
1026                 }
1027                 rmm_dbus_error_free(&error);
1028                 do_free_udis = TRUE;
1029         }
1030 
1031         for (i = 0; i < num_udis; i++) {
1032                 if (name == NULL) {
1033                         nicks = libhal_device_get_property_strlist(hal_ctx,
1034                             udis[i], "storage.solaris.nicknames", NULL);
1035                         if (nicks != NULL) {
1036                                 nickname = nicks[0];
1037                         } else {
1038                                 nickname = "";
1039                         }
1040                 }
1041                 if (!(ret = rmm_hal_rescan(hal_ctx, udis[i], &error))) {
1042                         (void) fprintf(stderr,
1043                             gettext("rescan of %s failed: %s\n"),
1044                             name ? name : nickname,
1045                             rmm_strerror(&error, -1));
1046                         libhal_free_string_array(nicks);
1047                         continue;
1048                 }
1049                 if (query) {
1050                         ret = libhal_device_get_property_bool(hal_ctx, udis[i],
1051                             "storage.removable.media_available", NULL);
1052                         if (ret) {
1053                                 printf(gettext("%s is available\n"),
1054                                     name ? name : nickname);
1055                         } else {
1056                                 printf(gettext("%s is not available\n"),
1057                                     name ? name : nickname);
1058                         }
1059                 }
1060                 libhal_free_string_array(nicks);
1061         }
1062 
1063         if (drive != NULL) {
1064                 libhal_drive_free(drive);
1065         }
1066         if (do_free_udis) {
1067                 libhal_free_string_array(udis);
1068         }
1069 
1070         return (ret);
1071 }
1072 
1073 
1074 /*
1075  * set action_arg from volume properties
1076  */
1077 boolean_t
1078 rmm_volume_aa_from_prop(LibHalContext *hal_ctx, const char *udi_arg,
1079     LibHalVolume *volume_arg, struct action_arg *aap)
1080 {
1081         LibHalVolume    *volume = volume_arg;
1082         const char      *udi = udi_arg;
1083         const char      *drive_udi;
1084         char            *volume_label;
1085         char            *mountpoint;
1086         int             len;
1087         int             ret = B_FALSE;
1088 
1089         /* at least udi or volume must be supplied */
1090         if ((udi == NULL) && (volume == NULL)) {
1091                 return (B_FALSE);
1092         }
1093         if (volume == NULL) {
1094                 if ((volume = libhal_volume_from_udi(hal_ctx, udi)) == NULL) {
1095                         dprintf("cannot get volume %s\n", udi);
1096                         goto out;
1097                 }
1098         }
1099         if (udi == NULL) {
1100                 if ((udi = libhal_volume_get_udi(volume)) == NULL) {
1101                         dprintf("cannot get udi\n");
1102                         goto out;
1103                 }
1104         }
1105         drive_udi = libhal_volume_get_storage_device_udi(volume);
1106 
1107         if (!(aap->aa_symdev = libhal_device_get_property_string(hal_ctx,
1108             drive_udi, "storage.solaris.legacy.symdev", NULL))) {
1109                 dprintf("property %s not found %s\n",
1110                     "storage.solaris.legacy.symdev", drive_udi);
1111                 goto out;
1112         }
1113         if (!(aap->aa_media = libhal_device_get_property_string(hal_ctx,
1114             drive_udi, "storage.solaris.legacy.media_type", NULL))) {
1115                 dprintf("property %s not found %s\n",
1116                     "storage.solaris.legacy.media_type", drive_udi);
1117                 goto out;
1118         }
1119 
1120         /* name is derived from volume label */
1121         aap->aa_name = NULL;
1122         if ((volume_label = (char *)libhal_device_get_property_string(hal_ctx,
1123             udi, "volume.label", NULL)) != NULL) {
1124                 if ((len = strlen(volume_label)) > 0) {
1125                         aap->aa_name = rmm_vold_convert_volume_label(
1126                             volume_label, len);
1127                         if (strlen(aap->aa_name) == 0) {
1128                                 free(aap->aa_name);
1129                                 aap->aa_name = NULL;
1130                         }
1131                 }
1132                 libhal_free_string(volume_label);
1133         }
1134         /* if no label, then unnamed_<mediatype> */
1135         if (aap->aa_name == NULL) {
1136                 aap->aa_name = (char *)calloc(1, sizeof ("unnamed_floppyNNNN"));
1137                 if (aap->aa_name == NULL) {
1138                         goto out;
1139                 }
1140                 (void) snprintf(aap->aa_name, sizeof ("unnamed_floppyNNNN"),
1141                     "unnamed_%s", aap->aa_media);
1142         }
1143 
1144         if (!(aap->aa_path = libhal_device_get_property_string(hal_ctx, udi,
1145             "block.device", NULL))) {
1146                 dprintf("property %s not found %s\n", "block.device", udi);
1147                 goto out;
1148         }
1149         if (!(aap->aa_rawpath = libhal_device_get_property_string(hal_ctx, udi,
1150             "block.solaris.raw_device", NULL))) {
1151                 dprintf("property %s not found %s\n",
1152                     "block.solaris.raw_device", udi);
1153                 goto out;
1154         }
1155         if (!(aap->aa_type = libhal_device_get_property_string(hal_ctx, udi,
1156             "volume.fstype", NULL))) {
1157                 dprintf("property %s not found %s\n", "volume.fstype", udi);
1158                 goto out;
1159         }
1160         if (!libhal_device_get_property_bool(hal_ctx, udi,
1161             "volume.is_partition", NULL)) {
1162                 aap->aa_partname = NULL;
1163         } else if (!(aap->aa_partname = libhal_device_get_property_string(
1164             hal_ctx, udi, "block.solaris.slice", NULL))) {
1165                 dprintf("property %s not found %s\n",
1166                     "block.solaris.slice", udi);
1167                 goto out;
1168         }
1169         if (!(mountpoint = libhal_device_get_property_string(hal_ctx, udi,
1170             "volume.mount_point", NULL))) {
1171                 dprintf("property %s not found %s\n",
1172                     "volume.mount_point", udi);
1173                 goto out;
1174         }
1175         /*
1176          * aa_mountpoint can be reallocated in rmm_volume_aa_update_mountpoint()
1177          * won't have to choose between free() or libhal_free_string() later on
1178          */
1179         aap->aa_mountpoint = strdup(mountpoint);
1180         libhal_free_string(mountpoint);
1181         if (aap->aa_mountpoint == NULL) {
1182                 dprintf("mountpoint is NULL %s\n", udi);
1183                 goto out;
1184         }
1185 
1186         ret = B_TRUE;
1187 
1188 out:
1189         if ((volume != NULL) && (volume != volume_arg)) {
1190                 libhal_volume_free(volume);
1191         }
1192         if (!ret) {
1193                 rmm_volume_aa_free(aap);
1194         }
1195         return (ret);
1196 }
1197 
1198 /* ARGSUSED */
1199 void
1200 rmm_volume_aa_update_mountpoint(LibHalContext *hal_ctx, const char *udi,
1201     struct action_arg *aap)
1202 {
1203         if (aap->aa_mountpoint != NULL) {
1204                 free(aap->aa_mountpoint);
1205         }
1206         aap->aa_mountpoint = rmm_get_mnttab_mount_point(aap->aa_path);
1207 }
1208 
1209 void
1210 rmm_volume_aa_free(struct action_arg *aap)
1211 {
1212         if (aap->aa_symdev != NULL) {
1213                 libhal_free_string(aap->aa_symdev);
1214                 aap->aa_symdev = NULL;
1215         }
1216         if (aap->aa_name != NULL) {
1217                 free(aap->aa_name);
1218                 aap->aa_name = NULL;
1219         }
1220         if (aap->aa_path != NULL) {
1221                 libhal_free_string(aap->aa_path);
1222                 aap->aa_path = NULL;
1223         }
1224         if (aap->aa_rawpath != NULL) {
1225                 libhal_free_string(aap->aa_rawpath);
1226                 aap->aa_rawpath = NULL;
1227         }
1228         if (aap->aa_type != NULL) {
1229                 libhal_free_string(aap->aa_type);
1230                 aap->aa_type = NULL;
1231         }
1232         if (aap->aa_media != NULL) {
1233                 libhal_free_string(aap->aa_media);
1234                 aap->aa_media = NULL;
1235         }
1236         if (aap->aa_partname != NULL) {
1237                 libhal_free_string(aap->aa_partname);
1238                 aap->aa_partname = NULL;
1239         }
1240         if (aap->aa_mountpoint != NULL) {
1241                 free(aap->aa_mountpoint);
1242                 aap->aa_mountpoint = NULL;
1243         }
1244 }
1245 
1246 /*
1247  * get device's mount point from mnttab
1248  */
1249 char *
1250 rmm_get_mnttab_mount_point(const char *special)
1251 {
1252         char            *mount_point = NULL;
1253         FILE            *f;
1254         struct mnttab   mnt;
1255         struct mnttab   mpref = { NULL, NULL, NULL, NULL, NULL };
1256 
1257         if ((f = fopen(MNTTAB, "r")) != NULL) {
1258                 mpref.mnt_special = (char *)special;
1259                 if (getmntany(f, &mnt, &mpref) == 0) {
1260                         mount_point = strdup(mnt.mnt_mountp);
1261                 }
1262                 fclose(f);
1263         }
1264 
1265         return (mount_point);
1266 }
1267 
1268 
1269 /*
1270  * get human readable string from error values
1271  */
1272 const char *
1273 rmm_strerror(DBusError *dbus_error, int rmm_error)
1274 {
1275         const char      *str;
1276 
1277         if ((dbus_error != NULL) && dbus_error_is_set(dbus_error)) {
1278                 str = dbus_error->message;
1279         } else {
1280                 switch (rmm_error) {
1281                 case RMM_EOK:
1282                         str = gettext("success");
1283                         break;
1284                 case RMM_EDBUS_CONNECT:
1285                         str = gettext("cannot connect to D-Bus");
1286                         break;
1287                 case RMM_EHAL_CONNECT:
1288                         str = gettext("cannot connect to HAL");
1289                         break;
1290                 default:
1291                         str = gettext("undefined error");
1292                         break;
1293                 }
1294         }
1295 
1296         return (str);
1297 }
1298 
1299 void
1300 rmm_dbus_error_free(DBusError *error)
1301 {
1302         if (error != NULL && dbus_error_is_set(error)) {
1303                 dbus_error_free(error);
1304         }
1305 }
1306 
1307 static int
1308 rmm_vold_isbadchar(int c)
1309 {
1310         int     ret_val = 0;
1311 
1312 
1313         switch (c) {
1314         case '/':
1315         case ';':
1316         case '|':
1317                 ret_val = 1;
1318                 break;
1319         default:
1320                 if (iscntrl(c) || isspace(c)) {
1321                         ret_val = 1;
1322                 }
1323         }
1324 
1325         return (ret_val);
1326 }
1327 
1328 char *
1329 rmm_vold_convert_volume_label(const char *name, size_t len)
1330 {
1331         char    buf[MAXNAMELEN+1];
1332         char    *s = buf;
1333         int     i;
1334 
1335         if (len > MAXNAMELEN) {
1336                 len = MAXNAMELEN;
1337         }
1338 
1339         for (i = 0; i < len; i++) {
1340                 if (name[i] == '\0') {
1341                         break;
1342                 }
1343                 if (isgraph((int)name[i])) {
1344                         if (isupper((int)name[i])) {
1345                                 *s++ = tolower((int)name[i]);
1346                         } else if (rmm_vold_isbadchar((int)name[i])) {
1347                                 *s++ = '_';
1348                         } else {
1349                                 *s++ = name[i];
1350                         }
1351                 }
1352         }
1353         *s = '\0';
1354         s = strdup(buf);
1355 
1356         return (s);
1357 }
1358 
1359 /*
1360  * swiped from mkdir.c
1361  */
1362 int
1363 makepath(char *dir, mode_t mode)
1364 {
1365         int             err;
1366         char            *slash;
1367 
1368 
1369         if ((mkdir(dir, mode) == 0) || (errno == EEXIST)) {
1370                 return (0);
1371         }
1372         if (errno != ENOENT) {
1373                 return (-1);
1374         }
1375         if ((slash = strrchr(dir, '/')) == NULL) {
1376                 return (-1);
1377         }
1378         *slash = '\0';
1379         err = makepath(dir, mode);
1380         *slash++ = '/';
1381 
1382         if (err || (*slash == '\0')) {
1383                 return (err);
1384         }
1385 
1386         return (mkdir(dir, mode));
1387 }
1388 
1389 
1390 void
1391 dprintf(const char *fmt, ...)
1392 {
1393 
1394         va_list         ap;
1395         const char      *p;
1396         char            msg[BUFSIZ];
1397         char            *errmsg = strerror(errno);
1398         char            *s;
1399 
1400         if (rmm_debug == 0) {
1401                 return;
1402         }
1403 
1404         (void) memset(msg, 0, BUFSIZ);
1405 
1406         /* scan for %m and replace with errno msg */
1407         s = &msg[strlen(msg)];
1408         p = fmt;
1409 
1410         while (*p != '\0') {
1411                 if ((*p == '%') && (*(p+1) == 'm')) {
1412                         (void) strcat(s, errmsg);
1413                         p += 2;
1414                         s += strlen(errmsg);
1415                         continue;
1416                 }
1417                 *s++ = *p++;
1418         }
1419         *s = '\0';      /* don't forget the null byte */
1420 
1421         va_start(ap, fmt);
1422         (void) vfprintf(stderr, msg, ap);
1423         va_end(ap);
1424 }