--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvolmgt/common/volmgt_on_private.c Tue Jun 14 00:00:00 2005 -0700
@@ -0,0 +1,800 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1996 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * routines in this module are meant to be called by other libvolmgt
+ * routines only
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string.h>
+#ifdef DEBUG
+#include <errno.h>
+#endif
+#include <libintl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <volmgt.h>
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <sys/dkio.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/mnttab.h>
+#include <sys/vol.h>
+#include "volmgt_private.h"
+
+
+#define NULL_PATH "/dev/null"
+
+
+
+/*
+ * This is an ON Consolidation Private interface.
+ *
+ * Is the specified path mounted?
+ *
+ * This function is really inadequate for ejection testing. For example,
+ * I could have /dev/fd0a mounted and eject /dev/fd0c, and it would be
+ * ejected. There needs to be some better way to make this check, although
+ * short of looking up the mounted dev_t in the kernel mount table and
+ * building in all kinds of knowledge into this function, I'm not sure
+ * how to do it.
+ */
+int
+_dev_mounted(char *path)
+{
+ static int vol_getmntdev(FILE *, struct mnttab *, dev_t,
+ struct dk_cinfo *);
+ int fd = -1;
+ struct dk_cinfo info;
+ static FILE *fp = NULL; /* mnttab file pointer */
+ struct mnttab mnt; /* set bug not used */
+ char *cn = NULL; /* char spcl pathname */
+ struct stat64 sb;
+ int ret_val = 0;
+
+
+
+#ifdef DEBUG
+ denter("_dev_mounted(%s): entering\n", path);
+#endif
+
+ /* ensure we have the block spcl pathname */
+ if ((cn = (char *)volmgt_getfullrawname(path)) == NULL) {
+#ifdef DEBUG
+ dprintf("_dev_mounted: volmgt_getfullrawname failed\n");
+#endif
+ goto dun;
+ }
+
+#ifdef DEBUG_OPEN
+ dprintf("_dev_mounted: fopen()ing \"%s\"\n", MNTTAB);
+#endif
+ if ((fp = fopen(MNTTAB, "r")) == NULL) {
+ /* mtab is gone... let him go */
+#ifdef DEBUG
+ perror(MNTTAB);
+#endif
+ goto dun;
+ }
+
+#ifdef DEBUG_OPEN
+ dprintf("_dev_mounted: open()ing \"%s\"\n", cn);
+#endif
+ if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
+#ifdef DEBUG
+ dprintf("_dev_mounted: can't open \"%s\" (%d)\n", cn, errno);
+#endif
+ goto dun;
+ }
+
+#ifdef DEBUG_STAT
+ dprintf("_dev_mounted: fstat()ing \"%s\"\n", cn);
+#endif
+ if (fstat64(fd, &sb) < 0) {
+#ifdef DEBUG
+ dprintf("_dev_mounted: stat of \"%s\" failed (%d)\n", cn,
+ errno);
+#endif
+ goto dun;
+ }
+
+#ifdef DEBUG_IOCTL
+ dprintf("_dev_mounted: ioctl(%s, DKIOCINFO)\n", cn);
+#endif
+ if (ioctl(fd, DKIOCINFO, &info) != 0) {
+#ifdef DEBUG
+ dprintf(
+ "_dev_mounted: ioctl(DKIOCINFO) on \"%s\" failed (%d)\n",
+ cn, errno);
+#endif
+ goto dun;
+ }
+
+ if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) {
+ ret_val = 1; /* match found! */
+ }
+
+dun:
+ if (cn != NULL) {
+ free(cn);
+ }
+ if (fp != NULL) {
+ (void) fclose(fp);
+ }
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+#ifdef DEBUG
+ dexit("_dev_mounted: returning %s\n",
+ ret_val ? "TRUE" : "FALSE");
+#endif
+ return (ret_val);
+}
+
+
+/*
+ * This is an ON Consolidation Private interface.
+ *
+ * Forks off rmmount and (in essence) returns the result
+ *
+ * a return value of 0 (FALSE) means failure, non-zero (TRUE) means success
+ */
+int
+_dev_unmount(char *path)
+{
+ static int call_unmount_prog(int, int, char *, int, char *,
+ char *);
+ static int get_media_info(char *, char **, int *, char **);
+ char *bn = NULL; /* block name */
+ char *mtype = NULL; /* media type */
+ char *spcl = NULL; /* special dev. path */
+ char *spcl_failed = NULL; /* spcl that failed */
+ int ret_val = FALSE; /* what we return */
+ char *vr; /* volmgt root dir */
+ int media_info_gotten = 0;
+ int mnum = 0;
+ int volume_is_not_managed;
+ char *pathbuf, *absname;
+
+#ifdef DEBUG
+ denter("_dev_unmount(%s): entering\n", path);
+#endif
+
+ if ((bn = (char *)volmgt_getfullblkname(path)) == NULL) {
+#ifdef DEBUG
+ dprintf("_dev_unmount: volmgt_getfullblkname failed\n");
+#endif
+ goto dun;
+ }
+
+ if ((pathbuf = malloc(PATH_MAX+1)) == NULL)
+ goto dun;
+
+ absname = bn;
+ if (realpath(bn, pathbuf) != NULL)
+ absname = pathbuf;
+
+ volume_is_not_managed = !volmgt_running() ||
+ (!volmgt_ownspath(absname) && volmgt_symname(bn) == NULL);
+
+ free(pathbuf);
+
+ /* decide of we should use rmmount to unmount the media */
+ if (!volume_is_not_managed) {
+ int use_rmm = FALSE; /* use rmmount?? */
+
+ /* at least volmgt is running */
+ vr = (char *)volmgt_root();
+ if (strncmp(bn, vr, strlen(vr)) == 0) {
+ /* the block path is rooted in /vol */
+ use_rmm = TRUE;
+ }
+
+ /* try to get info about media */
+ media_info_gotten = get_media_info(bn, &mtype, &mnum, &spcl);
+
+ ret_val = call_unmount_prog(media_info_gotten, use_rmm, mtype,
+ mnum, spcl, bn);
+
+ } else {
+
+ /* volmgt is *not* running */
+
+ if (get_media_info(bn, &mtype, &mnum, &spcl)) {
+
+ /*
+ * volmgt is off and get_media_info() has returned
+ * info on the media -- soo (this is kinda' a hack)
+ * ... we iterate, looking for multiple slices
+ * of (say) a floppy being mounted
+ *
+ * note: if an unmount fails we don't want to try
+ * to unmount the same device on the next try, so
+ * we try to watch for that
+ */
+
+ do {
+ /*
+ * don't call the unmount program is we're just
+ * trying to unmount the same device that
+ * failed last time -- if that's the case,
+ * then bail
+ */
+ if (spcl_failed != NULL) {
+ if (strcmp(spcl, spcl_failed) == 0) {
+ break;
+ }
+ }
+ ret_val = call_unmount_prog(TRUE, FALSE,
+ mtype, mnum, spcl, bn);
+
+ if (!ret_val) {
+ /* save spcl device name that failed */
+ spcl_failed = strdup(spcl);
+ } else {
+ /*
+ * unmount succeeded, so clean up
+ */
+ if (spcl_failed != NULL) {
+ free(spcl_failed);
+ spcl_failed = NULL;
+ }
+ }
+
+ } while (get_media_info(bn, &mtype, &mnum, &spcl));
+
+ } else {
+
+ /* just do the unmmount cycle once */
+ ret_val = call_unmount_prog(FALSE, FALSE, NULL, 0,
+ NULL, bn);
+ }
+
+ }
+
+ if (mtype != NULL) {
+ free(mtype);
+ }
+ if (spcl != NULL) {
+ free(spcl);
+ }
+ if (spcl_failed != NULL) {
+ free(spcl_failed);
+ }
+ if (bn != NULL) {
+ free(bn);
+ }
+
+dun:
+
+#ifdef DEBUG
+ dexit("_dev_unmount: returning %s\n", ret_val ? "TRUE" : "FALSE");
+#endif
+ return (ret_val);
+}
+
+
+/*
+ * find a mnttab entry that has the same dev as the supplied dev,
+ * returning it and a non-zero value if found, else returning 0
+ *
+ * this is just like getmntany(), except that it scans based on st_rdev,
+ * and it even finds different slices on the same device/unit (thanx to
+ * code copied from format.c)
+ */
+static int
+vol_getmntdev(FILE *fp, struct mnttab *mp, dev_t dev, struct dk_cinfo *ip)
+{
+ int fd; /* dev-in-question fd */
+ struct stat64 sb; /* dev-in-question stat struct */
+ int ret_val = 0; /* default value: no match found */
+ char *cn; /* char pathname */
+ struct dk_cinfo dkinfo; /* for testing for slices */
+
+
+#ifdef DEBUG
+ denter(
+ "vol_getmntdev: entering for %d.%d, ctype/cnum/unit = %d/%d/%d\n",
+ (int)major(dev), (int)minor(dev), ip->dki_ctype, ip->dki_cnum,
+ ip->dki_unit);
+#endif
+
+ /* reset the mnttab -- just in case */
+ rewind(fp);
+
+ /* scan each entry in mnttab */
+ while (getmntent(fp, mp) == 0) {
+
+ /* don't even try unless it's a local pathname */
+ if (mp->mnt_special[0] != '/') {
+ continue;
+ }
+
+ /* get char pathname */
+ if ((cn = volmgt_getfullrawname(mp->mnt_special)) == NULL) {
+ continue;
+ }
+ if (cn[0] == NULLC) {
+ free(cn);
+ continue; /* couldn't get raw name */
+ }
+
+ /* open the device */
+#ifdef DEBUG_OPEN
+ dprintf("vol_getmntdev: open()ing \"%s\"\n", cn);
+#endif
+ if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
+ /* if we can't open it *assume* it's not a match */
+#ifdef DEBUG
+ dprintf(
+ "vol_getmntdev: open of \"%s\" (%s) failed (%d)\n",
+ cn, mp->mnt_fstype, errno);
+#endif
+ free(cn);
+ continue;
+ }
+
+ /* stat the device */
+#ifdef DEBUG_STAT
+ dprintf("vol_getmntdev: fstat()ing \"%s\"\n", cn);
+#endif
+ if (fstat64(fd, &sb) < 0) {
+#ifdef DEBUG
+ dprintf(
+ "vol_getmntdev: stat of \"%s\" (%s) failed (%d)\n",
+ cn, mp->mnt_fstype, errno);
+#endif
+ free(cn);
+ (void) close(fd);
+ continue; /* ain't there: can't be a match */
+ }
+
+ /* ensure we have a spcl device (a double check) */
+ if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode)) {
+#ifdef DEBUG
+ dprintf(
+ "vol_getmntdev: \"%s\" not a blk- or chr-spcl device\n",
+ cn);
+#endif
+ free(cn);
+ (void) close(fd);
+ continue;
+ }
+
+ /* (almost) finally -- check the dev_t for equality */
+ if (sb.st_rdev == dev) {
+ ret_val = 1; /* match found! */
+ free(cn);
+ (void) close(fd);
+ break;
+ }
+
+ /*
+ * check that the major numbers match, since if they
+ * don't then there's no reason to use the DKIOCINFO
+ * ioctl to see if we have to major/minor pairs that
+ * really point to the same device
+ */
+ if (major(sb.st_rdev) != major(dev)) {
+ /* no use continuing, since major devs are different */
+ free(cn);
+ (void) close(fd);
+ continue;
+ }
+
+#ifdef DEBUG_IOCTL
+ dprintf("vol_getmntdev: ioctl(%s, DKIOCINFO)\n", cn);
+#endif
+ /* one last check -- for diff. slices of the same dev/unit */
+ if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) {
+#ifdef DEBUG
+ dprintf(
+ "vol_getmntdev: ioctl(DKIOCINFO) of \"%s\" failed (%d)\n",
+ cn, errno);
+#endif
+ free(cn);
+ (void) close(fd);
+ continue;
+ }
+
+ free(cn); /* all done with raw pathname */
+ (void) close(fd); /* all done with file descriptor */
+
+ /* if ctrler type/number and unit match, it's a match */
+ if ((ip->dki_ctype == dkinfo.dki_ctype) &&
+ (ip->dki_cnum == dkinfo.dki_cnum) &&
+ (ip->dki_unit == dkinfo.dki_unit)) {
+ /*
+ * even though minor numbers differ we have a
+ * match
+ */
+ ret_val = 1;
+ break;
+ }
+
+ /* go around again */
+ }
+
+#ifdef DEBUG
+ dexit("vol_getmntdev: returning %d (%s)\n", ret_val,
+ ret_val == 1 ? "SUCCESS" : "FAILURE");
+#endif
+ return (ret_val);
+}
+
+
+char *
+vol_basename(char *path)
+{
+ char *cp;
+
+
+ /* check for the degenerate case */
+ if (strcmp(path, "/") == 0) {
+ return (path);
+ }
+
+ /* look for the last slash in the name */
+ if ((cp = strrchr(path, '/')) == NULL) {
+ /* no slash */
+ return (path);
+ }
+
+ /* ensure something is after the slash */
+ if (*++cp != NULLC) {
+ return (cp);
+ }
+
+ /* a name that ends in slash -- back up until previous slash */
+ while (cp != path) {
+ if (*--cp == '/') {
+ return (--cp);
+ }
+ }
+
+ /* the only slash is the end of the name */
+ return (path);
+}
+
+
+static int
+get_media_info(char *path, char **mtypep, int *mnump, char **spclp)
+{
+ static int vol_getmntdev(FILE *, struct mnttab *, dev_t,
+ struct dk_cinfo *);
+ FILE *fp = NULL;
+ int fd = -1;
+ char *cn = NULL; /* char spcl pathname */
+ struct stat64 sb;
+ struct dk_cinfo info;
+ struct mnttab mnt;
+ int ret_val = FALSE;
+
+
+
+#ifdef DEBUG
+ denter("get_media_info(%s): entering\n", path);
+#endif
+
+#ifdef DEBUG_OPEN
+ dprintf("get_media_info: fopen()ing \"%s\"\n", MNTTAB);
+#endif
+ if ((fp = fopen(MNTTAB, "r")) == NULL) {
+ /* mtab is gone... let him go */
+#ifdef DEBUG
+ dprintf("get_media_info: can't open \"%s\" (%d)\n", MNTTAB,
+ errno);
+#endif
+ goto dun;
+ }
+
+ /* get char spcl pathname */
+ if ((cn = volmgt_getfullrawname(path)) == NULL) {
+ goto dun;
+ }
+ if (cn[0] == NULLC) {
+ goto dun;
+ }
+
+#ifdef DEBUG_OPEN
+ dprintf("get_media_info: open()ing \"%s\"\n", cn);
+#endif
+ if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
+#ifdef DEBUG
+ dprintf("get_media_info(): can't open \"%s\" (%d)\n", cn,
+ errno);
+#endif
+ goto dun;
+ }
+
+#ifdef DEBUG_STAT
+ dprintf("get_media_info: fstat()ing \"%s\"\n", cn);
+#endif
+ if (fstat64(fd, &sb) < 0) {
+#ifdef DEBUG
+ dprintf("get_media_info: can't stat \"%s\" (%d)\n", cn, errno);
+#endif
+ goto dun;
+ }
+
+#ifdef DEBUG_IOCTL
+ dprintf("get_media_info: ioctl(%s, DKIOCINFO)\n", cn);
+#endif
+ if (ioctl(fd, DKIOCINFO, &info) != 0) {
+#ifdef DEBUG
+ dprintf(
+ "get_media_info: ioctl(DKIOCINFO) on \"%s\" failed (%d)\n",
+ cn, errno);
+#endif
+ goto dun;
+ }
+
+ /* if we found the entry then disect it */
+ if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) {
+ char *cp;
+ char *mtype;
+ char *mnt_dir;
+ int mtype_len;
+ DIR *dirp = NULL;
+ struct dirent64 *dp;
+ char *volname;
+
+
+ /* return the spcl device name found */
+ *spclp = strdup(mnt.mnt_special);
+
+ /*
+ * try to get the media type (e.g. "floppy") from the mount
+ * point (e.g. "/floppy/NAME") if vold is running
+ */
+
+ if (!volmgt_running() ||
+ (!volmgt_ownspath(*spclp) &&
+ volmgt_symname(*spclp) == NULL)) {
+ ret_val = TRUE; /* success (if limited) */
+ goto dun;
+ }
+
+ /* get the first part of the mount point (e.g. "floppy") */
+ cp = mnt.mnt_mountp;
+ if (*cp++ != '/') {
+#ifdef DEBUG
+ dprintf(
+ "get_media_info warning: no leading '/' in mount point \"%s\"\n",
+ mnt.mnt_mountp);
+#endif
+ goto dun;
+ }
+ mtype = cp;
+ if ((cp = strchr(mtype, '/')) == NULL) {
+#ifdef DEBUG
+ dprintf(
+ "get_media_info warning: no 2nd '/' in mount point \"%s\"\n",
+ mnt.mnt_mountp);
+#endif
+ goto dun;
+ }
+ *cp++ = NULLC;
+ mnt_dir = mnt.mnt_mountp; /* save dir path */
+
+ /* get the volume name (e.g. "unnamed_floppy") */
+ volname = cp;
+
+ /* scan for the symlink that points to our volname */
+ if ((dirp = opendir(mnt_dir)) == NULL) {
+#ifdef DEBUG
+ dprintf(
+ "get_media_info warning: can't open directory \"%s\"\n",
+ mnt_dir);
+#endif
+ goto dun;
+ }
+ mtype_len = strlen(mtype);
+ while ((dp = readdir64(dirp)) != NULL) {
+ char lpath[2 * (MAXNAMELEN+1)];
+ char linkbuf[MAXPATHLEN+4];
+ int lb_len;
+ struct stat64 sb;
+
+
+ if (strncmp(dp->d_name, mtype, mtype_len) != 0) {
+ continue; /* not even close */
+ }
+
+ (void) sprintf(lpath, "%s/%s", mnt_dir,
+ dp->d_name);
+#ifdef DEBUG_STAT
+ dprintf("get_media_info: lstat()ing \"%s\"\n", lpath);
+#endif
+ if (lstat64(lpath, &sb) < 0) {
+ continue; /* what? */
+ }
+ if (!S_ISLNK(sb.st_mode)) {
+ continue; /* not our baby */
+ }
+ if ((lb_len = readlink(lpath, linkbuf,
+ sizeof (linkbuf))) < 0) {
+ continue;
+ }
+ linkbuf[lb_len] = NULLC; /* null terminate */
+ if ((cp = vol_basename(linkbuf)) == NULL) {
+ continue;
+ }
+ /* now we have the name! */
+ if (strcmp(cp, volname) == 0) {
+ /* found it !! */
+ if (sscanf(dp->d_name + mtype_len, "%d",
+ mnump) == 1) {
+ *mtypep = strdup(mtype);
+ ret_val = TRUE;
+ }
+ break;
+ }
+ }
+ (void) closedir(dirp);
+ }
+
+dun:
+ if (fp != NULL) {
+ (void) fclose(fp);
+ }
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+ if (cn != NULL) {
+ free(cn);
+ }
+#ifdef DEBUG
+ if (ret_val) {
+ dexit("get_media_info: returning mtype=%s, mnum=%d, spcl=%s\n",
+ *mtypep == NULL ? "<null ptr>" : *mtypep,
+ *mnump,
+ *spclp == NULL ? "<null ptr>" : *spclp);
+ } else {
+ dexit("get_media_info: FAILED\n");
+ }
+#endif
+ return (ret_val);
+}
+
+
+/*
+ * call the appropriate unmount program, returning its success (TRUE)
+ * or failure (FALSE)
+ */
+static int
+call_unmount_prog(int mi_gotten, int use_rmm, char *mtype, int mnum,
+ char *spcl, char *bn)
+{
+ pid_t pid; /* forked proc's pid */
+ int ret_val = FALSE;
+ const char *etc_umount = "/etc/umount";
+ const char *rmm = "/usr/sbin/rmmount";
+ int rval; /* proc's return value */
+
+
+#ifdef DEBUG
+ denter(
+ "call_unmount_prog(%s, %s, \"%s\", %d, \"%s\", \"%s\"): entering\n",
+ mi_gotten ? "TRUE" : "FALSE", use_rmm ? "TRUE" : "FALSE",
+ mtype ? mtype : "<null ptr>", mnum, spcl ? spcl : "<null ptr>",
+ bn);
+#endif
+ /* create a child to unmount the path */
+ if ((pid = fork()) < 0) {
+#ifdef DEBUG
+ dprintf("error in call_unmount_prog: fork failed (errno %d)\n",
+ errno);
+#endif
+ goto dun;
+ }
+
+ if (pid == 0) {
+ /* the child */
+#ifndef DEBUG
+ int xfd;
+#endif
+ char env_buf[MAXPATHLEN];
+
+#ifndef DEBUG
+ /* get rid of those nasty err messages */
+ if ((xfd = open(NULL_PATH, O_RDWR)) >= 0) {
+ (void) dup2(xfd, fileno(stdin));
+ (void) dup2(xfd, fileno(stdout));
+ (void) dup2(xfd, fileno(stderr));
+ }
+#endif
+
+ if (use_rmm) {
+ /* set up environment vars */
+ (void) putenv("VOLUME_ACTION=eject");
+ (void) putenv(strdup(env_buf));
+ if (mi_gotten) {
+ (void) sprintf(env_buf,
+ "VOLUME_MEDIATYPE=%s", mtype);
+ (void) putenv(strdup(env_buf));
+ (void) sprintf(env_buf, "VOLUME_SYMDEV=%s%d",
+ mtype, mnum);
+ (void) putenv(strdup(env_buf));
+ (void) sprintf(env_buf, "VOLUME_PATH=%s",
+ spcl);
+ (void) putenv(strdup(env_buf));
+ (void) sprintf(env_buf, "VOLUME_NAME=%s",
+ vol_basename(spcl));
+ (void) putenv(strdup(env_buf));
+ } else {
+ (void) sprintf(env_buf, "VOLUME_PATH=%s", bn);
+ (void) putenv(strdup(env_buf));
+ (void) sprintf(env_buf, "VOLUME_NAME=%s",
+ vol_basename(bn));
+ (void) putenv(strdup(env_buf));
+ }
+#ifdef DEBUG
+ dprintf("call_unmount_prog: calling \"%s -D\"\n", rmm);
+ (void) execl(rmm, rmm, "-D", NULL);
+#else
+ (void) execl(rmm, rmm, NULL);
+#endif
+ } else {
+#ifdef DEBUG
+ dprintf("call_unmount_prog: calling \"%s %s\"\n",
+ etc_umount, mi_gotten ? spcl : bn);
+#endif
+ (void) execl(etc_umount, etc_umount,
+ mi_gotten ? spcl : bn,
+ NULL);
+ }
+#ifdef DEBUG
+ dprintf("call_unmount_prog: exec failed (errno %d)\n", errno);
+#endif
+ exit(-1);
+ /*NOTREACHED*/
+ }
+
+ /* wait for the umount command to exit */
+ if (waitpid(pid, &rval, 0) == pid) {
+ if (WIFEXITED(rval)) {
+ if (WEXITSTATUS(rval) == 0) {
+ ret_val = TRUE; /* success */
+ }
+ }
+ }
+
+dun:
+#ifdef DEBUG
+ dexit("call_unmount_prog: returning %s\n", ret_val ? "TRUE" : "FALSE");
+#endif
+ return (ret_val);
+}