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 /*
29  * routines in this module are meant to be called by other libvolmgt
30  * routines only
31  */
32 
33 #include	<stdio.h>
34 #include	<string.h>
35 #include	<dirent.h>
36 #include	<fcntl.h>
37 #include	<string.h>
38 #ifdef	DEBUG
39 #include	<errno.h>
40 #endif
41 #include	<libintl.h>
42 #include	<limits.h>
43 #include	<unistd.h>
44 #include	<stdlib.h>
45 #include	<volmgt.h>
46 #include	<sys/types.h>
47 #include	<sys/mkdev.h>
48 #include	<sys/stat.h>
49 #include	<sys/dkio.h>
50 #include	<sys/param.h>
51 #include	<sys/wait.h>
52 #include	<sys/mnttab.h>
53 #include	<sys/vol.h>
54 #include	"volmgt_private.h"
55 
56 
57 #define	NULL_PATH		"/dev/null"
58 
59 
60 
61 /*
62  * This is an ON Consolidation Private interface.
63  *
64  * Is the specified path mounted?
65  *
66  * This function is really inadequate for ejection testing.  For example,
67  * I could have /dev/fd0a mounted and eject /dev/fd0c, and it would be
68  * ejected.  There needs to be some better way to make this check, although
69  * short of looking up the mounted dev_t in the kernel mount table and
70  * building in all kinds of knowledge into this function,  I'm not sure
71  * how to do it.
72  */
73 int
74 _dev_mounted(char *path)
75 {
76 	static int	vol_getmntdev(FILE *, struct mnttab *, dev_t,
77 			    struct dk_cinfo *);
78 	int		fd = -1;
79 	struct dk_cinfo	info;
80 	static FILE 	*fp = NULL;		/* mnttab file pointer */
81 	struct mnttab	mnt;			/* set bug not used */
82 	char		*cn = NULL;		/* char spcl pathname */
83 	struct stat64	sb;
84 	int		ret_val = 0;
85 
86 
87 
88 #ifdef	DEBUG
89 	denter("_dev_mounted(%s): entering\n", path);
90 #endif
91 
92 	/* ensure we have the block spcl pathname */
93 	if ((cn = (char *)volmgt_getfullrawname(path)) == NULL) {
94 #ifdef	DEBUG
95 		dprintf("_dev_mounted: volmgt_getfullrawname failed\n");
96 #endif
97 		goto dun;
98 	}
99 
100 #ifdef	DEBUG_OPEN
101 	dprintf("_dev_mounted: fopen()ing \"%s\"\n", MNTTAB);
102 #endif
103 	if ((fp = fopen(MNTTAB, "rF")) == NULL) {
104 		/* mtab is gone... let him go */
105 #ifdef	DEBUG
106 		perror(MNTTAB);
107 #endif
108 		goto dun;
109 	}
110 
111 #ifdef	DEBUG_OPEN
112 	dprintf("_dev_mounted: open()ing \"%s\"\n", cn);
113 #endif
114 	if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
115 #ifdef	DEBUG
116 		dprintf("_dev_mounted: can't open \"%s\" (%d)\n", cn, errno);
117 #endif
118 		goto dun;
119 	}
120 
121 #ifdef	DEBUG_STAT
122 	dprintf("_dev_mounted: fstat()ing \"%s\"\n", cn);
123 #endif
124 	if (fstat64(fd, &sb) < 0) {
125 #ifdef	DEBUG
126 		dprintf("_dev_mounted: stat of \"%s\" failed (%d)\n", cn,
127 		    errno);
128 #endif
129 		goto dun;
130 	}
131 
132 #ifdef	DEBUG_IOCTL
133 	dprintf("_dev_mounted: ioctl(%s, DKIOCINFO)\n", cn);
134 #endif
135 	if (ioctl(fd, DKIOCINFO, &info) != 0) {
136 #ifdef	DEBUG
137 		dprintf(
138 		    "_dev_mounted: ioctl(DKIOCINFO) on \"%s\" failed (%d)\n",
139 		    cn, errno);
140 #endif
141 		goto dun;
142 	}
143 
144 	if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) {
145 		ret_val = 1;			/* match found! */
146 	}
147 
148 dun:
149 	if (cn != NULL) {
150 		free(cn);
151 	}
152 	if (fp != NULL) {
153 		(void) fclose(fp);
154 	}
155 	if (fd >= 0) {
156 		(void) close(fd);
157 	}
158 #ifdef	DEBUG
159 	dexit("_dev_mounted: returning %s\n",
160 	    ret_val ? "TRUE" : "FALSE");
161 #endif
162 	return (ret_val);
163 }
164 
165 
166 /*
167  * This is an ON Consolidation Private interface.
168  *
169  * Forks off rmmount and (in essence) returns the result
170  *
171  * a return value of 0 (FALSE) means failure, non-zero (TRUE) means success
172  */
173 int
174 _dev_unmount(char *path)
175 {
176 	static int	call_unmount_prog(int, int, char *, int, char *,
177 			    char *);
178 	static int	get_media_info(char *, char **, int *, char **);
179 	char		*bn = NULL;		/* block name */
180 	char		*mtype = NULL;		/* media type */
181 	char		*spcl = NULL;		/* special dev. path */
182 	char		*spcl_failed = NULL;	/* spcl that failed */
183 	int		ret_val = FALSE;	/* what we return */
184 	char		*vr;			/* volmgt root dir */
185 	int		media_info_gotten = 0;
186 	int		mnum = 0;
187 	int		volume_is_not_managed;
188 	char		*pathbuf, *absname;
189 
190 #ifdef	DEBUG
191 	denter("_dev_unmount(%s): entering\n", path);
192 #endif
193 
194 	if ((bn = (char *)volmgt_getfullblkname(path)) == NULL) {
195 #ifdef	DEBUG
196 		dprintf("_dev_unmount: volmgt_getfullblkname failed\n");
197 #endif
198 		goto dun;
199 	}
200 
201 	if ((pathbuf = malloc(PATH_MAX+1)) == NULL)
202 		goto dun;
203 
204 	absname = bn;
205 	if (realpath(bn, pathbuf) != NULL)
206 		absname = pathbuf;
207 
208 	volume_is_not_managed = !volmgt_running() ||
209 		(!volmgt_ownspath(absname) && volmgt_symname(bn) == NULL);
210 
211 	free(pathbuf);
212 
213 	/* decide of we should use rmmount to unmount the media */
214 	if (!volume_is_not_managed) {
215 		int		use_rmm = FALSE;	/* use rmmount??  */
216 
217 		/* at least volmgt is running */
218 		vr = (char *)volmgt_root();
219 		if (strncmp(bn, vr, strlen(vr)) == 0) {
220 			/* the block path is rooted in /vol */
221 			use_rmm = TRUE;
222 		}
223 
224 		/* try to get info about media */
225 		media_info_gotten = get_media_info(bn, &mtype, &mnum, &spcl);
226 
227 		ret_val = call_unmount_prog(media_info_gotten, use_rmm, mtype,
228 		    mnum, spcl, bn);
229 
230 	} else {
231 
232 		/* volmgt is *not* running */
233 
234 		if (get_media_info(bn, &mtype, &mnum, &spcl)) {
235 
236 			/*
237 			 * volmgt is off and get_media_info() has returned
238 			 * info on the media -- soo (this is kinda' a hack)
239 			 * ... we iterate, looking for multiple slices
240 			 * of (say) a floppy being mounted
241 			 *
242 			 * note: if an unmount fails we don't want to try
243 			 * to unmount the same device on the next try, so
244 			 * we try to watch for that
245 			 */
246 
247 			do {
248 				/*
249 				 * don't call the unmount program is we're just
250 				 * trying to unmount the same device that
251 				 * failed last time -- if that's the case,
252 				 * then bail
253 				 */
254 				if (spcl_failed != NULL) {
255 					if (strcmp(spcl, spcl_failed) == 0) {
256 						break;
257 					}
258 				}
259 				ret_val = call_unmount_prog(TRUE, FALSE,
260 				    mtype, mnum, spcl, bn);
261 
262 				if (!ret_val) {
263 					/* save spcl device name that failed */
264 					spcl_failed = strdup(spcl);
265 				} else {
266 					/*
267 					 * unmount succeeded, so clean up
268 					 */
269 					if (spcl_failed != NULL) {
270 						free(spcl_failed);
271 						spcl_failed = NULL;
272 					}
273 				}
274 
275 			} while (get_media_info(bn, &mtype, &mnum, &spcl));
276 
277 		} else {
278 
279 			/* just do the unmmount cycle once */
280 			ret_val = call_unmount_prog(FALSE, FALSE, NULL, 0,
281 			    NULL, bn);
282 		}
283 
284 	}
285 
286 	if (mtype != NULL) {
287 		free(mtype);
288 	}
289 	if (spcl != NULL) {
290 		free(spcl);
291 	}
292 	if (spcl_failed != NULL) {
293 		free(spcl_failed);
294 	}
295 	if (bn != NULL) {
296 		free(bn);
297 	}
298 
299 dun:
300 
301 #ifdef	DEBUG
302 	dexit("_dev_unmount: returning %s\n", ret_val ? "TRUE" : "FALSE");
303 #endif
304 	return (ret_val);
305 }
306 
307 
308 /*
309  * find a mnttab entry that has the same dev as the supplied dev,
310  *  returning it and a non-zero value if found, else returning 0
311  *
312  * this is just like getmntany(), except that it scans based on st_rdev,
313  * and it even finds different slices on the same device/unit (thanx to
314  * code copied from format.c)
315  */
316 static int
317 vol_getmntdev(FILE *fp, struct mnttab *mp, dev_t dev, struct dk_cinfo *ip)
318 {
319 	int		fd;		/* dev-in-question fd */
320 	struct stat64	sb;		/* dev-in-question stat struct */
321 	int		ret_val = 0;	/* default value: no match found */
322 	char		*cn;		/* char pathname */
323 	struct dk_cinfo	dkinfo;		/* for testing for slices */
324 
325 
326 #ifdef	DEBUG
327 	denter(
328 	    "vol_getmntdev: entering for %d.%d, ctype/cnum/unit = %d/%d/%d\n",
329 	    (int)major(dev), (int)minor(dev), ip->dki_ctype, ip->dki_cnum,
330 	    ip->dki_unit);
331 #endif
332 
333 	/* reset the mnttab -- just in case */
334 	rewind(fp);
335 
336 	/* scan each entry in mnttab */
337 	while (getmntent(fp, mp) == 0) {
338 
339 		/* don't even try unless it's a local pathname */
340 		if (mp->mnt_special[0] != '/') {
341 			continue;
342 		}
343 
344 		/* get char pathname */
345 		if ((cn = volmgt_getfullrawname(mp->mnt_special)) == NULL) {
346 			continue;
347 		}
348 		if (cn[0] == NULLC) {
349 			free(cn);
350 			continue;	/* couldn't get raw name */
351 		}
352 
353 		/* open the device */
354 #ifdef	DEBUG_OPEN
355 		dprintf("vol_getmntdev: open()ing \"%s\"\n", cn);
356 #endif
357 		if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
358 			/* if we can't open it *assume* it's not a match */
359 #ifdef	DEBUG
360 			dprintf(
361 			    "vol_getmntdev: open of \"%s\" (%s) failed (%d)\n",
362 			    cn, mp->mnt_fstype, errno);
363 #endif
364 			free(cn);
365 			continue;
366 		}
367 
368 		/* stat the device */
369 #ifdef	DEBUG_STAT
370 		dprintf("vol_getmntdev: fstat()ing \"%s\"\n", cn);
371 #endif
372 		if (fstat64(fd, &sb) < 0) {
373 #ifdef	DEBUG
374 			dprintf(
375 			    "vol_getmntdev: stat of \"%s\" (%s) failed (%d)\n",
376 			    cn, mp->mnt_fstype, errno);
377 #endif
378 			free(cn);
379 			(void) close(fd);
380 			continue;	/* ain't there: can't be a match */
381 		}
382 
383 		/* ensure we have a spcl device (a double check) */
384 		if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode)) {
385 #ifdef	DEBUG
386 			dprintf(
387 		"vol_getmntdev: \"%s\" not a blk- or chr-spcl device\n",
388 			    cn);
389 #endif
390 			free(cn);
391 			(void) close(fd);
392 			continue;
393 		}
394 
395 		/* (almost) finally -- check the dev_t for equality */
396 		if (sb.st_rdev == dev) {
397 			ret_val = 1;		/* match found! */
398 			free(cn);
399 			(void) close(fd);
400 			break;
401 		}
402 
403 		/*
404 		 * check that the major numbers match, since if they
405 		 * don't then there's no reason to use the DKIOCINFO
406 		 * ioctl to see if we have to major/minor pairs that
407 		 * really point to the same device
408 		 */
409 		if (major(sb.st_rdev) != major(dev)) {
410 			/* no use continuing, since major devs are different */
411 			free(cn);
412 			(void) close(fd);
413 			continue;
414 		}
415 
416 #ifdef	DEBUG_IOCTL
417 		dprintf("vol_getmntdev: ioctl(%s, DKIOCINFO)\n", cn);
418 #endif
419 		/* one last check -- for diff. slices of the same dev/unit */
420 		if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) {
421 #ifdef	DEBUG
422 			dprintf(
423 		"vol_getmntdev: ioctl(DKIOCINFO) of \"%s\" failed (%d)\n",
424 			    cn, errno);
425 #endif
426 			free(cn);
427 			(void) close(fd);
428 			continue;
429 		}
430 
431 		free(cn);		/* all done with raw pathname */
432 		(void) close(fd);	/* all done with file descriptor */
433 
434 		/* if ctrler type/number and unit match, it's a match */
435 		if ((ip->dki_ctype == dkinfo.dki_ctype) &&
436 		    (ip->dki_cnum == dkinfo.dki_cnum) &&
437 		    (ip->dki_unit == dkinfo.dki_unit)) {
438 			/*
439 			 * even though minor numbers differ we have a
440 			 * match
441 			 */
442 			ret_val = 1;
443 			break;
444 		}
445 
446 		/* go around again */
447 	}
448 
449 #ifdef	DEBUG
450 	dexit("vol_getmntdev: returning %d (%s)\n", ret_val,
451 	    ret_val == 1 ? "SUCCESS" : "FAILURE");
452 #endif
453 	return (ret_val);
454 }
455 
456 
457 char *
458 vol_basename(char *path)
459 {
460 	char	*cp;
461 
462 
463 	/* check for the degenerate case */
464 	if (strcmp(path, "/") == 0) {
465 		return (path);
466 	}
467 
468 	/* look for the last slash in the name */
469 	if ((cp = strrchr(path, '/')) == NULL) {
470 		/* no slash */
471 		return (path);
472 	}
473 
474 	/* ensure something is after the slash */
475 	if (*++cp != NULLC) {
476 		return (cp);
477 	}
478 
479 	/* a name that ends in slash -- back up until previous slash */
480 	while (cp != path) {
481 		if (*--cp == '/') {
482 			return (--cp);
483 		}
484 	}
485 
486 	/* the only slash is the end of the name */
487 	return (path);
488 }
489 
490 
491 static int
492 get_media_info(char *path, char **mtypep, int *mnump, char **spclp)
493 {
494 	static int	vol_getmntdev(FILE *, struct mnttab *, dev_t,
495 			    struct dk_cinfo *);
496 	FILE		*fp = NULL;
497 	int		fd = -1;
498 	char		*cn = NULL;		/* char spcl pathname */
499 	struct stat64	sb;
500 	struct dk_cinfo	info;
501 	struct mnttab	mnt;
502 	int		ret_val = FALSE;
503 
504 
505 
506 #ifdef	DEBUG
507 	denter("get_media_info(%s): entering\n", path);
508 #endif
509 
510 #ifdef	DEBUG_OPEN
511 	dprintf("get_media_info: fopen()ing \"%s\"\n", MNTTAB);
512 #endif
513 	if ((fp = fopen(MNTTAB, "rF")) == NULL) {
514 		/* mtab is gone... let him go */
515 #ifdef	DEBUG
516 		dprintf("get_media_info: can't open \"%s\" (%d)\n", MNTTAB,
517 		    errno);
518 #endif
519 		goto dun;
520 	}
521 
522 	/* get char spcl pathname */
523 	if ((cn = volmgt_getfullrawname(path)) == NULL) {
524 		goto dun;
525 	}
526 	if (cn[0] == NULLC) {
527 		goto dun;
528 	}
529 
530 #ifdef	DEBUG_OPEN
531 	dprintf("get_media_info: open()ing \"%s\"\n", cn);
532 #endif
533 	if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
534 #ifdef	DEBUG
535 		dprintf("get_media_info(): can't open \"%s\" (%d)\n", cn,
536 		    errno);
537 #endif
538 		goto dun;
539 	}
540 
541 #ifdef	DEBUG_STAT
542 	dprintf("get_media_info: fstat()ing \"%s\"\n", cn);
543 #endif
544 	if (fstat64(fd, &sb) < 0) {
545 #ifdef	DEBUG
546 		dprintf("get_media_info: can't stat \"%s\" (%d)\n", cn, errno);
547 #endif
548 		goto dun;
549 	}
550 
551 #ifdef	DEBUG_IOCTL
552 	dprintf("get_media_info: ioctl(%s, DKIOCINFO)\n", cn);
553 #endif
554 	if (ioctl(fd, DKIOCINFO, &info) != 0) {
555 #ifdef	DEBUG
556 		dprintf(
557 		    "get_media_info: ioctl(DKIOCINFO) on \"%s\" failed (%d)\n",
558 		    cn, errno);
559 #endif
560 		goto dun;
561 	}
562 
563 	/* if we found the entry then disect it */
564 	if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) {
565 		char		*cp;
566 		char		*mtype;
567 		char		*mnt_dir;
568 		int		mtype_len;
569 		DIR		*dirp = NULL;
570 		struct dirent64	*dp;
571 		char		*volname;
572 
573 
574 		/* return the spcl device name found */
575 		*spclp = strdup(mnt.mnt_special);
576 
577 		/*
578 		 * try to get the media type (e.g. "floppy") from the mount
579 		 * point (e.g. "/floppy/NAME") if vold is running
580 		 */
581 
582 		if (!volmgt_running() ||
583 		    (!volmgt_ownspath(*spclp) &&
584 			volmgt_symname(*spclp) == NULL)) {
585 			ret_val = TRUE;		/* success (if limited) */
586 			goto dun;
587 		}
588 
589 		/* get the first part of the mount point (e.g. "floppy") */
590 		cp = mnt.mnt_mountp;
591 		if (*cp++ != '/') {
592 #ifdef	DEBUG
593 			dprintf(
594 	"get_media_info warning: no leading '/' in mount point \"%s\"\n",
595 			    mnt.mnt_mountp);
596 #endif
597 			goto dun;
598 		}
599 		mtype = cp;
600 		if ((cp = strchr(mtype, '/')) == NULL) {
601 #ifdef	DEBUG
602 			dprintf(
603 		"get_media_info warning: no 2nd '/' in mount point \"%s\"\n",
604 			    mnt.mnt_mountp);
605 #endif
606 			goto dun;
607 		}
608 		*cp++ = NULLC;
609 		mnt_dir = mnt.mnt_mountp;	/* save dir path */
610 
611 		/* get the volume name (e.g. "unnamed_floppy") */
612 		volname = cp;
613 
614 		/* scan for the symlink that points to our volname */
615 		if ((dirp = opendir(mnt_dir)) == NULL) {
616 #ifdef	DEBUG
617 			dprintf(
618 		"get_media_info warning: can't open directory \"%s\"\n",
619 			    mnt_dir);
620 #endif
621 			goto dun;
622 		}
623 		mtype_len = strlen(mtype);
624 		while ((dp = readdir64(dirp)) != NULL) {
625 			char		lpath[2 * (MAXNAMELEN+1)];
626 			char		linkbuf[MAXPATHLEN+4];
627 			int		lb_len;
628 			struct stat64	sb;
629 
630 
631 			if (strncmp(dp->d_name, mtype, mtype_len) != 0) {
632 				continue;	/* not even close */
633 			}
634 
635 			(void) sprintf(lpath, "%s/%s", mnt_dir,
636 			    dp->d_name);
637 #ifdef	DEBUG_STAT
638 			dprintf("get_media_info: lstat()ing \"%s\"\n", lpath);
639 #endif
640 			if (lstat64(lpath, &sb) < 0) {
641 				continue;	/* what? */
642 			}
643 			if (!S_ISLNK(sb.st_mode)) {
644 				continue;	/* not our baby */
645 			}
646 			if ((lb_len = readlink(lpath, linkbuf,
647 			    sizeof (linkbuf))) < 0) {
648 				continue;
649 			}
650 			linkbuf[lb_len] = NULLC; /* null terminate */
651 			if ((cp = vol_basename(linkbuf)) == NULL) {
652 				continue;
653 			}
654 			/* now we have the name! */
655 			if (strcmp(cp, volname) == 0) {
656 				/* found it !! */
657 				if (sscanf(dp->d_name + mtype_len, "%d",
658 				    mnump) == 1) {
659 					*mtypep = strdup(mtype);
660 					ret_val = TRUE;
661 				}
662 				break;
663 			}
664 		}
665 		(void) closedir(dirp);
666 	}
667 
668 dun:
669 	if (fp != NULL) {
670 		(void) fclose(fp);
671 	}
672 	if (fd >= 0) {
673 		(void) close(fd);
674 	}
675 	if (cn != NULL) {
676 		free(cn);
677 	}
678 #ifdef	DEBUG
679 	if (ret_val) {
680 		dexit("get_media_info: returning mtype=%s, mnum=%d, spcl=%s\n",
681 		    *mtypep == NULL ? "<null ptr>" : *mtypep,
682 		    *mnump,
683 		    *spclp == NULL ? "<null ptr>" : *spclp);
684 	} else {
685 		dexit("get_media_info: FAILED\n");
686 	}
687 #endif
688 	return (ret_val);
689 }
690 
691 
692 /*
693  * call the appropriate unmount program, returning its success (TRUE)
694  * or failure (FALSE)
695  */
696 static int
697 call_unmount_prog(int mi_gotten, int use_rmm, char *mtype, int mnum,
698     char *spcl, char *bn)
699 {
700 	pid_t		pid;			/* forked proc's pid */
701 	int		ret_val = FALSE;
702 	const char	*etc_umount = "/etc/umount";
703 	const char	*rmm = "/usr/sbin/rmmount";
704 	int		rval;			/* proc's return value */
705 
706 
707 #ifdef	DEBUG
708 	denter(
709 	"call_unmount_prog(%s, %s, \"%s\", %d, \"%s\", \"%s\"): entering\n",
710 	    mi_gotten ? "TRUE" : "FALSE", use_rmm ? "TRUE" : "FALSE",
711 	    mtype ? mtype : "<null ptr>", mnum, spcl ? spcl : "<null ptr>",
712 	    bn);
713 #endif
714 	/* create a child to unmount the path */
715 	if ((pid = fork()) < 0) {
716 #ifdef	DEBUG
717 		dprintf("error in call_unmount_prog: fork failed (errno %d)\n",
718 		    errno);
719 #endif
720 		goto dun;
721 	}
722 
723 	if (pid == 0) {
724 		/* the child */
725 #ifndef	DEBUG
726 		int		xfd;
727 #endif
728 		char		env_buf[MAXPATHLEN];
729 
730 #ifndef	DEBUG
731 		/* get rid of those nasty err messages */
732 		if ((xfd = open(NULL_PATH, O_RDWR)) >= 0) {
733 			(void) dup2(xfd, fileno(stdin));
734 			(void) dup2(xfd, fileno(stdout));
735 			(void) dup2(xfd, fileno(stderr));
736 		}
737 #endif
738 
739 		if (use_rmm) {
740 			/* set up environment vars */
741 			(void) putenv("VOLUME_ACTION=eject");
742 			(void) putenv(strdup(env_buf));
743 			if (mi_gotten) {
744 				(void) sprintf(env_buf,
745 				    "VOLUME_MEDIATYPE=%s", mtype);
746 				(void) putenv(strdup(env_buf));
747 				(void) sprintf(env_buf, "VOLUME_SYMDEV=%s%d",
748 				    mtype, mnum);
749 				(void) putenv(strdup(env_buf));
750 				(void) sprintf(env_buf, "VOLUME_PATH=%s",
751 				    spcl);
752 				(void) putenv(strdup(env_buf));
753 				(void) sprintf(env_buf, "VOLUME_NAME=%s",
754 				    vol_basename(spcl));
755 				(void) putenv(strdup(env_buf));
756 			} else {
757 				(void) sprintf(env_buf, "VOLUME_PATH=%s", bn);
758 				(void) putenv(strdup(env_buf));
759 				(void) sprintf(env_buf, "VOLUME_NAME=%s",
760 				    vol_basename(bn));
761 				(void) putenv(strdup(env_buf));
762 			}
763 #ifdef	DEBUG
764 			dprintf("call_unmount_prog: calling \"%s -D\"\n", rmm);
765 			(void) execl(rmm, rmm, "-D", NULL);
766 #else
767 			(void) execl(rmm, rmm, NULL);
768 #endif
769 		} else {
770 #ifdef	DEBUG
771 			dprintf("call_unmount_prog: calling \"%s %s\"\n",
772 			    etc_umount, mi_gotten ? spcl : bn);
773 #endif
774 			(void) execl(etc_umount, etc_umount,
775 			    mi_gotten ? spcl : bn,
776 			    NULL);
777 		}
778 #ifdef	DEBUG
779 		dprintf("call_unmount_prog: exec failed (errno %d)\n", errno);
780 #endif
781 		exit(-1);
782 		/*NOTREACHED*/
783 	}
784 
785 	/* wait for the umount command to exit */
786 	if (waitpid(pid, &rval, 0) == pid) {
787 		if (WIFEXITED(rval)) {
788 			if (WEXITSTATUS(rval) == 0) {
789 				ret_val = TRUE;	/* success */
790 			}
791 		}
792 	}
793 
794 dun:
795 #ifdef	DEBUG
796 	dexit("call_unmount_prog: returning %s\n", ret_val ? "TRUE" : "FALSE");
797 #endif
798 	return (ret_val);
799 }
800