1/*	$NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $	*/
2
3/*
4 * Copyright (c) 1990, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 4. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD$
32 */
33
34#include <sys/cdefs.h>
35#ifndef lint
36#if 0
37static char sccsid[] = "@(#)preen.c	8.5 (Berkeley) 4/28/95";
38#else
39__RCSID("$NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $");
40#endif
41#endif /* not lint */
42
43#include <sys/param.h>
44#include <sys/stat.h>
45#include <sys/wait.h>
46#include <sys/queue.h>
47
48#include <err.h>
49#include <ctype.h>
50#include <fstab.h>
51#include <string.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <unistd.h>
55
56#include "fsutil.h"
57
58struct partentry {
59	TAILQ_ENTRY(partentry)	 p_entries;
60	char		  	*p_devname;	/* device name */
61	char			*p_mntpt;	/* mount point */
62	char		  	*p_type;	/* file system type */
63};
64
65static TAILQ_HEAD(part, partentry) badh;
66
67struct diskentry {
68	TAILQ_ENTRY(diskentry) 	    d_entries;
69	char		       	   *d_name;	/* disk base name */
70	TAILQ_HEAD(prt, partentry)  d_part;	/* list of partitions on disk */
71	int			    d_pid;	/* 0 or pid of fsck proc */
72};
73
74static TAILQ_HEAD(disk, diskentry) diskh;
75
76static int nrun = 0, ndisks = 0;
77
78static struct diskentry *finddisk(const char *);
79static void addpart(const char *, const char *, const char *);
80static int startdisk(struct diskentry *,
81    int (*)(const char *, const char *, const char *, const char *, pid_t *));
82static void printpart(void);
83
84int
85checkfstab(int flags, int (*docheck)(struct fstab *),
86    int (*checkit)(const char *, const char *, const char *, const char *, pid_t *))
87{
88	struct fstab *fs;
89	struct diskentry *d, *nextdisk;
90	struct partentry *p;
91	int ret, pid, retcode, passno, sumstatus, status, nextpass;
92	const char *name;
93
94	TAILQ_INIT(&badh);
95	TAILQ_INIT(&diskh);
96
97	sumstatus = 0;
98
99	nextpass = 0;
100	for (passno = 1; nextpass != INT_MAX; passno = nextpass) {
101		if (flags & CHECK_DEBUG)
102			printf("pass %d\n", passno);
103
104		nextpass = INT_MAX;
105		if (setfsent() == 0) {
106			warnx("Can't open checklist file: %s\n", _PATH_FSTAB);
107			return (8);
108		}
109		while ((fs = getfsent()) != 0) {
110			name = fs->fs_spec;
111			if (fs->fs_passno > passno && fs->fs_passno < nextpass)
112				nextpass = fs->fs_passno;
113
114			if (passno != fs->fs_passno)
115				continue;
116
117			if ((*docheck)(fs) == 0)
118				continue;
119
120			if (flags & CHECK_DEBUG)
121				printf("pass %d, name %s\n", passno, name);
122
123			if ((flags & CHECK_PREEN) == 0 || passno == 1 ||
124			    (flags & DO_BACKGRD) != 0) {
125				if (name == NULL) {
126					if (flags & CHECK_PREEN)
127						return 8;
128					else
129						continue;
130				}
131				sumstatus = (*checkit)(fs->fs_vfstype,
132				    name, fs->fs_file, NULL, NULL);
133
134				if (sumstatus)
135					return (sumstatus);
136				continue;
137			}
138			if (name == NULL) {
139				(void) fprintf(stderr,
140				    "BAD DISK NAME %s\n", fs->fs_spec);
141				sumstatus |= 8;
142				continue;
143			}
144			addpart(fs->fs_vfstype, name, fs->fs_file);
145		}
146
147		if ((flags & CHECK_PREEN) == 0 || passno == 1 ||
148		    (flags & DO_BACKGRD) != 0)
149			continue;
150
151		if (flags & CHECK_DEBUG) {
152			printf("Parallel start\n");
153			printpart();
154		}
155
156		TAILQ_FOREACH(nextdisk, &diskh, d_entries) {
157			if ((ret = startdisk(nextdisk, checkit)) != 0)
158				return ret;
159		}
160
161		if (flags & CHECK_DEBUG)
162			printf("Parallel wait\n");
163		while ((pid = wait(&status)) != -1) {
164			TAILQ_FOREACH(d, &diskh, d_entries)
165				if (d->d_pid == pid)
166					break;
167
168			if (d == NULL) {
169				warnx("Unknown pid %d\n", pid);
170				continue;
171			}
172
173			if (WIFEXITED(status))
174				retcode = WEXITSTATUS(status);
175			else
176				retcode = 0;
177
178			p = TAILQ_FIRST(&d->d_part);
179
180			if (flags & (CHECK_DEBUG|CHECK_VERBOSE))
181				(void) printf("done %s: %s (%s) = 0x%x\n",
182				    p->p_type, p->p_devname, p->p_mntpt,
183				    status);
184
185			if (WIFSIGNALED(status)) {
186				(void) fprintf(stderr,
187				    "%s: %s (%s): EXITED WITH SIGNAL %d\n",
188				    p->p_type, p->p_devname, p->p_mntpt,
189				    WTERMSIG(status));
190				retcode = 8;
191			}
192
193			TAILQ_REMOVE(&d->d_part, p, p_entries);
194
195			if (retcode != 0) {
196				TAILQ_INSERT_TAIL(&badh, p, p_entries);
197				sumstatus |= retcode;
198			} else {
199				free(p->p_type);
200				free(p->p_devname);
201				free(p);
202			}
203			d->d_pid = 0;
204			nrun--;
205
206			if (TAILQ_EMPTY(&d->d_part)) {
207				TAILQ_REMOVE(&diskh, d, d_entries);
208				ndisks--;
209			} else {
210				if ((ret = startdisk(d, checkit)) != 0)
211					return ret;
212			}
213		}
214		if (flags & CHECK_DEBUG) {
215			printf("Parallel end\n");
216			printpart();
217		}
218	}
219
220	if (!(flags & CHECK_PREEN))
221			return 0;
222
223	if (sumstatus) {
224		p = TAILQ_FIRST(&badh);
225		if (p == NULL)
226			return (sumstatus);
227
228		(void) fprintf(stderr,
229			"THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
230			TAILQ_NEXT(p, p_entries) ? "S" : "",
231			"UNEXPECTED INCONSISTENCY:");
232
233		for (; p; p = TAILQ_NEXT(p, p_entries))
234			(void) fprintf(stderr,
235			    "%s: %s (%s)%s", p->p_type, p->p_devname,
236			    p->p_mntpt, TAILQ_NEXT(p, p_entries) ? ", " : "\n");
237
238		return sumstatus;
239	}
240	(void) endfsent();
241	return (0);
242}
243
244
245static struct diskentry *
246finddisk(const char *name)
247{
248	const char *p;
249	size_t len = 0;
250	struct diskentry *d;
251
252	p = strrchr(name, '/');
253	if (p == NULL)
254		p = name;
255	else
256		p++;
257	for (; *p && !isdigit(*p); p++)
258		continue;
259	for (; *p && isdigit(*p); p++)
260		continue;
261	len = p - name;
262	if (len == 0)
263		len = strlen(name);
264
265	TAILQ_FOREACH(d, &diskh, d_entries)
266		if (strncmp(d->d_name, name, len) == 0 && d->d_name[len] == 0)
267			return d;
268
269	d = emalloc(sizeof(*d));
270	d->d_name = estrdup(name);
271	d->d_name[len] = '\0';
272	TAILQ_INIT(&d->d_part);
273	d->d_pid = 0;
274
275	TAILQ_INSERT_TAIL(&diskh, d, d_entries);
276	ndisks++;
277
278	return d;
279}
280
281
282static void
283printpart(void)
284{
285	struct diskentry *d;
286	struct partentry *p;
287
288	TAILQ_FOREACH(d, &diskh, d_entries) {
289		(void) printf("disk %s: ", d->d_name);
290		TAILQ_FOREACH(p, &d->d_part, p_entries)
291			(void) printf("%s ", p->p_devname);
292		(void) printf("\n");
293	}
294}
295
296
297static void
298addpart(const char *type, const char *dev, const char *mntpt)
299{
300	struct diskentry *d = finddisk(dev);
301	struct partentry *p;
302
303	TAILQ_FOREACH(p, &d->d_part, p_entries)
304		if (strcmp(p->p_devname, dev) == 0) {
305			warnx("%s in fstab more than once!\n", dev);
306			return;
307		}
308
309	p = emalloc(sizeof(*p));
310	p->p_devname = estrdup(dev);
311	p->p_mntpt = estrdup(mntpt);
312	p->p_type = estrdup(type);
313
314	TAILQ_INSERT_TAIL(&d->d_part, p, p_entries);
315}
316
317
318static int
319startdisk(struct diskentry *d, int (*checkit)(const char *, const char *,
320    const char *, const char *, pid_t *))
321{
322	struct partentry *p = TAILQ_FIRST(&d->d_part);
323	int rv;
324
325	while ((rv = (*checkit)(p->p_type, p->p_devname, p->p_mntpt,
326	    NULL, &d->d_pid)) != 0 && nrun > 0)
327		sleep(10);
328
329	if (rv == 0)
330		nrun++;
331
332	return rv;
333}
334