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