xref: /original-bsd/usr.bin/quota/quota.c (revision 00695d63)
1 /*
2  * Copyright (c) 1980, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Robert Elz at The University of Melbourne.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1980, 1990, 1993\n\
14 	The Regents of the University of California.  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)quota.c	8.4 (Berkeley) 04/28/95";
19 #endif /* not lint */
20 
21 /*
22  * Disk quota reporting program.
23  */
24 #include <sys/param.h>
25 #include <sys/file.h>
26 #include <sys/stat.h>
27 #include <sys/queue.h>
28 
29 #include <ufs/ufs/quota.h>
30 
31 #include <ctype.h>
32 #include <errno.h>
33 #include <fstab.h>
34 #include <grp.h>
35 #include <pwd.h>
36 #include <stdio.h>
37 
38 char *qfname = QUOTAFILENAME;
39 char *qfextension[] = INITQFNAMES;
40 
41 struct quotause {
42 	struct	quotause *next;
43 	long	flags;
44 	struct	dqblk dqblk;
45 	char	fsname[MAXPATHLEN + 1];
46 } *getprivs();
47 #define	FOUND	0x01
48 
49 int	qflag;
50 int	vflag;
51 
52 main(argc, argv)
53 	char *argv[];
54 {
55 	int ngroups;
56 	gid_t gidset[NGROUPS];
57 	int i, gflag = 0, uflag = 0;
58 	char ch;
59 	extern char *optarg;
60 	extern int optind, errno;
61 
62 	if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) {
63 		fprintf(stderr, "There are no quotas on this system\n");
64 		exit(0);
65 	}
66 	while ((ch = getopt(argc, argv, "ugvq")) != EOF) {
67 		switch(ch) {
68 		case 'g':
69 			gflag++;
70 			break;
71 		case 'u':
72 			uflag++;
73 			break;
74 		case 'v':
75 			vflag++;
76 			break;
77 		case 'q':
78 			qflag++;
79 			break;
80 		default:
81 			usage();
82 		}
83 	}
84 	argc -= optind;
85 	argv += optind;
86 	if (!uflag && !gflag)
87 		uflag++;
88 	if (argc == 0) {
89 		if (uflag)
90 			showuid(getuid());
91 		if (gflag) {
92 			ngroups = getgroups(NGROUPS, gidset);
93 			if (ngroups < 0) {
94 				perror("quota: getgroups");
95 				exit(1);
96 			}
97 			for (i = 1; i < ngroups; i++)
98 				showgid(gidset[i]);
99 		}
100 		exit(0);
101 	}
102 	if (uflag && gflag)
103 		usage();
104 	if (uflag) {
105 		for (; argc > 0; argc--, argv++) {
106 			if (alldigits(*argv))
107 				showuid(atoi(*argv));
108 			else
109 				showusrname(*argv);
110 		}
111 		exit(0);
112 	}
113 	if (gflag) {
114 		for (; argc > 0; argc--, argv++) {
115 			if (alldigits(*argv))
116 				showgid(atoi(*argv));
117 			else
118 				showgrpname(*argv);
119 		}
120 		exit(0);
121 	}
122 }
123 
124 usage()
125 {
126 
127 	fprintf(stderr, "%s\n%s\n%s\n",
128 		"Usage: quota [-guqv]",
129 		"\tquota [-qv] -u username ...",
130 		"\tquota [-qv] -g groupname ...");
131 	exit(1);
132 }
133 
134 /*
135  * Print out quotas for a specified user identifier.
136  */
137 showuid(uid)
138 	u_long uid;
139 {
140 	struct passwd *pwd = getpwuid(uid);
141 	u_long myuid;
142 	char *name;
143 
144 	if (pwd == NULL)
145 		name = "(no account)";
146 	else
147 		name = pwd->pw_name;
148 	myuid = getuid();
149 	if (uid != myuid && myuid != 0) {
150 		printf("quota: %s (uid %d): permission denied\n", name, uid);
151 		return;
152 	}
153 	showquotas(USRQUOTA, uid, name);
154 }
155 
156 /*
157  * Print out quotas for a specifed user name.
158  */
159 showusrname(name)
160 	char *name;
161 {
162 	struct passwd *pwd = getpwnam(name);
163 	u_long myuid;
164 
165 	if (pwd == NULL) {
166 		fprintf(stderr, "quota: %s: unknown user\n", name);
167 		return;
168 	}
169 	myuid = getuid();
170 	if (pwd->pw_uid != myuid && myuid != 0) {
171 		fprintf(stderr, "quota: %s (uid %d): permission denied\n",
172 		    name, pwd->pw_uid);
173 		return;
174 	}
175 	showquotas(USRQUOTA, pwd->pw_uid, name);
176 }
177 
178 /*
179  * Print out quotas for a specified group identifier.
180  */
181 showgid(gid)
182 	u_long gid;
183 {
184 	struct group *grp = getgrgid(gid);
185 	int ngroups;
186 	gid_t gidset[NGROUPS];
187 	register int i;
188 	char *name;
189 
190 	if (grp == NULL)
191 		name = "(no entry)";
192 	else
193 		name = grp->gr_name;
194 	ngroups = getgroups(NGROUPS, gidset);
195 	if (ngroups < 0) {
196 		perror("quota: getgroups");
197 		return;
198 	}
199 	for (i = 1; i < ngroups; i++)
200 		if (gid == gidset[i])
201 			break;
202 	if (i >= ngroups && getuid() != 0) {
203 		fprintf(stderr, "quota: %s (gid %d): permission denied\n",
204 		    name, gid);
205 		return;
206 	}
207 	showquotas(GRPQUOTA, gid, name);
208 }
209 
210 /*
211  * Print out quotas for a specifed group name.
212  */
213 showgrpname(name)
214 	char *name;
215 {
216 	struct group *grp = getgrnam(name);
217 	int ngroups;
218 	gid_t gidset[NGROUPS];
219 	register int i;
220 
221 	if (grp == NULL) {
222 		fprintf(stderr, "quota: %s: unknown group\n", name);
223 		return;
224 	}
225 	ngroups = getgroups(NGROUPS, gidset);
226 	if (ngroups < 0) {
227 		perror("quota: getgroups");
228 		return;
229 	}
230 	for (i = 1; i < ngroups; i++)
231 		if (grp->gr_gid == gidset[i])
232 			break;
233 	if (i >= ngroups && getuid() != 0) {
234 		fprintf(stderr, "quota: %s (gid %d): permission denied\n",
235 		    name, grp->gr_gid);
236 		return;
237 	}
238 	showquotas(GRPQUOTA, grp->gr_gid, name);
239 }
240 
241 showquotas(type, id, name)
242 	int type;
243 	u_long id;
244 	char *name;
245 {
246 	register struct quotause *qup;
247 	struct quotause *quplist, *getprivs();
248 	char *msgi, *msgb, *timeprt();
249 	int myuid, fd, lines = 0;
250 	static int first;
251 	static time_t now;
252 
253 	if (now == 0)
254 		time(&now);
255 	quplist = getprivs(id, type);
256 	for (qup = quplist; qup; qup = qup->next) {
257 		if (!vflag &&
258 		    qup->dqblk.dqb_isoftlimit == 0 &&
259 		    qup->dqblk.dqb_ihardlimit == 0 &&
260 		    qup->dqblk.dqb_bsoftlimit == 0 &&
261 		    qup->dqblk.dqb_bhardlimit == 0)
262 			continue;
263 		msgi = (char *)0;
264 		if (qup->dqblk.dqb_ihardlimit &&
265 		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit)
266 			msgi = "File limit reached on";
267 		else if (qup->dqblk.dqb_isoftlimit &&
268 		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
269 			if (qup->dqblk.dqb_itime > now)
270 				msgi = "In file grace period on";
271 			else
272 				msgi = "Over file quota on";
273 		msgb = (char *)0;
274 		if (qup->dqblk.dqb_bhardlimit &&
275 		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit)
276 			msgb = "Block limit reached on";
277 		else if (qup->dqblk.dqb_bsoftlimit &&
278 		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
279 			if (qup->dqblk.dqb_btime > now)
280 				msgb = "In block grace period on";
281 			else
282 				msgb = "Over block quota on";
283 		if (qflag) {
284 			if ((msgi != (char *)0 || msgb != (char *)0) &&
285 			    lines++ == 0)
286 				heading(type, id, name, "");
287 			if (msgi != (char *)0)
288 				printf("\t%s %s\n", msgi, qup->fsname);
289 			if (msgb != (char *)0)
290 				printf("\t%s %s\n", msgb, qup->fsname);
291 			continue;
292 		}
293 		if (vflag ||
294 		    qup->dqblk.dqb_curblocks ||
295 		    qup->dqblk.dqb_curinodes) {
296 			if (lines++ == 0)
297 				heading(type, id, name, "");
298 			printf("%15s%8d%c%7d%8d%8s"
299 				, qup->fsname
300 				, dbtob(qup->dqblk.dqb_curblocks) / 1024
301 				, (msgb == (char *)0) ? ' ' : '*'
302 				, dbtob(qup->dqblk.dqb_bsoftlimit) / 1024
303 				, dbtob(qup->dqblk.dqb_bhardlimit) / 1024
304 				, (msgb == (char *)0) ? ""
305 				    : timeprt(qup->dqblk.dqb_btime));
306 			printf("%8d%c%7d%8d%8s\n"
307 				, qup->dqblk.dqb_curinodes
308 				, (msgi == (char *)0) ? ' ' : '*'
309 				, qup->dqblk.dqb_isoftlimit
310 				, qup->dqblk.dqb_ihardlimit
311 				, (msgi == (char *)0) ? ""
312 				    : timeprt(qup->dqblk.dqb_itime)
313 			);
314 			continue;
315 		}
316 	}
317 	if (!qflag && lines == 0)
318 		heading(type, id, name, "none");
319 }
320 
321 heading(type, id, name, tag)
322 	int type;
323 	u_long id;
324 	char *name, *tag;
325 {
326 
327 	printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type],
328 	    name, *qfextension[type], id, tag);
329 	if (!qflag && tag[0] == '\0') {
330 		printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
331 			, "Filesystem"
332 			, "blocks"
333 			, "quota"
334 			, "limit"
335 			, "grace"
336 			, "files"
337 			, "quota"
338 			, "limit"
339 			, "grace"
340 		);
341 	}
342 }
343 
344 /*
345  * Calculate the grace period and return a printable string for it.
346  */
347 char *
348 timeprt(seconds)
349 	time_t seconds;
350 {
351 	time_t hours, minutes;
352 	static char buf[20];
353 	static time_t now;
354 
355 	if (now == 0)
356 		time(&now);
357 	if (now > seconds)
358 		return ("none");
359 	seconds -= now;
360 	minutes = (seconds + 30) / 60;
361 	hours = (minutes + 30) / 60;
362 	if (hours >= 36) {
363 		sprintf(buf, "%ddays", (hours + 12) / 24);
364 		return (buf);
365 	}
366 	if (minutes >= 60) {
367 		sprintf(buf, "%2d:%d", minutes / 60, minutes % 60);
368 		return (buf);
369 	}
370 	sprintf(buf, "%2d", minutes);
371 	return (buf);
372 }
373 
374 /*
375  * Collect the requested quota information.
376  */
377 struct quotause *
378 getprivs(id, quotatype)
379 	register long id;
380 	int quotatype;
381 {
382 	register struct fstab *fs;
383 	register struct quotause *qup, *quptail;
384 	struct quotause *quphead;
385 	char *qfpathname;
386 	int qcmd, fd;
387 
388 	setfsent();
389 	quphead = (struct quotause *)0;
390 	qcmd = QCMD(Q_GETQUOTA, quotatype);
391 	while (fs = getfsent()) {
392 		if (strcmp(fs->fs_vfstype, "ufs"))
393 			continue;
394 		if (!hasquota(fs, quotatype, &qfpathname))
395 			continue;
396 		if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) {
397 			fprintf(stderr, "quota: out of memory\n");
398 			exit(2);
399 		}
400 		if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
401 			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
402 				perror(qfpathname);
403 				free(qup);
404 				continue;
405 			}
406 			lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET);
407 			switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
408 			case 0:			/* EOF */
409 				/*
410 				 * Convert implicit 0 quota (EOF)
411 				 * into an explicit one (zero'ed dqblk)
412 				 */
413 				bzero((caddr_t)&qup->dqblk,
414 				    sizeof(struct dqblk));
415 				break;
416 
417 			case sizeof(struct dqblk):	/* OK */
418 				break;
419 
420 			default:		/* ERROR */
421 				fprintf(stderr, "quota: read error");
422 				perror(qfpathname);
423 				close(fd);
424 				free(qup);
425 				continue;
426 			}
427 			close(fd);
428 		}
429 		strcpy(qup->fsname, fs->fs_file);
430 		if (quphead == NULL)
431 			quphead = qup;
432 		else
433 			quptail->next = qup;
434 		quptail = qup;
435 		qup->next = 0;
436 	}
437 	endfsent();
438 	return (quphead);
439 }
440 
441 /*
442  * Check to see if a particular quota is to be enabled.
443  */
444 hasquota(fs, type, qfnamep)
445 	register struct fstab *fs;
446 	int type;
447 	char **qfnamep;
448 {
449 	register char *opt;
450 	char *cp, *index(), *strtok();
451 	static char initname, usrname[100], grpname[100];
452 	static char buf[BUFSIZ];
453 
454 	if (!initname) {
455 		sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
456 		sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
457 		initname = 1;
458 	}
459 	strcpy(buf, fs->fs_mntops);
460 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
461 		if (cp = index(opt, '='))
462 			*cp++ = '\0';
463 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
464 			break;
465 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
466 			break;
467 	}
468 	if (!opt)
469 		return (0);
470 	if (cp) {
471 		*qfnamep = cp;
472 		return (1);
473 	}
474 	(void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
475 	*qfnamep = buf;
476 	return (1);
477 }
478 
479 alldigits(s)
480 	register char *s;
481 {
482 	register c;
483 
484 	c = *s++;
485 	do {
486 		if (!isdigit(c))
487 			return (0);
488 	} while (c = *s++);
489 	return (1);
490 }
491