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