xref: /netbsd-src/usr.bin/eject/eject.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*
2  * Copyright (c) 1995
3  *	Matthieu Herrb.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed for the NetBSD Project
16  *	by Matthieu Herrb.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Eject command
35  *
36  * It knows to eject floppies, CD-ROMS and tapes
37  * and tries to unmount file systems first
38  */
39 
40 #include <sys/param.h>
41 #include <sys/ioctl.h>
42 #include <sys/cdio.h>
43 #include <sys/mtio.h>
44 #include <sys/disklabel.h>
45 #include <sys/ucred.h>
46 #include <sys/mount.h>
47 #include <sys/cdefs.h>
48 
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <err.h>
54 #include <string.h>
55 
56 typedef struct DEVTAB {
57 	char   *name;
58 	char   *device;
59 	char   qualifier;
60 	u_int   type;
61 } DEVTAB;
62 
63 /*
64  * known device nicknames and types
65  * (used for selecting the proper ioctl to eject them)
66  */
67 #define DISK   0x00000002
68 #define TAPE   0x00010000
69 
70 #define MOUNTABLE(x) ((x) & 0x0000ffff)
71 
72 static DEVTAB devtab[] = {
73 	{ "diskette", "/dev/fd0", 'a', DISK },
74 	{ "diskette0", "/dev/fd0", 'a', DISK },
75 	{ "diskette1", "/dev/fd1", 'a', DISK },
76 	{ "floppy", "/dev/fd0", 'a', DISK },
77 	{ "floppy0", "/dev/fd0", 'a', DISK },
78 	{ "floppy1", "/dev/fd1", 'a', DISK },
79 	{ "fd", "/dev/fd0", 'a', DISK },
80 	{ "fd0", "/dev/fd0", 'a', DISK },
81 	{ "fd1", "/dev/fd1", 'a', DISK },
82 	{ "cdrom", "/dev/cd0", 'a', DISK },
83 	{ "cdrom0", "/dev/cd0", 'a', DISK },
84 	{ "cdrom1", "/dev/cd1", 'a', DISK },
85 	{ "cd", "/dev/cd0", 'a', DISK },
86 	{ "cd0", "/dev/cd0", 'a', DISK },
87 	{ "cd1", "/dev/cd1", 'a', DISK },
88 	{ "mcd", "/dev/mcd0", 'a', DISK },
89 	{ "mcd0", "/dev/mcd0", 'a', DISK },
90 	{ "mcd1", "/dev/mcd1", 'a', DISK },
91 	{ "tape", "/dev/rst0", '\0', TAPE },
92 	{ "tape0", "/dev/rst0", '\0', TAPE },
93 	{ "tape1", "/dev/rst1", '\0', TAPE },
94 	{ "st", "/dev/rst0", '\0', TAPE },
95 	{ "st0", "/dev/rst0", '\0', TAPE },
96 	{ "st1", "/dev/rst1", '\0', TAPE },
97 	{ "dat", "/dev/rst0", '\0', TAPE },
98 	{ "dat0", "/dev/rst0", '\0', TAPE },
99 	{ "dat1", "/dev/rst1", '\0', TAPE },
100 	{ "exabyte", "/dev/rst0", '\0', TAPE },
101 	{ "exabyte0", "/dev/rst0", '\0', TAPE },
102 	{ "exabyte1", "/dev/rst1", '\0', TAPE },
103 	{ NULL, NULL }
104 };
105 
106 struct types {
107 	char	*str;
108 	int	type;
109 } types[] = {
110 	{ "diskette", DISK },
111 	{ "floppy", DISK },
112 	{ "cdrom", DISK },
113 	{ "disk", DISK },
114 	{ "tape", TAPE },
115 	{ NULL, 0 }
116 };
117 
118 int verbose;
119 
120 /*
121  * remind the syntax of the command to the user
122  */
123 static void
124 usage()
125 {
126 	errx(1, "usage: eject [-n][-f][-t devtype][[-d] raw device | nickname ]");
127 	/*NOTREACHED*/
128 
129 }
130 
131 
132 /*
133  * given a device nick name, find its associated raw device and type
134  */
135 static char *
136 device_by_nickname(name, pdevtype, pqualifier)
137 	char	*name;
138 	int	*pdevtype;
139 	char	*pqualifier;
140 {
141 	int     i;
142 
143 	for (i = 0; devtab[i].name != NULL; i++) {
144 		if (strcmp(name, devtab[i].name) == 0) {
145 			*pdevtype = devtab[i].type;
146 			*pqualifier = devtab[i].qualifier;
147 			return devtab[i].device;
148 		}
149 	}
150 	*pdevtype = -1;
151 	return NULL;
152 }
153 
154 /*
155  * Given a raw device name, find its type and partition tag
156  * from the name.
157  */
158 static char *
159 device_by_name(device, pdevtype, pqualifier)
160 	char	*device;
161 	int	*pdevtype;
162 	char	*pqualifier;
163 {
164 	int     i;
165 
166 	for (i = 0; devtab[i].name != NULL; i++) {
167 		if (strncmp(devtab[i].device, device,
168 			    strlen(devtab[i].device)) == 0) {
169 			*pdevtype = devtab[i].type;
170 			*pqualifier = devtab[i].qualifier;
171 			return devtab[i].device;
172 		}
173 	}
174 	*pdevtype = -1;
175 	return NULL;
176 }
177 
178 /*
179  * eject a disk (including floppy and cdrom)
180  */
181 static void
182 eject_disk(device)
183 	char   *device;
184 {
185 	int     fd, arg = 0;
186 
187 	fd = open(device, O_RDONLY);
188 	if (fd < 0) {
189 		err(1, "%s: open", device);
190 	}
191 	if (ioctl(fd, DIOCLOCK, (char *)&arg) < 0) {
192 		err(1, "%s: DIOCLOCK", device);
193 	}
194 	if (ioctl(fd, DIOCEJECT, 0) < 0) {
195 		err(1, "%s: DIOCEJECT", device);
196 	}
197 	if (close(fd) != 0)
198 		err(1, "%s: close", device);
199 } /* eject_disk */
200 
201 /*
202  * eject a tape
203  */
204 static void
205 eject_tape(device)
206 	char   *device;
207 {
208 	int     fd;
209 	struct mtop mt_com;
210 
211 	fd = open(device, O_RDONLY);
212 	if (fd < 0) {
213 		err(1, "open %s", device);
214 	}
215 	mt_com.mt_op = MTOFFL;
216 
217 	if (ioctl(fd, MTIOCTOP, &mt_com) < 0) {
218 		err(1, "%s:  MTOFFL", device);
219 	}
220 	close(fd);
221 } /* eject_tape */
222 
223 /*
224  * test if partitions of a device are mounted
225  * and unmount them
226  */
227 static void
228 umount_mounted(device)
229 	char   *device;
230 {
231 	struct statfs *mntbuf;
232 	int     i, n, l;
233 
234 	n = getmntinfo(&mntbuf, MNT_NOWAIT);
235 	if (n == 0) {
236 		err(1, "getmntinfo");
237 	}
238 	l = strlen(device);
239 	for (i = 0; i < n; i++) {
240 		if (strncmp(device, mntbuf[i].f_mntfromname, l) == 0) {
241 			if (verbose)
242 				printf("Unmounting: %s\n",
243 					mntbuf[i].f_mntonname);
244 			if (unmount(mntbuf[i].f_mntonname, 0) < 0) {
245 				err(1, "umount %s from %s",
246 					mntbuf[i].f_mntfromname,
247 					mntbuf[i].f_mntonname);
248 			}
249 		}
250 	}
251 
252 }
253 
254 /*
255  * Eject - ejects various removable devices, including cdrom, tapes,
256  * diskettes, and other removable disks (like ZIP drives)
257  */
258 int
259 main(argc, argv)
260 	int     argc;
261 	char   *argv[];
262 {
263 	char    device[MAXPATHLEN];
264 	char	*devpath;
265 	char	qualifier;
266 	int     umount_flag, devtype;
267 	int     i, ch;
268 
269 	/* Default options values */
270 	devpath = NULL;
271 	devtype = -1;
272 	umount_flag = 1;
273 
274 	while ((ch = getopt(argc, argv, "d:fnt:v")) != EOF) {
275 		switch (ch) {
276 		case 'd':
277 			devpath = optarg;
278 			break;
279 		case 'f':
280 			umount_flag = 0;
281 			break;
282 		case 'n':
283 			for (i = 0; devtab[i].name != NULL; i++) {
284 				printf("%9s => %s%c\n",
285 					devtab[i].name,
286 					devtab[i].device, devtab[i].qualifier);
287 			}
288 			return 0;
289 		case 't':
290 			for (i = 0; types[i].str != NULL; i++) {
291 				if (strcasecmp(optarg, types[i].str) == 0) {
292 					devtype = types[i].type;
293 					break;
294 				}
295 			}
296 			if (devtype == -1)
297 				errx(1, "%s: unknown device type", optarg);
298 			break;
299 		case 'v':
300 			verbose = 1;
301 			break;
302 		case '?':
303 		default:
304 			usage();
305 			/*NOTREACHED*/
306 		}
307 	}
308 
309 	argc -= optind;
310 	argv += optind;
311 
312 	if (devpath != NULL) {
313 		/* device specified with 'd' option */
314 		if (devtype == -1)
315 			device_by_name(devpath, &devtype, &qualifier);
316 		else
317 			qualifier = '\0';
318 	} else {
319 		if (argc <= 0) {
320 			errx(1, "No device specified");
321 			/* NOTREACHED */
322 		}
323 		if (strncmp(argv[0], "/dev/", 5) == 0) {
324 			/*
325 			 * If argument begins with "/dev/", assume
326 			 * a device name.
327 			 */
328 			if (devtype == -1) {
329 				devpath = device_by_name(argv[0],
330 							 &devtype, &qualifier);
331 			} else {
332 				/* Device type specified; use literally */
333 				devpath = argv[0];
334 				qualifier = '\0';
335 			}
336 		} else {
337 			/* assume a nickname */
338 			devpath = device_by_nickname(argv[0],
339 						     &devtype, &qualifier);
340 		}
341 	}
342 
343 	if (devpath == NULL) {
344 		errx(1, "%s: unknown device", argv[0]);
345 		/*NOTREACHED*/
346 	}
347 
348 	if (umount_flag && MOUNTABLE(devtype)) {
349 		umount_mounted(devpath);
350 	}
351 
352 	snprintf(device, sizeof(device), "%s%c", devpath, qualifier);
353 	if (verbose)
354 		printf("Ejecting device `%s'\n", device);
355 	switch (devtype) {
356 	case DISK:
357 		eject_disk(device);
358 		break;
359 	case TAPE:
360 		eject_tape(device);
361 		break;
362 	default:
363 		errx(1, "impossible... devtype = %d", devtype);
364 	}
365 
366 	exit(0);
367 }
368