xref: /original-bsd/usr.sbin/quot/quot.c (revision 6386612b)
1 #ifndef lint
2 static char *sccsid = "@(#)quot.c	4.9 (Berkeley) 83/09/22";
3 #endif
4 
5 /*
6  * quot
7  */
8 
9 #include <stdio.h>
10 #include <ctype.h>
11 #include <sys/param.h>
12 #include <sys/inode.h>
13 #include <sys/fs.h>
14 #include <sys/file.h>
15 
16 #define	ISIZ	(MAXBSIZE/sizeof(struct dinode))
17 union {
18 	struct fs u_sblock;
19 	char dummy[SBSIZE];
20 } sb_un;
21 #define sblock sb_un.u_sblock
22 struct dinode itab[MAXBSIZE/sizeof(struct dinode)];
23 
24 struct du {
25 	struct	du *next;
26 	long	blocks;
27 	long	blocks30;
28 	long	blocks60;
29 	long	blocks90;
30 	long	nfiles;
31 	int	uid;
32 #define	NDU	2048
33 } du[NDU];
34 int	ndu;
35 #define	DUHASH	8209	/* smallest prime >= 4 * NDU */
36 #define	HASH(u)	((u) % DUHASH)
37 struct	du *duhash[DUHASH];
38 
39 #define	TSIZE	500
40 int	sizes[TSIZE];
41 long	overflow;
42 
43 int	nflg;
44 int	fflg;
45 int	cflg;
46 int	vflg;
47 int	hflg;
48 long	now;
49 
50 unsigned	ino;
51 
52 char	*malloc();
53 char	*getname();
54 
55 main(argc, argv)
56 	int argc;
57 	char *argv[];
58 {
59 	register int n;
60 
61 	now = time(0);
62 	argc--, argv++;
63 	while (argc > 0 && argv[0][0] == '-') {
64 		register char *cp;
65 
66 		for (cp = &argv[0][1]; *cp; cp++)
67 			switch (*cp) {
68 			case 'n':
69 				nflg++; break;
70 			case 'f':
71 				fflg++; break;
72 			case 'c':
73 				cflg++; break;
74 			case 'v':
75 				vflg++; break;
76 			case 'h':
77 				hflg++; break;
78 			default:
79 				fprintf(stderr,
80 				    "usage: quot [ -nfcvh ] [ device ... ]\n");
81 				exit(1);
82 			}
83 		argc--, argv++;
84 	}
85 	if (argc == 0)
86 		quotall();
87 	while (argc-- > 0)
88 		if (check(*argv++) == 0)
89 			report();
90 	exit (0);
91 }
92 
93 #include <fstab.h>
94 
95 quotall()
96 {
97 	register struct fstab *fs;
98 	register char *cp;
99 	char dev[80], *rindex();
100 
101 	if (setfsent() == 0) {
102 		fprintf(stderr, "quot: no %s file\n", FSTAB);
103 		exit(1);
104 	}
105 	while (fs = getfsent()) {
106 		if (strcmp(fs->fs_type, FSTAB_RO) &&
107 		    strcmp(fs->fs_type, FSTAB_RW) &&
108 		    strcmp(fs->fs_type, FSTAB_RQ))
109 			continue;
110 		cp = rindex(fs->fs_spec, '/');
111 		if (cp == 0)
112 			continue;
113 		sprintf(dev, "/dev/r%s", cp + 1);
114 		if (check(dev) == 0)
115 			report();
116 	}
117 	endfsent();
118 }
119 
120 check(file)
121 	char *file;
122 {
123 	register int i, j, nfiles;
124 	register struct du **dp;
125 	daddr_t iblk;
126 	int c, fd;
127 
128 	/*
129 	 * Initialize tables between checks;
130 	 * because of the qsort done in report()
131 	 * the hash tables must be rebuilt each time.
132 	 */
133 	for (i = 0; i < TSIZE; i++)
134 		sizes[i] = 0;
135 	overflow = 0;
136 	for (dp = duhash; dp < &duhash[DUHASH]; dp++)
137 		*dp = 0;
138 	ndu = 0;
139 	fd = open(file, O_RDONLY);
140 	if (fd < 0) {
141 		fprintf(stderr, "quot: ");
142 		perror(file);
143 		return (-1);
144 	}
145 	printf("%s:\n", file);
146 	sync();
147 	bread(fd, SBLOCK, (char *)&sblock, SBSIZE);
148 	if (nflg) {
149 		if (isdigit(c = getchar()))
150 			ungetc(c, stdin);
151 		else while (c != '\n' && c != EOF)
152 			c = getchar();
153 	}
154 	nfiles = sblock.fs_ipg * sblock.fs_ncg;
155 	for (ino = 0; ino < nfiles; ) {
156 		iblk = fsbtodb(&sblock, itod(&sblock, ino));
157 		bread(fd, iblk, (char *)itab, sblock.fs_bsize);
158 		for (j = 0; j < INOPB(&sblock) && ino < nfiles; j++, ino++) {
159 			if (ino < ROOTINO)
160 				continue;
161 			acct(&itab[j]);
162 		}
163 	}
164 	close(fd);
165 }
166 
167 acct(ip)
168 	register struct dinode *ip;
169 {
170 	register struct du *dp;
171 	struct du **hp;
172 	long blks, frags, size;
173 	char n;
174 	static fino;
175 
176 	if ((ip->di_mode & IFMT) == 0)
177 		return;
178 	/*
179 	 * By default, take block count in inode.  Otherwise (-h),
180 	 * take the size field and estimate the blocks allocated.
181 	 * The latter does not account for holes in files.
182 	 */
183 	if (!hflg)
184 		size = ip->di_blocks / 2;
185 	else {
186 		blks = lblkno(&sblock, ip->di_size);
187 		frags = blks * sblock.fs_frag +
188 			numfrags(&sblock, dblksize(&sblock, ip, blks));
189 		size = frags * sblock.fs_fsize / 1024;
190 	}
191 	if (cflg) {
192 		if ((ip->di_mode&IFMT) != IFDIR && (ip->di_mode&IFMT) != IFREG)
193 			return;
194 		if (size >= TSIZE) {
195 			overflow += size;
196 			size = TSIZE-1;
197 		}
198 		sizes[size]++;
199 		return;
200 	}
201 	hp = &duhash[HASH(ip->di_uid)];
202 	for (dp = *hp; dp; dp = dp->next)
203 		if (dp->uid == ip->di_uid)
204 			break;
205 	if (dp == 0) {
206 		if (ndu >= NDU)
207 			return;
208 		dp = &du[ndu++];
209 		dp->next = *hp;
210 		*hp = dp;
211 		dp->uid = ip->di_uid;
212 		dp->nfiles = 0;
213 		dp->blocks = 0;
214 		dp->blocks30 = 0;
215 		dp->blocks60 = 0;
216 		dp->blocks90 = 0;
217 	}
218 	dp->blocks += size;
219 #define	DAY (60 * 60 * 24)	/* seconds per day */
220 	if (now - ip->di_atime > 30 * DAY)
221 		dp->blocks30 += size;
222 	if (now - ip->di_atime > 60 * DAY)
223 		dp->blocks60 += size;
224 	if (now - ip->di_atime > 90 * DAY)
225 		dp->blocks90 += size;
226 	dp->nfiles++;
227 	while (nflg) {
228 		register char *np;
229 
230 		if (fino == 0)
231 			if (scanf("%d", &fino) <= 0)
232 				return;
233 		if (fino > ino)
234 			return;
235 		if (fino < ino) {
236 			while ((n = getchar()) != '\n' && n != EOF)
237 				;
238 			fino = 0;
239 			continue;
240 		}
241 		if (np = getname(dp->uid))
242 			printf("%.7s	", np);
243 		else
244 			printf("%d	", ip->di_uid);
245 		while ((n = getchar()) == ' ' || n == '\t')
246 			;
247 		putchar(n);
248 		while (n != EOF && n != '\n') {
249 			n = getchar();
250 			putchar(n);
251 		}
252 		fino = 0;
253 		break;
254 	}
255 }
256 
257 bread(fd, bno, buf, cnt)
258 	unsigned bno;
259 	char *buf;
260 {
261 
262 	lseek(fd, (long)bno * DEV_BSIZE, L_SET);
263 	if (read(fd, buf, cnt) != cnt) {
264 		fprintf(stderr, "quot: read error at block %u\n", bno);
265 		exit(1);
266 	}
267 }
268 
269 qcmp(p1, p2)
270 	register struct du *p1, *p2;
271 {
272 	char *s1, *s2;
273 
274 	if (p1->blocks > p2->blocks)
275 		return (-1);
276 	if (p1->blocks < p2->blocks)
277 		return (1);
278 	s1 = getname(p1->uid);
279 	if (s1 == 0)
280 		return (0);
281 	s2 = getname(p2->uid);
282 	if (s2 == 0)
283 		return (0);
284 	return (strcmp(s1, s2));
285 }
286 
287 report()
288 {
289 	register i;
290 	register struct du *dp;
291 
292 	if (nflg)
293 		return;
294 	if (cflg) {
295 		register long t = 0;
296 
297 		for (i = 0; i < TSIZE - 1; i++)
298 			if (sizes[i]) {
299 				t += i*sizes[i];
300 				printf("%d	%d	%D\n", i, sizes[i], t);
301 			}
302 		printf("%d	%d	%D\n",
303 		    TSIZE - 1, sizes[TSIZE - 1], overflow + t);
304 		return;
305 	}
306 	qsort(du, ndu, sizeof (du[0]), qcmp);
307 	for (dp = du; dp < &du[ndu]; dp++) {
308 		register char *cp;
309 
310 		if (dp->blocks == 0)
311 			return;
312 		printf("%5D\t", dp->blocks);
313 		if (fflg)
314 			printf("%5D\t", dp->nfiles);
315 		if (cp = getname(dp->uid))
316 			printf("%-8.8s", cp);
317 		else
318 			printf("#%-8d", dp->uid);
319 		if (vflg)
320 			printf("\t%5D\t%5D\t%5D",
321 			    dp->blocks30, dp->blocks60, dp->blocks90);
322 		printf("\n");
323 	}
324 }
325 
326 #include <pwd.h>
327 #include <utmp.h>
328 
329 struct	utmp utmp;
330 
331 #define NUID	2048
332 #define	NMAX	(sizeof (utmp.ut_name))
333 
334 char	names[NUID][NMAX+1];
335 char	outrangename[NMAX+1];
336 int	outrangeuid = -1;
337 
338 char *
339 getname(uid)
340 	int uid;
341 {
342 	register struct passwd *pw;
343 	static init;
344 	struct passwd *getpwent();
345 
346 	if (uid >= 0 && uid < NUID && names[uid][0])
347 		return (&names[uid][0]);
348 	if (uid >= 0 && uid == outrangeuid)
349 		return (outrangename);
350 rescan:
351 	if (init == 2) {
352 		if (uid < NUID)
353 			return (0);
354 		setpwent();
355 		while (pw = getpwent()) {
356 			if (pw->pw_uid != uid)
357 				continue;
358 			outrangeuid = pw->pw_uid;
359 			strncpy(outrangename, pw->pw_name, NMAX);
360 			endpwent();
361 			return (outrangename);
362 		}
363 		endpwent();
364 		return (0);
365 	}
366 	if (init == 0)
367 		setpwent(), init = 1;
368 	while (pw = getpwent()) {
369 		if (pw->pw_uid < 0 || pw->pw_uid >= NUID) {
370 			if (pw->pw_uid == uid) {
371 				outrangeuid = pw->pw_uid;
372 				strncpy(outrangename, pw->pw_name, NMAX);
373 				return (outrangename);
374 			}
375 			continue;
376 		}
377 		if (names[pw->pw_uid][0])
378 			continue;
379 		strncpy(names[pw->pw_uid], pw->pw_name, NMAX);
380 		if (pw->pw_uid == uid)
381 			return (&names[uid][0]);
382 	}
383 	init = 2;
384 	goto rescan;
385 }
386