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