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