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