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 * Assume anything other than EJECT and CLOSETRAY is a 922 * variant of mount or unmount and requires the device 923 * to have at least one volume. 924 */ 925 if (volumes == NULL && (action != EJECT && action != CLOSETRAY)) { 926 (void) fprintf(stderr, 927 gettext("cannot %s device '%s' with no volumes\n"), 928 action_strings[action], name); 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 libhal_drive_free(d); 978 979 return (ret); 980 } 981 982 983 /* 984 * rescan by name 985 * if name is NULL, rescan all drives 986 */ 987 boolean_t 988 rmm_rescan(LibHalContext *hal_ctx, const char *name, boolean_t query) 989 { 990 DBusError error; 991 GSList *volumes; 992 LibHalDrive *drive = NULL; 993 const char *drive_udi; 994 char **udis; 995 int num_udis; 996 char *nickname; 997 char **nicks = NULL; 998 boolean_t do_free_udis = FALSE; 999 int i; 1000 boolean_t ret = B_FALSE; 1001 1002 dprintf("rmm_rescan %s\n", name != NULL ? name : "all"); 1003 1004 dbus_error_init(&error); 1005 1006 if (name != NULL) { 1007 if ((drive = rmm_hal_volume_find(hal_ctx, name, &error, 1008 &volumes)) == NULL) { 1009 rmm_dbus_error_free(&error); 1010 (void) fprintf(stderr, 1011 gettext("cannot find '%s'\n"), name); 1012 return (B_FALSE); 1013 } 1014 rmm_dbus_error_free(&error); 1015 g_slist_free(volumes); 1016 1017 drive_udi = libhal_drive_get_udi(drive); 1018 udis = (char **)&drive_udi; 1019 num_udis = 1; 1020 } else { 1021 if ((udis = libhal_find_device_by_capability(hal_ctx, 1022 "storage", &num_udis, &error)) == NULL) { 1023 rmm_dbus_error_free(&error); 1024 return (B_TRUE); 1025 } 1026 rmm_dbus_error_free(&error); 1027 do_free_udis = TRUE; 1028 } 1029 1030 for (i = 0; i < num_udis; i++) { 1031 if (name == NULL) { 1032 nicks = libhal_device_get_property_strlist(hal_ctx, 1033 udis[i], "storage.solaris.nicknames", NULL); 1034 if (nicks != NULL) { 1035 nickname = nicks[0]; 1036 } else { 1037 nickname = ""; 1038 } 1039 } 1040 if (!(ret = rmm_hal_rescan(hal_ctx, udis[i], &error))) { 1041 (void) fprintf(stderr, 1042 gettext("rescan of %s failed: %s\n"), 1043 name ? name : nickname, 1044 rmm_strerror(&error, -1)); 1045 libhal_free_string_array(nicks); 1046 continue; 1047 } 1048 if (query) { 1049 ret = libhal_device_get_property_bool(hal_ctx, udis[i], 1050 "storage.removable.media_available", NULL); 1051 if (ret) { 1052 printf(gettext("%s is available\n"), 1053 name ? name : nickname); 1054 } else { 1055 printf(gettext("%s is not available\n"), 1056 name ? name : nickname); 1057 } 1058 } 1059 libhal_free_string_array(nicks); 1060 } 1061 1062 if (drive != NULL) { 1063 libhal_drive_free(drive); 1064 } 1065 if (do_free_udis) { 1066 libhal_free_string_array(udis); 1067 } 1068 1069 return (ret); 1070 } 1071 1072 1073 /* 1074 * set action_arg from volume properties 1075 */ 1076 boolean_t 1077 rmm_volume_aa_from_prop(LibHalContext *hal_ctx, const char *udi_arg, 1078 LibHalVolume *volume_arg, struct action_arg *aap) 1079 { 1080 LibHalVolume *volume = volume_arg; 1081 const char *udi = udi_arg; 1082 const char *drive_udi; 1083 char *volume_label; 1084 char *mountpoint; 1085 int len; 1086 int ret = B_FALSE; 1087 1088 /* at least udi or volume must be supplied */ 1089 if ((udi == NULL) && (volume == NULL)) { 1090 return (B_FALSE); 1091 } 1092 if (volume == NULL) { 1093 if ((volume = libhal_volume_from_udi(hal_ctx, udi)) == NULL) { 1094 dprintf("cannot get volume %s\n", udi); 1095 goto out; 1096 } 1097 } 1098 if (udi == NULL) { 1099 if ((udi = libhal_volume_get_udi(volume)) == NULL) { 1100 dprintf("cannot get udi\n"); 1101 goto out; 1102 } 1103 } 1104 drive_udi = libhal_volume_get_storage_device_udi(volume); 1105 1106 if (!(aap->aa_symdev = libhal_device_get_property_string(hal_ctx, 1107 drive_udi, "storage.solaris.legacy.symdev", NULL))) { 1108 dprintf("property %s not found %s\n", 1109 "storage.solaris.legacy.symdev", drive_udi); 1110 goto out; 1111 } 1112 if (!(aap->aa_media = libhal_device_get_property_string(hal_ctx, 1113 drive_udi, "storage.solaris.legacy.media_type", NULL))) { 1114 dprintf("property %s not found %s\n", 1115 "storage.solaris.legacy.media_type", drive_udi); 1116 goto out; 1117 } 1118 1119 /* name is derived from volume label */ 1120 aap->aa_name = NULL; 1121 if ((volume_label = (char *)libhal_device_get_property_string(hal_ctx, 1122 udi, "volume.label", NULL)) != NULL) { 1123 if ((len = strlen(volume_label)) > 0) { 1124 aap->aa_name = rmm_vold_convert_volume_label( 1125 volume_label, len); 1126 if (strlen(aap->aa_name) == 0) { 1127 free(aap->aa_name); 1128 aap->aa_name = NULL; 1129 } 1130 } 1131 libhal_free_string(volume_label); 1132 } 1133 /* if no label, then unnamed_<mediatype> */ 1134 if (aap->aa_name == NULL) { 1135 aap->aa_name = (char *)calloc(1, sizeof ("unnamed_floppyNNNN")); 1136 if (aap->aa_name == NULL) { 1137 goto out; 1138 } 1139 (void) snprintf(aap->aa_name, sizeof ("unnamed_floppyNNNN"), 1140 "unnamed_%s", aap->aa_media); 1141 } 1142 1143 if (!(aap->aa_path = libhal_device_get_property_string(hal_ctx, udi, 1144 "block.device", NULL))) { 1145 dprintf("property %s not found %s\n", "block.device", udi); 1146 goto out; 1147 } 1148 if (!(aap->aa_rawpath = libhal_device_get_property_string(hal_ctx, udi, 1149 "block.solaris.raw_device", NULL))) { 1150 dprintf("property %s not found %s\n", 1151 "block.solaris.raw_device", udi); 1152 goto out; 1153 } 1154 if (!(aap->aa_type = libhal_device_get_property_string(hal_ctx, udi, 1155 "volume.fstype", NULL))) { 1156 dprintf("property %s not found %s\n", "volume.fstype", udi); 1157 goto out; 1158 } 1159 if (!libhal_device_get_property_bool(hal_ctx, udi, 1160 "volume.is_partition", NULL)) { 1161 aap->aa_partname = NULL; 1162 } else if (!(aap->aa_partname = libhal_device_get_property_string( 1163 hal_ctx, udi, "block.solaris.slice", NULL))) { 1164 dprintf("property %s not found %s\n", 1165 "block.solaris.slice", udi); 1166 goto out; 1167 } 1168 if (!(mountpoint = libhal_device_get_property_string(hal_ctx, udi, 1169 "volume.mount_point", NULL))) { 1170 dprintf("property %s not found %s\n", 1171 "volume.mount_point", udi); 1172 goto out; 1173 } 1174 /* 1175 * aa_mountpoint can be reallocated in rmm_volume_aa_update_mountpoint() 1176 * won't have to choose between free() or libhal_free_string() later on 1177 */ 1178 aap->aa_mountpoint = strdup(mountpoint); 1179 libhal_free_string(mountpoint); 1180 if (aap->aa_mountpoint == NULL) { 1181 dprintf("mountpoint is NULL %s\n", udi); 1182 goto out; 1183 } 1184 1185 ret = B_TRUE; 1186 1187 out: 1188 if ((volume != NULL) && (volume != volume_arg)) { 1189 libhal_volume_free(volume); 1190 } 1191 if (!ret) { 1192 rmm_volume_aa_free(aap); 1193 } 1194 return (ret); 1195 } 1196 1197 /* ARGSUSED */ 1198 void 1199 rmm_volume_aa_update_mountpoint(LibHalContext *hal_ctx, const char *udi, 1200 struct action_arg *aap) 1201 { 1202 if (aap->aa_mountpoint != NULL) { 1203 free(aap->aa_mountpoint); 1204 } 1205 aap->aa_mountpoint = rmm_get_mnttab_mount_point(aap->aa_path); 1206 } 1207 1208 void 1209 rmm_volume_aa_free(struct action_arg *aap) 1210 { 1211 if (aap->aa_symdev != NULL) { 1212 libhal_free_string(aap->aa_symdev); 1213 aap->aa_symdev = NULL; 1214 } 1215 if (aap->aa_name != NULL) { 1216 free(aap->aa_name); 1217 aap->aa_name = NULL; 1218 } 1219 if (aap->aa_path != NULL) { 1220 libhal_free_string(aap->aa_path); 1221 aap->aa_path = NULL; 1222 } 1223 if (aap->aa_rawpath != NULL) { 1224 libhal_free_string(aap->aa_rawpath); 1225 aap->aa_rawpath = NULL; 1226 } 1227 if (aap->aa_type != NULL) { 1228 libhal_free_string(aap->aa_type); 1229 aap->aa_type = NULL; 1230 } 1231 if (aap->aa_media != NULL) { 1232 libhal_free_string(aap->aa_media); 1233 aap->aa_media = NULL; 1234 } 1235 if (aap->aa_partname != NULL) { 1236 libhal_free_string(aap->aa_partname); 1237 aap->aa_partname = NULL; 1238 } 1239 if (aap->aa_mountpoint != NULL) { 1240 free(aap->aa_mountpoint); 1241 aap->aa_mountpoint = NULL; 1242 } 1243 } 1244 1245 /* 1246 * get device's mount point from mnttab 1247 */ 1248 char * 1249 rmm_get_mnttab_mount_point(const char *special) 1250 { 1251 char *mount_point = NULL; 1252 FILE *f; 1253 struct mnttab mnt; 1254 struct mnttab mpref = { NULL, NULL, NULL, NULL, NULL }; 1255 1256 if ((f = fopen(MNTTAB, "r")) != NULL) { 1257 mpref.mnt_special = (char *)special; 1258 if (getmntany(f, &mnt, &mpref) == 0) { 1259 mount_point = strdup(mnt.mnt_mountp); 1260 } 1261 fclose(f); 1262 } 1263 1264 return (mount_point); 1265 } 1266 1267 1268 /* 1269 * get human readable string from error values 1270 */ 1271 const char * 1272 rmm_strerror(DBusError *dbus_error, int rmm_error) 1273 { 1274 const char *str; 1275 1276 if ((dbus_error != NULL) && dbus_error_is_set(dbus_error)) { 1277 str = dbus_error->message; 1278 } else { 1279 switch (rmm_error) { 1280 case RMM_EOK: 1281 str = gettext("success"); 1282 break; 1283 case RMM_EDBUS_CONNECT: 1284 str = gettext("cannot connect to D-Bus"); 1285 break; 1286 case RMM_EHAL_CONNECT: 1287 str = gettext("cannot connect to HAL"); 1288 break; 1289 default: 1290 str = gettext("undefined error"); 1291 break; 1292 } 1293 } 1294 1295 return (str); 1296 } 1297 1298 void 1299 rmm_dbus_error_free(DBusError *error) 1300 { 1301 if (error != NULL && dbus_error_is_set(error)) { 1302 dbus_error_free(error); 1303 } 1304 } 1305 1306 static int 1307 rmm_vold_isbadchar(int c) 1308 { 1309 int ret_val = 0; 1310 1311 1312 switch (c) { 1313 case '/': 1314 case ';': 1315 case '|': 1316 ret_val = 1; 1317 break; 1318 default: 1319 if (iscntrl(c) || isspace(c)) { 1320 ret_val = 1; 1321 } 1322 } 1323 1324 return (ret_val); 1325 } 1326 1327 char * 1328 rmm_vold_convert_volume_label(const char *name, size_t len) 1329 { 1330 char buf[MAXNAMELEN+1]; 1331 char *s = buf; 1332 int i; 1333 1334 if (len > MAXNAMELEN) { 1335 len = MAXNAMELEN; 1336 } 1337 1338 for (i = 0; i < len; i++) { 1339 if (name[i] == '\0') { 1340 break; 1341 } 1342 if (isgraph((int)name[i])) { 1343 if (isupper((int)name[i])) { 1344 *s++ = tolower((int)name[i]); 1345 } else if (rmm_vold_isbadchar((int)name[i])) { 1346 *s++ = '_'; 1347 } else { 1348 *s++ = name[i]; 1349 } 1350 } 1351 } 1352 *s = '\0'; 1353 s = strdup(buf); 1354 1355 return (s); 1356 } 1357 1358 /* 1359 * swiped from mkdir.c 1360 */ 1361 int 1362 makepath(char *dir, mode_t mode) 1363 { 1364 int err; 1365 char *slash; 1366 1367 1368 if ((mkdir(dir, mode) == 0) || (errno == EEXIST)) { 1369 return (0); 1370 } 1371 if (errno != ENOENT) { 1372 return (-1); 1373 } 1374 if ((slash = strrchr(dir, '/')) == NULL) { 1375 return (-1); 1376 } 1377 *slash = '\0'; 1378 err = makepath(dir, mode); 1379 *slash++ = '/'; 1380 1381 if (err || (*slash == '\0')) { 1382 return (err); 1383 } 1384 1385 return (mkdir(dir, mode)); 1386 } 1387 1388 1389 void 1390 dprintf(const char *fmt, ...) 1391 { 1392 1393 va_list ap; 1394 const char *p; 1395 char msg[BUFSIZ]; 1396 char *errmsg = strerror(errno); 1397 char *s; 1398 1399 if (rmm_debug == 0) { 1400 return; 1401 } 1402 1403 (void) memset(msg, 0, BUFSIZ); 1404 1405 /* scan for %m and replace with errno msg */ 1406 s = &msg[strlen(msg)]; 1407 p = fmt; 1408 1409 while (*p != '\0') { 1410 if ((*p == '%') && (*(p+1) == 'm')) { 1411 (void) strcat(s, errmsg); 1412 p += 2; 1413 s += strlen(errmsg); 1414 continue; 1415 } 1416 *s++ = *p++; 1417 } 1418 *s = '\0'; /* don't forget the null byte */ 1419 1420 va_start(ap, fmt); 1421 (void) vfprintf(stderr, msg, ap); 1422 va_end(ap); 1423 }