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