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