xref: /netbsd-src/usr.bin/eject/eject.c (revision f5d3fbbc6ff4a77159fb268d247bd94cb7d7e332)
1 /*	$NetBSD: eject.c,v 1.4 1997/10/18 13:38:15 lukem 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.4 1997/10/18 13:38:15 lukem 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 ]");
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 				printf("%9s => %s%c\n",
300 					devtab[i].name,
301 					devtab[i].device, devtab[i].qualifier);
302 			}
303 			return 0;
304 		case 't':
305 			for (i = 0; types[i].str != NULL; i++) {
306 				if (strcasecmp(optarg, types[i].str) == 0) {
307 					devtype = types[i].type;
308 					break;
309 				}
310 			}
311 			if (devtype == -1)
312 				errx(1, "%s: unknown device type", optarg);
313 			break;
314 		case 'v':
315 			verbose = 1;
316 			break;
317 		case '?':
318 		default:
319 			usage();
320 			/*NOTREACHED*/
321 		}
322 	}
323 
324 	argc -= optind;
325 	argv += optind;
326 
327 	if (devpath != NULL) {
328 		/* device specified with 'd' option */
329 		if (devtype == -1)
330 			device_by_name(devpath, &devtype, &qualifier);
331 		else
332 			qualifier = '\0';
333 	} else {
334 		if (argc <= 0) {
335 			errx(1, "No device specified");
336 			/* NOTREACHED */
337 		}
338 		if (strncmp(argv[0], "/dev/", 5) == 0) {
339 			/*
340 			 * If argument begins with "/dev/", assume
341 			 * a device name.
342 			 */
343 			if (devtype == -1) {
344 				devpath = device_by_name(argv[0],
345 							 &devtype, &qualifier);
346 			} else {
347 				/* Device type specified; use literally */
348 				devpath = argv[0];
349 				qualifier = '\0';
350 			}
351 		} else {
352 			/* assume a nickname */
353 			devpath = device_by_nickname(argv[0],
354 						     &devtype, &qualifier);
355 		}
356 	}
357 
358 	if (devpath == NULL) {
359 		errx(1, "%s: unknown device", argv[0]);
360 		/*NOTREACHED*/
361 	}
362 
363 	if (umount_flag && MOUNTABLE(devtype)) {
364 		umount_mounted(devpath);
365 	}
366 
367 	snprintf(device, sizeof(device), "%s%c", devpath, qualifier);
368 	if (verbose)
369 		printf("Ejecting device `%s'\n", device);
370 	switch (devtype) {
371 	case DISK:
372 		eject_disk(device);
373 		break;
374 	case TAPE:
375 		eject_tape(device);
376 		break;
377 	default:
378 		errx(1, "impossible... devtype = %d", devtype);
379 	}
380 
381 	exit(0);
382 }
383