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