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 bzero(&aa_local, sizeof (aa_local)); 843 aap = &aa_local; 844 } 845 846 dbus_error_init(&error); 847 848 /* find the drive and its volumes */ 849 d = rmm_hal_volume_find(hal_ctx, name, &error, &volumes); 850 rmm_dbus_error_free(&error); 851 if (d == NULL) { 852 (void) fprintf(stderr, gettext("cannot find '%s'\n"), name); 853 return (B_FALSE); 854 } 855 d_udi = libhal_drive_get_udi(d); 856 d_dev = libhal_drive_get_device_file(d); 857 if ((d_udi == NULL) || (d_dev == NULL)) { 858 goto out; 859 } 860 861 /* 862 * For those drives that do not require media eject, 863 * EJECT turns into UNMOUNT. 864 */ 865 if ((action == EJECT) && !libhal_drive_requires_eject(d)) { 866 action = UNMOUNT; 867 } 868 869 /* per drive action */ 870 if ((action == EJECT) || (action == CLOSETRAY)) { 871 ret = rmm_action_one(hal_ctx, name, action, d_dev, d_udi, NULL, 872 opts, num_opts, NULL); 873 874 if (!ret || (action == CLOSETRAY)) { 875 goto out; 876 } 877 } 878 879 /* per volume action */ 880 for (i = volumes; i != NULL; i = g_slist_next(i)) { 881 v = (LibHalVolume *)i->data; 882 udi = libhal_volume_get_udi(v); 883 dev = libhal_volume_get_device_file(v); 884 885 if ((udi == NULL) || (dev == NULL)) { 886 continue; 887 } 888 if (aap == &aa_local) { 889 if (!rmm_volume_aa_from_prop(hal_ctx, udi, v, aap)) { 890 dprintf("rmm_volume_aa_from_prop failed %s\n", 891 udi); 892 continue; 893 } 894 } 895 aap->aa_action = action; 896 897 /* ejected above, just need postprocess */ 898 if (action != EJECT) { 899 ret = rmm_action_one(hal_ctx, name, action, dev, udi, v, 900 opts, num_opts, mountpoint); 901 } 902 if (ret) { 903 (void) vold_postprocess(hal_ctx, udi, aap); 904 } 905 906 libhal_volume_free(v); 907 if (aap == &aa_local) { 908 rmm_volume_aa_free(aap); 909 } 910 } 911 912 out: 913 g_slist_free(volumes); 914 libhal_drive_free(d); 915 916 return (ret); 917 } 918 919 920 /* 921 * rescan by name 922 * if name is NULL, rescan all drives 923 */ 924 boolean_t 925 rmm_rescan(LibHalContext *hal_ctx, const char *name, boolean_t query) 926 { 927 DBusError error; 928 GSList *volumes; 929 LibHalDrive *drive = NULL; 930 const char *drive_udi; 931 char **udis; 932 int num_udis; 933 char *nickname; 934 char **nicks = NULL; 935 boolean_t do_free_udis = FALSE; 936 int i; 937 boolean_t ret = B_FALSE; 938 939 dprintf("rmm_rescan %s\n", name != NULL ? name : "all"); 940 941 dbus_error_init(&error); 942 943 if (name != NULL) { 944 if ((drive = rmm_hal_volume_find(hal_ctx, name, &error, 945 &volumes)) == NULL) { 946 rmm_dbus_error_free(&error); 947 (void) fprintf(stderr, 948 gettext("cannot find '%s'\n"), name); 949 return (B_FALSE); 950 } 951 rmm_dbus_error_free(&error); 952 g_slist_free(volumes); 953 954 drive_udi = libhal_drive_get_udi(drive); 955 udis = (char **)&drive_udi; 956 num_udis = 1; 957 } else { 958 if ((udis = libhal_find_device_by_capability(hal_ctx, 959 "storage", &num_udis, &error)) == NULL) { 960 rmm_dbus_error_free(&error); 961 return (B_TRUE); 962 } 963 rmm_dbus_error_free(&error); 964 do_free_udis = TRUE; 965 } 966 967 for (i = 0; i < num_udis; i++) { 968 if (name == NULL) { 969 nicks = libhal_device_get_property_strlist(hal_ctx, 970 udis[i], "storage.solaris.nicknames", NULL); 971 if (nicks != NULL) { 972 nickname = nicks[0]; 973 } else { 974 nickname = ""; 975 } 976 } 977 if (!(ret = rmm_hal_rescan(hal_ctx, udis[i], &error))) { 978 (void) fprintf(stderr, 979 gettext("rescan of %s failed: %s\n"), 980 name ? name : nickname, 981 rmm_strerror(&error, -1)); 982 libhal_free_string_array(nicks); 983 continue; 984 } 985 if (query) { 986 ret = libhal_device_get_property_bool(hal_ctx, udis[i], 987 "storage.removable.media_available", NULL); 988 if (ret) { 989 printf(gettext("%s is available\n"), 990 name ? name : nickname); 991 } else { 992 printf(gettext("%s is not available\n"), 993 name ? name : nickname); 994 } 995 } 996 libhal_free_string_array(nicks); 997 } 998 999 if (drive != NULL) { 1000 libhal_drive_free(drive); 1001 } 1002 if (do_free_udis) { 1003 libhal_free_string_array(udis); 1004 } 1005 1006 return (ret); 1007 } 1008 1009 1010 /* 1011 * set action_arg from volume properties 1012 */ 1013 boolean_t 1014 rmm_volume_aa_from_prop(LibHalContext *hal_ctx, const char *udi_arg, 1015 LibHalVolume *volume_arg, struct action_arg *aap) 1016 { 1017 LibHalVolume *volume = volume_arg; 1018 const char *udi = udi_arg; 1019 const char *drive_udi; 1020 char *volume_label; 1021 char *mountpoint; 1022 int len; 1023 int ret = B_FALSE; 1024 1025 /* at least udi or volume must be supplied */ 1026 if ((udi == NULL) && (volume == NULL)) { 1027 return (B_FALSE); 1028 } 1029 if (volume == NULL) { 1030 if ((volume = libhal_volume_from_udi(hal_ctx, udi)) == NULL) { 1031 dprintf("cannot get volume %s\n", udi); 1032 goto out; 1033 } 1034 } 1035 if (udi == NULL) { 1036 if ((udi = libhal_volume_get_udi(volume)) == NULL) { 1037 dprintf("cannot get udi\n"); 1038 goto out; 1039 } 1040 } 1041 drive_udi = libhal_volume_get_storage_device_udi(volume); 1042 1043 if (!(aap->aa_symdev = libhal_device_get_property_string(hal_ctx, 1044 drive_udi, "storage.solaris.legacy.symdev", NULL))) { 1045 dprintf("property %s not found %s\n", 1046 "storage.solaris.legacy.symdev", drive_udi); 1047 goto out; 1048 } 1049 if (!(aap->aa_media = libhal_device_get_property_string(hal_ctx, 1050 drive_udi, "storage.solaris.legacy.media_type", NULL))) { 1051 dprintf("property %s not found %s\n", 1052 "storage.solaris.legacy.media_type", drive_udi); 1053 goto out; 1054 } 1055 1056 /* name is derived from volume label */ 1057 aap->aa_name = NULL; 1058 if ((volume_label = (char *)libhal_device_get_property_string(hal_ctx, 1059 udi, "volume.label", NULL)) != NULL) { 1060 if ((len = strlen(volume_label)) > 0) { 1061 aap->aa_name = rmm_vold_convert_volume_label( 1062 volume_label, len); 1063 if (strlen(aap->aa_name) == 0) { 1064 free(aap->aa_name); 1065 aap->aa_name = NULL; 1066 } 1067 } 1068 libhal_free_string(volume_label); 1069 } 1070 /* if no label, then unnamed_<mediatype> */ 1071 if (aap->aa_name == NULL) { 1072 aap->aa_name = (char *)calloc(1, sizeof ("unnamed_floppyNNNN")); 1073 if (aap->aa_name == NULL) { 1074 goto out; 1075 } 1076 (void) snprintf(aap->aa_name, sizeof ("unnamed_floppyNNNN"), 1077 "unnamed_%s", aap->aa_media); 1078 } 1079 1080 if (!(aap->aa_path = libhal_device_get_property_string(hal_ctx, udi, 1081 "block.device", NULL))) { 1082 dprintf("property %s not found %s\n", "block.device", udi); 1083 goto out; 1084 } 1085 if (!(aap->aa_rawpath = libhal_device_get_property_string(hal_ctx, udi, 1086 "block.solaris.raw_device", NULL))) { 1087 dprintf("property %s not found %s\n", 1088 "block.solaris.raw_device", udi); 1089 goto out; 1090 } 1091 if (!(aap->aa_type = libhal_device_get_property_string(hal_ctx, udi, 1092 "volume.fstype", NULL))) { 1093 dprintf("property %s not found %s\n", "volume.fstype", udi); 1094 goto out; 1095 } 1096 if (!libhal_device_get_property_bool(hal_ctx, udi, 1097 "volume.is_partition", NULL)) { 1098 aap->aa_partname = NULL; 1099 } else if (!(aap->aa_partname = libhal_device_get_property_string( 1100 hal_ctx, udi, "block.solaris.slice", NULL))) { 1101 dprintf("property %s not found %s\n", 1102 "block.solaris.slice", udi); 1103 goto out; 1104 } 1105 if (!(mountpoint = libhal_device_get_property_string(hal_ctx, udi, 1106 "volume.mount_point", NULL))) { 1107 dprintf("property %s not found %s\n", 1108 "volume.mount_point", udi); 1109 goto out; 1110 } 1111 /* 1112 * aa_mountpoint can be reallocated in rmm_volume_aa_update_mountpoint() 1113 * won't have to choose between free() or libhal_free_string() later on 1114 */ 1115 aap->aa_mountpoint = strdup(mountpoint); 1116 libhal_free_string(mountpoint); 1117 if (aap->aa_mountpoint == NULL) { 1118 dprintf("mountpoint is NULL %s\n", udi); 1119 goto out; 1120 } 1121 1122 ret = B_TRUE; 1123 1124 out: 1125 if ((volume != NULL) && (volume != volume_arg)) { 1126 libhal_volume_free(volume); 1127 } 1128 if (!ret) { 1129 rmm_volume_aa_free(aap); 1130 } 1131 return (ret); 1132 } 1133 1134 /* ARGSUSED */ 1135 void 1136 rmm_volume_aa_update_mountpoint(LibHalContext *hal_ctx, const char *udi, 1137 struct action_arg *aap) 1138 { 1139 if (aap->aa_mountpoint != NULL) { 1140 free(aap->aa_mountpoint); 1141 } 1142 aap->aa_mountpoint = rmm_get_mnttab_mount_point(aap->aa_path); 1143 } 1144 1145 void 1146 rmm_volume_aa_free(struct action_arg *aap) 1147 { 1148 if (aap->aa_symdev != NULL) { 1149 libhal_free_string(aap->aa_symdev); 1150 aap->aa_symdev = NULL; 1151 } 1152 if (aap->aa_name != NULL) { 1153 free(aap->aa_name); 1154 aap->aa_name = NULL; 1155 } 1156 if (aap->aa_path != NULL) { 1157 libhal_free_string(aap->aa_path); 1158 aap->aa_path = NULL; 1159 } 1160 if (aap->aa_rawpath != NULL) { 1161 libhal_free_string(aap->aa_rawpath); 1162 aap->aa_rawpath = NULL; 1163 } 1164 if (aap->aa_type != NULL) { 1165 libhal_free_string(aap->aa_type); 1166 aap->aa_type = NULL; 1167 } 1168 if (aap->aa_media != NULL) { 1169 libhal_free_string(aap->aa_media); 1170 aap->aa_media = NULL; 1171 } 1172 if (aap->aa_partname != NULL) { 1173 libhal_free_string(aap->aa_partname); 1174 aap->aa_partname = NULL; 1175 } 1176 if (aap->aa_mountpoint != NULL) { 1177 free(aap->aa_mountpoint); 1178 aap->aa_mountpoint = NULL; 1179 } 1180 } 1181 1182 /* 1183 * get device's mount point from mnttab 1184 */ 1185 char * 1186 rmm_get_mnttab_mount_point(const char *special) 1187 { 1188 char *mount_point = NULL; 1189 FILE *f; 1190 struct mnttab mnt; 1191 struct mnttab mpref = { NULL, NULL, NULL, NULL, NULL }; 1192 1193 if ((f = fopen(MNTTAB, "r")) != NULL) { 1194 mpref.mnt_special = (char *)special; 1195 if (getmntany(f, &mnt, &mpref) == 0) { 1196 mount_point = strdup(mnt.mnt_mountp); 1197 } 1198 fclose(f); 1199 } 1200 1201 return (mount_point); 1202 } 1203 1204 1205 /* 1206 * get human readable string from error values 1207 */ 1208 const char * 1209 rmm_strerror(DBusError *dbus_error, int rmm_error) 1210 { 1211 const char *str; 1212 1213 if ((dbus_error != NULL) && dbus_error_is_set(dbus_error)) { 1214 str = dbus_error->message; 1215 } else { 1216 switch (rmm_error) { 1217 case RMM_EOK: 1218 str = gettext("success"); 1219 break; 1220 case RMM_EDBUS_CONNECT: 1221 str = gettext("cannot connect to D-Bus"); 1222 break; 1223 case RMM_EHAL_CONNECT: 1224 str = gettext("cannot connect to HAL"); 1225 break; 1226 default: 1227 str = gettext("undefined error"); 1228 break; 1229 } 1230 } 1231 1232 return (str); 1233 } 1234 1235 void 1236 rmm_dbus_error_free(DBusError *error) 1237 { 1238 if (error != NULL && dbus_error_is_set(error)) { 1239 dbus_error_free(error); 1240 } 1241 } 1242 1243 static int 1244 rmm_vold_isbadchar(int c) 1245 { 1246 int ret_val = 0; 1247 1248 1249 switch (c) { 1250 case '/': 1251 case ';': 1252 case '|': 1253 ret_val = 1; 1254 break; 1255 default: 1256 if (iscntrl(c) || isspace(c)) { 1257 ret_val = 1; 1258 } 1259 } 1260 1261 return (ret_val); 1262 } 1263 1264 char * 1265 rmm_vold_convert_volume_label(const char *name, size_t len) 1266 { 1267 char buf[MAXNAMELEN+1]; 1268 char *s = buf; 1269 int i; 1270 1271 if (len > MAXNAMELEN) { 1272 len = MAXNAMELEN; 1273 } 1274 1275 for (i = 0; i < len; i++) { 1276 if (name[i] == '\0') { 1277 break; 1278 } 1279 if (isgraph((int)name[i])) { 1280 if (isupper((int)name[i])) { 1281 *s++ = tolower((int)name[i]); 1282 } else if (rmm_vold_isbadchar((int)name[i])) { 1283 *s++ = '_'; 1284 } else { 1285 *s++ = name[i]; 1286 } 1287 } 1288 } 1289 *s = '\0'; 1290 s = strdup(buf); 1291 1292 return (s); 1293 } 1294 1295 /* 1296 * swiped from mkdir.c 1297 */ 1298 int 1299 makepath(char *dir, mode_t mode) 1300 { 1301 int err; 1302 char *slash; 1303 1304 1305 if ((mkdir(dir, mode) == 0) || (errno == EEXIST)) { 1306 return (0); 1307 } 1308 if (errno != ENOENT) { 1309 return (-1); 1310 } 1311 if ((slash = strrchr(dir, '/')) == NULL) { 1312 return (-1); 1313 } 1314 *slash = '\0'; 1315 err = makepath(dir, mode); 1316 *slash++ = '/'; 1317 1318 if (err || (*slash == '\0')) { 1319 return (err); 1320 } 1321 1322 return (mkdir(dir, mode)); 1323 } 1324 1325 1326 void 1327 dprintf(const char *fmt, ...) 1328 { 1329 1330 va_list ap; 1331 const char *p; 1332 char msg[BUFSIZ]; 1333 char *errmsg = strerror(errno); 1334 char *s; 1335 1336 if (rmm_debug == 0) { 1337 return; 1338 } 1339 1340 (void) memset(msg, 0, BUFSIZ); 1341 1342 /* scan for %m and replace with errno msg */ 1343 s = &msg[strlen(msg)]; 1344 p = fmt; 1345 1346 while (*p != '\0') { 1347 if ((*p == '%') && (*(p+1) == 'm')) { 1348 (void) strcat(s, errmsg); 1349 p += 2; 1350 s += strlen(errmsg); 1351 continue; 1352 } 1353 *s++ = *p++; 1354 } 1355 *s = '\0'; /* don't forget the null byte */ 1356 1357 va_start(ap, fmt); 1358 (void) vfprintf(stderr, msg, ap); 1359 va_end(ap); 1360 } 1361