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