Home | History | Annotate | Line # | Download | only in eject
eject.c revision 1.3
      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 DISK   0x00000002
     68 #define TAPE   0x00010000
     69 
     70 #define MOUNTABLE(x) ((x) & 0x0000ffff)
     71 
     72 static DEVTAB devtab[] = {
     73 	{ "diskette", "/dev/fd0", 'a', DISK },
     74 	{ "diskette0", "/dev/fd0", 'a', DISK },
     75 	{ "diskette1", "/dev/fd1", 'a', DISK },
     76 	{ "floppy", "/dev/fd0", 'a', DISK },
     77 	{ "floppy0", "/dev/fd0", 'a', DISK },
     78 	{ "floppy1", "/dev/fd1", 'a', DISK },
     79 	{ "fd", "/dev/fd0", 'a', DISK },
     80 	{ "fd0", "/dev/fd0", 'a', DISK },
     81 	{ "fd1", "/dev/fd1", 'a', DISK },
     82 	{ "cdrom", "/dev/cd0", 'a', DISK },
     83 	{ "cdrom0", "/dev/cd0", 'a', DISK },
     84 	{ "cdrom1", "/dev/cd1", 'a', DISK },
     85 	{ "cd", "/dev/cd0", 'a', DISK },
     86 	{ "cd0", "/dev/cd0", 'a', DISK },
     87 	{ "cd1", "/dev/cd1", 'a', DISK },
     88 	{ "mcd", "/dev/mcd0", 'a', DISK },
     89 	{ "mcd0", "/dev/mcd0", 'a', DISK },
     90 	{ "mcd1", "/dev/mcd1", 'a', DISK },
     91 	{ "tape", "/dev/rst0", '\0', TAPE },
     92 	{ "tape0", "/dev/rst0", '\0', TAPE },
     93 	{ "tape1", "/dev/rst1", '\0', TAPE },
     94 	{ "st", "/dev/rst0", '\0', TAPE },
     95 	{ "st0", "/dev/rst0", '\0', TAPE },
     96 	{ "st1", "/dev/rst1", '\0', TAPE },
     97 	{ "dat", "/dev/rst0", '\0', TAPE },
     98 	{ "dat0", "/dev/rst0", '\0', TAPE },
     99 	{ "dat1", "/dev/rst1", '\0', TAPE },
    100 	{ "exabyte", "/dev/rst0", '\0', TAPE },
    101 	{ "exabyte0", "/dev/rst0", '\0', TAPE },
    102 	{ "exabyte1", "/dev/rst1", '\0', TAPE },
    103 	{ NULL, NULL }
    104 };
    105 
    106 struct types {
    107 	char	*str;
    108 	int	type;
    109 } types[] = {
    110 	{ "diskette", DISK },
    111 	{ "floppy", DISK },
    112 	{ "cdrom", DISK },
    113 	{ "disk", DISK },
    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 disk (including floppy and cdrom)
    180  */
    181 static void
    182 eject_disk(device)
    183 	char   *device;
    184 {
    185 	int     fd, arg = 0;
    186 
    187 	fd = open(device, O_RDONLY);
    188 	if (fd < 0) {
    189 		err(1, "%s: open", device);
    190 	}
    191 	if (ioctl(fd, DIOCLOCK, (char *)&arg) < 0) {
    192 		err(1, "%s: DIOCLOCK", device);
    193 	}
    194 	if (ioctl(fd, DIOCEJECT, 0) < 0) {
    195 		err(1, "%s: DIOCEJECT", device);
    196 	}
    197 	if (close(fd) != 0)
    198 		err(1, "%s: close", device);
    199 } /* eject_disk */
    200 
    201 /*
    202  * eject a tape
    203  */
    204 static void
    205 eject_tape(device)
    206 	char   *device;
    207 {
    208 	int     fd;
    209 	struct mtop mt_com;
    210 
    211 	fd = open(device, O_RDONLY);
    212 	if (fd < 0) {
    213 		err(1, "open %s", device);
    214 	}
    215 	mt_com.mt_op = MTOFFL;
    216 
    217 	if (ioctl(fd, MTIOCTOP, &mt_com) < 0) {
    218 		err(1, "%s:  MTOFFL", device);
    219 	}
    220 	close(fd);
    221 } /* eject_tape */
    222 
    223 /*
    224  * test if partitions of a device are mounted
    225  * and unmount them
    226  */
    227 static void
    228 umount_mounted(device)
    229 	char   *device;
    230 {
    231 	struct statfs *mntbuf;
    232 	int     i, n, l;
    233 
    234 	n = getmntinfo(&mntbuf, MNT_NOWAIT);
    235 	if (n == 0) {
    236 		err(1, "getmntinfo");
    237 	}
    238 	l = strlen(device);
    239 	for (i = 0; i < n; i++) {
    240 		if (strncmp(device, mntbuf[i].f_mntfromname, l) == 0) {
    241 			if (verbose)
    242 				printf("Unmounting: %s\n",
    243 					mntbuf[i].f_mntonname);
    244 			if (unmount(mntbuf[i].f_mntonname, 0) < 0) {
    245 				err(1, "umount %s from %s",
    246 					mntbuf[i].f_mntfromname,
    247 					mntbuf[i].f_mntonname);
    248 			}
    249 		}
    250 	}
    251 
    252 }
    253 
    254 /*
    255  * Eject - ejects various removable devices, including cdrom, tapes,
    256  * diskettes, and other removable disks (like ZIP drives)
    257  */
    258 int
    259 main(argc, argv)
    260 	int     argc;
    261 	char   *argv[];
    262 {
    263 	char    device[MAXPATHLEN];
    264 	char	*devpath;
    265 	char	qualifier;
    266 	int     umount_flag, devtype;
    267 	int     i, ch;
    268 
    269 	/* Default options values */
    270 	devpath = NULL;
    271 	devtype = -1;
    272 	umount_flag = 1;
    273 
    274 	while ((ch = getopt(argc, argv, "d:fnt:v")) != EOF) {
    275 		switch (ch) {
    276 		case 'd':
    277 			devpath = optarg;
    278 			break;
    279 		case 'f':
    280 			umount_flag = 0;
    281 			break;
    282 		case 'n':
    283 			for (i = 0; devtab[i].name != NULL; i++) {
    284 				printf("%9s => %s%c\n",
    285 					devtab[i].name,
    286 					devtab[i].device, devtab[i].qualifier);
    287 			}
    288 			return 0;
    289 		case 't':
    290 			for (i = 0; types[i].str != NULL; i++) {
    291 				if (strcasecmp(optarg, types[i].str) == 0) {
    292 					devtype = types[i].type;
    293 					break;
    294 				}
    295 			}
    296 			if (devtype == -1)
    297 				errx(1, "%s: unknown device type", optarg);
    298 			break;
    299 		case 'v':
    300 			verbose = 1;
    301 			break;
    302 		case '?':
    303 		default:
    304 			usage();
    305 			/*NOTREACHED*/
    306 		}
    307 	}
    308 
    309 	argc -= optind;
    310 	argv += optind;
    311 
    312 	if (devpath != NULL) {
    313 		/* device specified with 'd' option */
    314 		if (devtype == -1)
    315 			device_by_name(devpath, &devtype, &qualifier);
    316 		else
    317 			qualifier = '\0';
    318 	} else {
    319 		if (argc <= 0) {
    320 			errx(1, "No device specified");
    321 			/* NOTREACHED */
    322 		}
    323 		if (strncmp(argv[0], "/dev/", 5) == 0) {
    324 			/*
    325 			 * If argument begins with "/dev/", assume
    326 			 * a device name.
    327 			 */
    328 			if (devtype == -1) {
    329 				devpath = device_by_name(argv[0],
    330 							 &devtype, &qualifier);
    331 			} else {
    332 				/* Device type specified; use literally */
    333 				devpath = argv[0];
    334 				qualifier = '\0';
    335 			}
    336 		} else {
    337 			/* assume a nickname */
    338 			devpath = device_by_nickname(argv[0],
    339 						     &devtype, &qualifier);
    340 		}
    341 	}
    342 
    343 	if (devpath == NULL) {
    344 		errx(1, "%s: unknown device", argv[0]);
    345 		/*NOTREACHED*/
    346 	}
    347 
    348 	if (umount_flag && MOUNTABLE(devtype)) {
    349 		umount_mounted(devpath);
    350 	}
    351 
    352 	snprintf(device, sizeof(device), "%s%c", devpath, qualifier);
    353 	if (verbose)
    354 		printf("Ejecting device `%s'\n", device);
    355 	switch (devtype) {
    356 	case DISK:
    357 		eject_disk(device);
    358 		break;
    359 	case TAPE:
    360 		eject_tape(device);
    361 		break;
    362 	default:
    363 		errx(1, "impossible... devtype = %d", devtype);
    364 	}
    365 
    366 	exit(0);
    367 }
    368