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