xref: /titanic_51/usr/src/cmd/rmvolmgr/rmm_common.c (revision 381a2a9a387f449fab7d0c7e97c4184c26963abf)
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 		aap = &aa_local;
843 	}
844 
845 	dbus_error_init(&error);
846 
847 	/* find the drive and its volumes */
848 	d = rmm_hal_volume_find(hal_ctx, name, &error, &volumes);
849 	rmm_dbus_error_free(&error);
850 	if (d == NULL) {
851 		(void) fprintf(stderr, gettext("cannot find '%s'\n"), name);
852 		return (B_FALSE);
853 	}
854 	d_udi = libhal_drive_get_udi(d);
855 	d_dev = libhal_drive_get_device_file(d);
856 	if ((d_udi == NULL) || (d_dev == NULL)) {
857 		goto out;
858 	}
859 
860 	/*
861 	 * For those drives that do not require media eject,
862 	 * EJECT turns into UNMOUNT.
863 	 */
864 	if ((action == EJECT) && !libhal_drive_requires_eject(d)) {
865 		action = UNMOUNT;
866 	}
867 
868 	/* per drive action */
869 	if ((action == EJECT) || (action == CLOSETRAY)) {
870 		ret = rmm_action_one(hal_ctx, name, action, d_dev, d_udi, NULL,
871 		    opts, num_opts, NULL);
872 
873 		if (!ret || (action == CLOSETRAY)) {
874 			goto out;
875 		}
876 	}
877 
878 	/* per volume action */
879 	for (i = volumes; i != NULL; i = g_slist_next(i)) {
880 		v = (LibHalVolume *)i->data;
881 		udi = libhal_volume_get_udi(v);
882 		dev = libhal_volume_get_device_file(v);
883 
884 		if ((udi == NULL) || (dev == NULL)) {
885 			continue;
886 		}
887 		if (aap == &aa_local) {
888 			if (!rmm_volume_aa_from_prop(hal_ctx, udi, v, aap)) {
889 				dprintf("rmm_volume_aa_from_prop failed %s\n",
890 				    udi);
891 				continue;
892 			}
893 		}
894 		aap->aa_action = action;
895 
896 		/* ejected above, just need postprocess */
897 		if (action != EJECT) {
898 			ret = rmm_action_one(hal_ctx, name, action, dev, udi, v,
899 			    opts, num_opts, mountpoint);
900 		}
901 		if (ret) {
902 			(void) vold_postprocess(hal_ctx, udi, aap);
903 		}
904 
905 		libhal_volume_free(v);
906 		if (aap == &aa_local) {
907 			rmm_volume_aa_free(aap);
908 		}
909 	}
910 
911 out:
912 	g_slist_free(volumes);
913 	libhal_drive_free(d);
914 
915 	return (ret);
916 }
917 
918 
919 /*
920  * rescan by name
921  * if name is NULL, rescan all drives
922  */
923 boolean_t
924 rmm_rescan(LibHalContext *hal_ctx, const char *name, boolean_t query)
925 {
926 	DBusError	error;
927 	GSList		*volumes;
928 	LibHalDrive	*drive = NULL;
929 	const char	*drive_udi;
930 	char		**udis;
931 	int		num_udis;
932 	char		*nickname;
933 	char		**nicks = NULL;
934 	boolean_t	do_free_udis = FALSE;
935 	int		i;
936 	int		ret = 0;
937 
938 	dprintf("rmm_rescan %s\n", name != NULL ? name : "all");
939 
940 	dbus_error_init(&error);
941 
942 	if (name != NULL) {
943 		if ((drive = rmm_hal_volume_find(hal_ctx, name, &error,
944 		    &volumes)) == NULL) {
945 			rmm_dbus_error_free(&error);
946 			(void) fprintf(stderr,
947 			    gettext("cannot find '%s'\n"), name);
948 			return (B_FALSE);
949 		}
950 		rmm_dbus_error_free(&error);
951 		g_slist_free(volumes);
952 
953 		drive_udi = libhal_drive_get_udi(drive);
954 		udis = (char **)&drive_udi;
955 		num_udis = 1;
956 	} else {
957 		if ((udis = libhal_find_device_by_capability(hal_ctx,
958 		    "storage", &num_udis, &error)) == NULL) {
959 			rmm_dbus_error_free(&error);
960 			return (B_TRUE);
961 		}
962 		rmm_dbus_error_free(&error);
963 		do_free_udis = TRUE;
964 	}
965 
966 	for (i = 0; i < num_udis; i++) {
967 		if (name == NULL) {
968 			nicks = libhal_device_get_property_strlist(hal_ctx,
969 			    udis[i], "storage.solaris.nicknames", NULL);
970 			if (nicks != NULL) {
971 				nickname = nicks[0];
972 			} else {
973 				nickname = "";
974 			}
975 		}
976 		if (!(ret = rmm_hal_rescan(hal_ctx, udis[i], &error))) {
977 			(void) fprintf(stderr,
978 			    gettext("rescan of %s failed: %s\n"),
979 			    name ? name : nickname,
980 			    rmm_strerror(&error, -1));
981 			libhal_free_string_array(nicks);
982 			continue;
983 		}
984 		if (query) {
985 			printf(gettext("%s is%s available\n"),
986 			    name ? name : nickname,
987 			    libhal_device_get_property_bool(hal_ctx, udis[i],
988 			    "storage.removable.media_available", NULL) ?
989 			    "" : " not");
990 		}
991 		libhal_free_string_array(nicks);
992 	}
993 
994 	if (drive != NULL) {
995 		libhal_drive_free(drive);
996 	}
997 	if (do_free_udis) {
998 		libhal_free_string_array(udis);
999 	}
1000 
1001 	return (ret);
1002 }
1003 
1004 
1005 /*
1006  * set action_arg from volume properties
1007  */
1008 boolean_t
1009 rmm_volume_aa_from_prop(LibHalContext *hal_ctx, const char *udi_arg,
1010     LibHalVolume *volume_arg, struct action_arg *aap)
1011 {
1012 	LibHalVolume	*volume = volume_arg;
1013 	const char	*udi = udi_arg;
1014 	const char	*drive_udi;
1015 	char		*volume_label;
1016 	char		*mountpoint;
1017 	int		len;
1018 	int		ret = B_FALSE;
1019 
1020 	/* at least udi or volume must be supplied */
1021 	if ((udi == NULL) && (volume == NULL)) {
1022 		return (B_FALSE);
1023 	}
1024 	if (volume == NULL) {
1025 		if ((volume = libhal_volume_from_udi(hal_ctx, udi)) == NULL) {
1026 			dprintf("cannot get volume %s\n", udi);
1027 			goto out;
1028 		}
1029 	}
1030 	if (udi == NULL) {
1031 		if ((udi = libhal_volume_get_udi(volume)) == NULL) {
1032 			dprintf("cannot get udi\n");
1033 			goto out;
1034 		}
1035 	}
1036 	drive_udi = libhal_volume_get_storage_device_udi(volume);
1037 
1038 	if (!(aap->aa_symdev = libhal_device_get_property_string(hal_ctx,
1039 	    drive_udi, "storage.solaris.legacy.symdev", NULL))) {
1040 		dprintf("property %s not found %s\n",
1041 		    "storage.solaris.legacy.symdev", drive_udi);
1042 		goto out;
1043 	}
1044 	if (!(aap->aa_media = libhal_device_get_property_string(hal_ctx,
1045 	    drive_udi, "storage.solaris.legacy.media_type", NULL))) {
1046 		dprintf("property %s not found %s\n",
1047 		    "storage.solaris.legacy.media_type", drive_udi);
1048 		goto out;
1049 	}
1050 
1051 	/* name is derived from volume label */
1052 	aap->aa_name = NULL;
1053 	if ((volume_label = (char *)libhal_device_get_property_string(hal_ctx,
1054 	    udi, "volume.label", NULL)) != NULL) {
1055 		if ((len = strlen(volume_label)) > 0) {
1056 			aap->aa_name = rmm_vold_convert_volume_label(
1057 			    volume_label, len);
1058 			if (strlen(aap->aa_name) == 0) {
1059 				free(aap->aa_name);
1060 				aap->aa_name = NULL;
1061 			}
1062 		}
1063 		libhal_free_string(volume_label);
1064 	}
1065 	/* if no label, then unnamed_<mediatype> */
1066 	if (aap->aa_name == NULL) {
1067 		aap->aa_name = (char *)calloc(1, sizeof ("unnamed_floppyNNNN"));
1068 		if (aap->aa_name == NULL) {
1069 			goto out;
1070 		}
1071 		(void) snprintf(aap->aa_name, sizeof ("unnamed_floppyNNNN"),
1072 		    "unnamed_%s", aap->aa_media);
1073 	}
1074 
1075 	if (!(aap->aa_path = libhal_device_get_property_string(hal_ctx, udi,
1076 	    "block.device", NULL))) {
1077 		dprintf("property %s not found %s\n", "block.device", udi);
1078 		goto out;
1079 	}
1080 	if (!(aap->aa_rawpath = libhal_device_get_property_string(hal_ctx, udi,
1081 	    "block.solaris.raw_device", NULL))) {
1082 		dprintf("property %s not found %s\n",
1083 		    "block.solaris.raw_device", udi);
1084 		goto out;
1085 	}
1086 	if (!(aap->aa_type = libhal_device_get_property_string(hal_ctx, udi,
1087 	    "volume.fstype", NULL))) {
1088 		dprintf("property %s not found %s\n", "volume.fstype", udi);
1089 		goto out;
1090 	}
1091 	if (!libhal_device_get_property_bool(hal_ctx, udi,
1092 	    "volume.is_partition", NULL)) {
1093 		aap->aa_partname = NULL;
1094 	} else if (!(aap->aa_partname = libhal_device_get_property_string(
1095 	    hal_ctx, udi, "block.solaris.slice", NULL))) {
1096 		dprintf("property %s not found %s\n",
1097 		    "block.solaris.slice", udi);
1098 		goto out;
1099 	}
1100 	if (!(mountpoint = libhal_device_get_property_string(hal_ctx, udi,
1101 	    "volume.mount_point", NULL))) {
1102 		dprintf("property %s not found %s\n",
1103 		    "volume.mount_point", udi);
1104 		goto out;
1105 	}
1106 	/*
1107 	 * aa_mountpoint can be reallocated in rmm_volume_aa_update_mountpoint()
1108 	 * won't have to choose between free() or libhal_free_string() later on
1109 	 */
1110 	aap->aa_mountpoint = strdup(mountpoint);
1111 	libhal_free_string(mountpoint);
1112 	if (aap->aa_mountpoint == NULL) {
1113 		dprintf("mountpoint is NULL %s\n", udi);
1114 		goto out;
1115 	}
1116 
1117 	ret = B_TRUE;
1118 
1119 out:
1120 	if ((volume != NULL) && (volume != volume_arg)) {
1121 		libhal_volume_free(volume);
1122 	}
1123 	if (!ret) {
1124 		rmm_volume_aa_free(aap);
1125 	}
1126 	return (ret);
1127 }
1128 
1129 /* ARGSUSED */
1130 void
1131 rmm_volume_aa_update_mountpoint(LibHalContext *hal_ctx, const char *udi,
1132     struct action_arg *aap)
1133 {
1134 	if (aap->aa_mountpoint != NULL) {
1135 		free(aap->aa_mountpoint);
1136 	}
1137 	aap->aa_mountpoint = rmm_get_mnttab_mount_point(aap->aa_path);
1138 }
1139 
1140 void
1141 rmm_volume_aa_free(struct action_arg *aap)
1142 {
1143 	if (aap->aa_symdev != NULL) {
1144 		libhal_free_string(aap->aa_symdev);
1145 		aap->aa_symdev = NULL;
1146 	}
1147 	if (aap->aa_name != NULL) {
1148 		free(aap->aa_name);
1149 		aap->aa_name = NULL;
1150 	}
1151 	if (aap->aa_path != NULL) {
1152 		libhal_free_string(aap->aa_path);
1153 		aap->aa_path = NULL;
1154 	}
1155 	if (aap->aa_rawpath != NULL) {
1156 		libhal_free_string(aap->aa_rawpath);
1157 		aap->aa_rawpath = NULL;
1158 	}
1159 	if (aap->aa_type != NULL) {
1160 		libhal_free_string(aap->aa_type);
1161 		aap->aa_type = NULL;
1162 	}
1163 	if (aap->aa_media != NULL) {
1164 		libhal_free_string(aap->aa_media);
1165 		aap->aa_media = NULL;
1166 	}
1167 	if (aap->aa_partname != NULL) {
1168 		libhal_free_string(aap->aa_partname);
1169 		aap->aa_partname = NULL;
1170 	}
1171 	if (aap->aa_mountpoint != NULL) {
1172 		free(aap->aa_mountpoint);
1173 		aap->aa_mountpoint = NULL;
1174 	}
1175 }
1176 
1177 /*
1178  * get device's mount point from mnttab
1179  */
1180 char *
1181 rmm_get_mnttab_mount_point(const char *special)
1182 {
1183 	char		*mount_point = NULL;
1184 	FILE		*f;
1185 	struct mnttab	mnt;
1186 	struct mnttab	mpref = { NULL, NULL, NULL, NULL, NULL };
1187 
1188 	if ((f = fopen(MNTTAB, "r")) != NULL) {
1189 		mpref.mnt_special = (char *)special;
1190 		if (getmntany(f, &mnt, &mpref) == 0) {
1191 			mount_point = strdup(mnt.mnt_mountp);
1192 		}
1193 		fclose(f);
1194 	}
1195 
1196 	return (mount_point);
1197 }
1198 
1199 
1200 /*
1201  * get human readable string from error values
1202  */
1203 const char *
1204 rmm_strerror(DBusError *dbus_error, int rmm_error)
1205 {
1206 	const char	*str;
1207 
1208 	if ((dbus_error != NULL) && dbus_error_is_set(dbus_error)) {
1209 		str = dbus_error->message;
1210 	} else {
1211 		switch (rmm_error) {
1212 		case RMM_EOK:
1213 			str = gettext("success");
1214 			break;
1215 		case RMM_EDBUS_CONNECT:
1216 			str = gettext("cannot connect to D-Bus");
1217 			break;
1218 		case RMM_EHAL_CONNECT:
1219 			str = gettext("cannot connect to HAL");
1220 			break;
1221 		default:
1222 			str = gettext("undefined error");
1223 			break;
1224 		}
1225 	}
1226 
1227 	return (str);
1228 }
1229 
1230 void
1231 rmm_dbus_error_free(DBusError *error)
1232 {
1233 	if (error != NULL && dbus_error_is_set(error)) {
1234 		dbus_error_free(error);
1235 	}
1236 }
1237 
1238 static int
1239 rmm_vold_isbadchar(int c)
1240 {
1241 	int	ret_val = 0;
1242 
1243 
1244 	switch (c) {
1245 	case '/':
1246 	case ';':
1247 	case '|':
1248 		ret_val = 1;
1249 		break;
1250 	default:
1251 		if (iscntrl(c) || isspace(c)) {
1252 			ret_val = 1;
1253 		}
1254 	}
1255 
1256 	return (ret_val);
1257 }
1258 
1259 char *
1260 rmm_vold_convert_volume_label(const char *name, size_t len)
1261 {
1262 	char	buf[MAXNAMELEN+1];
1263 	char	*s = buf;
1264 	int	i;
1265 
1266 	if (len > MAXNAMELEN) {
1267 		len = MAXNAMELEN;
1268 	}
1269 
1270 	for (i = 0; i < len; i++) {
1271 		if (name[i] == '\0') {
1272 			break;
1273 		}
1274 		if (isgraph((int)name[i])) {
1275 			if (isupper((int)name[i])) {
1276 				*s++ = tolower((int)name[i]);
1277 			} else if (rmm_vold_isbadchar((int)name[i])) {
1278 				*s++ = '_';
1279 			} else {
1280 				*s++ = name[i];
1281 			}
1282 		}
1283 	}
1284 	*s = '\0';
1285 	s = strdup(buf);
1286 
1287 	return (s);
1288 }
1289 
1290 /*
1291  * swiped from mkdir.c
1292  */
1293 int
1294 makepath(char *dir, mode_t mode)
1295 {
1296 	int		err;
1297 	char		*slash;
1298 
1299 
1300 	if ((mkdir(dir, mode) == 0) || (errno == EEXIST)) {
1301 		return (0);
1302 	}
1303 	if (errno != ENOENT) {
1304 		return (-1);
1305 	}
1306 	if ((slash = strrchr(dir, '/')) == NULL) {
1307 		return (-1);
1308 	}
1309 	*slash = '\0';
1310 	err = makepath(dir, mode);
1311 	*slash++ = '/';
1312 
1313 	if (err || (*slash == '\0')) {
1314 		return (err);
1315 	}
1316 
1317 	return (mkdir(dir, mode));
1318 }
1319 
1320 
1321 void
1322 dprintf(const char *fmt, ...)
1323 {
1324 
1325 	va_list		ap;
1326 	const char	*p;
1327 	char		msg[BUFSIZ];
1328 	char		*errmsg = strerror(errno);
1329 	char		*s;
1330 
1331 	if (rmm_debug == 0) {
1332 		return;
1333 	}
1334 
1335 	(void) memset(msg, 0, BUFSIZ);
1336 
1337 	/* scan for %m and replace with errno msg */
1338 	s = &msg[strlen(msg)];
1339 	p = fmt;
1340 
1341 	while (*p != '\0') {
1342 		if ((*p == '%') && (*(p+1) == 'm')) {
1343 			(void) strcat(s, errmsg);
1344 			p += 2;
1345 			s += strlen(errmsg);
1346 			continue;
1347 		}
1348 		*s++ = *p++;
1349 	}
1350 	*s = '\0';	/* don't forget the null byte */
1351 
1352 	va_start(ap, fmt);
1353 	(void) vfprintf(stderr, msg, ap);
1354 	va_end(ap);
1355 }
1356