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