quota.c revision 92921
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1990, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Robert Elz at The University of Melbourne.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#ifndef lint
3827888Scharnierstatic const char copyright[] =
391590Srgrimes"@(#) Copyright (c) 1980, 1990, 1993\n\
401590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411590Srgrimes#endif /* not lint */
421590Srgrimes
431590Srgrimes#ifndef lint
4427888Scharnier#if 0
4513365Sgraichenstatic char sccsid[] = "from: @(#)quota.c	8.1 (Berkeley) 6/6/93";
4627888Scharnier#endif
4727888Scharnierstatic const char rcsid[] =
4850477Speter  "$FreeBSD: head/usr.bin/quota/quota.c 92921 2002-03-22 01:33:25Z imp $";
491590Srgrimes#endif /* not lint */
501590Srgrimes
511590Srgrimes/*
521590Srgrimes * Disk quota reporting program.
531590Srgrimes */
541590Srgrimes#include <sys/param.h>
5513236Sgraichen#include <sys/types.h>
561590Srgrimes#include <sys/file.h>
571590Srgrimes#include <sys/stat.h>
5813236Sgraichen#include <sys/mount.h>
5913236Sgraichen#include <sys/socket.h>
601590Srgrimes#include <ufs/ufs/quota.h>
6127888Scharnier#include <ctype.h>
6227888Scharnier#include <err.h>
6327888Scharnier#include <fstab.h>
6427888Scharnier#include <grp.h>
6527888Scharnier#include <pwd.h>
661590Srgrimes#include <stdio.h>
6713236Sgraichen#include <stdlib.h>
6813236Sgraichen#include <string.h>
6916379Salex#include <unistd.h>
701590Srgrimes
7113236Sgraichen#include <netdb.h>
7213236Sgraichen#include <rpc/rpc.h>
7313236Sgraichen#include <rpc/pmap_prot.h>
7413236Sgraichen#include <rpcsvc/rquota.h>
7513236Sgraichen
761590Srgrimeschar *qfname = QUOTAFILENAME;
771590Srgrimeschar *qfextension[] = INITQFNAMES;
781590Srgrimes
791590Srgrimesstruct quotause {
801590Srgrimes	struct	quotause *next;
811590Srgrimes	long	flags;
821590Srgrimes	struct	dqblk dqblk;
831590Srgrimes	char	fsname[MAXPATHLEN + 1];
8413236Sgraichen};
851590Srgrimes#define	FOUND	0x01
861590Srgrimes
8792921Simpstatic char *timeprt(time_t seconds);
8892921Simpstatic struct quotause *getprivs(long id, int quotatype);
8916379Salexstatic void usage ();
9016379Salexstatic void showuid(u_long uid);
9116379Salexstatic void showgid(u_long gid);
9216379Salexstatic int alldigits(char *s);
9316379Salexstatic void showusrname(char *name);
9416379Salexstatic void showgrpname(char *name);
9516379Salexstatic void showquotas(int type, u_long id, char *name);
9616379Salexstatic void heading(int type, u_long id, char *name, char *tag);
9716379Salexstatic char *timeprt(time_t seconds);
9816379Salexstatic struct quotause *getprivs(long id, int quotatype);
9916379Salexstatic int ufshasquota(struct fstab *fs, int type, char **qfnamep);
10016379Salexstatic int getufsquota(struct statfs *fst, struct fstab *fs,
10116379Salex					   struct quotause *qup, long id, int quotatype);
10216379Salexstatic int getnfsquota(struct statfs *fst, struct fstab *fs,
10316379Salex					   struct quotause *qup, long id, int quotatype);
10416379Salexstatic int callaurpc(char *host, int prognum, int versnum, int procnum,
10516379Salex					 xdrproc_t inproc, char *in, xdrproc_t outproc, char *out);
10616379Salexstatic int alldigits(char *s);
10713236Sgraichen
1081590Srgrimesint	qflag;
1091590Srgrimesint	vflag;
1101590Srgrimes
11116379Salexint
1121590Srgrimesmain(argc, argv)
11316379Salex	int argc;
1141590Srgrimes	char *argv[];
1151590Srgrimes{
11613236Sgraichen	int ngroups;
11713236Sgraichen	gid_t mygid, gidset[NGROUPS];
1181590Srgrimes	int i, gflag = 0, uflag = 0;
1191590Srgrimes	char ch;
1201590Srgrimes
12124360Simp	while ((ch = getopt(argc, argv, "ugvq")) != -1) {
1221590Srgrimes		switch(ch) {
1231590Srgrimes		case 'g':
1241590Srgrimes			gflag++;
1251590Srgrimes			break;
1261590Srgrimes		case 'u':
1271590Srgrimes			uflag++;
1281590Srgrimes			break;
1291590Srgrimes		case 'v':
1301590Srgrimes			vflag++;
1311590Srgrimes			break;
1321590Srgrimes		case 'q':
1331590Srgrimes			qflag++;
1341590Srgrimes			break;
1351590Srgrimes		default:
1361590Srgrimes			usage();
1371590Srgrimes		}
1381590Srgrimes	}
1391590Srgrimes	argc -= optind;
1401590Srgrimes	argv += optind;
1411590Srgrimes	if (!uflag && !gflag)
1421590Srgrimes		uflag++;
1431590Srgrimes	if (argc == 0) {
1441590Srgrimes		if (uflag)
1451590Srgrimes			showuid(getuid());
1461590Srgrimes		if (gflag) {
14713236Sgraichen			mygid = getgid();
1481590Srgrimes			ngroups = getgroups(NGROUPS, gidset);
14927888Scharnier			if (ngroups < 0)
15027888Scharnier				err(1, "getgroups");
15113236Sgraichen			showgid(mygid);
15213236Sgraichen			for (i = 0; i < ngroups; i++)
15313236Sgraichen				if (gidset[i] != mygid)
15413236Sgraichen					showgid(gidset[i]);
1551590Srgrimes		}
15616379Salex		return(0);
1571590Srgrimes	}
1581590Srgrimes	if (uflag && gflag)
1591590Srgrimes		usage();
1601590Srgrimes	if (uflag) {
1611590Srgrimes		for (; argc > 0; argc--, argv++) {
1621590Srgrimes			if (alldigits(*argv))
1631590Srgrimes				showuid(atoi(*argv));
1641590Srgrimes			else
1651590Srgrimes				showusrname(*argv);
1661590Srgrimes		}
16716379Salex		return(0);
1681590Srgrimes	}
1691590Srgrimes	if (gflag) {
1701590Srgrimes		for (; argc > 0; argc--, argv++) {
1711590Srgrimes			if (alldigits(*argv))
1721590Srgrimes				showgid(atoi(*argv));
1731590Srgrimes			else
1741590Srgrimes				showgrpname(*argv);
1751590Srgrimes		}
1761590Srgrimes	}
17716379Salex	return(0);
1781590Srgrimes}
1791590Srgrimes
18016379Salexstatic void
1811590Srgrimesusage()
1821590Srgrimes{
1831590Srgrimes
1841590Srgrimes	fprintf(stderr, "%s\n%s\n%s\n",
18527888Scharnier		"usage: quota [-guqv]",
18627888Scharnier		"       quota [-qv] -u username ...",
18727888Scharnier		"       quota [-qv] -g groupname ...");
1881590Srgrimes	exit(1);
1891590Srgrimes}
1901590Srgrimes
1911590Srgrimes/*
1921590Srgrimes * Print out quotas for a specified user identifier.
1931590Srgrimes */
19416379Salexstatic void
1951590Srgrimesshowuid(uid)
1961590Srgrimes	u_long uid;
1971590Srgrimes{
1981590Srgrimes	struct passwd *pwd = getpwuid(uid);
1991590Srgrimes	u_long myuid;
2001590Srgrimes	char *name;
2011590Srgrimes
2021590Srgrimes	if (pwd == NULL)
2031590Srgrimes		name = "(no account)";
2041590Srgrimes	else
2051590Srgrimes		name = pwd->pw_name;
2061590Srgrimes	myuid = getuid();
2071590Srgrimes	if (uid != myuid && myuid != 0) {
20813365Sgraichen		printf("quota: %s (uid %lu): permission denied\n", name, uid);
2091590Srgrimes		return;
2101590Srgrimes	}
2111590Srgrimes	showquotas(USRQUOTA, uid, name);
2121590Srgrimes}
2131590Srgrimes
2141590Srgrimes/*
2151590Srgrimes * Print out quotas for a specifed user name.
2161590Srgrimes */
21716379Salexstatic void
2181590Srgrimesshowusrname(name)
2191590Srgrimes	char *name;
2201590Srgrimes{
2211590Srgrimes	struct passwd *pwd = getpwnam(name);
2221590Srgrimes	u_long myuid;
2231590Srgrimes
2241590Srgrimes	if (pwd == NULL) {
22527888Scharnier		warnx("%s: unknown user", name);
2261590Srgrimes		return;
2271590Srgrimes	}
2281590Srgrimes	myuid = getuid();
2291590Srgrimes	if (pwd->pw_uid != myuid && myuid != 0) {
23027888Scharnier		warnx("%s (uid %u): permission denied", name, pwd->pw_uid);
2311590Srgrimes		return;
2321590Srgrimes	}
2331590Srgrimes	showquotas(USRQUOTA, pwd->pw_uid, name);
2341590Srgrimes}
2351590Srgrimes
2361590Srgrimes/*
2371590Srgrimes * Print out quotas for a specified group identifier.
2381590Srgrimes */
23916379Salexstatic void
2401590Srgrimesshowgid(gid)
2411590Srgrimes	u_long gid;
2421590Srgrimes{
2431590Srgrimes	struct group *grp = getgrgid(gid);
24413236Sgraichen	int ngroups;
24513236Sgraichen	gid_t mygid, gidset[NGROUPS];
2461590Srgrimes	register int i;
2471590Srgrimes	char *name;
2481590Srgrimes
2491590Srgrimes	if (grp == NULL)
2501590Srgrimes		name = "(no entry)";
2511590Srgrimes	else
2521590Srgrimes		name = grp->gr_name;
25313236Sgraichen	mygid = getgid();
2541590Srgrimes	ngroups = getgroups(NGROUPS, gidset);
2551590Srgrimes	if (ngroups < 0) {
25627888Scharnier		warn("getgroups");
2571590Srgrimes		return;
2581590Srgrimes	}
25913236Sgraichen	if (gid != mygid) {
26013236Sgraichen		for (i = 0; i < ngroups; i++)
26113236Sgraichen			if (gid == gidset[i])
26213236Sgraichen				break;
26313236Sgraichen		if (i >= ngroups && getuid() != 0) {
26427888Scharnier			warnx("%s (gid %lu): permission denied", name, gid);
26513236Sgraichen			return;
26613236Sgraichen		}
2671590Srgrimes	}
2681590Srgrimes	showquotas(GRPQUOTA, gid, name);
2691590Srgrimes}
2701590Srgrimes
2711590Srgrimes/*
2721590Srgrimes * Print out quotas for a specifed group name.
2731590Srgrimes */
27416379Salexstatic void
2751590Srgrimesshowgrpname(name)
2761590Srgrimes	char *name;
2771590Srgrimes{
2781590Srgrimes	struct group *grp = getgrnam(name);
27913236Sgraichen	int ngroups;
28013236Sgraichen	gid_t mygid, gidset[NGROUPS];
2811590Srgrimes	register int i;
2821590Srgrimes
2831590Srgrimes	if (grp == NULL) {
28427888Scharnier		warnx("%s: unknown group", name);
2851590Srgrimes		return;
2861590Srgrimes	}
28713236Sgraichen	mygid = getgid();
2881590Srgrimes	ngroups = getgroups(NGROUPS, gidset);
2891590Srgrimes	if (ngroups < 0) {
29027888Scharnier		warn("getgroups");
2911590Srgrimes		return;
2921590Srgrimes	}
29313236Sgraichen	if (grp->gr_gid != mygid) {
29413236Sgraichen		for (i = 0; i < ngroups; i++)
29513236Sgraichen			if (grp->gr_gid == gidset[i])
29613236Sgraichen				break;
29713236Sgraichen		if (i >= ngroups && getuid() != 0) {
29858618Scharnier			warnx("%s (gid %u): permission denied", name,
29958618Scharnier						grp->gr_gid);
30013236Sgraichen			return;
30113236Sgraichen		}
3021590Srgrimes	}
3031590Srgrimes	showquotas(GRPQUOTA, grp->gr_gid, name);
3041590Srgrimes}
3051590Srgrimes
30616379Salexstatic void
3071590Srgrimesshowquotas(type, id, name)
3081590Srgrimes	int type;
3091590Srgrimes	u_long id;
3101590Srgrimes	char *name;
3111590Srgrimes{
3121590Srgrimes	register struct quotause *qup;
31313236Sgraichen	struct quotause *quplist;
31413236Sgraichen	char *msgi, *msgb, *nam;
31516379Salex	int lines = 0;
3161590Srgrimes	static time_t now;
3171590Srgrimes
3181590Srgrimes	if (now == 0)
3191590Srgrimes		time(&now);
3201590Srgrimes	quplist = getprivs(id, type);
3211590Srgrimes	for (qup = quplist; qup; qup = qup->next) {
3221590Srgrimes		if (!vflag &&
3231590Srgrimes		    qup->dqblk.dqb_isoftlimit == 0 &&
3241590Srgrimes		    qup->dqblk.dqb_ihardlimit == 0 &&
3251590Srgrimes		    qup->dqblk.dqb_bsoftlimit == 0 &&
3261590Srgrimes		    qup->dqblk.dqb_bhardlimit == 0)
3271590Srgrimes			continue;
3281590Srgrimes		msgi = (char *)0;
3291590Srgrimes		if (qup->dqblk.dqb_ihardlimit &&
3301590Srgrimes		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit)
3311590Srgrimes			msgi = "File limit reached on";
3321590Srgrimes		else if (qup->dqblk.dqb_isoftlimit &&
3331590Srgrimes		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
3341590Srgrimes			if (qup->dqblk.dqb_itime > now)
3351590Srgrimes				msgi = "In file grace period on";
3361590Srgrimes			else
3371590Srgrimes				msgi = "Over file quota on";
3381590Srgrimes		msgb = (char *)0;
3391590Srgrimes		if (qup->dqblk.dqb_bhardlimit &&
3401590Srgrimes		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit)
3411590Srgrimes			msgb = "Block limit reached on";
3421590Srgrimes		else if (qup->dqblk.dqb_bsoftlimit &&
3431590Srgrimes		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
3441590Srgrimes			if (qup->dqblk.dqb_btime > now)
3451590Srgrimes				msgb = "In block grace period on";
3461590Srgrimes			else
3471590Srgrimes				msgb = "Over block quota on";
3481590Srgrimes		if (qflag) {
3491590Srgrimes			if ((msgi != (char *)0 || msgb != (char *)0) &&
3501590Srgrimes			    lines++ == 0)
3511590Srgrimes				heading(type, id, name, "");
3521590Srgrimes			if (msgi != (char *)0)
3531590Srgrimes				printf("\t%s %s\n", msgi, qup->fsname);
3541590Srgrimes			if (msgb != (char *)0)
3551590Srgrimes				printf("\t%s %s\n", msgb, qup->fsname);
3561590Srgrimes			continue;
3571590Srgrimes		}
3581590Srgrimes		if (vflag ||
3591590Srgrimes		    qup->dqblk.dqb_curblocks ||
3601590Srgrimes		    qup->dqblk.dqb_curinodes) {
3611590Srgrimes			if (lines++ == 0)
3621590Srgrimes				heading(type, id, name, "");
36313236Sgraichen			nam = qup->fsname;
36413236Sgraichen			if (strlen(qup->fsname) > 15) {
36513236Sgraichen				printf("%s\n", qup->fsname);
36613236Sgraichen				nam = "";
36713236Sgraichen			}
3688327Sbde			printf("%15s%8lu%c%7lu%8lu%8s"
36913236Sgraichen				, nam
37013365Sgraichen				, (u_long) (dbtob(qup->dqblk.dqb_curblocks)
37113365Sgraichen					    / 1024)
3721590Srgrimes				, (msgb == (char *)0) ? ' ' : '*'
37313365Sgraichen				, (u_long) (dbtob(qup->dqblk.dqb_bsoftlimit)
37413365Sgraichen					    / 1024)
37513365Sgraichen				, (u_long) (dbtob(qup->dqblk.dqb_bhardlimit)
37613365Sgraichen					    / 1024)
3771590Srgrimes				, (msgb == (char *)0) ? ""
37813236Sgraichen				    :timeprt(qup->dqblk.dqb_btime));
37913365Sgraichen			printf("%8lu%c%7lu%8lu%8s\n"
3801590Srgrimes				, qup->dqblk.dqb_curinodes
3811590Srgrimes				, (msgi == (char *)0) ? ' ' : '*'
3821590Srgrimes				, qup->dqblk.dqb_isoftlimit
3831590Srgrimes				, qup->dqblk.dqb_ihardlimit
3841590Srgrimes				, (msgi == (char *)0) ? ""
3851590Srgrimes				    : timeprt(qup->dqblk.dqb_itime)
3861590Srgrimes			);
3871590Srgrimes			continue;
3881590Srgrimes		}
3891590Srgrimes	}
3901590Srgrimes	if (!qflag && lines == 0)
3911590Srgrimes		heading(type, id, name, "none");
3921590Srgrimes}
3931590Srgrimes
39416379Salexstatic void
3951590Srgrimesheading(type, id, name, tag)
3961590Srgrimes	int type;
3971590Srgrimes	u_long id;
3981590Srgrimes	char *name, *tag;
3991590Srgrimes{
4001590Srgrimes
40113365Sgraichen	printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type],
4021590Srgrimes	    name, *qfextension[type], id, tag);
4031590Srgrimes	if (!qflag && tag[0] == '\0') {
4041590Srgrimes		printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
4051590Srgrimes			, "Filesystem"
40677047Spirzyk			, "usage"
4071590Srgrimes			, "quota"
4081590Srgrimes			, "limit"
4091590Srgrimes			, "grace"
4101590Srgrimes			, "files"
4111590Srgrimes			, "quota"
4121590Srgrimes			, "limit"
4131590Srgrimes			, "grace"
4141590Srgrimes		);
4151590Srgrimes	}
4161590Srgrimes}
4171590Srgrimes
4181590Srgrimes/*
4191590Srgrimes * Calculate the grace period and return a printable string for it.
4201590Srgrimes */
42116379Salexstatic char *
4221590Srgrimestimeprt(seconds)
4231590Srgrimes	time_t seconds;
4241590Srgrimes{
4251590Srgrimes	time_t hours, minutes;
4261590Srgrimes	static char buf[20];
4271590Srgrimes	static time_t now;
4281590Srgrimes
4291590Srgrimes	if (now == 0)
4301590Srgrimes		time(&now);
4311590Srgrimes	if (now > seconds)
4321590Srgrimes		return ("none");
4331590Srgrimes	seconds -= now;
4341590Srgrimes	minutes = (seconds + 30) / 60;
4351590Srgrimes	hours = (minutes + 30) / 60;
4361590Srgrimes	if (hours >= 36) {
43713365Sgraichen		sprintf(buf, "%lddays", (hours + 12) / 24);
4381590Srgrimes		return (buf);
4391590Srgrimes	}
4401590Srgrimes	if (minutes >= 60) {
44113365Sgraichen		sprintf(buf, "%2ld:%ld", minutes / 60, minutes % 60);
4421590Srgrimes		return (buf);
4431590Srgrimes	}
44413365Sgraichen	sprintf(buf, "%2ld", minutes);
4451590Srgrimes	return (buf);
4461590Srgrimes}
4471590Srgrimes
4481590Srgrimes/*
4491590Srgrimes * Collect the requested quota information.
4501590Srgrimes */
45116379Salexstatic struct quotause *
4521590Srgrimesgetprivs(id, quotatype)
4531590Srgrimes	register long id;
4541590Srgrimes	int quotatype;
4551590Srgrimes{
45613236Sgraichen	register struct quotause *qup, *quptail;
4571590Srgrimes	register struct fstab *fs;
4581590Srgrimes	struct quotause *quphead;
45913236Sgraichen	struct statfs *fst;
46013236Sgraichen	int nfst, i;
4611590Srgrimes
46213236Sgraichen	qup = quphead = (struct quotause *)0;
46313236Sgraichen
46413236Sgraichen	nfst = getmntinfo(&fst, MNT_WAIT);
46527888Scharnier	if (nfst == 0)
46627888Scharnier		errx(2, "no filesystems mounted!");
4671590Srgrimes	setfsent();
46813236Sgraichen	for (i=0; i<nfst; i++) {
46913236Sgraichen		if (qup == NULL) {
47013365Sgraichen			if ((qup = (struct quotause *)malloc(sizeof *qup))
47127888Scharnier			    == NULL)
47227888Scharnier				errx(2, "out of memory");
4731590Srgrimes		}
47432651Sbde		if (strcmp(fst[i].f_fstypename, "nfs") == 0) {
47513365Sgraichen			if (getnfsquota(&fst[i], NULL, qup, id, quotatype)
47613365Sgraichen			    == 0)
4771590Srgrimes				continue;
47836880Sache		} else if (strcmp(fst[i].f_fstypename, "ufs") == 0) {
47913236Sgraichen			/*
48013236Sgraichen			 * XXX
48113236Sgraichen			 * UFS filesystems must be in /etc/fstab, and must
48213236Sgraichen			 * indicate that they have quotas on (?!) This is quite
48313236Sgraichen			 * unlike SunOS where quotas can be enabled/disabled
48413236Sgraichen			 * on a filesystem independent of /etc/fstab, and it
48513236Sgraichen			 * will still print quotas for them.
48613236Sgraichen			 */
48713236Sgraichen			if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL)
4881590Srgrimes				continue;
48913236Sgraichen			if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0)
49013236Sgraichen				continue;
49113236Sgraichen		} else
49213236Sgraichen			continue;
49313236Sgraichen		strcpy(qup->fsname, fst[i].f_mntonname);
4941590Srgrimes		if (quphead == NULL)
4951590Srgrimes			quphead = qup;
4961590Srgrimes		else
4971590Srgrimes			quptail->next = qup;
4981590Srgrimes		quptail = qup;
49913236Sgraichen		quptail->next = 0;
50013236Sgraichen		qup = NULL;
5011590Srgrimes	}
50213236Sgraichen	if (qup)
50313236Sgraichen		free(qup);
5041590Srgrimes	endfsent();
5051590Srgrimes	return (quphead);
5061590Srgrimes}
5071590Srgrimes
5081590Srgrimes/*
5091590Srgrimes * Check to see if a particular quota is to be enabled.
5101590Srgrimes */
51116379Salexstatic int
51213236Sgraichenufshasquota(fs, type, qfnamep)
5131590Srgrimes	register struct fstab *fs;
5141590Srgrimes	int type;
5151590Srgrimes	char **qfnamep;
5161590Srgrimes{
5171590Srgrimes	static char initname, usrname[100], grpname[100];
5181590Srgrimes	static char buf[BUFSIZ];
51913236Sgraichen	char *opt, *cp;
5201590Srgrimes
5211590Srgrimes	if (!initname) {
5221590Srgrimes		sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
5231590Srgrimes		sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
5241590Srgrimes		initname = 1;
5251590Srgrimes	}
5261590Srgrimes	strcpy(buf, fs->fs_mntops);
5271590Srgrimes	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
52816379Salex		if ((cp = index(opt, '=')))
5291590Srgrimes			*cp++ = '\0';
5301590Srgrimes		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
5311590Srgrimes			break;
5321590Srgrimes		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
5331590Srgrimes			break;
5341590Srgrimes	}
5351590Srgrimes	if (!opt)
5361590Srgrimes		return (0);
5371590Srgrimes	if (cp) {
5381590Srgrimes		*qfnamep = cp;
5391590Srgrimes		return (1);
5401590Srgrimes	}
5411590Srgrimes	(void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
5421590Srgrimes	*qfnamep = buf;
5431590Srgrimes	return (1);
5441590Srgrimes}
5451590Srgrimes
54616379Salexstatic int
54713236Sgraichengetufsquota(fst, fs, qup, id, quotatype)
54813236Sgraichen	struct statfs *fst;
54913236Sgraichen	struct fstab *fs;
55013236Sgraichen	struct quotause *qup;
55113236Sgraichen	long id;
55213236Sgraichen	int quotatype;
55313236Sgraichen{
55413236Sgraichen	char *qfpathname;
55513236Sgraichen	int fd, qcmd;
55613236Sgraichen
55713236Sgraichen	qcmd = QCMD(Q_GETQUOTA, quotatype);
55813236Sgraichen	if (!ufshasquota(fs, quotatype, &qfpathname))
55913236Sgraichen		return (0);
56013236Sgraichen
56116379Salex	if (quotactl(fs->fs_file, qcmd, id, (char *)&qup->dqblk) != 0) {
56213236Sgraichen		if ((fd = open(qfpathname, O_RDONLY)) < 0) {
56358618Scharnier			warn("%s", qfpathname);
56413236Sgraichen			return (0);
56513236Sgraichen		}
56613236Sgraichen		(void) lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET);
56713236Sgraichen		switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
56813236Sgraichen		case 0:				/* EOF */
56913236Sgraichen			/*
57013236Sgraichen			 * Convert implicit 0 quota (EOF)
57113236Sgraichen			 * into an explicit one (zero'ed dqblk)
57213236Sgraichen			 */
57313236Sgraichen			bzero((caddr_t)&qup->dqblk, sizeof(struct dqblk));
57413236Sgraichen			break;
57513236Sgraichen		case sizeof(struct dqblk):	/* OK */
57613236Sgraichen			break;
57713236Sgraichen		default:		/* ERROR */
57827888Scharnier			warn("read error: %s", qfpathname);
57913236Sgraichen			close(fd);
58013236Sgraichen			return (0);
58113236Sgraichen		}
58213236Sgraichen		close(fd);
58313236Sgraichen	}
58413236Sgraichen	return (1);
58513236Sgraichen}
58613236Sgraichen
58716379Salexstatic int
58813236Sgraichengetnfsquota(fst, fs, qup, id, quotatype)
58913236Sgraichen	struct statfs *fst;
59013236Sgraichen	struct fstab *fs;
59113236Sgraichen	struct quotause *qup;
59213236Sgraichen	long id;
59313236Sgraichen	int quotatype;
59413236Sgraichen{
59513236Sgraichen	struct getquota_args gq_args;
59613236Sgraichen	struct getquota_rslt gq_rslt;
59713236Sgraichen	struct dqblk *dqp = &qup->dqblk;
59813236Sgraichen	struct timeval tv;
59913236Sgraichen	char *cp;
60013236Sgraichen
60113236Sgraichen	if (fst->f_flags & MNT_LOCAL)
60213236Sgraichen		return (0);
60313236Sgraichen
60413236Sgraichen	/*
60513236Sgraichen	 * rpc.rquotad does not support group quotas
60613236Sgraichen	 */
60713236Sgraichen	if (quotatype != USRQUOTA)
60813236Sgraichen		return (0);
60913236Sgraichen
61013236Sgraichen	/*
61113236Sgraichen	 * must be some form of "hostname:/path"
61213236Sgraichen	 */
61313236Sgraichen	cp = strchr(fst->f_mntfromname, ':');
61413236Sgraichen	if (cp == NULL) {
61558618Scharnier		warnx("cannot find hostname for %s", fst->f_mntfromname);
61613236Sgraichen		return (0);
61713236Sgraichen	}
61813236Sgraichen
61913236Sgraichen	*cp = '\0';
62013236Sgraichen	if (*(cp+1) != '/') {
62113236Sgraichen		*cp = ':';
62213236Sgraichen		return (0);
62313236Sgraichen	}
62413236Sgraichen
62513236Sgraichen	gq_args.gqa_pathp = cp + 1;
62613236Sgraichen	gq_args.gqa_uid = id;
62713236Sgraichen	if (callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
62816379Salex	    RQUOTAPROC_GETQUOTA, xdr_getquota_args, (char *)&gq_args,
62916379Salex	    xdr_getquota_rslt, (char *)&gq_rslt) != 0) {
63013236Sgraichen		*cp = ':';
63113236Sgraichen		return (0);
63213236Sgraichen	}
63313236Sgraichen
63413236Sgraichen	switch (gq_rslt.status) {
63513236Sgraichen	case Q_NOQUOTA:
63613236Sgraichen		break;
63713236Sgraichen	case Q_EPERM:
63827888Scharnier		warnx("quota permission error, host: %s",
63913236Sgraichen			fst->f_mntfromname);
64013236Sgraichen		break;
64113236Sgraichen	case Q_OK:
64213236Sgraichen		gettimeofday(&tv, NULL);
64313236Sgraichen			/* blocks*/
64413236Sgraichen		dqp->dqb_bhardlimit =
64513236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
64613236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
64713236Sgraichen		dqp->dqb_bsoftlimit =
64813236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
64913236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
65013236Sgraichen		dqp->dqb_curblocks =
65113236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
65213236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
65313236Sgraichen			/* inodes */
65413236Sgraichen		dqp->dqb_ihardlimit =
65513236Sgraichen			gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
65613236Sgraichen		dqp->dqb_isoftlimit =
65713236Sgraichen			gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
65813236Sgraichen		dqp->dqb_curinodes =
65913236Sgraichen			gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
66013236Sgraichen			/* grace times */
66113236Sgraichen		dqp->dqb_btime =
66213236Sgraichen		    tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
66313236Sgraichen		dqp->dqb_itime =
66413236Sgraichen		    tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
66513236Sgraichen		*cp = ':';
66613236Sgraichen		return (1);
66713236Sgraichen	default:
66858618Scharnier		warnx("bad rpc result, host: %s", fst->f_mntfromname);
66913236Sgraichen		break;
67013236Sgraichen	}
67113236Sgraichen	*cp = ':';
67213236Sgraichen	return (0);
67313236Sgraichen}
67413236Sgraichen
67516379Salexstatic int
67613236Sgraichencallaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
67713236Sgraichen	char *host;
67813236Sgraichen	xdrproc_t inproc, outproc;
67913236Sgraichen	char *in, *out;
68016379Salex	int prognum, versnum, procnum;
68113236Sgraichen{
68213236Sgraichen	struct sockaddr_in server_addr;
68313236Sgraichen	enum clnt_stat clnt_stat;
68413236Sgraichen	struct hostent *hp;
68513236Sgraichen	struct timeval timeout, tottimeout;
68613236Sgraichen
68713236Sgraichen	CLIENT *client = NULL;
68813236Sgraichen	int socket = RPC_ANYSOCK;
68913236Sgraichen
69013236Sgraichen	if ((hp = gethostbyname(host)) == NULL)
69113236Sgraichen		return ((int) RPC_UNKNOWNHOST);
69213236Sgraichen	timeout.tv_usec = 0;
69313236Sgraichen	timeout.tv_sec = 6;
69458618Scharnier	bcopy(hp->h_addr, &server_addr.sin_addr,
69558618Scharnier			MIN(hp->h_length,sizeof(server_addr.sin_addr)));
69613236Sgraichen	server_addr.sin_family = AF_INET;
69713236Sgraichen	server_addr.sin_port =  0;
69813236Sgraichen
69913236Sgraichen	if ((client = clntudp_create(&server_addr, prognum,
70013236Sgraichen	    versnum, timeout, &socket)) == NULL)
70113236Sgraichen		return ((int) rpc_createerr.cf_stat);
70213236Sgraichen
70313236Sgraichen	client->cl_auth = authunix_create_default();
70413236Sgraichen	tottimeout.tv_sec = 25;
70513236Sgraichen	tottimeout.tv_usec = 0;
70613236Sgraichen	clnt_stat = clnt_call(client, procnum, inproc, in,
70713236Sgraichen	    outproc, out, tottimeout);
70813236Sgraichen
70913236Sgraichen	return ((int) clnt_stat);
71013236Sgraichen}
71113236Sgraichen
71216379Salexstatic int
7131590Srgrimesalldigits(s)
7141590Srgrimes	register char *s;
7151590Srgrimes{
7161590Srgrimes	register c;
7171590Srgrimes
7181590Srgrimes	c = *s++;
7191590Srgrimes	do {
7201590Srgrimes		if (!isdigit(c))
7211590Srgrimes			return (0);
72216379Salex	} while ((c = *s++));
7231590Srgrimes	return (1);
7241590Srgrimes}
725