eject.c revision 1.6 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