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 }