xref: /titanic_44/usr/src/cmd/eject/eject.c (revision 7544909da5f7d5b467625910225a72e142c4b6b7)
118c2aff7Sartem /*
218c2aff7Sartem  * CDDL HEADER START
318c2aff7Sartem  *
418c2aff7Sartem  * The contents of this file are subject to the terms of the
518c2aff7Sartem  * Common Development and Distribution License (the "License").
618c2aff7Sartem  * You may not use this file except in compliance with the License.
718c2aff7Sartem  *
818c2aff7Sartem  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
918c2aff7Sartem  * or http://www.opensolaris.org/os/licensing.
1018c2aff7Sartem  * See the License for the specific language governing permissions
1118c2aff7Sartem  * and limitations under the License.
1218c2aff7Sartem  *
1318c2aff7Sartem  * When distributing Covered Code, include this CDDL HEADER in each
1418c2aff7Sartem  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1518c2aff7Sartem  * If applicable, add the following below this CDDL HEADER, with the
1618c2aff7Sartem  * fields enclosed by brackets "[]" replaced with your own identifying
1718c2aff7Sartem  * information: Portions Copyright [yyyy] [name of copyright owner]
1818c2aff7Sartem  *
1918c2aff7Sartem  * CDDL HEADER END
2018c2aff7Sartem  */
2118c2aff7Sartem /*
2218c2aff7Sartem  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2318c2aff7Sartem  * Use is subject to license terms.
2418c2aff7Sartem  */
2518c2aff7Sartem 
2618c2aff7Sartem #pragma ident	"%Z%%M%	%I%	%E% SMI"
2718c2aff7Sartem 
2818c2aff7Sartem /*
2918c2aff7Sartem  * Program to eject one or more pieces of media.
3018c2aff7Sartem  */
3118c2aff7Sartem 
3218c2aff7Sartem #include	<stdio.h>
3318c2aff7Sartem #include	<stdlib.h>
3418c2aff7Sartem #include	<string.h>
3518c2aff7Sartem #include	<sys/types.h>
3618c2aff7Sartem #include	<sys/stat.h>
3718c2aff7Sartem #include	<sys/fdio.h>
3818c2aff7Sartem #include	<sys/dkio.h>
3918c2aff7Sartem #include	<sys/cdio.h>
4018c2aff7Sartem #include	<sys/param.h>
4118c2aff7Sartem #include	<sys/wait.h>
4218c2aff7Sartem #include	<dirent.h>
4318c2aff7Sartem #include	<fcntl.h>
4418c2aff7Sartem #include	<string.h>
4518c2aff7Sartem #include	<errno.h>
4618c2aff7Sartem #include	<locale.h>
4718c2aff7Sartem #include	<libintl.h>
4818c2aff7Sartem #include	<unistd.h>
4918c2aff7Sartem #include	<pwd.h>
5018c2aff7Sartem #include	<volmgt.h>
5118c2aff7Sartem #include	<sys/mnttab.h>
5218c2aff7Sartem #include	<signal.h>
5318c2aff7Sartem 
5418c2aff7Sartem static char		*prog_name = NULL;
5518c2aff7Sartem static boolean_t	do_default = B_FALSE;
5618c2aff7Sartem static boolean_t	do_list = B_FALSE;
5718c2aff7Sartem static boolean_t	do_closetray = B_FALSE;
5818c2aff7Sartem static boolean_t 	force_eject = B_FALSE;
5918c2aff7Sartem static boolean_t	do_query = B_FALSE;
6018c2aff7Sartem static boolean_t	is_direct = B_FALSE;
6118c2aff7Sartem 
6218c2aff7Sartem static int		work(char *, char *);
6318c2aff7Sartem static void		usage(void);
6418c2aff7Sartem static int		ejectit(char *);
6518c2aff7Sartem static boolean_t	query(char *, boolean_t);
6618c2aff7Sartem static boolean_t	floppy_in_drive(char *, int, boolean_t *);
6718c2aff7Sartem static boolean_t	display_busy(char *, boolean_t);
6818c2aff7Sartem static char		*eject_getfullblkname(char *, boolean_t);
6918c2aff7Sartem extern char		*getfullrawname(char *);
7018c2aff7Sartem 
7118c2aff7Sartem /*
7218c2aff7Sartem  * ON-private libvolmgt routines
7318c2aff7Sartem  */
7418c2aff7Sartem int		_dev_mounted(char *path);
7518c2aff7Sartem int		_dev_unmount(char *path);
7618c2aff7Sartem char		*_media_oldaliases(char *name);
7718c2aff7Sartem void		_media_printaliases(void);
7818c2aff7Sartem 
7918c2aff7Sartem 
8018c2aff7Sartem /*
8118c2aff7Sartem  * Hold over from old eject.
8218c2aff7Sartem  * returns exit codes:	(KEEP THESE - especially important for query)
8318c2aff7Sartem  *	0 = -n, -d or eject operation was ok, -q = media in drive
8418c2aff7Sartem  *	1 = -q only = media not in drive
8518c2aff7Sartem  *	2 = various parameter errors, etc.
8618c2aff7Sartem  *	3 = eject ioctl failed
8718c2aff7Sartem  * New Value (2/94)
8818c2aff7Sartem  *	4 = eject partially succeeded, but now manually remove media
8918c2aff7Sartem  */
9018c2aff7Sartem 
9118c2aff7Sartem #define	EJECT_OK		0
9218c2aff7Sartem #define	EJECT_NO_MEDIA		1
9318c2aff7Sartem #define	EJECT_PARM_ERR		2
9418c2aff7Sartem #define	EJECT_IOCTL_ERR		3
9518c2aff7Sartem #define	EJECT_MAN_EJ		4
9618c2aff7Sartem 
9718c2aff7Sartem #define	AVAIL_MSG		"%s is available\n"
9818c2aff7Sartem #define	NOT_AVAIL_MSG		"%s is not available\n"
9918c2aff7Sartem 
10018c2aff7Sartem #define	OK_TO_EJECT_MSG		"%s can now be manually ejected\n"
10118c2aff7Sartem 
10218c2aff7Sartem #define	FLOPPY_MEDIA_TYPE	"floppy"
10318c2aff7Sartem #define	CDROM_MEDIA_TYPE	"cdrom"
10418c2aff7Sartem 
10518c2aff7Sartem 
10618c2aff7Sartem int
main(int argc,char ** argv)10718c2aff7Sartem main(int argc, char **argv)
10818c2aff7Sartem {
10918c2aff7Sartem 	int		c;
110*7544909dSartem 	const char	*opts = "dqflt";
11118c2aff7Sartem 	int		excode;
11218c2aff7Sartem 	int		res;
11318c2aff7Sartem 	boolean_t	err_seen = B_FALSE;
11418c2aff7Sartem 	boolean_t	man_eject_seen = B_FALSE;
11518c2aff7Sartem 	char		*rmmount_opt = NULL;
11618c2aff7Sartem 
11718c2aff7Sartem 	(void) setlocale(LC_ALL, "");
11818c2aff7Sartem 
11918c2aff7Sartem #if !defined(TEXT_DOMAIN)
12018c2aff7Sartem #define	TEXT_DOMAIN	"SYS_TEST"
12118c2aff7Sartem #endif
12218c2aff7Sartem 
12318c2aff7Sartem 	(void) textdomain(TEXT_DOMAIN);
12418c2aff7Sartem 
12518c2aff7Sartem 	prog_name = argv[0];
12618c2aff7Sartem 
12718c2aff7Sartem 	is_direct = (getenv("EJECT_DIRECT") != NULL);
12818c2aff7Sartem 
12918c2aff7Sartem 	/* process arguments */
13018c2aff7Sartem 	while ((c = getopt(argc, argv, opts)) != EOF) {
13118c2aff7Sartem 		switch (c) {
13218c2aff7Sartem 		case 'd':
13318c2aff7Sartem 			do_default = B_TRUE;
13418c2aff7Sartem 			rmmount_opt = "-d";
13518c2aff7Sartem 			break;
13618c2aff7Sartem 		case 'q':
13718c2aff7Sartem 			do_query = B_TRUE;
13818c2aff7Sartem 			break;
13918c2aff7Sartem 		case 'l':
14018c2aff7Sartem 			do_list = B_TRUE;
14118c2aff7Sartem 			rmmount_opt = "-l";
14218c2aff7Sartem 			break;
14318c2aff7Sartem 		case 'f':
14418c2aff7Sartem 			force_eject = B_TRUE;
14518c2aff7Sartem 			break;
14618c2aff7Sartem 		case 't':
14718c2aff7Sartem 			do_closetray = B_TRUE;
14818c2aff7Sartem 			break;
14918c2aff7Sartem 		default:
15018c2aff7Sartem 			usage();
15118c2aff7Sartem 			exit(EJECT_PARM_ERR);
15218c2aff7Sartem 		}
15318c2aff7Sartem 	}
15418c2aff7Sartem 
15518c2aff7Sartem 	if (argc == optind) {
15618c2aff7Sartem 		/* no argument -- use the default */
15718c2aff7Sartem 		excode = work(NULL, rmmount_opt);
15818c2aff7Sartem 	} else {
15918c2aff7Sartem 		/* multiple things to eject */
16018c2aff7Sartem 		for (; optind < argc; optind++) {
16118c2aff7Sartem 			res = work(argv[optind], rmmount_opt);
16218c2aff7Sartem 			if (res == EJECT_MAN_EJ) {
16318c2aff7Sartem 				man_eject_seen = B_TRUE;
16418c2aff7Sartem 			} else if (res != EJECT_OK) {
16518c2aff7Sartem 				err_seen = B_TRUE;
16618c2aff7Sartem 			}
16718c2aff7Sartem 		}
16818c2aff7Sartem 		if (err_seen) {
16918c2aff7Sartem 			if (!is_direct) {
17018c2aff7Sartem 				excode = res;
17118c2aff7Sartem 			} else {
17218c2aff7Sartem 				excode = EJECT_IOCTL_ERR;
17318c2aff7Sartem 			}
17418c2aff7Sartem 		} else if (man_eject_seen) {
17518c2aff7Sartem 			excode = EJECT_MAN_EJ;
17618c2aff7Sartem 		} else {
17718c2aff7Sartem 			excode = EJECT_OK;
17818c2aff7Sartem 		}
17918c2aff7Sartem 	}
18018c2aff7Sartem 
18118c2aff7Sartem 	return (excode);
18218c2aff7Sartem }
18318c2aff7Sartem 
18418c2aff7Sartem /*
18518c2aff7Sartem  * the the real work of ejecting (and notifying)
18618c2aff7Sartem  */
18718c2aff7Sartem static int
work(char * arg,char * rmmount_opt)18818c2aff7Sartem work(char *arg, char *rmmount_opt)
18918c2aff7Sartem {
19018c2aff7Sartem 	char 		*name;
19118c2aff7Sartem 	int		excode = EJECT_OK;
19218c2aff7Sartem 	struct stat64	sb;
19318c2aff7Sartem 	char		*arg1, *arg2;
19418c2aff7Sartem 	pid_t		pid;
19518c2aff7Sartem 	int		status = 1;
19618c2aff7Sartem 
19718c2aff7Sartem 	if (!is_direct) {
19818c2aff7Sartem 		/* exec rmmount */
19918c2aff7Sartem 		if (do_closetray) {
20018c2aff7Sartem 			(void) putenv("EJECT_CLOSETRAY=1");
20118c2aff7Sartem 		}
20218c2aff7Sartem 		if (do_query) {
20318c2aff7Sartem 			(void) putenv("EJECT_QUERY=1");
20418c2aff7Sartem 		}
20518c2aff7Sartem 		pid = fork();
20618c2aff7Sartem 		if (pid < 0) {
20718c2aff7Sartem 			exit(1);
20818c2aff7Sartem 		} else if (pid == 0) {
20918c2aff7Sartem 			/* child */
21018c2aff7Sartem 			if (rmmount_opt != NULL) {
21118c2aff7Sartem 				arg1 = rmmount_opt;
21218c2aff7Sartem 				arg2 = arg;
21318c2aff7Sartem 			} else {
21418c2aff7Sartem 				arg1 = arg;
21518c2aff7Sartem 				arg2 = NULL;
21618c2aff7Sartem 			}
21718c2aff7Sartem 
21818c2aff7Sartem 			if (execl("/usr/bin/rmmount", "eject",
21918c2aff7Sartem 			    arg1, arg2, 0) < 0) {
220*7544909dSartem 				excode = 99;
22118c2aff7Sartem 			} else {
22218c2aff7Sartem 				exit(0);
22318c2aff7Sartem 			}
224*7544909dSartem 		} else {
22518c2aff7Sartem 			/* parent */
22618c2aff7Sartem 			if (waitpid(pid, &status, 0) != pid) {
22718c2aff7Sartem 				excode = 1;
228*7544909dSartem 			} else if (WIFEXITED(status) &&
229*7544909dSartem 			    (WEXITSTATUS(status) != 0)) {
23018c2aff7Sartem 				excode = WEXITSTATUS(status);
23118c2aff7Sartem 			} else {
23218c2aff7Sartem 				excode = 0;
23318c2aff7Sartem 			}
23418c2aff7Sartem 		}
235*7544909dSartem 	}
23618c2aff7Sartem 
23718c2aff7Sartem 	/*
23818c2aff7Sartem 	 * rmmount returns 99 if HAL not running -
23918c2aff7Sartem 	 * fallback to direct in that case
24018c2aff7Sartem 	 */
24118c2aff7Sartem 	if (is_direct || (excode == 99)) {
242*7544909dSartem 		excode = EJECT_OK;
243*7544909dSartem 
24418c2aff7Sartem 		if (arg == NULL) {
24518c2aff7Sartem 			arg = "floppy";
24618c2aff7Sartem 		}
24718c2aff7Sartem 		if ((name = _media_oldaliases(arg)) == NULL) {
24818c2aff7Sartem 			name = arg;
24918c2aff7Sartem 		}
25018c2aff7Sartem 		if (do_default) {
25118c2aff7Sartem 			(void) printf("%s\n", name);
25218c2aff7Sartem 			goto out;
25318c2aff7Sartem 		}
25418c2aff7Sartem 		if (do_list) {
25518c2aff7Sartem 			(void) printf("%s\t%s\n", name, arg);
25618c2aff7Sartem 			goto out;
25718c2aff7Sartem 		}
25818c2aff7Sartem 		if (access(name, R_OK) != 0) {
25918c2aff7Sartem 			if (do_query) {
26018c2aff7Sartem 				(void) fprintf(stderr,
26118c2aff7Sartem 				    gettext("%s: no media\n"), name);
26218c2aff7Sartem 				return (EJECT_NO_MEDIA);
26318c2aff7Sartem 			} else {
26418c2aff7Sartem 				perror(name);
26518c2aff7Sartem 				return (EJECT_PARM_ERR);
26618c2aff7Sartem 			}
26718c2aff7Sartem 		}
26818c2aff7Sartem 
26918c2aff7Sartem 		if (do_query) {
27018c2aff7Sartem 			if ((stat64(name, &sb) == 0) && S_ISDIR(sb.st_mode)) {
27118c2aff7Sartem 				(void) fprintf(stderr,
27218c2aff7Sartem 				    gettext("%s: no media\n"), name);
27318c2aff7Sartem 				return (EJECT_NO_MEDIA);
27418c2aff7Sartem 			}
27518c2aff7Sartem 			if (!query(name, B_TRUE)) {
27618c2aff7Sartem 				excode = EJECT_NO_MEDIA;
27718c2aff7Sartem 			}
27818c2aff7Sartem 		} else {
27918c2aff7Sartem 			excode = ejectit(name);
28018c2aff7Sartem 		}
28118c2aff7Sartem 	}
28218c2aff7Sartem out:
28318c2aff7Sartem 	return (excode);
28418c2aff7Sartem }
28518c2aff7Sartem 
28618c2aff7Sartem 
28718c2aff7Sartem static void
usage(void)28818c2aff7Sartem usage(void)
28918c2aff7Sartem {
29018c2aff7Sartem 	(void) fprintf(stderr,
29118c2aff7Sartem 	    gettext("usage: %s [-fldqt] [name | nickname]\n"),
29218c2aff7Sartem 	    prog_name);
29318c2aff7Sartem 	(void) fprintf(stderr,
29418c2aff7Sartem 	    gettext("options:\t-f force eject\n"));
29518c2aff7Sartem 	(void) fprintf(stderr,
29618c2aff7Sartem 	    gettext("\t\t-l list ejectable devices\n"));
29718c2aff7Sartem 	(void) fprintf(stderr,
29818c2aff7Sartem 	    gettext("\t\t-d show default device\n"));
29918c2aff7Sartem 	(void) fprintf(stderr,
30018c2aff7Sartem 	    gettext("\t\t-q query for media present\n"));
30118c2aff7Sartem 	(void) fprintf(stderr,
30218c2aff7Sartem 	    gettext("\t\t-t close tray\n"));
30318c2aff7Sartem }
30418c2aff7Sartem 
30518c2aff7Sartem 
30618c2aff7Sartem static int
ejectit(char * name)30718c2aff7Sartem ejectit(char *name)
30818c2aff7Sartem {
30918c2aff7Sartem 	int 		fd, r;
31018c2aff7Sartem 	boolean_t	mejectable = B_FALSE;	/* manually ejectable */
31118c2aff7Sartem 	int		result = EJECT_OK;
31218c2aff7Sartem 
31318c2aff7Sartem 	/*
31418c2aff7Sartem 	 * If volume management is either not running or not being managed by
31518c2aff7Sartem 	 * vold, and the device is mounted, we try to umount the device.  If we
31618c2aff7Sartem 	 * fail, we give up, unless he used the -f flag.
31718c2aff7Sartem 	 */
31818c2aff7Sartem 
31918c2aff7Sartem 	if (_dev_mounted(name)) {
32018c2aff7Sartem 		r = _dev_unmount(name);
32118c2aff7Sartem 		if (r == 0) {
32218c2aff7Sartem 			if (!force_eject) {
32318c2aff7Sartem 				(void) fprintf(stderr,
32418c2aff7Sartem gettext("WARNING: can not unmount %s, the file system is (probably) busy\n"),
32518c2aff7Sartem 				    name);
32618c2aff7Sartem 				return (EJECT_PARM_ERR);
32718c2aff7Sartem 			} else {
32818c2aff7Sartem 				(void) fprintf(stderr,
32918c2aff7Sartem gettext("WARNING: %s has a mounted filesystem, ejecting anyway\n"),
33018c2aff7Sartem 				    name);
33118c2aff7Sartem 			}
33218c2aff7Sartem 		}
33318c2aff7Sartem 	}
33418c2aff7Sartem 
33518c2aff7Sartem 	/*
33618c2aff7Sartem 	 * Require O_NDELAY for when floppy is not formatted
33718c2aff7Sartem 	 * will still id floppy in drive
33818c2aff7Sartem 	 */
33918c2aff7Sartem 
34018c2aff7Sartem 	/*
34118c2aff7Sartem 	 * make sure we are dealing with a raw device
34218c2aff7Sartem 	 *
34318c2aff7Sartem 	 * XXX: NOTE: results from getfullrawname()
34418c2aff7Sartem 	 * really should be free()d when no longer
34518c2aff7Sartem 	 * in use
34618c2aff7Sartem 	 */
34718c2aff7Sartem 	name = getfullrawname(name);
34818c2aff7Sartem 
34918c2aff7Sartem 	if ((fd = open(name, O_RDONLY | O_NDELAY)) < 0) {
35018c2aff7Sartem 		if (errno == EBUSY) {
35118c2aff7Sartem 			(void) fprintf(stderr,
35218c2aff7Sartem gettext("%s is busy (try 'eject floppy' or 'eject cdrom'?)\n"),
35318c2aff7Sartem 			    name);
35418c2aff7Sartem 			return (EJECT_PARM_ERR);
35518c2aff7Sartem 		}
35618c2aff7Sartem 		perror(name);
35718c2aff7Sartem 		return (EJECT_PARM_ERR);
35818c2aff7Sartem 	}
35918c2aff7Sartem 
36018c2aff7Sartem 	if (do_closetray) {
36118c2aff7Sartem 		if (ioctl(fd, CDROMCLOSETRAY) < 0) {
36218c2aff7Sartem 			result = EJECT_IOCTL_ERR;
36318c2aff7Sartem 		}
36418c2aff7Sartem 	} else if (ioctl(fd, DKIOCEJECT, 0) < 0) {
36518c2aff7Sartem 		/* check on why eject failed */
36618c2aff7Sartem 
36718c2aff7Sartem 		/* check for no floppy in manually ejectable drive */
36818c2aff7Sartem 		if ((errno == ENOSYS) &&
36918c2aff7Sartem 		    !floppy_in_drive(name, fd, &mejectable)) {
37018c2aff7Sartem 			/* use code below to handle "not present" */
37118c2aff7Sartem 			errno = ENXIO;
37218c2aff7Sartem 		}
37318c2aff7Sartem 
37418c2aff7Sartem 		if (errno == ENOSYS || errno == ENOTSUP) {
37518c2aff7Sartem 			(void) fprintf(stderr, gettext(OK_TO_EJECT_MSG), name);
37618c2aff7Sartem 		}
37718c2aff7Sartem 
37818c2aff7Sartem 		if ((errno == ENOSYS || errno == ENOTSUP) && mejectable) {
37918c2aff7Sartem 			/*
38018c2aff7Sartem 			 * keep track of the fact that this is a manual
38118c2aff7Sartem 			 * ejection
38218c2aff7Sartem 			 */
38318c2aff7Sartem 			result = EJECT_MAN_EJ;
38418c2aff7Sartem 
38518c2aff7Sartem 		} else if (errno == EBUSY) {
38618c2aff7Sartem 			/*
38718c2aff7Sartem 			 * if our pathname is s slice (UFS is great) then
38818c2aff7Sartem 			 * check to see what really is busy
38918c2aff7Sartem 			 */
39018c2aff7Sartem 			if (!display_busy(name, B_FALSE)) {
39118c2aff7Sartem 				perror(name);
39218c2aff7Sartem 			}
39318c2aff7Sartem 			result = EJECT_IOCTL_ERR;
39418c2aff7Sartem 
39518c2aff7Sartem 		} else if ((errno == EAGAIN) || (errno == ENODEV) ||
39618c2aff7Sartem 		    (errno == ENXIO)) {
39718c2aff7Sartem 			(void) fprintf(stderr,
39818c2aff7Sartem 			    gettext("%s not present in a drive\n"),
39918c2aff7Sartem 			    name);
40018c2aff7Sartem 			result = EJECT_OK;
40118c2aff7Sartem 		} else {
40218c2aff7Sartem 			perror(name);
40318c2aff7Sartem 			result = EJECT_IOCTL_ERR;
40418c2aff7Sartem 		}
40518c2aff7Sartem 	}
40618c2aff7Sartem 
40718c2aff7Sartem 	(void) close(fd);
40818c2aff7Sartem 	return (result);
40918c2aff7Sartem }
41018c2aff7Sartem 
41118c2aff7Sartem 
41218c2aff7Sartem /*
41318c2aff7Sartem  * return B_TRUE if a floppy is in the drive, B_FALSE otherwise
41418c2aff7Sartem  *
41518c2aff7Sartem  * this routine assumes that the file descriptor passed in is for
41618c2aff7Sartem  * a floppy disk.  this works because it's only called if the device
41718c2aff7Sartem  * is "manually ejectable", which only (currently) occurs for floppies.
41818c2aff7Sartem  */
41918c2aff7Sartem static boolean_t
floppy_in_drive(char * name,int fd,boolean_t * is_floppy)42018c2aff7Sartem floppy_in_drive(char *name, int fd, boolean_t *is_floppy)
42118c2aff7Sartem {
42218c2aff7Sartem 	int	ival = 0;
42318c2aff7Sartem 	boolean_t rval = B_FALSE;
42418c2aff7Sartem 
42518c2aff7Sartem 
42618c2aff7Sartem 	if (ioctl(fd, FDGETCHANGE, &ival) >= 0) {
42718c2aff7Sartem 		if (!(ival & FDGC_CURRENT)) {
42818c2aff7Sartem 			rval = B_TRUE;
42918c2aff7Sartem 		}
43018c2aff7Sartem 		*is_floppy = B_TRUE;
43118c2aff7Sartem 	} else {
43218c2aff7Sartem 		*is_floppy = B_FALSE;
43318c2aff7Sartem 		(void) fprintf(stderr, gettext("%s is not a floppy disk\n"),
43418c2aff7Sartem 		    name);
43518c2aff7Sartem 	}
43618c2aff7Sartem 
43718c2aff7Sartem 	return (rval);
43818c2aff7Sartem }
43918c2aff7Sartem 
44018c2aff7Sartem 
44118c2aff7Sartem /*
44218c2aff7Sartem  * display a "busy" message for the supplied pathname
44318c2aff7Sartem  *
44418c2aff7Sartem  * if the pathname is not a slice, then just display a busy message
44518c2aff7Sartem  * else if the pathname is some slice subdirectory then look for the
44618c2aff7Sartem  * *real* culprits
44718c2aff7Sartem  *
44818c2aff7Sartem  * if this is not done then the user can get a message like
44918c2aff7Sartem  *	/vol/dev/rdsk/c0t6d0/solaris_2_5_sparc/s5: Device busy
45018c2aff7Sartem  * when they try to eject "cdrom0", but "s0" (e.g.) may be the only busy
45118c2aff7Sartem  * slice
45218c2aff7Sartem  *
45318c2aff7Sartem  * return B_TRUE iff we printed the appropriate error message, else
45418c2aff7Sartem  * return B_FALSE (and caller will print error message itself)
45518c2aff7Sartem  */
45618c2aff7Sartem static boolean_t
display_busy(char * path,boolean_t vm_running)45718c2aff7Sartem display_busy(char *path, boolean_t vm_running)
45818c2aff7Sartem {
45918c2aff7Sartem 	int		errno_save = errno;	/* to save errno */
46018c2aff7Sartem 	char		*blk;			/* block name */
46118c2aff7Sartem 	FILE		*fp = NULL;		/* for scanning mnttab */
46218c2aff7Sartem 	struct mnttab	mref;			/* for scanning mnttab */
46318c2aff7Sartem 	struct mnttab	mp;			/* for scanning mnttab */
46418c2aff7Sartem 	boolean_t	res = B_FALSE;		/* return value */
46518c2aff7Sartem 	char		busy_base[MAXPATHLEN];	/* for keeping base dir name */
46618c2aff7Sartem 	uint_t		bblen;			/* busy_base string length */
46718c2aff7Sartem 	char		*cp;			/* for truncating path */
46818c2aff7Sartem 
46918c2aff7Sartem 
47018c2aff7Sartem 
47118c2aff7Sartem #ifdef	DEBUG
47218c2aff7Sartem 	(void) fprintf(stderr, "display_busy(\"%s\"): entering\n", path);
47318c2aff7Sartem #endif
47418c2aff7Sartem 
47518c2aff7Sartem 	/*
47618c2aff7Sartem 	 * get the block pathname.
47718c2aff7Sartem 	 * eject_getfullblkname returns NULL or pathname which
47818c2aff7Sartem 	 * has length < MAXPATHLEN.
47918c2aff7Sartem 	 */
48018c2aff7Sartem 	blk = eject_getfullblkname(path, vm_running);
48118c2aff7Sartem 	if (blk == NULL)
48218c2aff7Sartem 		goto dun;
48318c2aff7Sartem 
48418c2aff7Sartem 	/* open mnttab for scanning */
48518c2aff7Sartem 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
48618c2aff7Sartem 		/* can't open mnttab!? -- give up */
48718c2aff7Sartem 		goto dun;
48818c2aff7Sartem 	}
48918c2aff7Sartem 
49018c2aff7Sartem 	(void) memset((void *)&mref, '\0', sizeof (struct mnttab));
49118c2aff7Sartem 	mref.mnt_special = blk;
49218c2aff7Sartem 	if (getmntany(fp, &mp, &mref) == 0) {
49318c2aff7Sartem 		/* we found our entry -- we're done */
49418c2aff7Sartem 		goto dun;
49518c2aff7Sartem 	}
49618c2aff7Sartem 
49718c2aff7Sartem 	/* perhaps we have a sub-slice (which is what we exist to test for) */
49818c2aff7Sartem 
49918c2aff7Sartem 	/* create a base pathname */
50018c2aff7Sartem 	(void) strcpy(busy_base, blk);
50118c2aff7Sartem 	if ((cp = strrchr(busy_base, '/')) == NULL) {
50218c2aff7Sartem 		/* no last slash in pathname!!?? -- give up */
50318c2aff7Sartem 		goto dun;
50418c2aff7Sartem 	}
50518c2aff7Sartem 	*cp = '\0';
50618c2aff7Sartem 	bblen = strlen(busy_base);
50718c2aff7Sartem 	/* bblen = (uint)(cp - busy_base); */
50818c2aff7Sartem 
50918c2aff7Sartem 	/* scan for matches */
51018c2aff7Sartem 	rewind(fp);				/* rescan mnttab */
51118c2aff7Sartem 	while (getmntent(fp, &mp) == 0) {
51218c2aff7Sartem 		/*
51318c2aff7Sartem 		 * work around problem where '-' in /etc/mnttab for
51418c2aff7Sartem 		 * special device turns to NULL which isn't expected
51518c2aff7Sartem 		 */
51618c2aff7Sartem 		if (mp.mnt_special == NULL)
51718c2aff7Sartem 			mp.mnt_special = "-";
51818c2aff7Sartem 		if (strncmp(busy_base, mp.mnt_special, bblen) == 0) {
51918c2aff7Sartem 			res = B_TRUE;
52018c2aff7Sartem 			(void) fprintf(stderr, "%s: %s\n", mp.mnt_special,
52118c2aff7Sartem 			    strerror(EBUSY));
52218c2aff7Sartem 		}
52318c2aff7Sartem 	}
52418c2aff7Sartem 
52518c2aff7Sartem dun:
52618c2aff7Sartem 	if (fp != NULL) {
52718c2aff7Sartem 		(void) fclose(fp);
52818c2aff7Sartem 	}
52918c2aff7Sartem #ifdef	DEBUG
53018c2aff7Sartem 	(void) fprintf(stderr, "display_busy: returning %s\n",
53118c2aff7Sartem 	    res ? "B_TRUE" : "B_FALSE");
53218c2aff7Sartem #endif
53318c2aff7Sartem 	errno = errno_save;
53418c2aff7Sartem 	return (res);
53518c2aff7Sartem }
53618c2aff7Sartem 
53718c2aff7Sartem 
53818c2aff7Sartem /*
53918c2aff7Sartem  * In my experience with removable media drivers so far... the
54018c2aff7Sartem  * most reliable way to tell if a piece of media is in a drive
54118c2aff7Sartem  * is simply to open it.  If the open works, there's something there,
54218c2aff7Sartem  * if it fails, there's not.  We check for two errnos which we
54318c2aff7Sartem  * want to interpret for the user,  ENOENT and EPERM.  All other
54418c2aff7Sartem  * errors are considered to be "media isn't there".
54518c2aff7Sartem  *
54618c2aff7Sartem  * return B_TRUE if media found, else B_FALSE (XXX: was 0 and -1)
54718c2aff7Sartem  */
54818c2aff7Sartem static boolean_t
query(char * name,boolean_t doprint)54918c2aff7Sartem query(char *name, boolean_t doprint)
55018c2aff7Sartem {
55118c2aff7Sartem 	int		fd;
55218c2aff7Sartem 	int		rval;			/* FDGETCHANGE return value */
55318c2aff7Sartem 	enum dkio_state	state;
55418c2aff7Sartem 
55518c2aff7Sartem 	if ((fd = open(name, O_RDONLY|O_NONBLOCK)) < 0) {
55618c2aff7Sartem 		if ((errno == EPERM) || (errno == ENOENT)) {
55718c2aff7Sartem 			if (doprint) {
55818c2aff7Sartem 				perror(name);
55918c2aff7Sartem 			}
56018c2aff7Sartem 		} else {
56118c2aff7Sartem 			if (doprint) {
56218c2aff7Sartem 				(void) fprintf(stderr, gettext(NOT_AVAIL_MSG),
56318c2aff7Sartem 				    name);
56418c2aff7Sartem 			}
56518c2aff7Sartem 		}
56618c2aff7Sartem 		return (B_FALSE);
56718c2aff7Sartem 	}
56818c2aff7Sartem 
56918c2aff7Sartem 	rval = 0;
57018c2aff7Sartem 	if (ioctl(fd, FDGETCHANGE, &rval) >= 0) {
57118c2aff7Sartem 		/* hey, it worked, what a deal, it must be a floppy */
57218c2aff7Sartem 		(void) close(fd);
57318c2aff7Sartem 		if (!(rval & FDGC_CURRENT)) {
57418c2aff7Sartem 			if (doprint) {
57518c2aff7Sartem 				(void) fprintf(stderr, gettext(AVAIL_MSG),
57618c2aff7Sartem 				    name);
57718c2aff7Sartem 			}
57818c2aff7Sartem 			return (B_TRUE);
57918c2aff7Sartem 		}
58018c2aff7Sartem 		if (rval & FDGC_CURRENT) {
58118c2aff7Sartem 			if (doprint) {
58218c2aff7Sartem 				(void) fprintf(stderr,	gettext(NOT_AVAIL_MSG),
58318c2aff7Sartem 				    name);
58418c2aff7Sartem 			}
58518c2aff7Sartem 			return (B_FALSE);
58618c2aff7Sartem 		}
58718c2aff7Sartem 	}
58818c2aff7Sartem 
58918c2aff7Sartem again:
59018c2aff7Sartem 	state = DKIO_NONE;
59118c2aff7Sartem 	if (ioctl(fd, DKIOCSTATE, &state) >= 0) {
59218c2aff7Sartem 		/* great, the fancy ioctl is supported. */
59318c2aff7Sartem 		if (state == DKIO_INSERTED) {
59418c2aff7Sartem 			if (doprint) {
59518c2aff7Sartem 				(void) fprintf(stderr, gettext(AVAIL_MSG),
59618c2aff7Sartem 				    name);
59718c2aff7Sartem 			}
59818c2aff7Sartem 			(void) close(fd);
59918c2aff7Sartem 			return (B_TRUE);
60018c2aff7Sartem 		}
60118c2aff7Sartem 		if (state == DKIO_EJECTED) {
60218c2aff7Sartem 			if (doprint) {
60318c2aff7Sartem 				(void) fprintf(stderr,	gettext(NOT_AVAIL_MSG),
60418c2aff7Sartem 				    name);
60518c2aff7Sartem 			}
60618c2aff7Sartem 			(void) close(fd);
60718c2aff7Sartem 			return (B_FALSE);
60818c2aff7Sartem 		}
60918c2aff7Sartem 		/*
61018c2aff7Sartem 		 * Silly retry loop.
61118c2aff7Sartem 		 */
61218c2aff7Sartem 		(void) sleep(1);
61318c2aff7Sartem 		goto again;
61418c2aff7Sartem 	}
61518c2aff7Sartem 	(void) close(fd);
61618c2aff7Sartem 
61718c2aff7Sartem 	/*
61818c2aff7Sartem 	 * Ok, we've tried the non-blocking/ioctl route.  The
61918c2aff7Sartem 	 * device doesn't support any of our nice ioctls, so
62018c2aff7Sartem 	 * we'll just say that if it opens it's there, if it
62118c2aff7Sartem 	 * doesn't, it's not.
62218c2aff7Sartem 	 */
62318c2aff7Sartem 	if ((fd = open(name, O_RDONLY)) < 0) {
62418c2aff7Sartem 		if (doprint) {
62518c2aff7Sartem 			(void) fprintf(stderr, gettext(NOT_AVAIL_MSG), name);
62618c2aff7Sartem 		}
62718c2aff7Sartem 		return (B_FALSE);
62818c2aff7Sartem 	}
62918c2aff7Sartem 
63018c2aff7Sartem 	(void) close(fd);
63118c2aff7Sartem 	if (doprint) {
63218c2aff7Sartem 		(void) fprintf(stderr, gettext(AVAIL_MSG), name);
63318c2aff7Sartem 	}
63418c2aff7Sartem 	return (B_TRUE);	/* success */
63518c2aff7Sartem }
63618c2aff7Sartem 
63718c2aff7Sartem 
63818c2aff7Sartem /*
63918c2aff7Sartem  * this routine will return the volmgt block name given the volmgt
64018c2aff7Sartem  *  raw (char spcl) name
64118c2aff7Sartem  *
64218c2aff7Sartem  * if anything but a volmgt raw pathname is supplied that pathname will
64318c2aff7Sartem  *  be returned
64418c2aff7Sartem  *
64518c2aff7Sartem  * NOTE: non-null return value will point to static data, overwritten with
64618c2aff7Sartem  *  each call
64718c2aff7Sartem  *
64818c2aff7Sartem  * e.g. names starting with "/vol/r" will be changed to start with "/vol/",
64918c2aff7Sartem  * and names starting with "vol/dev/r" will be changed to start with
65018c2aff7Sartem  * "/vol/dev/"
65118c2aff7Sartem  */
65218c2aff7Sartem static char *
eject_getfullblkname(char * path,boolean_t vm_running)65318c2aff7Sartem eject_getfullblkname(char *path, boolean_t vm_running)
65418c2aff7Sartem {
65518c2aff7Sartem 	char		raw_root[MAXPATHLEN];
65618c2aff7Sartem 	const char	*vm_root;
65718c2aff7Sartem 	static char	res_buf[MAXPATHLEN];
65818c2aff7Sartem 	uint_t		raw_root_len;
65918c2aff7Sartem 
66018c2aff7Sartem #ifdef	DEBUG
66118c2aff7Sartem 	(void) fprintf(stderr, "eject_getfullblkname(\"%s\", %s): entering\n",
66218c2aff7Sartem 	    path, vm_running ? "B_TRUE" : "B_FALSE");
66318c2aff7Sartem #endif
66418c2aff7Sartem 	/*
66518c2aff7Sartem 	 * try different strategies based on whether or not vold is running
66618c2aff7Sartem 	 */
66718c2aff7Sartem 	if (vm_running) {
66818c2aff7Sartem 
66918c2aff7Sartem 		/* vold IS running -- look in /vol (or its alternate) */
67018c2aff7Sartem 
67118c2aff7Sartem 		/* get vm root dir */
67218c2aff7Sartem 		vm_root = volmgt_root();
67318c2aff7Sartem 
67418c2aff7Sartem 		/* get first volmgt root dev directory (and its length) */
67518c2aff7Sartem 		(void) snprintf(raw_root, sizeof (raw_root), "%s/r", vm_root);
67618c2aff7Sartem 		raw_root_len = strlen(raw_root);
67718c2aff7Sartem 
67818c2aff7Sartem 		/* see if we have a raw volmgt pathname (e.g. "/vol/r*") */
67918c2aff7Sartem 		if (strncmp(path, raw_root, raw_root_len) == 0) {
68018c2aff7Sartem 			if (snprintf(res_buf, sizeof (res_buf), "%s/%s",
68118c2aff7Sartem 			    vm_root, path + raw_root_len) >= sizeof (res_buf)) {
68218c2aff7Sartem 				return (NULL);
68318c2aff7Sartem 			}
68418c2aff7Sartem 			goto dun;		/* found match in /vol */
68518c2aff7Sartem 		}
68618c2aff7Sartem 
68718c2aff7Sartem 		/* get second volmgt root dev directory (and its length) */
68818c2aff7Sartem 		(void) snprintf(raw_root, sizeof (raw_root),
68918c2aff7Sartem 		    "%s/dev/r", vm_root);
69018c2aff7Sartem 		raw_root_len = strlen(raw_root);
69118c2aff7Sartem 
69218c2aff7Sartem 		/* see if we have a raw volmgt pathname (e.g. "/vol/dev/r*") */
69318c2aff7Sartem 		if (strncmp(path, raw_root, raw_root_len) == 0) {
69418c2aff7Sartem 			if (snprintf(res_buf, sizeof (res_buf), "%s/dev/%s",
69518c2aff7Sartem 			    vm_root, path + raw_root_len) >= sizeof (res_buf)) {
69618c2aff7Sartem 				return (NULL);
69718c2aff7Sartem 			}
69818c2aff7Sartem 			goto dun;		/* found match in /vol/dev */
69918c2aff7Sartem 		}
70018c2aff7Sartem 
70118c2aff7Sartem 	} else {
70218c2aff7Sartem 
70318c2aff7Sartem 		/* vold is NOT running -- look in /dev */
70418c2aff7Sartem 
70518c2aff7Sartem 		(void) strcpy(raw_root, "/dev/r");
70618c2aff7Sartem 		raw_root_len = strlen(raw_root);
70718c2aff7Sartem 		if (strncmp(path, raw_root, raw_root_len) == 0) {
70818c2aff7Sartem 			if (snprintf(res_buf, sizeof (res_buf), "/dev/%s",
70918c2aff7Sartem 			    path + raw_root_len) >= sizeof (res_buf)) {
71018c2aff7Sartem 				return (NULL);
71118c2aff7Sartem 			}
71218c2aff7Sartem 			goto dun;		/* found match in /dev */
71318c2aff7Sartem 		}
71418c2aff7Sartem 	}
71518c2aff7Sartem 
71618c2aff7Sartem 	/* no match -- return what we got */
71718c2aff7Sartem 	(void) strcpy(res_buf, path);
71818c2aff7Sartem 
71918c2aff7Sartem dun:
72018c2aff7Sartem #ifdef	DEBUG
72118c2aff7Sartem 	(void) fprintf(stderr, "eject_getfullblkname: returning %s\n",
72218c2aff7Sartem 	    res_buf ? res_buf : "<null ptr>");
72318c2aff7Sartem #endif
72418c2aff7Sartem 	return (res_buf);
72518c2aff7Sartem }
726