Home | History | Annotate | Line # | Download | only in eject
eject.c revision 1.7
      1 /*	$NetBSD: eject.c,v 1.7 1999/02/08 16:35:33 bouyer Exp $	*/
      2 /*
      3  * Copyright (c) 1995
      4  *	Matthieu Herrb.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *	This product includes software developed for the NetBSD Project
     17  *	by Matthieu Herrb.
     18  * 4. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #include <sys/cdefs.h>
     35 #ifndef lint
     36 __RCSID("$NetBSD: eject.c,v 1.7 1999/02/08 16:35:33 bouyer Exp $");
     37 #endif
     38 
     39 /*
     40  * Eject command
     41  *
     42  * It knows to eject floppies, CD-ROMS and tapes
     43  * and tries to unmount file systems first
     44  */
     45 
     46 #include <sys/param.h>
     47 #include <sys/ioctl.h>
     48 #include <sys/cdio.h>
     49 #include <sys/mtio.h>
     50 #include <sys/disklabel.h>
     51 #include <sys/ucred.h>
     52 #include <sys/mount.h>
     53 #include <sys/cdefs.h>
     54 
     55 #include <stdio.h>
     56 #include <stdlib.h>
     57 #include <unistd.h>
     58 #include <fcntl.h>
     59 #include <err.h>
     60 #include <string.h>
     61 
     62 typedef struct DEVTAB {
     63 	char   *name;
     64 	char   *device;
     65 	char   qualifier;
     66 	u_int   type;
     67 } DEVTAB;
     68 
     69 /*
     70  * known device nicknames and types
     71  * (used for selecting the proper ioctl to eject them)
     72  */
     73 #define DISK   0x00000002
     74 #define CDROM  0x00000003
     75 #define TAPE   0x00010000
     76 
     77 #define MOUNTABLE(x) ((x) & 0x0000ffff)
     78 
     79 #define RPART ('a' + RAW_PART)
     80 
     81 static DEVTAB devtab[] = {
     82 	{ "diskette", "/dev/rfd0", 'a', DISK },
     83 	{ "diskette0", "/dev/rfd0", 'a', DISK },
     84 	{ "diskette1", "/dev/rfd1", 'a', DISK },
     85 	{ "floppy", "/dev/rfd0", 'a', DISK },
     86 	{ "floppy0", "/dev/rfd0", 'a', DISK },
     87 	{ "floppy1", "/dev/rfd1", 'a', DISK },
     88 	{ "fd", "/dev/rfd0", 'a', DISK },
     89 	{ "fd0", "/dev/rfd0", 'a', DISK },
     90 	{ "fd1", "/dev/rfd1", RPART, DISK },
     91 	{ "sd0", "/dev/rsd0", RPART, DISK },
     92 	{ "sd1", "/dev/rsd1", RPART, DISK },
     93 	{ "sd2", "/dev/rsd2", RPART, DISK },
     94 	{ "cdrom", "/dev/rcd0", RPART, CDROM },
     95 	{ "cdrom0", "/dev/rcd0", RPART, CDROM },
     96 	{ "cdrom1", "/dev/rcd1", RPART, CDROM },
     97 	{ "cd", "/dev/rcd0", RPART, CDROM },
     98 	{ "cd0", "/dev/rcd0", RPART, CDROM },
     99 	{ "cd1", "/dev/rcd1", RPART, CDROM },
    100 	{ "mcd", "/dev/rmcd0", RPART, CDROM },
    101 	{ "mcd0", "/dev/rmcd0", RPART, CDROM },
    102 	{ "mcd1", "/dev/rmcd1", RPART, CDROM },
    103 	{ "tape", "/dev/rst0", '\0', TAPE },
    104 	{ "tape0", "/dev/rst0", '\0', TAPE },
    105 	{ "tape1", "/dev/rst1", '\0', TAPE },
    106 	{ "st", "/dev/rst0", '\0', TAPE },
    107 	{ "st0", "/dev/rst0", '\0', TAPE },
    108 	{ "st1", "/dev/rst1", '\0', TAPE },
    109 	{ "dat", "/dev/rst0", '\0', TAPE },
    110 	{ "dat0", "/dev/rst0", '\0', TAPE },
    111 	{ "dat1", "/dev/rst1", '\0', TAPE },
    112 	{ "exabyte", "/dev/rst0", '\0', TAPE },
    113 	{ "exabyte0", "/dev/rst0", '\0', TAPE },
    114 	{ "exabyte1", "/dev/rst1", '\0', TAPE },
    115 	{ NULL, NULL }
    116 };
    117 
    118 struct types {
    119 	char	*str;
    120 	int	type;
    121 } types[] = {
    122 	{ "diskette", DISK },
    123 	{ "floppy", DISK },
    124 	{ "cdrom", CDROM },
    125 	{ "disk", DISK },
    126 	{ "tape", TAPE },
    127 	{ NULL, 0 }
    128 };
    129 
    130 int verbose;
    131 
    132 static	void	usage __P((void));
    133 static	char   *device_by_name __P((char *, int *, char *));
    134 static	char   *device_by_nickname __P((char *, int *, char *));
    135 static	void	load_cdrom __P((char *));
    136 static	void	eject_disk __P((char *, int));
    137 static	void	eject_tape __P((char *));
    138 	int	main __P((int, char **));
    139 static	void	umount_mounted __P((char *));
    140 
    141 /*
    142  * remind the syntax of the command to the user
    143  */
    144 static void
    145 usage()
    146 {
    147 	fprintf(stderr,
    148 	    "usage: eject [-n][-f][-l][-t devtype][[-d] raw device | "
    149 	    "nickname ]\n");
    150 	exit(1);
    151 	/*NOTREACHED*/
    152 }
    153 
    154 
    155 /*
    156  * given a device nick name, find its associated raw device and type
    157  */
    158 static char *
    159 device_by_nickname(name, pdevtype, pqualifier)
    160 	char	*name;
    161 	int	*pdevtype;
    162 	char	*pqualifier;
    163 {
    164 	int     i;
    165 
    166 	for (i = 0; devtab[i].name != NULL; i++) {
    167 		if (strcmp(name, devtab[i].name) == 0) {
    168 			*pdevtype = devtab[i].type;
    169 			*pqualifier = devtab[i].qualifier;
    170 			return devtab[i].device;
    171 		}
    172 	}
    173 	*pdevtype = -1;
    174 	return NULL;
    175 }
    176 
    177 /*
    178  * Given a raw device name, find its type and partition tag
    179  * from the name.
    180  */
    181 static char *
    182 device_by_name(device, pdevtype, pqualifier)
    183 	char	*device;
    184 	int	*pdevtype;
    185 	char	*pqualifier;
    186 {
    187 	int     i;
    188 
    189 	for (i = 0; devtab[i].name != NULL; i++) {
    190 		if (strncmp(devtab[i].device, device,
    191 			    strlen(devtab[i].device)) == 0) {
    192 			*pdevtype = devtab[i].type;
    193 			*pqualifier = devtab[i].qualifier;
    194 			return devtab[i].device;
    195 		}
    196 	}
    197 	*pdevtype = -1;
    198 	return NULL;
    199 }
    200 
    201 /*
    202  * load a disk (cdrom only)
    203  */
    204 static void
    205 load_cdrom(device)
    206 	char   *device;
    207 {
    208 	int     fd;
    209 
    210 	fd = open(device, O_RDONLY);
    211 	if (fd < 0) {
    212 		err(1, "%s: open", device);
    213 	}
    214 
    215 	if (ioctl(fd, CDIOCCLOSE, NULL) < 0) {
    216 		err(1, "%s: DIOCEJECT", device);
    217 	}
    218 	if (close(fd) != 0)
    219 		err(1, "%s: close", device);
    220 }
    221 
    222 /*
    223  * eject a disk (including floppy and cdrom)
    224  */
    225 static void
    226 eject_disk(device, umount_flag)
    227 	char   *device;
    228 	int    umount_flag;
    229 {
    230 	int     fd, arg = 0;
    231 
    232 	fd = open(device, O_RDONLY);
    233 	if (fd < 0) {
    234 		err(1, "%s: open", device);
    235 	}
    236 	if (umount_flag == 0) {
    237 		if (ioctl(fd, DIOCLOCK, (char *)&arg) < 0) {
    238 			err(1, "%s: DIOCLOCK", device);
    239 		}
    240 		arg = 1; /* eject without device busy check */
    241 	}
    242 	if (ioctl(fd, DIOCEJECT, (char *)&arg) < 0) {
    243 		err(1, "%s: DIOCEJECT", device);
    244 	}
    245 	if (close(fd) != 0)
    246 		err(1, "%s: close", device);
    247 } /* eject_disk */
    248 
    249 /*
    250  * eject a tape
    251  */
    252 static void
    253 eject_tape(device)
    254 	char   *device;
    255 {
    256 	int     fd;
    257 	struct mtop mt_com;
    258 
    259 	fd = open(device, O_RDONLY);
    260 	if (fd < 0) {
    261 		err(1, "open %s", device);
    262 	}
    263 	mt_com.mt_op = MTOFFL;
    264 
    265 	if (ioctl(fd, MTIOCTOP, &mt_com) < 0) {
    266 		err(1, "%s:  MTOFFL", device);
    267 	}
    268 	close(fd);
    269 } /* eject_tape */
    270 
    271 /*
    272  * test if partitions of a device are mounted
    273  * and unmount them
    274  */
    275 static void
    276 umount_mounted(device)
    277 	char   *device;
    278 {
    279 	struct statfs *mntbuf;
    280 	int     i, n, l;
    281 	static char blkdev[32];
    282 	struct stat stb;
    283 	const char *dp;
    284 
    285 	/* convert path to block device if needed, remove partition letter */
    286 	if (stat(device, &stb) < 0)
    287 		return;
    288 	if (S_ISBLK(stb.st_mode)) {
    289 		strncpy(blkdev, device, 32);
    290 	} else if ((dp = strrchr(device, '/')) != 0 && dp[1] == 'r') {
    291 		snprintf(blkdev, 32, "%.*s/%s", (int)(dp - device),
    292 		    device, dp + 2);
    293 	} else
    294 		return;
    295 	blkdev[strlen(blkdev) - 1] = '\0';
    296 
    297 	n = getmntinfo(&mntbuf, MNT_NOWAIT);
    298 	if (n == 0) {
    299 		err(1, "getmntinfo");
    300 	}
    301 	l = strlen(blkdev);
    302 	for (i = 0; i < n; i++) {
    303 		if (strncmp(blkdev, mntbuf[i].f_mntfromname, l) == 0) {
    304 			if (verbose)
    305 				printf("Unmounting: %s\n",
    306 					mntbuf[i].f_mntonname);
    307 			if (unmount(mntbuf[i].f_mntonname, 0) < 0) {
    308 				err(1, "umount %s from %s",
    309 					mntbuf[i].f_mntfromname,
    310 					mntbuf[i].f_mntonname);
    311 			}
    312 		}
    313 	}
    314 
    315 }
    316 
    317 /*
    318  * Eject - ejects various removable devices, including cdrom, tapes,
    319  * diskettes, and other removable disks (like ZIP drives)
    320  */
    321 int
    322 main(argc, argv)
    323 	int     argc;
    324 	char   *argv[];
    325 {
    326 	char    device[MAXPATHLEN];
    327 	char	*devpath;
    328 	char	qualifier;
    329 	int     umount_flag, load_flag, devtype;
    330 	int     i, ch;
    331 
    332 	/* Default options values */
    333 	devpath = NULL;
    334 	devtype = -1;
    335 	umount_flag = 1;
    336 	load_flag = 0;
    337 
    338 	while ((ch = getopt(argc, argv, "d:flnt:v")) != -1) {
    339 		switch (ch) {
    340 		case 'd':
    341 			devpath = optarg;
    342 			break;
    343 		case 'f':
    344 			umount_flag = 0;
    345 			break;
    346 		case 'l':
    347 			load_flag = 1;
    348 			break;
    349 		case 'n':
    350 			for (i = 0; devtab[i].name != NULL; i++) {
    351 				if (devtab[i].qualifier != '\0') {
    352 					printf("%9s => %s%c\n", devtab[i].name,
    353 					       devtab[i].device,
    354 					       devtab[i].qualifier);
    355 				} else {
    356 					printf("%9s => %s\n", devtab[i].name,
    357 					       devtab[i].device);
    358 				}
    359 			}
    360 			return 0;
    361 		case 't':
    362 			for (i = 0; types[i].str != NULL; i++) {
    363 				if (strcasecmp(optarg, types[i].str) == 0) {
    364 					devtype = types[i].type;
    365 					break;
    366 				}
    367 			}
    368 			if (devtype == -1)
    369 				errx(1, "%s: unknown device type", optarg);
    370 			break;
    371 		case 'v':
    372 			verbose = 1;
    373 			break;
    374 		case '?':
    375 		default:
    376 			usage();
    377 			/*NOTREACHED*/
    378 		}
    379 	}
    380 
    381 	argc -= optind;
    382 	argv += optind;
    383 
    384 	if (devpath != NULL) {
    385 		/* device specified with 'd' option */
    386 		if (devtype == -1)
    387 			device_by_name(devpath, &devtype, &qualifier);
    388 		else
    389 			qualifier = '\0';
    390 	} else {
    391 		if (argc <= 0) {
    392 			errx(1, "No device specified");
    393 			/* NOTREACHED */
    394 		}
    395 		if (strncmp(argv[0], "/dev/", 5) == 0) {
    396 			/*
    397 			 * If argument begins with "/dev/", assume
    398 			 * a device name.
    399 			 */
    400 			if (devtype == -1) {
    401 				devpath = device_by_name(argv[0],
    402 							 &devtype, &qualifier);
    403 			} else {
    404 				/* Device type specified; use literally */
    405 				devpath = argv[0];
    406 				qualifier = '\0';
    407 			}
    408 		} else {
    409 			/* assume a nickname */
    410 			devpath = device_by_nickname(argv[0],
    411 						     &devtype, &qualifier);
    412 		}
    413 	}
    414 
    415 	if (devpath == NULL) {
    416 		errx(1, "%s: unknown device", argv[0]);
    417 		/*NOTREACHED*/
    418 	}
    419 
    420 	snprintf(device, sizeof(device), "%s%c", devpath, qualifier);
    421 
    422 	if (load_flag) {
    423 		if (devtype != CDROM)
    424 			errx(1, "Can only load CDROM device type\n");
    425 		if (verbose)
    426 			printf("Loading device `%s'\n", device);
    427 		load_cdrom(device);
    428 		exit(0);
    429 	}
    430 
    431 	if (umount_flag && MOUNTABLE(devtype)) {
    432 		umount_mounted(device);
    433 	}
    434 
    435 	if (verbose)
    436 		printf("Ejecting device `%s'\n", device);
    437 	switch (devtype) {
    438 	case DISK:
    439 	case CDROM:
    440 		eject_disk(device, umount_flag);
    441 		break;
    442 	case TAPE:
    443 		eject_tape(device);
    444 		break;
    445 	default:
    446 		errx(1, "impossible... devtype = %d", devtype);
    447 	}
    448 
    449 	exit(0);
    450 }
    451