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