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