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