Home | History | Annotate | Line # | Download | only in quota
quota.c revision 1.43
      1 /*	$NetBSD: quota.c,v 1.43 2011/11/30 16:12:32 dholland Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1990, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Robert Elz at The University of Melbourne.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 #ifndef lint
     37 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
     38  The Regents of the University of California.  All rights reserved.");
     39 #endif /* not lint */
     40 
     41 #ifndef lint
     42 #if 0
     43 static char sccsid[] = "@(#)quota.c	8.4 (Berkeley) 4/28/95";
     44 #else
     45 __RCSID("$NetBSD: quota.c,v 1.43 2011/11/30 16:12:32 dholland Exp $");
     46 #endif
     47 #endif /* not lint */
     48 
     49 /*
     50  * Disk quota reporting program.
     51  */
     52 #include <sys/param.h>
     53 #include <sys/types.h>
     54 #include <sys/file.h>
     55 #include <sys/stat.h>
     56 #include <sys/mount.h>
     57 #include <sys/socket.h>
     58 
     59 #include <assert.h>
     60 #include <ctype.h>
     61 #include <err.h>
     62 #include <errno.h>
     63 #include <fstab.h>
     64 #include <grp.h>
     65 #include <netdb.h>
     66 #include <pwd.h>
     67 #include <stdio.h>
     68 #include <stdlib.h>
     69 #include <string.h>
     70 #include <time.h>
     71 #include <unistd.h>
     72 
     73 #include <quota/quotaprop.h>
     74 #include <quota/quota.h>
     75 
     76 #include "printquota.h"
     77 #include "getvfsquota.h"
     78 
     79 struct quotause {
     80 	struct	quotause *next;
     81 	long	flags;
     82 	uid_t	id;
     83 	struct	quotaval *qvs;
     84 	unsigned numqvs;
     85 	char	fsname[MAXPATHLEN + 1];
     86 };
     87 #define	FOUND	0x01
     88 #define	QUOTA2	0x02
     89 
     90 static int	anyusage(struct quotaval *, unsigned);
     91 static int	anyover(struct quotaval *, unsigned, time_t);
     92 static const char *getovermsg(struct quotaval *, const char *, time_t);
     93 static struct quotause	*getprivs(id_t, int);
     94 static void	heading(int, id_t, const char *, const char *);
     95 static int	isover(struct quotaval *qv, time_t now);
     96 static void	printqv(struct quotaval *, int, int, time_t);
     97 static void	showgid(gid_t);
     98 static void	showgrpname(const char *);
     99 static void	showonequota(int, id_t, const char *, struct quotause *);
    100 static void	showquotas(int, id_t, const char *);
    101 static void	showuid(uid_t);
    102 static void	showusrname(const char *);
    103 static int	unlimited(struct quotaval *qvs, unsigned numqvs);
    104 static void	usage(void) __dead;
    105 
    106 static int	qflag = 0;
    107 static int	vflag = 0;
    108 static int	hflag = 0;
    109 static int	dflag = 0;
    110 static int	Dflag = 0;
    111 static uid_t	myuid;
    112 static int needheading;
    113 
    114 int
    115 main(int argc, char *argv[])
    116 {
    117 	int ngroups;
    118 	gid_t mygid, gidset[NGROUPS];
    119 	int i, gflag = 0, uflag = 0;
    120 	int ch;
    121 
    122 	myuid = getuid();
    123 	while ((ch = getopt(argc, argv, "Ddhugvq")) != -1) {
    124 		switch(ch) {
    125 		case 'g':
    126 			gflag++;
    127 			break;
    128 		case 'u':
    129 			uflag++;
    130 			break;
    131 		case 'v':
    132 			vflag++;
    133 			break;
    134 		case 'q':
    135 			qflag++;
    136 			break;
    137 		case 'h':
    138 			hflag++;
    139 			break;
    140 		case 'd':
    141 			dflag++;
    142 			break;
    143 		case 'D':
    144 			Dflag++;
    145 			break;
    146 		default:
    147 			usage();
    148 		}
    149 	}
    150 	argc -= optind;
    151 	argv += optind;
    152 	if (!uflag && !gflag)
    153 		uflag++;
    154 	if (dflag) {
    155 #if 0
    156 		if (myuid != 0)
    157 			errx(1, "-d: permission denied");
    158 #endif
    159 		if (uflag)
    160 			showquotas(QUOTA_CLASS_USER, 0, "");
    161 		if (gflag)
    162 			showquotas(QUOTA_CLASS_GROUP, 0, "");
    163 		return 0;
    164 	}
    165 	if (argc == 0) {
    166 		if (uflag)
    167 			showuid(myuid);
    168 		if (gflag) {
    169 			if (dflag)
    170 				showgid(0);
    171 			else {
    172 				mygid = getgid();
    173 				ngroups = getgroups(NGROUPS, gidset);
    174 				if (ngroups < 0)
    175 					err(1, "getgroups");
    176 				showgid(mygid);
    177 				for (i = 0; i < ngroups; i++)
    178 					if (gidset[i] != mygid)
    179 						showgid(gidset[i]);
    180 			}
    181 		}
    182 		return 0;
    183 	}
    184 	if (uflag && gflag)
    185 		usage();
    186 	if (uflag) {
    187 		for (; argc > 0; argc--, argv++) {
    188 			if (alldigits(*argv))
    189 				showuid((uid_t)atoi(*argv));
    190 			else
    191 				showusrname(*argv);
    192 		}
    193 		return 0;
    194 	}
    195 	if (gflag) {
    196 		for (; argc > 0; argc--, argv++) {
    197 			if (alldigits(*argv))
    198 				showgid((gid_t)atoi(*argv));
    199 			else
    200 				showgrpname(*argv);
    201 		}
    202 		return 0;
    203 	}
    204 	/* NOTREACHED */
    205 	return 0;
    206 }
    207 
    208 static void
    209 usage(void)
    210 {
    211 	const char *p = getprogname();
    212 	fprintf(stderr, "Usage: %s [-Dhguqv]\n"
    213 	    "\t%s [-Dhqv] -u username ...\n"
    214 	    "\t%s [-Dhqv] -g groupname ...\n"
    215 	    "\t%s -d [-Dhguqv]\n", p, p, p, p);
    216 	exit(1);
    217 }
    218 
    219 /*
    220  * Print out quotas for a specified user identifier.
    221  */
    222 static void
    223 showuid(uid_t uid)
    224 {
    225 	struct passwd *pwd = getpwuid(uid);
    226 	const char *name;
    227 
    228 	if (pwd == NULL)
    229 		name = "(no account)";
    230 	else
    231 		name = pwd->pw_name;
    232 	if (uid != myuid && myuid != 0) {
    233 		warnx("%s (uid %d): permission denied", name, uid);
    234 		return;
    235 	}
    236 	showquotas(QUOTA_CLASS_USER, uid, name);
    237 }
    238 
    239 /*
    240  * Print out quotas for a specified user name.
    241  */
    242 static void
    243 showusrname(const char *name)
    244 {
    245 	struct passwd *pwd = getpwnam(name);
    246 
    247 	if (pwd == NULL) {
    248 		warnx("%s: unknown user", name);
    249 		return;
    250 	}
    251 	if (pwd->pw_uid != myuid && myuid != 0) {
    252 		warnx("%s (uid %d): permission denied", name, pwd->pw_uid);
    253 		return;
    254 	}
    255 	showquotas(QUOTA_CLASS_USER, pwd->pw_uid, name);
    256 }
    257 
    258 /*
    259  * Print out quotas for a specified group identifier.
    260  */
    261 static void
    262 showgid(gid_t gid)
    263 {
    264 	struct group *grp = getgrgid(gid);
    265 	int ngroups;
    266 	gid_t mygid, gidset[NGROUPS];
    267 	int i;
    268 	const char *name;
    269 
    270 	if (grp == NULL)
    271 		name = "(no entry)";
    272 	else
    273 		name = grp->gr_name;
    274 	mygid = getgid();
    275 	ngroups = getgroups(NGROUPS, gidset);
    276 	if (ngroups < 0) {
    277 		warn("getgroups");
    278 		return;
    279 	}
    280 	if (gid != mygid) {
    281 		for (i = 0; i < ngroups; i++)
    282 			if (gid == gidset[i])
    283 				break;
    284 		if (i >= ngroups && myuid != 0) {
    285 			warnx("%s (gid %d): permission denied", name, gid);
    286 			return;
    287 		}
    288 	}
    289 	showquotas(QUOTA_CLASS_GROUP, gid, name);
    290 }
    291 
    292 /*
    293  * Print out quotas for a specified group name.
    294  */
    295 static void
    296 showgrpname(const char *name)
    297 {
    298 	struct group *grp = getgrnam(name);
    299 	int ngroups;
    300 	gid_t mygid, gidset[NGROUPS];
    301 	int i;
    302 
    303 	if (grp == NULL) {
    304 		warnx("%s: unknown group", name);
    305 		return;
    306 	}
    307 	mygid = getgid();
    308 	ngroups = getgroups(NGROUPS, gidset);
    309 	if (ngroups < 0) {
    310 		warn("getgroups");
    311 		return;
    312 	}
    313 	if (grp->gr_gid != mygid) {
    314 		for (i = 0; i < ngroups; i++)
    315 			if (grp->gr_gid == gidset[i])
    316 				break;
    317 		if (i >= ngroups && myuid != 0) {
    318 			warnx("%s (gid %d): permission denied",
    319 			    name, grp->gr_gid);
    320 			return;
    321 		}
    322 	}
    323 	showquotas(QUOTA_CLASS_GROUP, grp->gr_gid, name);
    324 }
    325 
    326 static void
    327 showquotas(int type, id_t id, const char *idname)
    328 {
    329 	struct quotause *qup;
    330 	struct quotause *quplist;
    331 
    332 	needheading = 1;
    333 
    334 	quplist = getprivs(id, type);
    335 	for (qup = quplist; qup; qup = qup->next) {
    336 		showonequota(type, id, idname, qup);
    337 	}
    338 	if (!qflag) {
    339 		/* In case nothing printed, issue a header saying "none" */
    340 		heading(type, id, idname, "none");
    341 	}
    342 }
    343 
    344 static void
    345 showonequota(int type, id_t id, const char *idname, struct quotause *qup)
    346 {
    347 	static const int isbytes[QUOTA_NLIMITS] = {
    348 		[QUOTA_LIMIT_BLOCK] = 1,
    349 		[QUOTA_LIMIT_FILE] = 0,
    350 	};
    351 	static time_t now;
    352 	struct quotaval *qvs;
    353 	unsigned numqvs, i;
    354 	const char *msg;
    355 	int isquota2;
    356 
    357 	qvs = qup->qvs;
    358 	numqvs = qup->numqvs;
    359 
    360 	if (now == 0) {
    361 		time(&now);
    362 	}
    363 
    364 	if (!vflag && unlimited(qvs, numqvs)) {
    365 		return;
    366 	}
    367 
    368 	if (qflag) {
    369 		for (i=0; i<numqvs; i++) {
    370 			msg = getovermsg(&qvs[i], ufs_quota_limit_names[i],
    371 					 now);
    372 			if (msg != NULL) {
    373 				heading(type, id, idname, "");
    374 				printf("\t%s %s\n", msg, qup->fsname);
    375 			}
    376 		}
    377 		return;
    378 	}
    379 
    380 	/*
    381 	 * XXX: anyover can in fact be true if anyusage is not true,
    382 	 * if there's a quota of zero set on some volume. This is
    383 	 * because the check we do checks if adding one more thing
    384 	 * will go over. That is reasonable, I suppose, but Arguably
    385 	 * the resulting behavior with usage 0 is a bug. (Also, what
    386 	 * reason do we have to believe that the reported grace expire
    387 	 * time is valid if we aren't in fact over yet?)
    388 	 */
    389 
    390 	if (vflag || dflag || anyover(qvs, numqvs, now) ||
    391 	    anyusage(qvs, numqvs)) {
    392 		heading(type, id, idname, "");
    393 		if (strlen(qup->fsname) > 4) {
    394 			printf("%s\n", qup->fsname);
    395 			printf("%12s", "");
    396 		} else {
    397 			printf("%12s", qup->fsname);
    398 		}
    399 
    400 		isquota2 = (qup->flags & QUOTA2) != 0;
    401 
    402 		for (i=0; i<numqvs; i++) {
    403 			printqv(&qvs[i], isquota2,
    404 				i >= QUOTA_NLIMITS ? 0 : isbytes[i], now);
    405 		}
    406 		printf("\n");
    407 	}
    408 }
    409 
    410 static void
    411 heading(int type, id_t id, const char *idname, const char *tag)
    412 {
    413 	if (needheading == 0)
    414 		return;
    415 	needheading = 0;
    416 
    417 	if (dflag)
    418 		printf("Default %s disk quotas: %s\n",
    419 		    ufs_quota_class_names[type], tag);
    420 	else
    421 		printf("Disk quotas for %s %s (%cid %u): %s\n",
    422 		    ufs_quota_class_names[type], idname,
    423 		    *ufs_quota_class_names[type], id, tag);
    424 
    425 	if (!qflag && tag[0] == '\0') {
    426 		printf("%12s%9s %8s%9s%8s%8s %7s%8s%8s\n"
    427 		    , "Filesystem"
    428 		    , "blocks"
    429 		    , "quota"
    430 		    , "limit"
    431 		    , "grace"
    432 		    , "files"
    433 		    , "quota"
    434 		    , "limit"
    435 		    , "grace"
    436 		);
    437 	}
    438 }
    439 
    440 static void
    441 printqv(struct quotaval *qv, int isquota2, int isbytes, time_t now)
    442 {
    443 	char buf[20];
    444 	const char *str;
    445 	int intprtflags, over, width;
    446 
    447 	/*
    448 	 * The assorted finagling of width is to match the previous
    449 	 * open-coded formatting for exactly two quota object types,
    450 	 * which was chosen to make the default report fit in 80
    451 	 * columns.
    452 	 */
    453 
    454 	width = isbytes ? 9 : 8;
    455 	intprtflags = isbytes ? HN_B : 0;
    456 	over = isover(qv, now);
    457 
    458 	str = intprt(buf, width, qv->qv_usage, intprtflags, hflag);
    459 	printf("%*s", width, str);
    460 
    461 	printf("%c", over ? '*' : ' ');
    462 
    463 	str = intprt(buf, width, qv->qv_softlimit, intprtflags, hflag);
    464 	printf("%*s", width-1, str);
    465 
    466 	str = intprt(buf, width, qv->qv_hardlimit, intprtflags, hflag);
    467 	printf("%*s", width, str);
    468 
    469 	if (over) {
    470 		str = timeprt(buf, 9, now, qv->qv_expiretime);
    471 	} else if (isquota2 && vflag) {
    472 		str = timeprt(buf, 9, 0, qv->qv_grace);
    473 	} else {
    474 		str = "";
    475 	}
    476 	printf("%8s", str);
    477 }
    478 
    479 /*
    480  * Collect the requested quota information.
    481  */
    482 static struct quotause *
    483 getprivs(id_t id, int quotatype)
    484 {
    485 	struct quotause *qup, *quptail;
    486 	struct quotause *quphead;
    487 	struct statvfs *fst;
    488 	int nfst, i;
    489 	int8_t version;
    490 
    491 	qup = quphead = quptail = NULL;
    492 
    493 	nfst = getmntinfo(&fst, MNT_WAIT);
    494 	if (nfst == 0)
    495 		errx(2, "no filesystems mounted!");
    496 	for (i = 0; i < nfst; i++) {
    497 		if (qup == NULL) {
    498 			if ((qup = malloc(sizeof *qup)) == NULL)
    499 				err(1, "Out of memory");
    500 		}
    501 		if (strncmp(fst[i].f_fstypename, "nfs",
    502 		    sizeof(fst[i].f_fstypename)) == 0) {
    503 			version = 0;
    504 			qup->numqvs = QUOTA_NLIMITS;
    505 			qup->qvs = malloc(qup->numqvs * sizeof(qup->qvs[0]));
    506 			if (qup->qvs == NULL) {
    507 				err(1, "Out of memory");
    508 			}
    509 			if (getnfsquota(fst[i].f_mntfromname,
    510 			    qup->qvs, id, ufs_quota_class_names[quotatype]) != 1)
    511 				continue;
    512 		} else if ((fst[i].f_flag & ST_QUOTA) != 0) {
    513 			qup->numqvs = QUOTA_NLIMITS;
    514 			qup->qvs = malloc(qup->numqvs * sizeof(qup->qvs[0]));
    515 			if (qup->qvs == NULL) {
    516 				err(1, "Out of memory");
    517 			}
    518 			if (getvfsquota(fst[i].f_mntonname, qup->qvs, &version,
    519 			    id, quotatype, dflag, Dflag) != 1)
    520 				continue;
    521 		} else
    522 			continue;
    523 		(void)strncpy(qup->fsname, fst[i].f_mntonname,
    524 		    sizeof(qup->fsname) - 1);
    525 		if (version == 2)
    526 			qup->flags |= QUOTA2;
    527 		if (quphead == NULL)
    528 			quphead = qup;
    529 		else
    530 			quptail->next = qup;
    531 		quptail = qup;
    532 		quptail->next = 0;
    533 		qup = NULL;
    534 	}
    535 	free(qup);
    536 	return quphead;
    537 }
    538 
    539 static int
    540 unlimited(struct quotaval *qvs, unsigned numqvs)
    541 {
    542 	unsigned i;
    543 
    544 	for (i=0; i<numqvs; i++) {
    545 		if (qvs[i].qv_softlimit != UQUAD_MAX ||
    546 		    qvs[i].qv_hardlimit != UQUAD_MAX) {
    547 			return 0;
    548 		}
    549 	}
    550 	return 1;
    551 }
    552 
    553 static int
    554 anyusage(struct quotaval *qvs, unsigned numqvs)
    555 {
    556 	unsigned i;
    557 
    558 	for (i=0; i<numqvs; i++) {
    559 		if (qvs[i].qv_usage > 0) {
    560 			return 1;
    561 		}
    562 	}
    563 	return 0;
    564 }
    565 
    566 static int
    567 anyover(struct quotaval *qvs, unsigned numqvs, time_t now)
    568 {
    569 	unsigned i;
    570 
    571 	for (i=0; i<numqvs; i++) {
    572 		if (isover(&qvs[i], now)) {
    573 			return 1;
    574 		}
    575 	}
    576 	return 0;
    577 }
    578 
    579 static int
    580 isover(struct quotaval *qv, time_t now)
    581 {
    582 	int ql_stat;
    583 
    584 	ql_stat = quota_check_limit(qv->qv_usage, 1,
    585 				    qv->qv_softlimit,
    586 				    qv->qv_hardlimit,
    587 				    qv->qv_expiretime, now);
    588 	switch(QL_STATUS(ql_stat)) {
    589 	    case QL_S_DENY_HARD:
    590 	    case QL_S_DENY_GRACE:
    591 	    case QL_S_ALLOW_SOFT:
    592 		return 1;
    593 	    default:
    594 		break;
    595 	}
    596 	return 0;
    597 }
    598 
    599 static const char *
    600 getovermsg(struct quotaval *qv, const char *what, time_t now)
    601 {
    602 	static char buf[64];
    603 	int ql_stat;
    604 
    605 	ql_stat = quota_check_limit(qv->qv_usage, 1,
    606 				    qv->qv_softlimit,
    607 				    qv->qv_hardlimit,
    608 				    qv->qv_expiretime, now);
    609 	switch(QL_STATUS(ql_stat)) {
    610 	    case QL_S_DENY_HARD:
    611 		snprintf(buf, sizeof(buf), "%c%s limit reached on",
    612 			 toupper((unsigned char)what[0]), what+1);
    613 		break;
    614 	    case QL_S_DENY_GRACE:
    615 		snprintf(buf, sizeof(buf), "Over %s quota on", what);
    616 		break;
    617 	    case QL_S_ALLOW_SOFT:
    618 		snprintf(buf, sizeof(buf), "In %s grace period on", what);
    619 		break;
    620 	    default:
    621 		return NULL;
    622 	}
    623 	return buf;
    624 }
    625