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