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