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