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