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