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