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