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