xref: /original-bsd/usr.sbin/quot/quot.c (revision 6066c21e)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1991, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)quot.c	8.4 (Berkeley) 01/07/94";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/time.h>
20 #include <sys/stat.h>
21 #include <sys/mount.h>
22 #include <ufs/ufs/dinode.h>
23 #include <ufs/ffs/fs.h>
24 
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 struct timeval now;
64 
65 unsigned	ino;
66 
67 char	*user_from_uid();
68 
69 main(argc, argv)
70 	int argc;
71 	char *argv[];
72 {
73 	extern char *optarg;
74 	extern int optind;
75 	int ch;
76 
77 	while ((ch = getopt(argc, argv, "cfhnv")) != EOF)
78 		switch((char)ch) {
79 		case 'c':
80 			cflg++; break;
81 		case 'f':
82 			fflg++; break;
83 		case 'h':		/* undocumented */
84 			hflg++; break;
85 		case 'n':
86 			nflg++; break;
87 		case 'v':		/* undocumented */
88 			vflg++; break;
89 		case '?':
90 		default:
91 			fputs("usage: quot [-cfn] [filesystem ...]\n", stderr);
92 			exit(1);
93 		}
94 	argc -= optind;
95 	argv += optind;
96 
97 	(void)gettimeofday(&now, NULL);
98 	setpassent(1);
99 	if (argc)
100 		for (; *argv; ++argv) {
101 			if (check(*argv, (char *)NULL) == 0)
102 				report();
103 		}
104 	else
105 		quotall();
106 	exit(0);
107 }
108 
109 #include <sys/dir.h>
110 #include <fstab.h>
111 
112 quotall()
113 {
114 	register struct fstab *fs;
115 	register char *cp;
116 	char dev[MAXNAMLEN + 10], *rindex();
117 
118 	while (fs = getfsent()) {
119 		if (strcmp(fs->fs_vfstype, "ufs") ||
120 		    (strcmp(fs->fs_type, FSTAB_RO) &&
121 		    strcmp(fs->fs_type, FSTAB_RW) &&
122 		    strcmp(fs->fs_type, FSTAB_RQ)))
123 			continue;
124 		cp = rindex(fs->fs_spec, '/');
125 		if (cp == 0)
126 			continue;
127 		(void)snprintf(dev, sizeof(dev), "%sr%s", _PATH_DEV, cp + 1);
128 		if (check(dev, fs->fs_file) == 0)
129 			report();
130 	}
131 }
132 
133 check(file, fsdir)
134 	char *file;
135 	char *fsdir;
136 {
137 	register int i, j, nfiles;
138 	register struct du **dp;
139 	daddr_t iblk;
140 	long dev_bsize;
141 	int c, fd;
142 	struct stat sb;
143 	struct statfs sfs;
144 #define FNBSZ	128
145 	char filebuf[FNBSZ];
146 
147 	/*
148 	 * Initialize tables between checks; because of the qsort done in
149 	 * report() the hash tables must be rebuilt each time.
150 	 */
151 	for (i = 0; i < TSIZE; i++)
152 		sizes[i] = 0;
153 	overflow = 0;
154 	for (dp = duhash; dp < &duhash[DUHASH]; dp++)
155 		*dp = 0;
156 	ndu = 0;
157 	/*
158 	 * Make sure "file" refers to a char special file.
159 	 * Convert if necessary.  Also make sure we have an fsdir.
160 	 */
161 	if (stat(file, &sb) < 0) {
162 		fprintf(stderr, "quot: ");
163 		perror(file);
164 		return (-1);
165 	}
166 	/*
167 	 * User specified "file".
168 	 * Attempt to come up with the appropriate char special file
169 	 * and a mount point.
170 	 */
171 	if (fsdir == NULL) {
172 		register struct fstab *fs;
173 		int len = strlen(_PATH_DEV);
174 
175 		strcpy(filebuf, _PATH_DEV);
176 		if (S_ISCHR(sb.st_mode)) {
177 			if (strncmp(_PATH_DEV, file, len) == 0 &&
178 			    file[len] == 'r') {
179 				strncpy(&filebuf[len], &file[len+1], FNBSZ-6);
180 				fs = getfsspec(filebuf);
181 				if (fs != NULL)
182 					fsdir = fs->fs_file;
183 			}
184 		} else {
185 			if (S_ISBLK(sb.st_mode)) {
186 				fs = getfsspec(file);
187 				if (fs != NULL)
188 					fsdir = fs->fs_file;
189 			} else {
190 				if (statfs(file, &sfs) == 0) {
191 					file = sfs.f_mntfromname;
192 					fsdir = sfs.f_mntonname;
193 				}
194 			}
195 			if (strncmp(_PATH_DEV, file, len) == 0) {
196 				filebuf[len] = 'r';
197 				strncpy(&filebuf[len+1], &file[len], FNBSZ-7);
198 				file = filebuf;
199 			}
200 		}
201 	}
202 	fd = open(file, O_RDONLY);
203 	if (fd < 0) {
204 		fprintf(stderr, "quot: ");
205 		perror(file);
206 		return (-1);
207 	}
208 	printf("%s", file);
209 	if (fsdir == NULL) {
210 		register struct fstab *fs = getfsspec(file);
211 		if (fs != NULL)
212 			fsdir = fs->fs_file;
213 	}
214 	if (fsdir != NULL && *fsdir != '\0')
215 		printf(" (%s)", fsdir);
216 	printf(":\n");
217 	sync();
218 	bread(fd, (off_t)SBOFF, (char *)&sblock, SBSIZE);
219 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
220 	if (nflg) {
221 		if (isdigit(c = getchar()))
222 			(void)ungetc(c, stdin);
223 		else while (c != '\n' && c != EOF)
224 			c = getchar();
225 	}
226 	nfiles = sblock.fs_ipg * sblock.fs_ncg;
227 	for (ino = 0; ino < nfiles; ) {
228 		iblk = fsbtodb(&sblock, ino_to_fsba(&sblock, ino));
229 		bread(fd, (off_t)iblk * dev_bsize, itab, (int)sblock.fs_bsize);
230 		for (j = 0; j < INOPB(&sblock) && ino < nfiles; j++, ino++) {
231 			if (ino < ROOTINO)
232 				continue;
233 			qacct(&itab[j]);
234 		}
235 	}
236 	close(fd);
237 	return (0);
238 }
239 
240 qacct(ip)
241 	register struct dinode *ip;
242 {
243 	register struct du *dp;
244 	struct du **hp;
245 	long blks, frags, size;
246 	int n;
247 	static fino;
248 
249 	if ((ip->di_mode & IFMT) == 0)
250 		return;
251 	/*
252 	 * By default, take block count in inode.  Otherwise (-h),
253 	 * take the size field and estimate the blocks allocated.
254 	 * The latter does not account for holes in files.
255 	 */
256 	if (!hflg)
257 		size = ip->di_blocks / 2;
258 	else {
259 		blks = lblkno(&sblock, ip->di_size);
260 		frags = blks * sblock.fs_frag +
261 			numfrags(&sblock, dblksize(&sblock, ip, blks));
262 		size = frags * sblock.fs_fsize / 1024;
263 	}
264 	if (cflg) {
265 		if ((ip->di_mode&IFMT) != IFDIR && (ip->di_mode&IFMT) != IFREG)
266 			return;
267 		if (size >= TSIZE) {
268 			overflow += size;
269 			size = TSIZE-1;
270 		}
271 		sizes[size]++;
272 		return;
273 	}
274 	hp = &duhash[HASH(ip->di_uid)];
275 	for (dp = *hp; dp; dp = dp->next)
276 		if (dp->uid == ip->di_uid)
277 			break;
278 	if (dp == 0) {
279 		if (ndu >= NDU)
280 			return;
281 		dp = &du[ndu++];
282 		dp->next = *hp;
283 		*hp = dp;
284 		dp->uid = ip->di_uid;
285 		dp->nfiles = 0;
286 		dp->blocks = 0;
287 		dp->blocks30 = 0;
288 		dp->blocks60 = 0;
289 		dp->blocks90 = 0;
290 	}
291 	dp->blocks += size;
292 #define	DAY (60 * 60 * 24)	/* seconds per day */
293 	if (now.tv_sec - ip->di_atime.ts_sec > 30 * DAY)
294 		dp->blocks30 += size;
295 	if (now.tv_sec - ip->di_atime.ts_sec > 60 * DAY)
296 		dp->blocks60 += size;
297 	if (now.tv_sec - ip->di_atime.ts_sec > 90 * DAY)
298 		dp->blocks90 += size;
299 	dp->nfiles++;
300 	while (nflg) {
301 		register char *np;
302 
303 		if (fino == 0)
304 			if (scanf("%d", &fino) <= 0)
305 				return;
306 		if (fino > ino)
307 			return;
308 		if (fino < ino) {
309 			while ((n = getchar()) != '\n' && n != EOF)
310 				;
311 			fino = 0;
312 			continue;
313 		}
314 		if (np = user_from_uid(dp->uid, 1))
315 			printf("%.7s\t", np);
316 		else
317 			printf("%u\t", ip->di_uid);
318 		while ((n = getchar()) == ' ' || n == '\t')
319 			;
320 		putchar(n);
321 		while (n != EOF && n != '\n') {
322 			n = getchar();
323 			putchar(n);
324 		}
325 		fino = 0;
326 		break;
327 	}
328 }
329 
330 bread(fd, bno, buf, cnt)
331 	int fd, cnt;
332 	off_t bno;
333 	void *buf;
334 {
335 	(void)lseek(fd, bno, L_SET);
336 	if (read(fd, buf, cnt) != cnt) {
337 		fprintf(stderr, "quot: read error at block %ld\n", bno);
338 		exit(1);
339 	}
340 }
341 
342 qcmp(p1, p2)
343 	register struct du *p1, *p2;
344 {
345 	char *s1, *s2;
346 
347 	if (p1->blocks > p2->blocks)
348 		return (-1);
349 	if (p1->blocks < p2->blocks)
350 		return (1);
351 	s1 = user_from_uid(p1->uid, 1);
352 	if (s1 == 0)
353 		return (0);
354 	s2 = user_from_uid(p2->uid, 1);
355 	if (s2 == 0)
356 		return (0);
357 	return (strcmp(s1, s2));
358 }
359 
360 report()
361 {
362 	register i;
363 	register struct du *dp;
364 
365 	if (nflg)
366 		return;
367 	if (cflg) {
368 		register long t = 0;
369 
370 		for (i = 0; i < TSIZE - 1; i++)
371 			if (sizes[i]) {
372 				t += i*sizes[i];
373 				printf("%d\t%d\t%ld\n", i, sizes[i], t);
374 			}
375 		printf("%d\t%d\t%ld\n",
376 		    TSIZE - 1, sizes[TSIZE - 1], overflow + t);
377 		return;
378 	}
379 	qsort(du, ndu, sizeof (du[0]), qcmp);
380 	for (dp = du; dp < &du[ndu]; dp++) {
381 		register char *cp;
382 
383 		if (dp->blocks == 0)
384 			return;
385 		printf("%5D\t", dp->blocks);
386 		if (fflg)
387 			printf("%5D\t", dp->nfiles);
388 		if (cp = user_from_uid(dp->uid, 1))
389 			printf("%-8.8s", cp);
390 		else
391 			printf("#%-8d", dp->uid);
392 		if (vflg)
393 			printf("\t%5D\t%5D\t%5D",
394 			    dp->blocks30, dp->blocks60, dp->blocks90);
395 		printf("\n");
396 	}
397 }
398