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