1 /* $NetBSD: eject.c,v 1.6 1997/12/07 19:04:36 msaitoh 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.6 1997/12/07 19:04:36 msaitoh 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 TAPE 0x00010000 75 76 #define MOUNTABLE(x) ((x) & 0x0000ffff) 77 78 static DEVTAB devtab[] = { 79 { "diskette", "/dev/fd0", 'a', DISK }, 80 { "diskette0", "/dev/fd0", 'a', DISK }, 81 { "diskette1", "/dev/fd1", 'a', DISK }, 82 { "floppy", "/dev/fd0", 'a', DISK }, 83 { "floppy0", "/dev/fd0", 'a', DISK }, 84 { "floppy1", "/dev/fd1", 'a', DISK }, 85 { "fd", "/dev/fd0", 'a', DISK }, 86 { "fd0", "/dev/fd0", 'a', DISK }, 87 { "fd1", "/dev/fd1", 'a', DISK }, 88 { "cdrom", "/dev/cd0", 'a', DISK }, 89 { "cdrom0", "/dev/cd0", 'a', DISK }, 90 { "cdrom1", "/dev/cd1", 'a', DISK }, 91 { "cd", "/dev/cd0", 'a', DISK }, 92 { "cd0", "/dev/cd0", 'a', DISK }, 93 { "cd1", "/dev/cd1", 'a', DISK }, 94 { "mcd", "/dev/mcd0", 'a', DISK }, 95 { "mcd0", "/dev/mcd0", 'a', DISK }, 96 { "mcd1", "/dev/mcd1", 'a', DISK }, 97 { "tape", "/dev/rst0", '\0', TAPE }, 98 { "tape0", "/dev/rst0", '\0', TAPE }, 99 { "tape1", "/dev/rst1", '\0', TAPE }, 100 { "st", "/dev/rst0", '\0', TAPE }, 101 { "st0", "/dev/rst0", '\0', TAPE }, 102 { "st1", "/dev/rst1", '\0', TAPE }, 103 { "dat", "/dev/rst0", '\0', TAPE }, 104 { "dat0", "/dev/rst0", '\0', TAPE }, 105 { "dat1", "/dev/rst1", '\0', TAPE }, 106 { "exabyte", "/dev/rst0", '\0', TAPE }, 107 { "exabyte0", "/dev/rst0", '\0', TAPE }, 108 { "exabyte1", "/dev/rst1", '\0', TAPE }, 109 { NULL, NULL } 110 }; 111 112 struct types { 113 char *str; 114 int type; 115 } types[] = { 116 { "diskette", DISK }, 117 { "floppy", DISK }, 118 { "cdrom", DISK }, 119 { "disk", DISK }, 120 { "tape", TAPE }, 121 { NULL, 0 } 122 }; 123 124 int verbose; 125 126 static void usage __P((void)); 127 static char *device_by_name __P((char *, int *, char *)); 128 static char *device_by_nickname __P((char *, int *, char *)); 129 static void eject_disk __P((char *)); 130 static void eject_tape __P((char *)); 131 int main __P((int, char **)); 132 static void umount_mounted __P((char *)); 133 134 /* 135 * remind the syntax of the command to the user 136 */ 137 static void 138 usage() 139 { 140 fprintf(stderr, 141 "usage: eject [-n][-f][-t devtype][[-d] raw device | nickname ]\n"); 142 exit(1); 143 /*NOTREACHED*/ 144 } 145 146 147 /* 148 * given a device nick name, find its associated raw device and type 149 */ 150 static char * 151 device_by_nickname(name, pdevtype, pqualifier) 152 char *name; 153 int *pdevtype; 154 char *pqualifier; 155 { 156 int i; 157 158 for (i = 0; devtab[i].name != NULL; i++) { 159 if (strcmp(name, devtab[i].name) == 0) { 160 *pdevtype = devtab[i].type; 161 *pqualifier = devtab[i].qualifier; 162 return devtab[i].device; 163 } 164 } 165 *pdevtype = -1; 166 return NULL; 167 } 168 169 /* 170 * Given a raw device name, find its type and partition tag 171 * from the name. 172 */ 173 static char * 174 device_by_name(device, pdevtype, pqualifier) 175 char *device; 176 int *pdevtype; 177 char *pqualifier; 178 { 179 int i; 180 181 for (i = 0; devtab[i].name != NULL; i++) { 182 if (strncmp(devtab[i].device, device, 183 strlen(devtab[i].device)) == 0) { 184 *pdevtype = devtab[i].type; 185 *pqualifier = devtab[i].qualifier; 186 return devtab[i].device; 187 } 188 } 189 *pdevtype = -1; 190 return NULL; 191 } 192 193 /* 194 * eject a disk (including floppy and cdrom) 195 */ 196 static void 197 eject_disk(device) 198 char *device; 199 { 200 int fd, arg = 0; 201 202 fd = open(device, O_RDONLY); 203 if (fd < 0) { 204 err(1, "%s: open", device); 205 } 206 if (ioctl(fd, DIOCLOCK, (char *)&arg) < 0) { 207 err(1, "%s: DIOCLOCK", device); 208 } 209 if (ioctl(fd, DIOCEJECT, 0) < 0) { 210 err(1, "%s: DIOCEJECT", device); 211 } 212 if (close(fd) != 0) 213 err(1, "%s: close", device); 214 } /* eject_disk */ 215 216 /* 217 * eject a tape 218 */ 219 static void 220 eject_tape(device) 221 char *device; 222 { 223 int fd; 224 struct mtop mt_com; 225 226 fd = open(device, O_RDONLY); 227 if (fd < 0) { 228 err(1, "open %s", device); 229 } 230 mt_com.mt_op = MTOFFL; 231 232 if (ioctl(fd, MTIOCTOP, &mt_com) < 0) { 233 err(1, "%s: MTOFFL", device); 234 } 235 close(fd); 236 } /* eject_tape */ 237 238 /* 239 * test if partitions of a device are mounted 240 * and unmount them 241 */ 242 static void 243 umount_mounted(device) 244 char *device; 245 { 246 struct statfs *mntbuf; 247 int i, n, l; 248 249 n = getmntinfo(&mntbuf, MNT_NOWAIT); 250 if (n == 0) { 251 err(1, "getmntinfo"); 252 } 253 l = strlen(device); 254 for (i = 0; i < n; i++) { 255 if (strncmp(device, mntbuf[i].f_mntfromname, l) == 0) { 256 if (verbose) 257 printf("Unmounting: %s\n", 258 mntbuf[i].f_mntonname); 259 if (unmount(mntbuf[i].f_mntonname, 0) < 0) { 260 err(1, "umount %s from %s", 261 mntbuf[i].f_mntfromname, 262 mntbuf[i].f_mntonname); 263 } 264 } 265 } 266 267 } 268 269 /* 270 * Eject - ejects various removable devices, including cdrom, tapes, 271 * diskettes, and other removable disks (like ZIP drives) 272 */ 273 int 274 main(argc, argv) 275 int argc; 276 char *argv[]; 277 { 278 char device[MAXPATHLEN]; 279 char *devpath; 280 char qualifier; 281 int umount_flag, devtype; 282 int i, ch; 283 284 /* Default options values */ 285 devpath = NULL; 286 devtype = -1; 287 umount_flag = 1; 288 289 while ((ch = getopt(argc, argv, "d:fnt:v")) != -1) { 290 switch (ch) { 291 case 'd': 292 devpath = optarg; 293 break; 294 case 'f': 295 umount_flag = 0; 296 break; 297 case 'n': 298 for (i = 0; devtab[i].name != NULL; i++) { 299 if (devtab[i].qualifier != '\0') { 300 printf("%9s => %s%c\n", devtab[i].name, 301 devtab[i].device, 302 devtab[i].qualifier); 303 } else { 304 printf("%9s => %s\n", devtab[i].name, 305 devtab[i].device); 306 } 307 } 308 return 0; 309 case 't': 310 for (i = 0; types[i].str != NULL; i++) { 311 if (strcasecmp(optarg, types[i].str) == 0) { 312 devtype = types[i].type; 313 break; 314 } 315 } 316 if (devtype == -1) 317 errx(1, "%s: unknown device type", optarg); 318 break; 319 case 'v': 320 verbose = 1; 321 break; 322 case '?': 323 default: 324 usage(); 325 /*NOTREACHED*/ 326 } 327 } 328 329 argc -= optind; 330 argv += optind; 331 332 if (devpath != NULL) { 333 /* device specified with 'd' option */ 334 if (devtype == -1) 335 device_by_name(devpath, &devtype, &qualifier); 336 else 337 qualifier = '\0'; 338 } else { 339 if (argc <= 0) { 340 errx(1, "No device specified"); 341 /* NOTREACHED */ 342 } 343 if (strncmp(argv[0], "/dev/", 5) == 0) { 344 /* 345 * If argument begins with "/dev/", assume 346 * a device name. 347 */ 348 if (devtype == -1) { 349 devpath = device_by_name(argv[0], 350 &devtype, &qualifier); 351 } else { 352 /* Device type specified; use literally */ 353 devpath = argv[0]; 354 qualifier = '\0'; 355 } 356 } else { 357 /* assume a nickname */ 358 devpath = device_by_nickname(argv[0], 359 &devtype, &qualifier); 360 } 361 } 362 363 if (devpath == NULL) { 364 errx(1, "%s: unknown device", argv[0]); 365 /*NOTREACHED*/ 366 } 367 368 if (umount_flag && MOUNTABLE(devtype)) { 369 umount_mounted(devpath); 370 } 371 372 snprintf(device, sizeof(device), "%s%c", devpath, qualifier); 373 if (verbose) 374 printf("Ejecting device `%s'\n", device); 375 switch (devtype) { 376 case DISK: 377 eject_disk(device); 378 break; 379 case TAPE: 380 eject_tape(device); 381 break; 382 default: 383 errx(1, "impossible... devtype = %d", devtype); 384 } 385 386 exit(0); 387 } 388