eject.c revision 1.8 1 /* $NetBSD: eject.c,v 1.8 1999/02/17 22:59:14 tron Exp $ */
2
3 /*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Chris Jones.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1999 The NetBSD Foundation, Inc.\n\
42 All rights reserved.\n");
43 #endif /* not lint */
44
45 #ifndef lint
46 __RCSID("$NetBSD: eject.c,v 1.8 1999/02/17 22:59:14 tron Exp $");
47 #endif /* not lint */
48
49 #include <sys/param.h>
50 #include <sys/wait.h>
51
52 #include <ctype.h>
53 #include <err.h>
54 #include <locale.h>
55 #include <paths.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #define DEFAULT_CC "cc"
62 #define DEFAULT_PATH _PATH_DEFPATH
63 #define DEFAULT_FILENAME ".depend"
64
65 void usage __P((void));
66 char *findcc __P((const char *));
67 int main __P((int, char **));
68
69 void
70 usage()
71 {
72 (void)fprintf(stderr,
73 "usage: mkdep [-a] [-p] [-f file ...] flags file ...\n");
74 exit(EXIT_FAILURE);
75 }
76
77 char *
78 findcc(progname)
79 const char *progname;
80 {
81 char *path, *dir, *next;
82 char buffer[MAXPATHLEN];
83
84 if (strchr(progname, '/') != NULL)
85 return access(progname, X_OK) ? NULL : strdup(progname);
86
87 if (((path = getenv("PATH")) == NULL) ||
88 ((path = strdup(path)) == NULL))
89 return NULL;
90
91 dir = path;
92 while (dir != NULL) {
93 if ((next = strchr(dir, ':')) != NULL)
94 *next++ = '\0';
95
96 if (snprintf(buffer, sizeof(buffer),
97 "%s/%s", dir, progname) < sizeof(buffer)) {
98 if (!access(buffer, X_OK)) {
99 free(path);
100 return strdup(buffer);
101 }
102 }
103 dir = next;
104 }
105
106 free(path);
107 return NULL;
108 }
109
110 int
111 main(argc, argv)
112 int argc;
113 char **argv;
114 {
115 int aflag, pflag, index, tmpfd, status;
116 pid_t cpid, pid;
117 char *filename, *CC, *pathname, tmpfilename[MAXPATHLEN], **args;
118 FILE *tmpfile, *dependfile;
119 char buffer[32768];
120
121 setlocale(LC_ALL, "");
122
123 aflag = 0;
124 pflag = 0;
125 filename = DEFAULT_FILENAME;
126 for (index=1; index< argc; index++)
127 if (strcmp(argv[index], "-a") == 0)
128 aflag = 1;
129 else
130 if (strcmp(argv[index], "-f") == 0) {
131 if (++index < argc)
132 filename = argv[index];
133 }
134 else
135 if (strcmp(argv[index], "-p") == 0)
136 pflag = 1;
137 else
138 break;
139
140 argc -= index;
141 argv += index;
142 if (argc == 0)
143 usage();
144
145 if ((CC = getenv("CC")) == NULL)
146 CC = DEFAULT_CC;
147 if ((pathname = findcc(CC)) == NULL)
148 if (!setenv("PATH", DEFAULT_PATH, 1))
149 pathname = findcc(CC);
150 if (pathname == NULL) {
151 (void)fprintf(stderr, "mkdep: %s: not found\n", CC);
152 return EXIT_FAILURE;
153 }
154
155 if ((args = malloc((argc + 3) * sizeof(char *))) == NULL) {
156 perror("mkdep");
157 exit(EXIT_FAILURE);
158 }
159 args[0] = CC;
160 args[1] = "-M";
161 (void)memcpy(&args[2], argv, (argc + 1) * sizeof(char *));
162
163 (void)strcpy(tmpfilename, _PATH_TMP "mkdepXXXXXX");
164 if ((tmpfd = mkstemp (tmpfilename)) < 0) {
165 warn("unable to create temporary file %s", tmpfilename);
166 return EXIT_FAILURE;
167 }
168
169 #ifdef __GNUC__ /* to shut up gcc warnings */
170 (void)&aflag;
171 (void)&pflag;
172 (void)&filename;
173 (void)&pathname;
174 #endif
175
176 switch (cpid = vfork()) {
177 case 0:
178 (void)dup2(tmpfd, STDOUT_FILENO);
179 (void)close(tmpfd);
180
181 (void)execv(pathname, args);
182 _exit(EXIT_FAILURE);
183
184 case -1:
185 (void)fputs("mkdep: unable to fork.\n", stderr);
186 (void)close(tmpfd);
187 (void)unlink(tmpfilename);
188 return EXIT_FAILURE;
189 }
190
191 while (((pid = wait(&status)) != cpid) && (pid >= 0));
192
193 if (status) {
194 (void)fputs("mkdep: compile failed.\n", stderr);
195 (void)close(tmpfd);
196 (void)unlink(tmpfilename);
197 return EXIT_FAILURE;
198 }
199
200 (void)lseek(tmpfd, 0, SEEK_SET);
201 if ((tmpfile = fdopen(tmpfd, "r")) == NULL) {
202 (void)fprintf(stderr,
203 "mkdep: unable to read temporary file %s\n",
204 tmpfilename);
205 (void)close(tmpfd);
206 (void)unlink(tmpfilename);
207 return EXIT_FAILURE;
208 }
209
210 if ((dependfile = fopen(filename, aflag ? "a" : "w")) == NULL) {
211 (void)fprintf(stderr,
212 "mkdep: unable to %s to file %s\n",
213 aflag ? "append" : "write", filename);
214 (void)fclose(tmpfile);
215 (void)unlink(tmpfilename);
216 return EXIT_FAILURE;
217 }
218
219 while (fgets(buffer, sizeof(buffer), tmpfile) != NULL) {
220 char *ptr;
221
222 if (pflag && ((ptr = strstr(buffer, ".o")) != NULL)) {
223 char *colon;
224
225 colon = ptr + 2;
226 while (isspace(*colon)) colon++;
227 if (*colon == ':')
228 (void)strcpy(ptr, colon);
229 }
230
231 ptr = buffer;
232 while (*ptr)
233 if (isspace(*ptr++))
234 if ((ptr[0] == '.') && (ptr[1] == '/'))
235 (void)strcpy(ptr, ptr + 2);
236
237 (void)fputs(buffer, dependfile);
238 }
239
240 (void)fclose(dependfile);
241 (void)fclose(tmpfile);
242 (void)unlink(tmpfilename);
243
244 return EXIT_SUCCESS;
245 }
246
247 /*
248 * Eject program. Deals with floppies, cdroms, and tapes. We could
249 * really use a generic GDIOCEJECT ioctl, that would work with
250 * everything. This eject also knows how to load media into cdrom
251 * drives, and it will attempt to extrapolate a device name from a
252 * nickname and number.
253 */
254
255 #include <sys/cdefs.h>
256 #ifndef lint
257 __RCSID("$NetBSD: eject.c,v 1.8 1999/02/17 22:59:14 tron Exp $");
258 #endif
259
260 #include <ctype.h>
261 #include <err.h>
262 #include <fcntl.h>
263 #include <stdio.h>
264 #include <stdlib.h>
265 #include <string.h>
266 #include <unistd.h>
267 #include <sys/cdio.h>
268 #include <sys/disklabel.h>
269 #include <sys/ioctl.h>
270 #include <sys/param.h>
271 #include <sys/ucred.h>
272 #include <sys/mount.h>
273 #include <sys/mtio.h>
274
275 struct nicknames_s {
276 char *name; /* The name given on the command line. */
277 char *devname; /* The base name of the device */
278 int type; /* The type of device, for determining
279 what ioctl to use. */
280 #define TAPE 0x10
281 #define DISK 0x20
282 /* OR one of the above with one of the below: */
283 #define NOTLOADABLE 0x00
284 #define LOADABLE 0x01
285 #define TYPEMASK ((int)~0x01)
286 } nicknames[] = {
287 { "diskette", "fd", DISK | NOTLOADABLE },
288 { "floppy", "fd", DISK | NOTLOADABLE },
289 { "fd", "fd", DISK | NOTLOADABLE },
290 { "cdrom", "cd", DISK | LOADABLE },
291 { "cd", "cd", DISK | LOADABLE },
292 { "mcd", "mcd", DISK | LOADABLE }, /* XXX Is this true? */
293 { "tape", "st", TAPE | NOTLOADABLE },
294 { "st", "st", TAPE | NOTLOADABLE },
295 { "dat", "st", TAPE | NOTLOADABLE },
296 { "exabyte", "st", TAPE | NOTLOADABLE },
297 };
298 #define MAXNICKLEN 12 /* at least enough room for the
299 longest nickname */
300 #define MAXDEVLEN (MAXNICKLEN + 7) /* "/dev/r" ... "a" */
301
302 struct devtypes_s {
303 char *name;
304 int type;
305 } devtypes[] = {
306 { "diskette", DISK | NOTLOADABLE },
307 { "floppy", DISK | NOTLOADABLE },
308 { "cdrom", DISK | LOADABLE },
309 { "disk", DISK | NOTLOADABLE },
310 { "tape", TAPE | NOTLOADABLE },
311 };
312
313 int verbose_f = 0;
314 int umount_f = 1;
315 int load_f = 0;
316
317 int main __P((int, char *[]));
318 void usage __P((void));
319 char *nick2dev __P((char *));
320 char *nick2rdev __P((char *));
321 int guess_devtype __P((char *));
322 char *guess_nickname __P((char *));
323 void eject_tape __P((char *));
324 void eject_disk __P((char *));
325 void unmount_dev __P((char *));
326
327 int
328 main(int argc,
329 char *argv[])
330 {
331 int ch;
332 int devtype = -1;
333 int n, i;
334 char *devname = NULL;
335
336 while((ch = getopt(argc, argv, "d:flnt:v")) != -1) {
337 switch(ch) {
338 case 'd':
339 devname = optarg;
340 break;
341 case 'f':
342 umount_f = 0;
343 break;
344 case 'l':
345 load_f = 1;
346 break;
347 case 'n':
348 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]);
349 n++) {
350 struct nicknames_s *np = &nicknames[n];
351
352 printf("%s -> %s\n", np->name, nick2dev(np->name));
353 }
354 return(0);
355 case 't':
356 for(i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]);
357 i++) {
358 if(strcasecmp(devtypes[i].name, optarg) == 0) {
359 devtype = devtypes[i].type;
360 break;
361 }
362 }
363 if(devtype == -1) {
364 errx(1, "%s: unknown device type\n", optarg);
365 }
366 break;
367 case 'v':
368 verbose_f = 1;
369 break;
370 default:
371 warnx("-%c: unknown switch", ch);
372 usage();
373 /* NOTREACHED */
374 }
375 }
376 argc -= optind;
377 argv += optind;
378
379 if(devname == NULL) {
380 if(argc == 0) {
381 usage();
382 /* NOTREACHED */
383 } else
384 devname = argv[0];
385 }
386
387
388 if(devtype == -1) {
389 devtype = guess_devtype(devname);
390 }
391 if(devtype == -1) {
392 errx(1, "%s: unable to determine type of device",
393 devname);
394 }
395 if(verbose_f) {
396 printf("device type == ");
397 if((devtype & TYPEMASK) == TAPE)
398 printf("tape\n");
399 else
400 printf("disk, floppy, or cdrom\n");
401 }
402
403 if(umount_f)
404 unmount_dev(devname);
405
406 /* XXX Tapes and disks have different ioctl's: */
407 if((devtype & TYPEMASK) == TAPE)
408 eject_tape(devname);
409 else
410 eject_disk(devname);
411
412 if(verbose_f)
413 printf("done.\n");
414
415 return(0);
416 }
417
418 void
419 usage(void)
420 {
421 errx(1, "Usage: eject [-n][-f][-v][-l][-t type][-d] device | nickname");
422 }
423
424 int
425 guess_devtype(char *devname)
426 {
427 int n;
428
429 /* Nickname match: */
430 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]);
431 n++) {
432 if(strncasecmp(nicknames[n].name, devname,
433 strlen(nicknames[n].name)) == 0)
434 return(nicknames[n].type);
435 }
436
437 /*
438 * If we still don't know it, then try to compare vs. dev
439 * and rdev names that we know.
440 */
441 /* dev first: */
442 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
443 char *name;
444 name = nick2dev(nicknames[n].name);
445 /*
446 * Assume that the part of the name that distinguishes the
447 * instance of this device begins with a 0.
448 */
449 *(strchr(name, '0')) = '\0';
450 if(strncmp(name, devname, strlen(name)) == 0)
451 return(nicknames[n].type);
452 }
453
454 /* Now rdev: */
455 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
456 char *name = nick2rdev(nicknames[n].name);
457 *(strchr(name, '0')) = '\0';
458 if(strncmp(name, devname, strlen(name)) == 0)
459 return(nicknames[n].type);
460 }
461
462 /* Not found. */
463 return(-1);
464 }
465
466 /* "floppy5" -> "/dev/fd5a". Yep, this uses a static buffer. */
467 char *
468 nick2dev(char *nn)
469 {
470 int n;
471 static char devname[MAXDEVLEN];
472 int devnum = 0;
473
474 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
475 if(strncasecmp(nicknames[n].name, nn,
476 strlen(nicknames[n].name)) == 0) {
477 sscanf(nn, "%*[^0-9]%d", &devnum);
478 sprintf(devname, "/dev/%s%d", nicknames[n].devname,
479 devnum);
480 if((nicknames[n].type & TYPEMASK) != TAPE)
481 strcat(devname, "a");
482 return(devname);
483 }
484 }
485
486 return(NULL);
487 }
488
489 /* "floppy5" -> "/dev/rfd5c". Static buffer. */
490 char *
491 nick2rdev(char *nn)
492 {
493 int n;
494 static char devname[MAXDEVLEN];
495 int devnum = 0;
496
497 for(n = 0; n < sizeof(nicknames) / sizeof(nicknames[0]); n++) {
498 if(strncasecmp(nicknames[n].name, nn,
499 strlen(nicknames[n].name)) == 0) {
500 sscanf(nn, "%*[^0-9]%d", &devnum);
501 sprintf(devname, "/dev/r%s%d", nicknames[n].devname,
502 devnum);
503 if((nicknames[n].type & TYPEMASK) != TAPE) {
504 strcat(devname, "a");
505 devname[strlen(devname) - 1] += RAW_PART;
506 }
507 return(devname);
508 }
509 }
510
511 return(NULL);
512 }
513
514 /* Unmount all filesystems attached to dev. */
515 void
516 unmount_dev(char *name)
517 {
518 struct statfs *mounts;
519 int i, nmnts, len;
520 char *dn;
521
522 nmnts = getmntinfo(&mounts, MNT_NOWAIT);
523 if(nmnts == 0) {
524 err(1, "getmntinfo");
525 }
526
527 /* Make sure we have a device name: */
528 dn = nick2dev(name);
529 if(dn == NULL)
530 dn = name;
531
532 /* Set len to strip off the partition name: */
533 len = strlen(dn);
534 if(!isdigit(dn[len - 1]))
535 len--;
536 if(!isdigit(dn[len - 1])) {
537 errx(1, "Can't figure out base name for dev name %s", dn);
538 }
539
540 for(i = 0; i < nmnts; i++) {
541 if(strncmp(mounts[i].f_mntfromname, dn, len) == 0) {
542 if(verbose_f)
543 printf("Unmounting %s from %s...\n",
544 mounts[i].f_mntfromname,
545 mounts[i].f_mntonname);
546
547 if(unmount(mounts[i].f_mntonname, 0) == -1) {
548 err(1, "unmount: %s", mounts[i].f_mntonname);
549 }
550 }
551 }
552
553 return;
554 }
555
556 void
557 eject_tape(char *name)
558 {
559 struct mtop m;
560 int fd;
561 char *dn;
562
563 dn = nick2rdev(name);
564 if(dn == NULL) {
565 dn = name; /* Hope for the best. */
566 }
567
568 fd = open(dn, O_RDONLY);
569 if(fd == -1) {
570 err(1, "open: %s", dn);
571 }
572
573 if(verbose_f)
574 printf("Ejecting %s...\n", dn);
575
576 m.mt_op = MTOFFL;
577 m.mt_count = 0;
578 if(ioctl(fd, MTIOCTOP, &m) == -1) {
579 err(1, "ioctl: MTIOCTOP: %s", dn);
580 }
581
582 close(fd);
583 return;
584 }
585
586 void
587 eject_disk(char *name)
588 {
589 int fd;
590 char *dn;
591 int arg;
592
593 dn = nick2rdev(name);
594 if(dn == NULL) {
595 dn = name; /* Hope for the best. */
596 }
597
598 fd = open(dn, O_RDONLY);
599 if(fd == -1) {
600 err(1, "open: %s", dn);
601 }
602
603 if(load_f) {
604 if(verbose_f)
605 printf("Closing %s...\n", dn);
606
607 if(ioctl(fd, CDIOCCLOSE, NULL) == -1) {
608 err(1, "ioctl: CDIOCCLOSE: %s", dn);
609 }
610 } else {
611 if(verbose_f)
612 printf("Ejecting %s...\n", dn);
613
614 arg = 0;
615 if(ioctl(fd, DIOCLOCK, (char *)&arg) == -1)
616 err(1, "ioctl: DIOCLOCK: %s", dn);
617 if(ioctl(fd, DIOCEJECT, &arg) == -1)
618 err(1, "ioctl: DIOCEJECT: %s", dn);
619 }
620
621 close(fd);
622 return;
623 }
624