xref: /illumos-gate/usr/src/cmd/fs.d/fsck.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include	<stdio.h>
34 #include	<errno.h>
35 #include	<limits.h>
36 #include	<fcntl.h>
37 #include	<string.h>
38 #include	<sys/types.h>
39 #include	<sys/stat.h>
40 #include	<sys/wait.h>
41 #include	<sys/vfstab.h>
42 #include	<sys/mntent.h>
43 #include	<locale.h>
44 #include	<libintl.h>
45 
46 #define	ARGV_MAX	16
47 #define	FSTYPE_MAX	8
48 #define	VFS_PATH	"/usr/lib/fs"
49 #define	VFS_PATH2	"/etc/fs"
50 
51 #define	CHECK(xx, yy)\
52 	if (xx == (yy)-1) {\
53 		fprintf(stderr, gettext("%s: too many arguments\n"), myname); \
54 		usage(); \
55 	}
56 #define	OPTION(flag)\
57 		options++; \
58 		nargv[nargc++] = flag; \
59 		CHECK(nargc, ARGV_MAX); \
60 		break
61 #define	OPTARG(flag)\
62 		nargv[nargc++] = flag; \
63 		CHECK(nargc, ARGV_MAX); \
64 		if (optarg) {\
65 			nargv[nargc++] = optarg; \
66 			CHECK(nargc, ARGV_MAX); \
67 		}\
68 		break
69 
70 
71 int	nrun, ndisks;
72 int	maxrun = 8;	/* should be based on the machine resources */
73 
74 extern char	*optarg;
75 extern int	optind;
76 extern char	*default_fstype();
77 
78 int	nargc = 2;
79 int	options = 0;
80 int	mnt_passno = 0;
81 int	exitstat = 0;
82 char	*nargv[ARGV_MAX];
83 char	*myname, *fstype;
84 char	*malloc();
85 char	vfstab[] = VFSTAB;
86 char	pflg = 0, Vflg = 0;
87 
88 /*
89  * Keep an idea of the last device arg type as a hint to the
90  * type of the next arg. In the case of mountall, it's very likely
91  * to be the same type and the next entry in the file. This should
92  * help speed vfstab lookups.
93  */
94 enum dev_arg_t { UNKNOWN, SPECIAL, FSCKDEV, MOUNTPT };
95 enum dev_arg_t arg_hint = UNKNOWN;
96 
97 static struct devlist {
98 	char *name;
99 	char *fsname;
100 	pid_t pid;
101 	struct devlist *nxt;
102 } *newdev(), *getdev();
103 
104 /*
105  * private copy vfstab functions
106  */
107 static struct vfstab	vfsave = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
108 
109 static int
110 vfdup(struct vfstab *vp)
111 {
112 	if (vfsave.vfs_special != NULL) {
113 		free(vfsave.vfs_special);
114 		vfsave.vfs_special = NULL;
115 	}
116 	if ((vp->vfs_special != NULL) &&
117 	    ((vfsave.vfs_special = strdup(vp->vfs_special)) == NULL)) {
118 		perror(myname);
119 		return (4);	/* XXX */
120 	}
121 
122 	if (vfsave.vfs_fsckdev != NULL) {
123 		free(vfsave.vfs_fsckdev);
124 		vfsave.vfs_fsckdev = NULL;
125 	}
126 	if ((vp->vfs_fsckdev != NULL) &&
127 	    ((vfsave.vfs_fsckdev = strdup(vp->vfs_fsckdev)) == NULL)) {
128 		perror(myname);
129 		return (4);	/* XXX */
130 	}
131 
132 	if (vfsave.vfs_mountp != NULL) {
133 		free(vfsave.vfs_mountp);
134 		vfsave.vfs_mountp = NULL;
135 	}
136 	if ((vp->vfs_mountp != NULL) &&
137 	    ((vfsave.vfs_mountp = strdup(vp->vfs_mountp)) == NULL)) {
138 		perror(myname);
139 		return (4);	/* XXX */
140 	}
141 
142 	if (vfsave.vfs_fstype != NULL) {
143 		free(vfsave.vfs_fstype);
144 		vfsave.vfs_fstype = NULL;
145 	}
146 	if ((vp->vfs_fstype != NULL) &&
147 	    ((vfsave.vfs_fstype = strdup(vp->vfs_fstype)) == NULL)) {
148 		perror(myname);
149 		return (4);	/* XXX */
150 	}
151 
152 	if (vfsave.vfs_fsckpass != NULL) {
153 		free(vfsave.vfs_fsckpass);
154 		vfsave.vfs_fsckpass = NULL;
155 	}
156 	if ((vp->vfs_fsckpass != NULL) &&
157 	    ((vfsave.vfs_fsckpass = strdup(vp->vfs_fsckpass)) == NULL)) {
158 		perror(myname);
159 		return (4);	/* XXX */
160 	}
161 
162 	if (vfsave.vfs_automnt != NULL) {
163 		free(vfsave.vfs_automnt);
164 		vfsave.vfs_automnt = NULL;
165 	}
166 	if ((vp->vfs_automnt != NULL) &&
167 	    ((vfsave.vfs_automnt = strdup(vp->vfs_automnt)) == NULL)) {
168 		perror(myname);
169 		return (4);	/* XXX */
170 	}
171 
172 	if (vfsave.vfs_mntopts != NULL) {
173 		free(vfsave.vfs_mntopts);
174 		vfsave.vfs_mntopts = NULL;
175 	}
176 	if ((vp->vfs_mntopts != NULL) &&
177 	    ((vfsave.vfs_mntopts = strdup(vp->vfs_mntopts)) == NULL)) {
178 		perror(myname);
179 		return (4);	/* XXX */
180 	}
181 
182 	*vp = vfsave;
183 	return (0);
184 }
185 
186 static int
187 mygetvfsent(FILE *fp, struct vfstab *vp)
188 {
189 	int	error;
190 
191 	if ((error = getvfsent(fp, vp)) != 0)
192 		return (error);
193 	return (vfdup(vp));
194 }
195 
196 static int
197 mygetvfsany(FILE *fp, struct vfstab *vp, struct vfstab *vrefp)
198 {
199 	int	error;
200 
201 	if ((error = getvfsany(fp, vp, vrefp)) != 0)
202 		return (error);
203 	return (vfdup(vp));
204 }
205 
206 main(argc, argv)
207 	int	argc;
208 	char	*argv[];
209 {
210 	int	cc, ret, other_than_ufs = 0;
211 	int	questflg = 0, Fflg = 0, Vflg = 0, sanity = 0;
212 	char	*subopt;
213 	FILE	*fd = NULL;
214 	struct vfstab	vget, vref;
215 	int preencnt = 0;
216 	struct devlist *dp, *devs = NULL;
217 	int status;
218 
219 	(void) setlocale(LC_ALL, "");
220 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
221 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
222 #endif
223 	(void) textdomain(TEXT_DOMAIN);
224 
225 	myname = strrchr(argv[0], '/');
226 	if (myname)
227 		myname++;
228 	else
229 		myname = argv[0];
230 
231 	while ((cc = getopt(argc, argv, "?F:mnNo:VyY")) != -1) {
232 		switch (cc) {
233 		case '?':
234 			questflg++;
235 			if (questflg > 1)
236 				usage();
237 			nargv[nargc++] = "-?";
238 			CHECK(nargc, ARGV_MAX);
239 			break;
240 		case 'F':
241 			Fflg++;
242 			/* check for more that one -F */
243 			if (Fflg > 1) {
244 				fprintf(stderr,
245 				gettext("%s: more than one fstype specified\n"),
246 					myname);
247 				usage();
248 			}
249 			fstype = optarg;
250 			if (strlen(fstype) > (size_t) FSTYPE_MAX) {
251 				fprintf(stderr,
252 			gettext("%s: Fstype %s exceeds %d characters\n"),
253 					myname, fstype, FSTYPE_MAX);
254 						exit(1);
255 			}
256 			break;
257 		case 'm':
258 			sanity++;
259 			OPTION("-m");
260 		case 'n':
261 			OPTION("-n");
262 		case 'N':
263 			OPTION("-N");
264 		case 'o':
265 			subopt = optarg;
266 			while (*subopt != '\0') {
267 				if (*subopt == 'p') {
268 					pflg++;
269 					break;
270 				}
271 				subopt++;
272 			}
273 			OPTARG("-o");
274 		case 'V':
275 			Vflg++;
276 			if (Vflg > 1)
277 				usage();
278 			break;
279 		case 'y':
280 			OPTION("-y");
281 		case 'Y':
282 			OPTION("-Y");
283 		}
284 		optarg = NULL;
285 	}
286 
287 	/* copy '--' to specific */
288 	if (strcmp(argv[optind-1], "--") == 0) {
289 		nargv[nargc++] = argv[optind-1];
290 		CHECK(nargc, ARGV_MAX);
291 	}
292 
293 	if (questflg) {
294 		if (Fflg) {
295 			nargc = 2;
296 			nargv[nargc++] = "-?";
297 			nargv[nargc] = NULL;
298 			do_exec(fstype, nargv);
299 		}
300 		usage();
301 	}
302 
303 	if ((sanity) && (options > 1)) {
304 		usage();
305 	}
306 
307 	if (optind == argc) {	/* no device name is specified */
308 		if (fstype == NULL) {
309 			if ((argc > 2) && (sanity)) {
310 				usage();
311 			}
312 		}
313 		/*
314 		 * Try to check UFS filesystems first, then check other
315 		 * filesystems if they exist.
316 		 * Note: Parallel checking is only available in UFS for now.
317 		 */
318 		if (fstype == NULL || strcmp(fstype, MNTTYPE_UFS) == 0) {
319 			if ((fd = fopen(vfstab, "r")) == NULL) {
320 				fprintf(stderr,
321 					gettext("%s: cannot open vfstab\n"),
322 					myname);
323 				exit(1);
324 			}
325 			while ((ret = mygetvfsent(fd, &vget)) == 0) {
326 				if (strcmp(vget.vfs_fstype, MNTTYPE_UFS) &&
327 				    numbers(vget.vfs_fsckpass)) {
328 					other_than_ufs ++;
329 					continue;
330 				}
331 				if (numbers(vget.vfs_fsckpass))
332 					mnt_passno = atoi(vget.vfs_fsckpass);
333 				else
334 					continue;
335 				if (mnt_passno < 1)
336 					continue;
337 				if (pflg == 0 || mnt_passno == 1) {
338 					status = execute(vget.vfs_fsckdev,
339 					    MNTTYPE_UFS, Vflg, fd);
340 					/* return the highest exit code */
341 					if (status > exitstat)
342 						exitstat = status;
343 				} else if (preen_addev(vget.vfs_fsckdev) == 0) {
344 					preencnt++;
345 					dp = newdev(&vget);
346 					dp->nxt = devs;
347 					devs = dp;
348 				} else {
349 					/*
350 					 * preening setup failed, so
351 					 * execute serially here...
352 					 */
353 					fprintf(stderr,
354 					gettext("%s: preen_addev error\n"),
355 						myname);
356 					status = execute(vget.vfs_fsckdev,
357 					    MNTTYPE_UFS, Vflg, fd);
358 					/* return the highest exit code */
359 					if (status > exitstat)
360 						exitstat = status;
361 				}
362 			}
363 			fclose(fd);
364 			if (ret > 0)
365 				vfserror(ret);
366 			if (pflg && exitstat == 0) {
367 				fsck_dopreen(&devs, preencnt);
368 			}
369 		}
370 		else
371 			other_than_ufs = 1;
372 
373 		if (other_than_ufs) {
374 			if ((fd = fopen(vfstab, "r")) == NULL) {
375 				fprintf(stderr,
376 					gettext("%s: cannot open vfstab\n"),
377 					myname);
378 				exit(1);
379 			}
380 			while ((ret = mygetvfsent(fd, &vget)) == 0)
381 				if (strcmp(vget.vfs_fstype, MNTTYPE_UFS) &&
382 				    numbers(vget.vfs_fsckpass) &&
383 				    vget.vfs_fsckdev != NULL &&
384 				    (fstype == NULL ||
385 				    strcmp(fstype, vget.vfs_fstype) == 0)) {
386 					status = execute(vget.vfs_fsckdev,
387 					    vget.vfs_fstype, Vflg, fd);
388 					/* return the highest exit code */
389 					if (status > exitstat)
390 						exitstat = status;
391 				}
392 			fclose(fd);
393 			if (ret > 0)
394 				vfserror(ret);
395 		}
396 
397 	} else {	/* device name is specified */
398 		if (fstype == NULL && (fd = fopen(vfstab, "r")) == NULL) {
399 			fprintf(stderr, gettext("%s: cannot open vfstab\n"),
400 				myname);
401 			exit(1);
402 		}
403 		while (optind < argc) {
404 			/*
405 			 * If "-F FStype" is specified, use that fs type.
406 			 * Otherwise, determine the fs type from /etc/vfstab
407 			 * if the entry exists.  Otherwise, determine the
408 			 * local or remote fs type from /etc/default/df
409 			 * or /etc/dfs/fstypes respectively.
410 			 */
411 			if (fstype == NULL) {
412 				if ((argc > 3) && (sanity)) {
413 					usage();
414 				}
415 				/* must check for both special && raw devices */
416 				vfsnull(&vref);
417 
418 				/*
419 				 * Find the vfstab entry for this device.
420 				 * arg_hint tells us what to try to match,
421 				 * based on the type of the last arg. If
422 				 * arg_hint equals UNKNOWN, then we're not
423 				 * sure of the type and need to fallthrough
424 				 * all 3 possibilities for vfstab lookup.
425 				 * Try it as a mountpt first, since that's
426 				 * what mountall gives us.
427 				 */
428 try_again:
429 				switch (arg_hint) {
430 				case UNKNOWN:
431 					/* FALLTHROUGH */
432 
433 				case MOUNTPT:
434 					vref.vfs_mountp = argv[optind];
435 					if ((ret = mygetvfsany(fd, &vget,
436 						&vref)) == -1 ||
437 						vget.vfs_fstype == NULL) {
438 
439 						vref.vfs_mountp = NULL;
440 						rewind(fd);
441 
442 						if (arg_hint == MOUNTPT) {
443 							arg_hint = UNKNOWN;
444 							goto try_again;
445 						}
446 						/* FALLTHROUGH */
447 					} else {
448 						/* Found it */
449 						if (vget.vfs_fsckdev != NULL) {
450 							argv[optind] =
451 							vget.vfs_fsckdev;
452 						}
453 						arg_hint = MOUNTPT;
454 						break;
455 					}
456 
457 				case FSCKDEV:
458 					vref.vfs_fsckdev = argv[optind];
459 					if ((ret = mygetvfsany(fd, &vget,
460 						&vref)) == -1 ||
461 						vget.vfs_fstype == NULL) {
462 
463 						vref.vfs_fsckdev = NULL;
464 						rewind(fd);
465 
466 						if (arg_hint == FSCKDEV) {
467 							arg_hint = UNKNOWN;
468 							goto try_again;
469 						}
470 						/* FALLTHROUGH */
471 					} else {
472 						/* Found it */
473 						arg_hint = FSCKDEV;
474 						break;
475 					}
476 
477 				case SPECIAL:
478 					vref.vfs_special = argv[optind];
479 					if ((ret = mygetvfsany(fd, &vget,
480 						&vref)) == -1 ||
481 						vget.vfs_fstype == NULL) {
482 
483 						vref.vfs_special = NULL;
484 						rewind(fd);
485 
486 						if (arg_hint == SPECIAL) {
487 							arg_hint = UNKNOWN;
488 							goto try_again;
489 						}
490 						/* FALLTHROUGH */
491 					} else {
492 						/* Found it */
493 						arg_hint = SPECIAL;
494 						break;
495 					}
496 				}
497 
498 				if (ret == 0 && vget.vfs_fstype) {
499 					if ((pflg) && (strcmp(vget.vfs_fstype,
500 					    MNTTYPE_UFS) == 0) && (preen_addev(
501 					    vget.vfs_fsckdev) == 0)) {
502 						preencnt++;
503 						dp = newdev(&vget);
504 						dp->nxt = devs;
505 						devs = dp;
506 					} else {
507 						status = execute(argv[optind],
508 						    vget.vfs_fstype, Vflg, fd);
509 						if (status > exitstat)
510 							exitstat = status;
511 					}
512 				} else if (ret == -1 ||
513 				    vget.vfs_fstype == NULL) {
514 					fstype =
515 					    default_fstype(argv[optind]);
516 					status = execute(argv[optind], fstype,
517 					    Vflg, fd);
518 					/* return the highest exit code */
519 					if (status > exitstat)
520 						exitstat = status;
521 				} else
522 					vfserror(ret);
523 			} else {
524 				status = execute(argv[optind], fstype,
525 				    Vflg, NULL);
526 				/* return the highest exit code */
527 				if (status > exitstat)
528 					exitstat = status;
529 			}
530 			optind++;
531 		}
532 		if (fd != NULL)
533 			fclose(fd);
534 		if ((pflg) && (exitstat == 0)) {
535 			fsck_dopreen(&devs, preencnt);
536 		}
537 	}
538 	exit(exitstat);
539 }
540 
541 static
542 fsck_dopreen(devp, ndevs)
543 	struct devlist **devp;
544 	int ndevs;
545 {
546 	char name[1024];
547 	int rc;
548 	register int i;
549 	struct devlist *bl, *bdp;
550 	struct devlist *badlist;
551 
552 	bl = badlist = NULL;
553 	while (ndevs > 0) {
554 		if (nrun > maxrun)
555 			waiter(&bl, &badlist);
556 		rc = preen_getdev(name);
557 		switch (rc) {
558 		case 0:
559 			break;
560 		case 1:
561 			bdp = getdev(name, devp);
562 			if (bdp == NULL) {
563 				fprintf(stderr,
564 					gettext("%s: unknown dev: `%s'\n"),
565 					myname, name);
566 				exit(1);
567 			}
568 			bdp->nxt = bl;
569 			bl = bdp;
570 			startdisk(bdp);
571 			ndevs--;
572 			break;
573 		case 2:
574 			waiter(&bl, &badlist);
575 			break;
576 		default:
577 			fprintf(stderr,
578 			gettext("%s: bad return `%d' from preen_getdev\n"),
579 				myname, rc);
580 			break;
581 		}
582 	}
583 	while (bl != NULL) {
584 		waiter(&bl, &badlist);
585 	}
586 
587 	if (badlist != NULL)
588 		print_badlist(badlist);
589 }
590 
591 static
592 startdisk(dp)
593 	struct devlist *dp;
594 {
595 	pid_t pid;
596 
597 	nrun++;
598 	if ((pid = fork()) == -1) {
599 		perror("fork");
600 		exit(1);
601 	} else if (pid == 0) {
602 		exitstat = execute(dp->name, MNTTYPE_UFS, Vflg, NULL);
603 		exit(exitstat);
604 	} else {
605 		dp->pid = pid;
606 	}
607 }
608 
609 static
610 waiter(blp, badlist)
611 	struct devlist **blp;
612 	struct devlist **badlist;
613 {
614 	pid_t curpid;
615 	int status;
616 	register struct devlist *bdp, *pbdp;
617 
618 	curpid = wait(&status);
619 	if (curpid == -1) {
620 		perror("wait");
621 		exit(1);
622 	}
623 
624 	for (pbdp = NULL, bdp = *blp; bdp != NULL; pbdp = bdp, bdp = bdp->nxt) {
625 		if (bdp->pid == curpid) {
626 			break;
627 		}
628 	}
629 	if (bdp == NULL)
630 		return;
631 	nrun--;
632 
633 	if (pbdp)
634 		pbdp->nxt = bdp->nxt;
635 	else
636 		*blp = bdp->nxt;
637 	preen_releasedev(bdp->name);
638 
639 	if (WTERMSIG(status)) {
640 		printf(gettext("%s (%s): EXITED WITH SIGNAL %d\n"),
641 			bdp->name, bdp->fsname, WTERMSIG(status));
642 		status = status&0377 | 8<<8;
643 	}
644 	if (WHIBYTE(status) != 0) {
645 		if (WHIBYTE(status) > exitstat)
646 			exitstat = WHIBYTE(status);
647 		while (*badlist != NULL)
648 			badlist = &(*badlist)->nxt;
649 		*badlist = bdp;
650 		bdp->nxt = NULL;
651 	}
652 }
653 
654 static
655 print_badlist(lp)
656 	struct devlist *lp;
657 {
658 	int x, len;
659 
660 	printf(
661 gettext("\nTHE FOLLOWING FILE SYSTEM(S) HAD AN UNEXPECTED INCONSISTENCY:"));
662 	for (x = 3; lp != NULL; lp = lp->nxt) {
663 		len = strlen(lp->name) + strlen(lp->fsname) + 5;
664 		x += len;
665 		if (x >= 80) {
666 			printf("\n   ");
667 			x = len + 3;
668 		} else {
669 			printf(" ");
670 		}
671 		printf("%s (%s)%s", lp->name, lp->fsname,
672 		    lp->nxt ? "," : "\n");
673 	}
674 }
675 
676 /*
677  * allocate and initialize a `devlist' structure
678  */
679 static
680 struct devlist *
681 newdev(vfsp)
682 	struct vfstab *vfsp;
683 {
684 	struct devlist *dp;
685 	extern char *strdup();
686 
687 	dp = (struct devlist *)malloc(sizeof (struct devlist));
688 	if (dp == NULL) {
689 		fprintf(stderr, gettext("%s: out of memory\n"), myname);
690 		exit(1);
691 	}
692 	dp->name = strdup(vfsp->vfs_fsckdev);
693 	dp->fsname = strdup(vfsp->vfs_mountp);
694 	if (dp->name == NULL || dp->fsname == NULL) {
695 		fprintf(stderr, gettext("%s: out of memory\n"), myname);
696 		exit(1);
697 	}
698 	return (dp);
699 }
700 
701 /*
702  * locate the devlist structure in the given list that matches `name'.
703  * If found, the structure is removed from the list, and a pointer to
704  * it is returned.  If not, NULL is returned.
705  */
706 static
707 struct devlist *
708 getdev(name, list)
709 	char *name;
710 	struct devlist **list;
711 {
712 	register struct devlist *p, *lp;
713 
714 	for (lp = NULL, p = *list; p != NULL; lp = p, p = p->nxt) {
715 		if (strcmp(p->name, name) == 0)
716 			break;
717 	}
718 
719 	if (p != NULL) {
720 		if (lp != NULL)
721 			lp->nxt = p->nxt;
722 		else
723 			*list = p->nxt;
724 	}
725 	return (p);
726 }
727 
728 /* see if all numbers */
729 numbers(yp)
730 	char	*yp;
731 {
732 	if (yp == NULL)
733 		return (0);
734 	while ('0' <= *yp && *yp <= '9')
735 		yp++;
736 	if (*yp)
737 		return (0);
738 	return (1);
739 }
740 
741 execute(fsckdev, fstype, Vflg, fd)
742 	char	*fsckdev, *fstype;
743 	int	Vflg;
744 	FILE	*fd;
745 {
746 	int	st;
747 	pid_t	fk;
748 	char	full_path[PATH_MAX];
749 	char	*vfs_path = VFS_PATH;
750 	int	status = 0;
751 
752 	nargv[nargc] = fsckdev;
753 
754 	if (Vflg) {
755 		prnt_cmd(stdout, fstype);
756 		return (0);
757 	}
758 
759 	if (fd)
760 		fcntl(fileno(fd), F_SETFD, 1);	/* close on exec */
761 
762 	if ((fk = fork()) == (pid_t)-1) {
763 		fprintf(stderr,
764 			gettext("%s: cannot fork.  Try again later\n"),
765 			myname);
766 		perror(myname);
767 		exit(1);
768 	}
769 
770 	if (fk == 0) {
771 		/* Try to exec the fstype dependent portion of the fsck. */
772 		do_exec(fstype, nargv);
773 	} else {
774 		/* parent waits for child */
775 		if (wait(&st) == (pid_t)-1) {
776 			fprintf(stderr, gettext("%s: bad wait\n"), myname);
777 			perror(myname);
778 			exit(1);
779 		}
780 
781 		if ((st & 0xff) == 0x7f) {
782 			fprintf(stderr,
783 				gettext("%s: warning: the following command"
784 				" (process %d) was stopped by signal %d\n"),
785 				myname, fk, (st >> 8) & 0xff);
786 			prnt_cmd(stderr, fstype);
787 			status = ((st >> 8) & 0xff) | 0x80;
788 		} else if (st & 0xff) {
789 			if (st & 0x80)
790 				fprintf(stderr,
791 				gettext("%s: warning: the following command"
792 				" (process %d) was terminated by signal %d"
793 				" and dumped core\n"),
794 				myname, fk, st & 0x7f);
795 			else
796 				fprintf(stderr,
797 				gettext("%s: warning: the following command"
798 				" (process %d) was terminated by signal %d\n"),
799 				myname, fk, st & 0x7f);
800 
801 			prnt_cmd(stderr, fstype);
802 			status = ((st & 0xff) | 0x80);
803 		} else if (st & 0xff00)
804 			status = (st >> 8) & 0xff;
805 	}
806 
807 	return (status);
808 }
809 
810 do_exec(fstype, nargv)
811 	char	*fstype, *nargv[];
812 {
813 	char	full_path[PATH_MAX];
814 	char	*vfs_path = VFS_PATH;
815 
816 	if (strlen(fstype) > (size_t) FSTYPE_MAX) {
817 		fprintf(stderr,
818 			gettext("%s: Fstype %s exceeds %d characters\n"),
819 			myname, fstype, FSTYPE_MAX);
820 		exit(1);
821 	}
822 	/* build the full pathname of the fstype dependent command. */
823 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
824 
825 	/* set the new argv[0] to the filename */
826 	nargv[1] = myname;
827 	/* Try to exec the fstype dependent portion of the fsck. */
828 	execv(full_path, &nargv[1]);
829 	if (errno == EACCES) {
830 		fprintf(stderr,
831 			gettext("%s: cannot execute %s - permission denied\n"),
832 			myname, full_path);
833 	}
834 	if (errno == ENOEXEC) {
835 		nargv[0] = "sh";
836 		nargv[1] = full_path;
837 		execv("/sbin/sh", &nargv[0]);
838 	}
839 	/* second path to try */
840 	vfs_path = VFS_PATH2;
841 	/* build the full pathname of the fstype dependent command. */
842 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
843 
844 	/* set the new argv[0] to the filename */
845 	nargv[1] = myname;
846 	/* Try to exec the second fstype dependent portion of the fsck. */
847 	execv(full_path, &nargv[1]);
848 	if (errno == EACCES) {
849 		fprintf(stderr,
850 			gettext("%s: cannot execute %s - permission denied\n"),
851 			myname, full_path);
852 		exit(1);
853 	}
854 	if (errno == ENOEXEC) {
855 		nargv[0] = "sh";
856 		nargv[1] = full_path;
857 		execv("/sbin/sh", &nargv[0]);
858 	}
859 	fprintf(stderr,
860 		gettext("%s: operation not applicable to FSType %s\n"),
861 		myname, fstype);
862 	exit(1);
863 }
864 
865 prnt_cmd(fd, fstype)
866 	FILE	*fd;
867 	char	*fstype;
868 {
869 	char	**argp;
870 
871 	fprintf(fd, "%s -F %s", myname, fstype);
872 	for (argp = &nargv[2]; *argp; argp++)
873 		fprintf(fd, " %s", *argp);
874 	fprintf(fd, "\n");
875 }
876 
877 vfserror(flag)
878 	int	flag;
879 {
880 	switch (flag) {
881 	case VFS_TOOLONG:
882 		fprintf(stderr,
883 			gettext("%s: line in vfstab exceeds %d characters\n"),
884 			myname, VFS_LINE_MAX-2);
885 		break;
886 	case VFS_TOOFEW:
887 		fprintf(stderr,
888 			gettext("%s: line in vfstab has too few entries\n"),
889 			myname);
890 		break;
891 	case VFS_TOOMANY:
892 		fprintf(stderr,
893 			gettext("%s: line in vfstab has too many entries\n"),
894 			myname);
895 		break;
896 	}
897 	exit(1);
898 }
899 
900 int opterr = 1, optind = 1, optopt = 0;
901 char *optarg = 0;
902 
903 int
904 getopt(int argc, char * const *argv, const char *opts)
905 {
906 	static int sp = 1;
907 	register int c;
908 	register char *cp;
909 
910 	if (sp == 1)
911 		if (optind >= argc ||
912 		    argv[optind][0] != '-' || argv[optind][1] == '\0')
913 			return (-1);
914 		else if (strcmp(argv[optind], "--") == 0) {
915 			optind++;
916 			return (-1);
917 		}
918 	optopt = c = argv[optind][sp];
919 	if (c == ':' || (cp = strchr(opts, c)) == 0) {
920 		if (opterr)
921 			fprintf(stderr,
922 				gettext("%s: illegal option -- %c\n"),
923 				*argv, c);
924 		if (argv[optind][++sp] == '\0') {
925 			optind++;
926 			sp = 1;
927 		}
928 		return ('?');
929 	}
930 	if (*++cp == ':') {
931 		if (argv[optind][sp+1] != '\0')
932 			optarg = &argv[optind++][sp+1];
933 		else if (++optind >= argc) {
934 			if (opterr)
935 				fprintf(stderr,
936 		gettext("%s: option requires an argument -- %c\n"), *argv, c);
937 			sp = 1;
938 			return ('?');
939 		} else
940 			optarg = argv[optind++];
941 		sp = 1;
942 	} else if (*cp == ';') {
943 		if (argv[optind][++sp] != '\0')
944 			if (isoptarg(c, &argv[optind][sp])) {
945 				optarg = &argv[optind++][sp];
946 				sp = 1;
947 			} else
948 				optarg = NULL;
949 		else {
950 			sp = 1;
951 			if (++optind >= argc || !isoptarg(c, &argv[optind][0]))
952 				optarg = NULL;
953 			else
954 				optarg = argv[optind++];
955 		}
956 	} else {
957 		if (argv[optind][++sp] == '\0') {
958 			sp = 1;
959 			optind++;
960 		}
961 		optarg = NULL;
962 	}
963 	return (c);
964 }
965 
966 isoptarg(cc, arg)
967 	int	cc;
968 	char	*arg;
969 {
970 	if (cc == 's' || cc == 'S') {
971 		while (*arg >= '0' && *arg <= '9')
972 			arg++;
973 		if (*arg++ != ':')
974 			return (0);
975 		while (*arg >= '0' && *arg <= '9')
976 			arg++;
977 		if (*arg)
978 			return (0);
979 		return (1);
980 	}
981 	return (0);
982 }
983 
984 usage()
985 {
986 	fprintf(stderr,
987 		gettext("Usage:\n%s [-F FSType] [-V] [-m] [special ...]\n"
988 			"%s [-F FSType] [-V] [-y|Y|n|N]"
989 			" [-o specific_options] [special ...]\n"),
990 			myname, myname);
991 
992 	exit(1);
993 }
994