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 * 4. Neither the name of the University nor the names of its contributors
171590Srgrimes *    may be used to endorse or promote products derived from this software
181590Srgrimes *    without specific prior written permission.
191590Srgrimes *
201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301590Srgrimes * SUCH DAMAGE.
311590Srgrimes */
321590Srgrimes
331590Srgrimes#ifndef lint
3427888Scharnierstatic const char copyright[] =
351590Srgrimes"@(#) Copyright (c) 1980, 1990, 1993\n\
361590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3795624Smarkm#endif
381590Srgrimes
391590Srgrimes#ifndef lint
4095624Smarkmstatic const char sccsid[] = "from: @(#)quota.c	8.1 (Berkeley) 6/6/93";
411590Srgrimes#endif /* not lint */
421590Srgrimes
431590Srgrimes/*
441590Srgrimes * Disk quota reporting program.
451590Srgrimes */
4695624Smarkm#include <sys/cdefs.h>
4795624Smarkm__FBSDID("$FreeBSD: stable/11/usr.bin/quota/quota.c 352576 2019-09-21 14:06:16Z hrs $");
4895624Smarkm
491590Srgrimes#include <sys/param.h>
5013236Sgraichen#include <sys/types.h>
511590Srgrimes#include <sys/file.h>
521590Srgrimes#include <sys/stat.h>
5313236Sgraichen#include <sys/mount.h>
5413236Sgraichen#include <sys/socket.h>
5595624Smarkm
5695624Smarkm#include <rpc/rpc.h>
5795624Smarkm#include <rpc/pmap_prot.h>
5895624Smarkm#include <rpcsvc/rquota.h>
5995624Smarkm
601590Srgrimes#include <ufs/ufs/quota.h>
6195624Smarkm
6227888Scharnier#include <ctype.h>
6327888Scharnier#include <err.h>
6427888Scharnier#include <fstab.h>
6527888Scharnier#include <grp.h>
66163599Sru#include <libutil.h>
6795624Smarkm#include <netdb.h>
6827888Scharnier#include <pwd.h>
691590Srgrimes#include <stdio.h>
70169345Sdwmalone#include <stdint.h>
7113236Sgraichen#include <stdlib.h>
7213236Sgraichen#include <string.h>
73166646Smpp#include <time.h>
7416379Salex#include <unistd.h>
751590Srgrimes
76227176Sedstatic const char *qfextension[] = INITQFNAMES;
7713236Sgraichen
781590Srgrimesstruct quotause {
791590Srgrimes	struct	quotause *next;
801590Srgrimes	long	flags;
811590Srgrimes	struct	dqblk dqblk;
821590Srgrimes	char	fsname[MAXPATHLEN + 1];
8313236Sgraichen};
841590Srgrimes
85207736Smckusickstatic char *timeprt(int64_t seconds);
8692921Simpstatic struct quotause *getprivs(long id, int quotatype);
8795624Smarkmstatic void usage(void);
88166388Smppstatic int showuid(u_long uid);
89166388Smppstatic int showgid(u_long gid);
90166388Smppstatic int showusrname(char *name);
91166388Smppstatic int showgrpname(char *name);
92166388Smppstatic int showquotas(int type, u_long id, const char *name);
93166646Smppstatic void showrawquotas(int type, u_long id, struct quotause *qup);
9495624Smarkmstatic void heading(int type, u_long id, const char *name, const char *tag);
9595624Smarkmstatic int getufsquota(struct fstab *fs, struct quotause *qup, long id,
9695624Smarkm	int quotatype);
9795624Smarkmstatic int getnfsquota(struct statfs *fst, struct quotause *qup, long id,
9895624Smarkm	int quotatype);
99339008Ssefstatic enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum,
10095624Smarkm	xdrproc_t inproc, char *in, xdrproc_t outproc, char *out);
10116379Salexstatic int alldigits(char *s);
10213236Sgraichen
103227176Sedstatic int	hflag;
104227176Sedstatic int	lflag;
105227176Sedstatic int	rflag;
106227176Sedstatic int	qflag;
107227176Sedstatic int	vflag;
108227176Sedstatic char	*filename = NULL;
1091590Srgrimes
11016379Salexint
11195624Smarkmmain(int argc, char *argv[])
1121590Srgrimes{
11313236Sgraichen	int ngroups;
114207736Smckusick	gid_t mygid, gidset[NGROUPS];
115166388Smpp	int i, ch, gflag = 0, uflag = 0, errflag = 0;
1161590Srgrimes
117166646Smpp	while ((ch = getopt(argc, argv, "f:ghlrquv")) != -1) {
1181590Srgrimes		switch(ch) {
119166646Smpp		case 'f':
120166646Smpp			filename = optarg;
121166646Smpp			break;
1221590Srgrimes		case 'g':
1231590Srgrimes			gflag++;
1241590Srgrimes			break;
125163599Sru		case 'h':
126163599Sru			hflag++;
127163599Sru			break;
128101545Siedowse		case 'l':
129101545Siedowse			lflag++;
130101545Siedowse			break;
131101544Siedowse		case 'q':
132101544Siedowse			qflag++;
133101544Siedowse			break;
134166646Smpp		case 'r':
135166646Smpp			rflag++;
136166646Smpp			break;
1371590Srgrimes		case 'u':
1381590Srgrimes			uflag++;
1391590Srgrimes			break;
1401590Srgrimes		case 'v':
1411590Srgrimes			vflag++;
1421590Srgrimes			break;
1431590Srgrimes		default:
1441590Srgrimes			usage();
1451590Srgrimes		}
1461590Srgrimes	}
1471590Srgrimes	argc -= optind;
1481590Srgrimes	argv += optind;
1491590Srgrimes	if (!uflag && !gflag)
1501590Srgrimes		uflag++;
1511590Srgrimes	if (argc == 0) {
1521590Srgrimes		if (uflag)
153166388Smpp			errflag += showuid(getuid());
1541590Srgrimes		if (gflag) {
15513236Sgraichen			mygid = getgid();
156207736Smckusick			ngroups = getgroups(NGROUPS, gidset);
15727888Scharnier			if (ngroups < 0)
15827888Scharnier				err(1, "getgroups");
159166388Smpp			errflag += showgid(mygid);
16013236Sgraichen			for (i = 0; i < ngroups; i++)
16113236Sgraichen				if (gidset[i] != mygid)
162166388Smpp					errflag += showgid(gidset[i]);
1631590Srgrimes		}
164166388Smpp		return(errflag);
1651590Srgrimes	}
1661590Srgrimes	if (uflag && gflag)
1671590Srgrimes		usage();
1681590Srgrimes	if (uflag) {
1691590Srgrimes		for (; argc > 0; argc--, argv++) {
1701590Srgrimes			if (alldigits(*argv))
171166388Smpp				errflag += showuid(atoi(*argv));
1721590Srgrimes			else
173166388Smpp				errflag += showusrname(*argv);
1741590Srgrimes		}
175166388Smpp		return(errflag);
1761590Srgrimes	}
1771590Srgrimes	if (gflag) {
1781590Srgrimes		for (; argc > 0; argc--, argv++) {
1791590Srgrimes			if (alldigits(*argv))
180166388Smpp				errflag += showgid(atoi(*argv));
1811590Srgrimes			else
182166388Smpp				errflag += showgrpname(*argv);
1831590Srgrimes		}
1841590Srgrimes	}
185166388Smpp	return(errflag);
1861590Srgrimes}
1871590Srgrimes
18816379Salexstatic void
18995624Smarkmusage(void)
1901590Srgrimes{
1911590Srgrimes
1921590Srgrimes	fprintf(stderr, "%s\n%s\n%s\n",
193166646Smpp	    "usage: quota [-ghlu] [-f path] [-v | -q | -r]",
194166646Smpp	    "       quota [-hlu] [-f path] [-v | -q | -r] user ...",
195166646Smpp	    "       quota -g [-hl] [-f path] [-v | -q | -r] group ...");
1961590Srgrimes	exit(1);
1971590Srgrimes}
1981590Srgrimes
1991590Srgrimes/*
2001590Srgrimes * Print out quotas for a specified user identifier.
2011590Srgrimes */
202166388Smppstatic int
20395624Smarkmshowuid(u_long uid)
2041590Srgrimes{
2051590Srgrimes	struct passwd *pwd = getpwuid(uid);
20695624Smarkm	const char *name;
2071590Srgrimes
2081590Srgrimes	if (pwd == NULL)
2091590Srgrimes		name = "(no account)";
2101590Srgrimes	else
2111590Srgrimes		name = pwd->pw_name;
212166388Smpp	return(showquotas(USRQUOTA, uid, name));
2131590Srgrimes}
2141590Srgrimes
2151590Srgrimes/*
2161590Srgrimes * Print out quotas for a specifed user name.
2171590Srgrimes */
218166388Smppstatic int
21995624Smarkmshowusrname(char *name)
2201590Srgrimes{
2211590Srgrimes	struct passwd *pwd = getpwnam(name);
2221590Srgrimes
2231590Srgrimes	if (pwd == NULL) {
22427888Scharnier		warnx("%s: unknown user", name);
225166388Smpp		return(1);
2261590Srgrimes	}
227166388Smpp	return(showquotas(USRQUOTA, pwd->pw_uid, name));
2281590Srgrimes}
2291590Srgrimes
2301590Srgrimes/*
2311590Srgrimes * Print out quotas for a specified group identifier.
2321590Srgrimes */
233166388Smppstatic int
23495624Smarkmshowgid(u_long gid)
2351590Srgrimes{
2361590Srgrimes	struct group *grp = getgrgid(gid);
23795624Smarkm	const char *name;
2381590Srgrimes
2391590Srgrimes	if (grp == NULL)
2401590Srgrimes		name = "(no entry)";
2411590Srgrimes	else
2421590Srgrimes		name = grp->gr_name;
243166388Smpp	return(showquotas(GRPQUOTA, gid, name));
2441590Srgrimes}
2451590Srgrimes
2461590Srgrimes/*
2471590Srgrimes * Print out quotas for a specifed group name.
2481590Srgrimes */
249166388Smppstatic int
25095624Smarkmshowgrpname(char *name)
2511590Srgrimes{
2521590Srgrimes	struct group *grp = getgrnam(name);
2531590Srgrimes
2541590Srgrimes	if (grp == NULL) {
25527888Scharnier		warnx("%s: unknown group", name);
256166388Smpp		return(1);
2571590Srgrimes	}
258166388Smpp	return(showquotas(GRPQUOTA, grp->gr_gid, name));
2591590Srgrimes}
2601590Srgrimes
26116379Salexstatic void
262207736Smckusickprthumanval(int len, u_int64_t bytes)
263163599Sru{
264163599Sru	char buf[len + 1];
265163599Sru
266223690Spluknet	/*
267223690Spluknet	 * Limit the width to 5 bytes as that is what users expect.
268223690Spluknet	 */
269298682Saraujo	humanize_number(buf, MIN(sizeof(buf), 5), bytes, "",
270298682Saraujo			HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
271163599Sru
272163599Sru	(void)printf(" %*s", len, buf);
273163599Sru}
274163599Sru
275166388Smppstatic int
27695624Smarkmshowquotas(int type, u_long id, const char *name)
2771590Srgrimes{
27895624Smarkm	struct quotause *qup;
27913236Sgraichen	struct quotause *quplist;
28095624Smarkm	const char *msgi, *msgb;
28195624Smarkm	const char *nam;
282181267Sdelphij	char *bgrace = NULL, *igrace = NULL;
283166388Smpp	int lines = 0, overquota = 0;
2841590Srgrimes	static time_t now;
2851590Srgrimes
2861590Srgrimes	if (now == 0)
2871590Srgrimes		time(&now);
2881590Srgrimes	quplist = getprivs(id, type);
2891590Srgrimes	for (qup = quplist; qup; qup = qup->next) {
290207736Smckusick		msgi = NULL;
2911590Srgrimes		if (qup->dqblk.dqb_ihardlimit &&
292166388Smpp		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) {
293166388Smpp			overquota++;
2941590Srgrimes			msgi = "File limit reached on";
295166388Smpp		}
2961590Srgrimes		else if (qup->dqblk.dqb_isoftlimit &&
29795624Smarkm		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) {
298166388Smpp			overquota++;
2991590Srgrimes			if (qup->dqblk.dqb_itime > now)
3001590Srgrimes				msgi = "In file grace period on";
3011590Srgrimes			else
3021590Srgrimes				msgi = "Over file quota on";
30395624Smarkm		}
304207736Smckusick		msgb = NULL;
3051590Srgrimes		if (qup->dqblk.dqb_bhardlimit &&
306166388Smpp		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) {
307166388Smpp			overquota++;
3081590Srgrimes			msgb = "Block limit reached on";
309166388Smpp		}
3101590Srgrimes		else if (qup->dqblk.dqb_bsoftlimit &&
31195624Smarkm		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) {
312166388Smpp			overquota++;
3131590Srgrimes			if (qup->dqblk.dqb_btime > now)
3141590Srgrimes				msgb = "In block grace period on";
3151590Srgrimes			else
3161590Srgrimes				msgb = "Over block quota on";
31795624Smarkm		}
318166646Smpp		if (rflag) {
319166646Smpp			showrawquotas(type, id, qup);
320166646Smpp			continue;
321166646Smpp		}
322166646Smpp		if (!vflag &&
323166646Smpp		    qup->dqblk.dqb_isoftlimit == 0 &&
324166646Smpp		    qup->dqblk.dqb_ihardlimit == 0 &&
325166646Smpp		    qup->dqblk.dqb_bsoftlimit == 0 &&
326166646Smpp		    qup->dqblk.dqb_bhardlimit == 0)
327166646Smpp			continue;
3281590Srgrimes		if (qflag) {
329207736Smckusick			if ((msgi != NULL || msgb != NULL) &&
3301590Srgrimes			    lines++ == 0)
3311590Srgrimes				heading(type, id, name, "");
332207736Smckusick			if (msgi != NULL)
3331590Srgrimes				printf("\t%s %s\n", msgi, qup->fsname);
334207736Smckusick			if (msgb != NULL)
3351590Srgrimes				printf("\t%s %s\n", msgb, qup->fsname);
3361590Srgrimes			continue;
3371590Srgrimes		}
338207736Smckusick		if (!vflag &&
339207736Smckusick		    qup->dqblk.dqb_curblocks == 0 &&
340207736Smckusick		    qup->dqblk.dqb_curinodes == 0)
3411590Srgrimes			continue;
342207736Smckusick		if (lines++ == 0)
343207736Smckusick			heading(type, id, name, "");
344207736Smckusick		nam = qup->fsname;
345207736Smckusick		if (strlen(qup->fsname) > 15) {
346207736Smckusick			printf("%s\n", qup->fsname);
347207736Smckusick			nam = "";
348207736Smckusick		}
349207736Smckusick		printf("%-15s", nam);
350207736Smckusick		if (hflag) {
351207736Smckusick			prthumanval(7, dbtob(qup->dqblk.dqb_curblocks));
352207736Smckusick			printf("%c", (msgb == NULL) ? ' ' : '*');
353207736Smckusick			prthumanval(7, dbtob(qup->dqblk.dqb_bsoftlimit));
354207736Smckusick			prthumanval(7, dbtob(qup->dqblk.dqb_bhardlimit));
355207736Smckusick		} else {
356207736Smckusick			printf(" %7ju%c %7ju %7ju",
357223690Spluknet			    (uintmax_t)dbtob(qup->dqblk.dqb_curblocks)
358223690Spluknet				/ 1024,
359207736Smckusick			    (msgb == NULL) ? ' ' : '*',
360223690Spluknet			    (uintmax_t)dbtob(qup->dqblk.dqb_bsoftlimit)
361223690Spluknet				/ 1024,
362223690Spluknet			    (uintmax_t)dbtob(qup->dqblk.dqb_bhardlimit)
363223690Spluknet				/ 1024);
3641590Srgrimes		}
365207736Smckusick		if (msgb != NULL)
366207736Smckusick			bgrace = timeprt(qup->dqblk.dqb_btime);
367207736Smckusick		if (msgi != NULL)
368207736Smckusick			igrace = timeprt(qup->dqblk.dqb_itime);
369207736Smckusick		printf("%8s %6ju%c %6ju %6ju%8s\n"
370207736Smckusick			, (msgb == NULL) ? "" : bgrace
371207736Smckusick			, (uintmax_t)qup->dqblk.dqb_curinodes
372207736Smckusick			, (msgi == NULL) ? ' ' : '*'
373207736Smckusick			, (uintmax_t)qup->dqblk.dqb_isoftlimit
374207736Smckusick			, (uintmax_t)qup->dqblk.dqb_ihardlimit
375207736Smckusick			, (msgi == NULL) ? "" : igrace
376207736Smckusick		);
377207736Smckusick		if (msgb != NULL)
378207736Smckusick			free(bgrace);
379207736Smckusick		if (msgi != NULL)
380207736Smckusick			free(igrace);
3811590Srgrimes	}
382166646Smpp	if (!qflag && !rflag && lines == 0)
3831590Srgrimes		heading(type, id, name, "none");
384207736Smckusick	return (overquota);
3851590Srgrimes}
3861590Srgrimes
38716379Salexstatic void
388181267Sdelphijshowrawquotas(int type, u_long id, struct quotause *qup)
389166646Smpp{
390207736Smckusick	time_t t;
391207736Smckusick
392166646Smpp	printf("Raw %s quota information for id %lu on %s\n",
393166646Smpp	    type == USRQUOTA ? "user" : "group", id, qup->fsname);
394207736Smckusick	printf("block hard limit:     %ju\n",
395207736Smckusick	    (uintmax_t)qup->dqblk.dqb_bhardlimit);
396207736Smckusick	printf("block soft limit:     %ju\n",
397207736Smckusick	    (uintmax_t)qup->dqblk.dqb_bsoftlimit);
398207736Smckusick	printf("current block count:  %ju\n",
399207736Smckusick	    (uintmax_t)qup->dqblk.dqb_curblocks);
400207736Smckusick	printf("i-node hard limit:    %ju\n",
401207736Smckusick	    (uintmax_t)qup->dqblk.dqb_ihardlimit);
402207736Smckusick	printf("i-node soft limit:    %ju\n",
403207736Smckusick	    (uintmax_t)qup->dqblk.dqb_isoftlimit);
404207736Smckusick	printf("current i-node count: %ju\n",
405207736Smckusick	    (uintmax_t)qup->dqblk.dqb_curinodes);
406207736Smckusick	printf("block grace time:     %jd",
407207736Smckusick	    (intmax_t)qup->dqblk.dqb_btime);
408181262Scognet	if (qup->dqblk.dqb_btime != 0) {
409207736Smckusick		t = qup->dqblk.dqb_btime;
410207736Smckusick		printf(" %s", ctime(&t));
411207736Smckusick	} else {
412166646Smpp		printf("\n");
413207736Smckusick	}
414169345Sdwmalone	printf("i-node grace time:    %jd", (intmax_t)qup->dqblk.dqb_itime);
415181262Scognet	if (qup->dqblk.dqb_itime != 0) {
416207736Smckusick		t = qup->dqblk.dqb_itime;
417207736Smckusick		printf(" %s", ctime(&t));
418207736Smckusick	} else {
419166646Smpp		printf("\n");
420207736Smckusick	}
421166646Smpp}
422166646Smpp
423166646Smpp
424166646Smppstatic void
42595624Smarkmheading(int type, u_long id, const char *name, const char *tag)
4261590Srgrimes{
4271590Srgrimes
42813365Sgraichen	printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type],
4291590Srgrimes	    name, *qfextension[type], id, tag);
4301590Srgrimes	if (!qflag && tag[0] == '\0') {
431207736Smckusick		printf("%-15s %7s %8s %7s %7s %6s %7s %6s%8s\n"
4321590Srgrimes			, "Filesystem"
43377047Spirzyk			, "usage"
4341590Srgrimes			, "quota"
4351590Srgrimes			, "limit"
4361590Srgrimes			, "grace"
4371590Srgrimes			, "files"
4381590Srgrimes			, "quota"
4391590Srgrimes			, "limit"
4401590Srgrimes			, "grace"
4411590Srgrimes		);
4421590Srgrimes	}
4431590Srgrimes}
4441590Srgrimes
4451590Srgrimes/*
4461590Srgrimes * Calculate the grace period and return a printable string for it.
4471590Srgrimes */
448166495Smppstatic char *
449207736Smckusicktimeprt(int64_t seconds)
4501590Srgrimes{
4511590Srgrimes	time_t hours, minutes;
452207736Smckusick	char *buf;
4531590Srgrimes	static time_t now;
4541590Srgrimes
4551590Srgrimes	if (now == 0)
4561590Srgrimes		time(&now);
457166495Smpp	if (now > seconds) {
458207736Smckusick		if ((buf = strdup("none")) == NULL)
459207736Smckusick			errx(1, "strdup() failed in timeprt()");
460207736Smckusick		return (buf);
461166495Smpp	}
4621590Srgrimes	seconds -= now;
4631590Srgrimes	minutes = (seconds + 30) / 60;
4641590Srgrimes	hours = (minutes + 30) / 60;
4651590Srgrimes	if (hours >= 36) {
466166495Smpp		if (asprintf(&buf, "%lddays", ((long)hours + 12) / 24) < 0)
467207736Smckusick			errx(1, "asprintf() failed in timeprt(1)");
4681590Srgrimes		return (buf);
4691590Srgrimes	}
4701590Srgrimes	if (minutes >= 60) {
471166495Smpp		if (asprintf(&buf, "%2ld:%ld", (long)minutes / 60,
472166495Smpp		    (long)minutes % 60) < 0)
473207736Smckusick			errx(1, "asprintf() failed in timeprt(2)");
4741590Srgrimes		return (buf);
4751590Srgrimes	}
476166495Smpp	if (asprintf(&buf, "%2ld", (long)minutes) < 0)
477207736Smckusick		errx(1, "asprintf() failed in timeprt(3)");
4781590Srgrimes	return (buf);
4791590Srgrimes}
4801590Srgrimes
4811590Srgrimes/*
4821590Srgrimes * Collect the requested quota information.
4831590Srgrimes */
48416379Salexstatic struct quotause *
48595624Smarkmgetprivs(long id, int quotatype)
4861590Srgrimes{
487101544Siedowse	struct quotause *qup, *quptail = NULL;
48895624Smarkm	struct fstab *fs;
4891590Srgrimes	struct quotause *quphead;
49013236Sgraichen	struct statfs *fst;
49113236Sgraichen	int nfst, i;
492166646Smpp	struct statfs sfb;
4931590Srgrimes
49413236Sgraichen	qup = quphead = (struct quotause *)0;
49513236Sgraichen
496166646Smpp	if (filename != NULL && statfs(filename, &sfb) != 0)
497166646Smpp		err(1, "cannot statfs %s", filename);
49897764Siedowse	nfst = getmntinfo(&fst, MNT_NOWAIT);
49927888Scharnier	if (nfst == 0)
50027888Scharnier		errx(2, "no filesystems mounted!");
5011590Srgrimes	setfsent();
502207736Smckusick	for (i = 0; i < nfst; i++) {
50313236Sgraichen		if (qup == NULL) {
50413365Sgraichen			if ((qup = (struct quotause *)malloc(sizeof *qup))
50527888Scharnier			    == NULL)
50627888Scharnier				errx(2, "out of memory");
5071590Srgrimes		}
508166646Smpp		/*
509166646Smpp		 * See if the user requested a specific file system
510166646Smpp		 * or specified a file inside a mounted file system.
511166646Smpp		 */
512166646Smpp		if (filename != NULL &&
513166646Smpp		    strcmp(sfb.f_mntonname, fst[i].f_mntonname) != 0)
514166646Smpp			continue;
51532651Sbde		if (strcmp(fst[i].f_fstypename, "nfs") == 0) {
516101545Siedowse			if (lflag)
517101545Siedowse				continue;
518166646Smpp			if (getnfsquota(&fst[i], qup, id, quotatype) == 0)
5191590Srgrimes				continue;
52036880Sache		} else if (strcmp(fst[i].f_fstypename, "ufs") == 0) {
52113236Sgraichen			/*
52213236Sgraichen			 * XXX
52313236Sgraichen			 * UFS filesystems must be in /etc/fstab, and must
52413236Sgraichen			 * indicate that they have quotas on (?!) This is quite
52513236Sgraichen			 * unlike SunOS where quotas can be enabled/disabled
52613236Sgraichen			 * on a filesystem independent of /etc/fstab, and it
52713236Sgraichen			 * will still print quotas for them.
52813236Sgraichen			 */
52913236Sgraichen			if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL)
5301590Srgrimes				continue;
53195624Smarkm			if (getufsquota(fs, qup, id, quotatype) == 0)
53213236Sgraichen				continue;
53313236Sgraichen		} else
53413236Sgraichen			continue;
53513236Sgraichen		strcpy(qup->fsname, fst[i].f_mntonname);
5361590Srgrimes		if (quphead == NULL)
5371590Srgrimes			quphead = qup;
5381590Srgrimes		else
5391590Srgrimes			quptail->next = qup;
5401590Srgrimes		quptail = qup;
54113236Sgraichen		quptail->next = 0;
54213236Sgraichen		qup = NULL;
5431590Srgrimes	}
54413236Sgraichen	if (qup)
54513236Sgraichen		free(qup);
5461590Srgrimes	endfsent();
5471590Srgrimes	return (quphead);
5481590Srgrimes}
5491590Srgrimes
5501590Srgrimes/*
551207736Smckusick * Check to see if a particular quota is available.
5521590Srgrimes */
55316379Salexstatic int
554207736Smckusickgetufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype)
5551590Srgrimes{
556207736Smckusick	struct quotafile *qf;
5571590Srgrimes
558207736Smckusick	if ((qf = quota_open(fs, quotatype, O_RDONLY)) == NULL)
5591590Srgrimes		return (0);
560207736Smckusick	if (quota_read(qf, &qup->dqblk, id) != 0)
561166485Smpp		return (0);
562207736Smckusick	quota_close(qf);
5631590Srgrimes	return (1);
5641590Srgrimes}
5651590Srgrimes
56616379Salexstatic int
56795624Smarkmgetnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype)
56813236Sgraichen{
569339008Ssef	struct ext_getquota_args gq_args;
570339008Ssef	struct getquota_args old_gq_args;
57113236Sgraichen	struct getquota_rslt gq_rslt;
57213236Sgraichen	struct dqblk *dqp = &qup->dqblk;
57313236Sgraichen	struct timeval tv;
574285253Shrs	char *cp, host[NI_MAXHOST];
575339008Ssef	enum clnt_stat call_stat;
57613236Sgraichen
57713236Sgraichen	if (fst->f_flags & MNT_LOCAL)
57813236Sgraichen		return (0);
57913236Sgraichen
58013236Sgraichen	/*
58113236Sgraichen	 * must be some form of "hostname:/path"
58213236Sgraichen	 */
583285253Shrs	cp = fst->f_mntfromname;
584285253Shrs	do {
585285253Shrs		cp = strrchr(cp, ':');
586285253Shrs	} while (cp != NULL && *(cp + 1) != '/');
58713236Sgraichen	if (cp == NULL) {
58858618Scharnier		warnx("cannot find hostname for %s", fst->f_mntfromname);
58913236Sgraichen		return (0);
59013236Sgraichen	}
591285253Shrs	memset(host, 0, sizeof(host));
592285253Shrs	memcpy(host, fst->f_mntfromname, cp - fst->f_mntfromname);
593285253Shrs	host[sizeof(host) - 1] = '\0';
59413236Sgraichen
59597764Siedowse	/* Avoid attempting the RPC for special amd(8) filesystems. */
59697764Siedowse	if (strncmp(fst->f_mntfromname, "pid", 3) == 0 &&
597285253Shrs	    strchr(fst->f_mntfromname, '@') != NULL)
59897764Siedowse		return (0);
59997764Siedowse
60013236Sgraichen	gq_args.gqa_pathp = cp + 1;
601339008Ssef	gq_args.gqa_id = id;
602339008Ssef	gq_args.gqa_type = quotatype;
60313236Sgraichen
604339008Ssef	call_stat = callaurpc(host, RQUOTAPROG, EXT_RQUOTAVERS,
605339008Ssef			      RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char *)&gq_args,
606339008Ssef			      (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt);
607352576Shrs	if (call_stat == RPC_PROGVERSMISMATCH || call_stat == RPC_PROGNOTREGISTERED) {
608339008Ssef		if (quotatype == USRQUOTA) {
609339008Ssef			old_gq_args.gqa_pathp = cp + 1;
610339008Ssef			old_gq_args.gqa_uid = id;
611339008Ssef			call_stat = callaurpc(host, RQUOTAPROG, RQUOTAVERS,
612339008Ssef					      RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&old_gq_args,
613339008Ssef					      (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt);
614339008Ssef		} else {
615339008Ssef			/* Old rpc quota does not support group type */
616339008Ssef			return (0);
617339008Ssef		}
618339008Ssef	}
619339008Ssef	if (call_stat != 0)
620339008Ssef		return (call_stat);
621339008Ssef
62213236Sgraichen	switch (gq_rslt.status) {
62313236Sgraichen	case Q_NOQUOTA:
62413236Sgraichen		break;
62513236Sgraichen	case Q_EPERM:
62627888Scharnier		warnx("quota permission error, host: %s",
62713236Sgraichen			fst->f_mntfromname);
62813236Sgraichen		break;
62913236Sgraichen	case Q_OK:
63013236Sgraichen		gettimeofday(&tv, NULL);
63113236Sgraichen			/* blocks*/
63213236Sgraichen		dqp->dqb_bhardlimit =
63313236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
634118464Sdas		    (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
63513236Sgraichen		dqp->dqb_bsoftlimit =
63613236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
637118464Sdas		    (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
63813236Sgraichen		dqp->dqb_curblocks =
63913236Sgraichen		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
640118464Sdas		    (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
64113236Sgraichen			/* inodes */
64213236Sgraichen		dqp->dqb_ihardlimit =
64313236Sgraichen			gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
64413236Sgraichen		dqp->dqb_isoftlimit =
64513236Sgraichen			gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
64613236Sgraichen		dqp->dqb_curinodes =
64713236Sgraichen			gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
64813236Sgraichen			/* grace times */
64913236Sgraichen		dqp->dqb_btime =
65013236Sgraichen		    tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
65113236Sgraichen		dqp->dqb_itime =
65213236Sgraichen		    tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
65313236Sgraichen		return (1);
65413236Sgraichen	default:
65558618Scharnier		warnx("bad rpc result, host: %s", fst->f_mntfromname);
65613236Sgraichen		break;
65713236Sgraichen	}
658285253Shrs
65913236Sgraichen	return (0);
66013236Sgraichen}
66113236Sgraichen
662339008Ssefstatic enum clnt_stat
66395624Smarkmcallaurpc(char *host, int prognum, int versnum, int procnum,
66495624Smarkm    xdrproc_t inproc, char *in, xdrproc_t outproc, char *out)
66513236Sgraichen{
66613236Sgraichen	enum clnt_stat clnt_stat;
66713236Sgraichen	struct timeval timeout, tottimeout;
66813236Sgraichen
66913236Sgraichen	CLIENT *client = NULL;
670285253Shrs
671285253Shrs 	client = clnt_create(host, prognum, versnum, "udp");
672285253Shrs	if (client == NULL)
673285253Shrs		return ((int)rpc_createerr.cf_stat);
67413236Sgraichen	timeout.tv_usec = 0;
67513236Sgraichen	timeout.tv_sec = 6;
676285253Shrs	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&timeout);
67713236Sgraichen
67813236Sgraichen	client->cl_auth = authunix_create_default();
67913236Sgraichen	tottimeout.tv_sec = 25;
68013236Sgraichen	tottimeout.tv_usec = 0;
68113236Sgraichen	clnt_stat = clnt_call(client, procnum, inproc, in,
68213236Sgraichen	    outproc, out, tottimeout);
683339008Ssef	return (clnt_stat);
68413236Sgraichen}
68513236Sgraichen
68616379Salexstatic int
68795624Smarkmalldigits(char *s)
6881590Srgrimes{
68995624Smarkm	int c;
6901590Srgrimes
6911590Srgrimes	c = *s++;
6921590Srgrimes	do {
6931590Srgrimes		if (!isdigit(c))
6941590Srgrimes			return (0);
69516379Salex	} while ((c = *s++));
6961590Srgrimes	return (1);
6971590Srgrimes}
698