eject.c revision 1.13 1 /* $NetBSD: eject.c,v 1.13 2001/01/21 09:55:40 mycroft 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.13 2001/01/21 09:55:40 mycroft Exp $");
47 #endif /* not lint */
48
49 #include <ctype.h>
50 #include <err.h>
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <sys/cdio.h>
57 #include <sys/disklabel.h>
58 #include <sys/ioctl.h>
59 #include <sys/param.h>
60 #include <sys/ucred.h>
61 #include <sys/mount.h>
62 #include <sys/mtio.h>
63
64 struct nicknames_s {
65 char *name; /* The name given on the command line. */
66 char *devname; /* The base name of the device */
67 int type; /* The type of device, for determining
68 what ioctl to use. */
69 #define TAPE 0x10
70 #define DISK 0x20
71 /* OR one of the above with one of the below: */
72 #define NOTLOADABLE 0x00
73 #define LOADABLE 0x01
74 #define TYPEMASK ((int)~0x01)
75 } nicknames[] = {
76 { "diskette", "fd", DISK | NOTLOADABLE },
77 { "floppy", "fd", DISK | NOTLOADABLE },
78 { "fd", "fd", DISK | NOTLOADABLE },
79 { "sd", "sd", DISK | NOTLOADABLE },
80 { "cdrom", "cd", DISK | LOADABLE },
81 { "cd", "cd", DISK | LOADABLE },
82 { "cdr", "cd", DISK | LOADABLE },
83 { "cdrw", "cd", DISK | LOADABLE },
84 { "dvdrom", "cd", DISK | LOADABLE },
85 { "dvd", "cd", DISK | LOADABLE },
86 { "dvdr", "cd", DISK | LOADABLE },
87 { "dvdrw", "cd", DISK | LOADABLE },
88 { "mcd", "mcd", DISK | LOADABLE }, /* XXX Is this true? */
89 { "tape", "st", TAPE | NOTLOADABLE },
90 { "st", "st", TAPE | NOTLOADABLE },
91 { "dat", "st", TAPE | NOTLOADABLE },
92 { "exabyte", "st", TAPE | NOTLOADABLE },
93 };
94 #define MAXNICKLEN 12 /* at least enough room for the
95 longest nickname */
96 #define MAXDEVLEN (MAXNICKLEN + 7) /* "/dev/r" ... "a" */
97
98 struct devtypes_s {
99 char *name;
100 int type;
101 } devtypes[] = {
102 { "diskette", DISK | NOTLOADABLE },
103 { "floppy", DISK | NOTLOADABLE },
104 { "cdrom", DISK | LOADABLE },
105 { "disk", DISK | NOTLOADABLE },
106 { "tape", TAPE | NOTLOADABLE },
107 };
108
109 int verbose_f = 0;
110 int umount_f = 1;
111 int load_f = 0;
112
113 int main __P((int, char *[]));
114 void usage __P((void));
115 char *nick2dev __P((char *));
116 char *nick2rdev __P((char *));
117 int guess_devtype __P((char *));
118 char *guess_nickname __P((char *));
119 void eject_tape __P((char *));
120 void eject_disk __P((char *));
121 void unmount_dev __P((char *));
122
123 int
124 main(int argc,
125 char *argv[])
126 {
127 int ch;
128 int devtype = -1;
129 int n, i;
130 char *devname = NULL;
131
132 while((ch = getopt(argc, argv, "d:flnt:v")) != -1) {
133 switch(ch) {
134 case 'd':
135 devname = optarg;
136 break;
137 case 'f':
138 umount_f = 0;
139 break;
140 case 'l':
141 load_f = 1;
142 break;
143 case 'n':
144 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]);
145 n++) {
146 struct nicknames_s *np = &nicknames[n];
147
148 printf("%s -> %s\n", np->name, nick2dev(np->name));
149 }
150 return(0);
151 case 't':
152 for(i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]);
153 i++) {
154 if(strcasecmp(devtypes[i].name, optarg) == 0) {
155 devtype = devtypes[i].type;
156 break;
157 }
158 }
159 if(devtype == -1) {
160 errx(1, "%s: unknown device type\n", optarg);
161 }
162 break;
163 case 'v':
164 verbose_f = 1;
165 break;
166 default:
167 warnx("-%c: unknown switch", ch);
168 usage();
169 /* NOTREACHED */
170 }
171 }
172 argc -= optind;
173 argv += optind;
174
175 if(devname == NULL) {
176 if(argc == 0) {
177 usage();
178 /* NOTREACHED */
179 } else
180 devname = argv[0];
181 }
182
183
184 if(devtype == -1) {
185 devtype = guess_devtype(devname);
186 }
187 if(devtype == -1) {
188 errx(1, "%s: unable to determine type of device",
189 devname);
190 }
191 if(verbose_f) {
192 printf("device type == ");
193 if((devtype & TYPEMASK) == TAPE)
194 printf("tape\n");
195 else
196 printf("disk, floppy, or cdrom\n");
197 }
198
199 if(umount_f)
200 unmount_dev(devname);
201
202 /* XXX Tapes and disks have different ioctl's: */
203 if((devtype & TYPEMASK) == TAPE)
204 eject_tape(devname);
205 else
206 eject_disk(devname);
207
208 if(verbose_f)
209 printf("done.\n");
210
211 return(0);
212 }
213
214 void
215 usage(void)
216 {
217 errx(1, "Usage: eject [-n][-f][-v][-l][-t type][-d] device | nickname");
218 }
219
220 int
221 guess_devtype(char *devname)
222 {
223 int n;
224
225 /* Nickname match: */
226 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]);
227 n++) {
228 if(strncasecmp(nicknames[n].name, devname,
229 strlen(nicknames[n].name)) == 0)
230 return(nicknames[n].type);
231 }
232
233 /*
234 * If we still don't know it, then try to compare vs. dev
235 * and rdev names that we know.
236 */
237 /* dev first: */
238 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
239 char *name;
240 name = nick2dev(nicknames[n].name);
241 /*
242 * Assume that the part of the name that distinguishes the
243 * instance of this device begins with a 0.
244 */
245 *(strchr(name, '0')) = '\0';
246 if(strncmp(name, devname, strlen(name)) == 0)
247 return(nicknames[n].type);
248 }
249
250 /* Now rdev: */
251 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
252 char *name = nick2rdev(nicknames[n].name);
253 *(strchr(name, '0')) = '\0';
254 if(strncmp(name, devname, strlen(name)) == 0)
255 return(nicknames[n].type);
256 }
257
258 /* Not found. */
259 return(-1);
260 }
261
262 /* "floppy5" -> "/dev/fd5a". Yep, this uses a static buffer. */
263 char *
264 nick2dev(char *nn)
265 {
266 int n;
267 static char devname[MAXDEVLEN];
268 int devnum = 0;
269
270 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
271 if(strncasecmp(nicknames[n].name, nn,
272 strlen(nicknames[n].name)) == 0) {
273 sscanf(nn, "%*[^0-9]%d", &devnum);
274 sprintf(devname, "/dev/%s%d", nicknames[n].devname,
275 devnum);
276 if((nicknames[n].type & TYPEMASK) != TAPE)
277 strcat(devname, "a");
278 return(devname);
279 }
280 }
281
282 return(NULL);
283 }
284
285 /* "floppy5" -> "/dev/rfd5c". Static buffer. */
286 char *
287 nick2rdev(char *nn)
288 {
289 int n;
290 static char devname[MAXDEVLEN];
291 int devnum = 0;
292
293 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
294 if(strncasecmp(nicknames[n].name, nn,
295 strlen(nicknames[n].name)) == 0) {
296 sscanf(nn, "%*[^0-9]%d", &devnum);
297 sprintf(devname, "/dev/r%s%d", nicknames[n].devname,
298 devnum);
299 if((nicknames[n].type & TYPEMASK) != TAPE) {
300 strcat(devname, "a");
301 devname[strlen(devname) - 1] += RAW_PART;
302 }
303 return(devname);
304 }
305 }
306
307 return(NULL);
308 }
309
310 /* Unmount all filesystems attached to dev. */
311 void
312 unmount_dev(char *name)
313 {
314 struct statfs *mounts;
315 int i, nmnts, len;
316 char *dn;
317
318 nmnts = getmntinfo(&mounts, MNT_NOWAIT);
319 if(nmnts == 0) {
320 err(1, "getmntinfo");
321 }
322
323 /* Make sure we have a device name: */
324 dn = nick2dev(name);
325 if(dn == NULL)
326 dn = name;
327
328 /* Set len to strip off the partition name: */
329 len = strlen(dn);
330 if(!isdigit(dn[len - 1]))
331 len--;
332 if(!isdigit(dn[len - 1])) {
333 errx(1, "Can't figure out base name for dev name %s", dn);
334 }
335
336 for(i = 0; i < nmnts; i++) {
337 if(strncmp(mounts[i].f_mntfromname, dn, len) == 0) {
338 if(verbose_f)
339 printf("Unmounting %s from %s...\n",
340 mounts[i].f_mntfromname,
341 mounts[i].f_mntonname);
342
343 if(unmount(mounts[i].f_mntonname, 0) == -1) {
344 err(1, "unmount: %s", mounts[i].f_mntonname);
345 }
346 }
347 }
348
349 return;
350 }
351
352 void
353 eject_tape(char *name)
354 {
355 struct mtop m;
356 int fd;
357 char *dn;
358
359 dn = nick2rdev(name);
360 if(dn == NULL) {
361 dn = name; /* Hope for the best. */
362 }
363
364 fd = open(dn, O_RDONLY);
365 if(fd == -1) {
366 err(1, "open: %s", dn);
367 }
368
369 if(verbose_f)
370 printf("Ejecting %s...\n", dn);
371
372 m.mt_op = MTOFFL;
373 m.mt_count = 0;
374 if(ioctl(fd, MTIOCTOP, &m) == -1) {
375 err(1, "ioctl: MTIOCTOP: %s", dn);
376 }
377
378 close(fd);
379 return;
380 }
381
382 void
383 eject_disk(char *name)
384 {
385 int fd;
386 char *dn;
387 int arg;
388
389 dn = nick2rdev(name);
390 if(dn == NULL) {
391 dn = name; /* Hope for the best. */
392 }
393
394 fd = open(dn, O_RDONLY);
395 if(fd == -1) {
396 err(1, "open: %s", dn);
397 }
398
399 if(load_f) {
400 if(verbose_f)
401 printf("Closing %s...\n", dn);
402
403 if(ioctl(fd, CDIOCCLOSE, NULL) == -1) {
404 err(1, "ioctl: CDIOCCLOSE: %s", dn);
405 }
406 } else {
407 if(verbose_f)
408 printf("Ejecting %s...\n", dn);
409
410 arg = 0;
411 if (umount_f == 0) {
412 /* force eject, unlock the device first */
413 if(ioctl(fd, DIOCLOCK, &arg) == -1)
414 err(1, "ioctl: DIOCEJECT: %s", dn);
415 arg = 1;
416 }
417 if(ioctl(fd, DIOCEJECT, &arg) == -1)
418 err(1, "ioctl: DIOCEJECT: %s", dn);
419 }
420
421 close(fd);
422 return;
423 }
424