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