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 }