eject.c revision 1.20 1 /* $NetBSD: eject.c,v 1.20 2006/09/24 08:42:55 xtraeme Exp $ */
2
3 /*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Chris Jones.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1999 The NetBSD Foundation, Inc.\n\
42 All rights reserved.\n");
43 #endif /* not lint */
44
45 #ifndef lint
46 __RCSID("$NetBSD: eject.c,v 1.20 2006/09/24 08:42:55 xtraeme Exp $");
47 #endif /* not lint */
48
49 #include <sys/types.h>
50 #include <sys/cdio.h>
51 #include <sys/disklabel.h>
52 #include <sys/ioctl.h>
53 #include <sys/param.h>
54 #include <sys/ucred.h>
55 #include <sys/mount.h>
56 #include <sys/mtio.h>
57
58 #include <ctype.h>
59 #include <err.h>
60 #include <fcntl.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include <util.h>
66
67 struct nicknames_s {
68 char *name; /* The name given on the command line. */
69 char *devname; /* The base name of the device */
70 int type; /* The type of device, for determining what
71 * ioctl to use. */
72 #define TAPE 0x10
73 #define DISK 0x20
74 /* OR one of the above with one of the below: */
75 #define NOTLOADABLE 0x00
76 #define LOADABLE 0x01
77 #define FLOPPY 0x2
78 #define TYPEMASK ((int)~0x01)
79 } nicknames[] = {
80 { "diskette", "fd", DISK | FLOPPY | NOTLOADABLE },
81 { "floppy", "fd", DISK | FLOPPY | NOTLOADABLE },
82 { "fd", "fd", DISK | FLOPPY | NOTLOADABLE },
83 { "sd", "sd", DISK | NOTLOADABLE },
84 { "cdrom", "cd", DISK | LOADABLE },
85 { "cd", "cd", DISK | LOADABLE },
86 { "cdr", "cd", DISK | LOADABLE },
87 { "cdrw", "cd", DISK | LOADABLE },
88 { "dvdrom", "cd", DISK | LOADABLE },
89 { "dvd", "cd", DISK | LOADABLE },
90 { "dvdr", "cd", DISK | LOADABLE },
91 { "dvdrw", "cd", DISK | LOADABLE },
92 { "mcd", "mcd", DISK | LOADABLE }, /* XXX Is this true? */
93 { "tape", "st", TAPE | NOTLOADABLE },
94 { "st", "st", TAPE | NOTLOADABLE },
95 { "dat", "st", TAPE | NOTLOADABLE },
96 { "exabyte", "st", TAPE | NOTLOADABLE },
97 };
98 #define MAXNICKLEN 12 /* at least enough room for the longest
99 * nickname */
100 #define MAXDEVLEN (MAXNICKLEN + 7) /* "/dev/r" ... "a" */
101
102 struct devtypes_s {
103 char *name;
104 int type;
105 } devtypes[] = {
106 { "diskette", DISK | NOTLOADABLE },
107 { "floppy", DISK | NOTLOADABLE },
108 { "cdrom", DISK | LOADABLE },
109 { "disk", DISK | NOTLOADABLE },
110 { "tape", TAPE | NOTLOADABLE },
111 };
112
113 enum eject_op {
114 OP_EJECT, OP_LOAD, OP_LOCK, OP_UNLOCK
115 };
116
117 int verbose_f = 0;
118 int umount_f = 1;
119
120 int main(int, char *[]);
121 void usage(void);
122 char *nick2dev(char *);
123 char *nick2rdev(char *);
124 int guess_devtype(char *);
125 char *guess_nickname(char *);
126 void eject_tape(char *, enum eject_op);
127 void eject_disk(char *, enum eject_op);
128 void unmount_dev(char *);
129
130 int
131 main(int argc, char *argv[])
132 {
133 int ch;
134 int devtype = -1;
135 int n, i;
136 char *devname = NULL;
137 enum eject_op op = OP_EJECT;
138
139 while ((ch = getopt(argc, argv, "d:flLnt:Uv")) != -1) {
140 switch (ch) {
141 case 'd':
142 devname = optarg;
143 break;
144 case 'f':
145 umount_f = 0;
146 break;
147 case 'l':
148 if (op != OP_EJECT)
149 usage();
150 op = OP_LOAD;
151 break;
152 case 'L':
153 if (op != OP_EJECT)
154 usage();
155 op = OP_LOCK;
156 break;
157 case 'n':
158 for (n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]);
159 n++) {
160 struct nicknames_s *np = &nicknames[n];
161
162 printf("%s -> %s\n", np->name, nick2dev(np->name));
163 }
164 return (0);
165 case 't':
166 for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]);
167 i++) {
168 if (strcasecmp(devtypes[i].name, optarg) == 0) {
169 devtype = devtypes[i].type;
170 break;
171 }
172 }
173 if (devtype == -1)
174 errx(1, "%s: unknown device type", optarg);
175 break;
176 case 'U':
177 if (op != OP_EJECT)
178 usage();
179 op = OP_UNLOCK;
180 break;
181 case 'v':
182 verbose_f = 1;
183 break;
184 default:
185 usage();
186 /* NOTREACHED */
187 }
188 }
189 argc -= optind;
190 argv += optind;
191
192 if (devname == NULL) {
193 if (argc == 0) {
194 usage();
195 /* NOTREACHED */
196 } else
197 devname = argv[0];
198 }
199 if (devtype == -1)
200 devtype = guess_devtype(devname);
201 if (devtype == -1)
202 errx(1, "%s: unable to determine type of device",
203 devname);
204 if (verbose_f) {
205 printf("device type == ");
206 if ((devtype & TYPEMASK) == TAPE)
207 printf("tape\n");
208 else
209 printf("disk, floppy, or cdrom\n");
210 }
211 if (umount_f)
212 unmount_dev(devname);
213
214 /* XXX Tapes and disks have different ioctl's: */
215 if ((devtype & TYPEMASK) == TAPE)
216 eject_tape(devname, op);
217 else
218 eject_disk(devname, op);
219
220 if (verbose_f)
221 printf("done.\n");
222
223 return (0);
224 }
225
226 void
227 usage(void)
228 {
229
230 fprintf(stderr, "usage: eject [-fv] [-l | -L | -U] "
231 "[-t device-type] [-d] device\n");
232 fprintf(stderr, " eject -n\n");
233 exit(1);
234 }
235
236 int
237 guess_devtype(char *devname)
238 {
239 int n;
240
241 /* Nickname match: */
242 for (n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]);
243 n++) {
244 if (strncasecmp(nicknames[n].name, devname,
245 strlen(nicknames[n].name)) == 0)
246 return (nicknames[n].type);
247 }
248
249 /*
250 * If we still don't know it, then try to compare vs. dev
251 * and rdev names that we know.
252 */
253 /* dev first: */
254 for (n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
255 char *name;
256 name = nick2dev(nicknames[n].name);
257 /*
258 * Assume that the part of the name that distinguishes the
259 * instance of this device begins with a 0.
260 */
261 *(strchr(name, '0')) = '\0';
262 if (strncmp(name, devname, strlen(name)) == 0)
263 return (nicknames[n].type);
264 }
265
266 /* Now rdev: */
267 for (n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
268 char *name = nick2rdev(nicknames[n].name);
269 *(strchr(name, '0')) = '\0';
270 if (strncmp(name, devname, strlen(name)) == 0)
271 return (nicknames[n].type);
272 }
273
274 /* Not found. */
275 return (-1);
276 }
277 /* "floppy5" -> "/dev/fd5a". Yep, this uses a static buffer. */
278 char *
279 nick2dev(char *nn)
280 {
281 int n;
282 static char devname[MAXDEVLEN];
283 int devnum = 0;
284
285 for (n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
286 if (strncasecmp(nicknames[n].name, nn,
287 strlen(nicknames[n].name)) == 0) {
288 sscanf(nn, "%*[^0-9]%d", &devnum);
289 sprintf(devname, "/dev/%s%d", nicknames[n].devname,
290 devnum);
291 if ((nicknames[n].type & TYPEMASK) != TAPE)
292 strcat(devname, "a");
293 return (devname);
294 }
295 }
296
297 return (NULL);
298 }
299 /* "floppy5" -> "/dev/rfd5c". Static buffer. */
300 char *
301 nick2rdev(char *nn)
302 {
303 int n;
304 static char devname[MAXDEVLEN];
305 int devnum = 0;
306
307 for (n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
308 if (strncasecmp(nicknames[n].name, nn,
309 strlen(nicknames[n].name)) == 0) {
310 sscanf(nn, "%*[^0-9]%d", &devnum);
311 sprintf(devname, "/dev/r%s%d", nicknames[n].devname,
312 devnum);
313 if ((nicknames[n].type & TYPEMASK) != TAPE) {
314 strcat(devname, "a");
315 if ((nicknames[n].type & FLOPPY) != FLOPPY)
316 devname[strlen(devname) - 1] += getrawpartition();
317 }
318 return (devname);
319 }
320 }
321
322 return (NULL);
323 }
324 /* Unmount all filesystems attached to dev. */
325 void
326 unmount_dev(char *name)
327 {
328 struct statvfs *mounts;
329 int i, nmnts, len;
330 char *dn;
331
332 nmnts = getmntinfo(&mounts, MNT_NOWAIT);
333 if (nmnts == 0) {
334 err(1, "getmntinfo");
335 }
336 /* Make sure we have a device name: */
337 dn = nick2dev(name);
338 if (dn == NULL)
339 dn = name;
340
341 /* Set len to strip off the partition name: */
342 len = strlen(dn);
343 if (!isdigit((unsigned char)dn[len - 1]))
344 len--;
345 if (!isdigit((unsigned char)dn[len - 1])) {
346 errx(1, "Can't figure out base name for dev name %s", dn);
347 }
348 for (i = 0; i < nmnts; i++) {
349 if (strncmp(mounts[i].f_mntfromname, dn, len) == 0) {
350 if (verbose_f)
351 printf("Unmounting %s from %s...\n",
352 mounts[i].f_mntfromname,
353 mounts[i].f_mntonname);
354
355 if (unmount(mounts[i].f_mntonname, 0) == -1) {
356 err(1, "unmount: %s", mounts[i].f_mntonname);
357 }
358 }
359 }
360
361 return;
362 }
363
364 void
365 eject_tape(char *name, enum eject_op op)
366 {
367 struct mtop m;
368 int fd;
369 char *dn;
370
371 dn = nick2rdev(name);
372 if (dn == NULL)
373 dn = name; /* Hope for the best. */
374 fd = open(dn, O_RDONLY);
375 if (fd == -1)
376 err(1, "open: %s", dn);
377 switch (op) {
378 case OP_EJECT:
379 if (verbose_f)
380 printf("Ejecting %s...\n", dn);
381
382 m.mt_op = MTOFFL;
383 m.mt_count = 0;
384 if (ioctl(fd, MTIOCTOP, &m) == -1)
385 err(1, "ioctl: MTIOCTOP: %s", dn);
386 break;
387 case OP_LOAD:
388 errx(1, "cannot load tapes");
389 /* NOTREACHED */
390 case OP_LOCK:
391 errx(1, "cannot lock tapes");
392 /* NOTREACHED */
393 case OP_UNLOCK:
394 errx(1, "cannot unlock tapes");
395 /* NOTREACHED */
396 }
397 close(fd);
398 return;
399 }
400
401 void
402 eject_disk(char *name, enum eject_op op)
403 {
404 int fd;
405 char *dn;
406 int arg;
407
408 dn = nick2rdev(name);
409 if (dn == NULL)
410 dn = name; /* Hope for the best. */
411 fd = open(dn, O_RDONLY);
412 if (fd == -1)
413 err(1, "open: %s", dn);
414 switch (op) {
415 case OP_LOAD:
416 if (verbose_f)
417 printf("Closing %s...\n", dn);
418
419 if (ioctl(fd, CDIOCCLOSE, NULL) == -1)
420 err(1, "ioctl: CDIOCCLOSE: %s", dn);
421 break;
422 case OP_EJECT:
423 if (verbose_f)
424 printf("Ejecting %s...\n", dn);
425
426 arg = 0;
427 if (umount_f == 0) {
428 /* force eject, unlock the device first */
429 if (ioctl(fd, DIOCLOCK, &arg) == -1)
430 err(1, "ioctl: DIOCLOCK: %s", dn);
431 arg = 1;
432 }
433 if (ioctl(fd, DIOCEJECT, &arg) == -1)
434 err(1, "ioctl: DIOCEJECT: %s", dn);
435 break;
436 case OP_LOCK:
437 if (verbose_f)
438 printf("Locking %s...\n", dn);
439
440 arg = 1;
441 if (ioctl(fd, DIOCLOCK, &arg) == -1)
442 err(1, "ioctl: DIOCLOCK: %s", dn);
443 break;
444 case OP_UNLOCK:
445 if (verbose_f)
446 printf("Unlocking %s...\n", dn);
447
448 arg = 0;
449 if (ioctl(fd, DIOCLOCK, &arg) == -1)
450 err(1, "ioctl: DIOCLOCK: %s", dn);
451 break;
452 }
453
454 close(fd);
455 return;
456 }
457