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