Home | History | Annotate | Line # | Download | only in quota
quota.c revision 1.37.4.1
      1 /*	$NetBSD: quota.c,v 1.37.4.1 2012/04/17 00:09:38 yamt 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.37.4.1 2012/04/17 00:09:38 yamt 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.h>
     74 
     75 #include "printquota.h"
     76 
     77 struct quotause {
     78 	struct	quotause *next;
     79 	uid_t	id;
     80 	struct	quotaval *qvs;
     81 	unsigned numqvs;
     82 	char	fsname[MAXPATHLEN + 1];
     83 	struct	quotahandle *qh;
     84 };
     85 
     86 static int	anyusage(struct quotaval *, unsigned);
     87 static int	anyover(struct quotaval *, unsigned, time_t);
     88 static const char *getovermsg(struct quotaval *, const char *, time_t);
     89 static struct quotause	*getprivs(id_t, int);
     90 static void	heading(int, const char *, id_t, const char *, const char *);
     91 static int	isover(struct quotaval *qv, time_t now);
     92 static void	printqv(struct quotaval *, int, time_t);
     93 static void	showgid(gid_t);
     94 static void	showgrpname(const char *);
     95 static void	showonequota(int, const char *, id_t, const char *,
     96 			     struct quotause *);
     97 static void	showquotas(int, const char *, id_t, const char *);
     98 static void	showuid(uid_t);
     99 static void	showusrname(const char *);
    100 static int	unlimited(struct quotaval *qvs, unsigned numqvs);
    101 static void	usage(void) __dead;
    102 
    103 static int	qflag = 0;
    104 static int	vflag = 0;
    105 static int	hflag = 0;
    106 static int	dflag = 0;
    107 static int	Dflag = 0;
    108 static uid_t	myuid;
    109 static int needheading;
    110 
    111 int
    112 main(int argc, char *argv[])
    113 {
    114 	int ngroups;
    115 	gid_t mygid, gidset[NGROUPS];
    116 	int i, gflag = 0, uflag = 0;
    117 	int ch;
    118 
    119 	myuid = getuid();
    120 	while ((ch = getopt(argc, argv, "Ddhugvq")) != -1) {
    121 		switch(ch) {
    122 		case 'g':
    123 			gflag++;
    124 			break;
    125 		case 'u':
    126 			uflag++;
    127 			break;
    128 		case 'v':
    129 			vflag++;
    130 			break;
    131 		case 'q':
    132 			qflag++;
    133 			break;
    134 		case 'h':
    135 			hflag++;
    136 			break;
    137 		case 'd':
    138 			dflag++;
    139 			break;
    140 		case 'D':
    141 			Dflag++;
    142 			break;
    143 		default:
    144 			usage();
    145 		}
    146 	}
    147 	argc -= optind;
    148 	argv += optind;
    149 	if (!uflag && !gflag)
    150 		uflag++;
    151 	if (dflag) {
    152 #if 0
    153 		if (myuid != 0)
    154 			errx(1, "-d: permission denied");
    155 #endif
    156 		if (uflag)
    157 			showquotas(QUOTA_IDTYPE_USER, "user", 0, "");
    158 		if (gflag)
    159 			showquotas(QUOTA_IDTYPE_GROUP, "group", 0, "");
    160 		return 0;
    161 	}
    162 	if (argc == 0) {
    163 		if (uflag)
    164 			showuid(myuid);
    165 		if (gflag) {
    166 			if (dflag)
    167 				showgid(0);
    168 			else {
    169 				mygid = getgid();
    170 				ngroups = getgroups(NGROUPS, gidset);
    171 				if (ngroups < 0)
    172 					err(1, "getgroups");
    173 				showgid(mygid);
    174 				for (i = 0; i < ngroups; i++)
    175 					if (gidset[i] != mygid)
    176 						showgid(gidset[i]);
    177 			}
    178 		}
    179 		return 0;
    180 	}
    181 	if (uflag && gflag)
    182 		usage();
    183 	if (uflag) {
    184 		for (; argc > 0; argc--, argv++) {
    185 			if (alldigits(*argv))
    186 				showuid((uid_t)atoi(*argv));
    187 			else
    188 				showusrname(*argv);
    189 		}
    190 		return 0;
    191 	}
    192 	if (gflag) {
    193 		for (; argc > 0; argc--, argv++) {
    194 			if (alldigits(*argv))
    195 				showgid((gid_t)atoi(*argv));
    196 			else
    197 				showgrpname(*argv);
    198 		}
    199 		return 0;
    200 	}
    201 	/* NOTREACHED */
    202 	return 0;
    203 }
    204 
    205 static void
    206 usage(void)
    207 {
    208 	const char *p = getprogname();
    209 	fprintf(stderr, "Usage: %s [-Dhguqv]\n"
    210 	    "\t%s [-Dhqv] -u username ...\n"
    211 	    "\t%s [-Dhqv] -g groupname ...\n"
    212 	    "\t%s -d [-Dhguqv]\n", p, p, p, p);
    213 	exit(1);
    214 }
    215 
    216 /*
    217  * Print out quotas for a specified user identifier.
    218  */
    219 static void
    220 showuid(uid_t uid)
    221 {
    222 	struct passwd *pwd = getpwuid(uid);
    223 	const char *name;
    224 
    225 	if (pwd == NULL)
    226 		name = "(no account)";
    227 	else
    228 		name = pwd->pw_name;
    229 	if (uid != myuid && myuid != 0) {
    230 		warnx("%s (uid %d): permission denied", name, uid);
    231 		return;
    232 	}
    233 	showquotas(QUOTA_IDTYPE_USER, "user", uid, name);
    234 }
    235 
    236 /*
    237  * Print out quotas for a specified user name.
    238  */
    239 static void
    240 showusrname(const char *name)
    241 {
    242 	struct passwd *pwd = getpwnam(name);
    243 
    244 	if (pwd == NULL) {
    245 		warnx("%s: unknown user", name);
    246 		return;
    247 	}
    248 	if (pwd->pw_uid != myuid && myuid != 0) {
    249 		warnx("%s (uid %d): permission denied", name, pwd->pw_uid);
    250 		return;
    251 	}
    252 	showquotas(QUOTA_IDTYPE_USER, "user", pwd->pw_uid, name);
    253 }
    254 
    255 /*
    256  * Print out quotas for a specified group identifier.
    257  */
    258 static void
    259 showgid(gid_t gid)
    260 {
    261 	struct group *grp = getgrgid(gid);
    262 	int ngroups;
    263 	gid_t mygid, gidset[NGROUPS];
    264 	int i;
    265 	const char *name;
    266 
    267 	if (grp == NULL)
    268 		name = "(no entry)";
    269 	else
    270 		name = grp->gr_name;
    271 	mygid = getgid();
    272 	ngroups = getgroups(NGROUPS, gidset);
    273 	if (ngroups < 0) {
    274 		warn("getgroups");
    275 		return;
    276 	}
    277 	if (gid != mygid) {
    278 		for (i = 0; i < ngroups; i++)
    279 			if (gid == gidset[i])
    280 				break;
    281 		if (i >= ngroups && myuid != 0) {
    282 			warnx("%s (gid %d): permission denied", name, gid);
    283 			return;
    284 		}
    285 	}
    286 	showquotas(QUOTA_IDTYPE_GROUP, "group", gid, name);
    287 }
    288 
    289 /*
    290  * Print out quotas for a specified group name.
    291  */
    292 static void
    293 showgrpname(const char *name)
    294 {
    295 	struct group *grp = getgrnam(name);
    296 	int ngroups;
    297 	gid_t mygid, gidset[NGROUPS];
    298 	int i;
    299 
    300 	if (grp == NULL) {
    301 		warnx("%s: unknown group", name);
    302 		return;
    303 	}
    304 	mygid = getgid();
    305 	ngroups = getgroups(NGROUPS, gidset);
    306 	if (ngroups < 0) {
    307 		warn("getgroups");
    308 		return;
    309 	}
    310 	if (grp->gr_gid != mygid) {
    311 		for (i = 0; i < ngroups; i++)
    312 			if (grp->gr_gid == gidset[i])
    313 				break;
    314 		if (i >= ngroups && myuid != 0) {
    315 			warnx("%s (gid %d): permission denied",
    316 			    name, grp->gr_gid);
    317 			return;
    318 		}
    319 	}
    320 	showquotas(QUOTA_IDTYPE_GROUP, "group", grp->gr_gid, name);
    321 }
    322 
    323 static void
    324 showquotas(int idtype, const char *idtypename, id_t id, const char *idname)
    325 {
    326 	struct quotause *qup;
    327 	struct quotause *quplist;
    328 
    329 	needheading = 1;
    330 
    331 	quplist = getprivs(id, idtype);
    332 	for (qup = quplist; qup; qup = qup->next) {
    333 		showonequota(idtype, idtypename, id, idname, qup);
    334 	}
    335 	if (!qflag) {
    336 		/* In case nothing printed, issue a header saying "none" */
    337 		heading(idtype, idtypename, id, idname, "none");
    338 	}
    339 }
    340 
    341 static void
    342 showonequota(int idtype, const char *idtypename, id_t id, const char *idname,
    343 	     struct quotause *qup)
    344 {
    345 	static time_t now;
    346 	struct quotaval *qvs;
    347 	unsigned numqvs, i;
    348 	const char *msg;
    349 
    350 	qvs = qup->qvs;
    351 	numqvs = qup->numqvs;
    352 
    353 	if (now == 0) {
    354 		time(&now);
    355 	}
    356 
    357 	if (!vflag && unlimited(qvs, numqvs)) {
    358 		return;
    359 	}
    360 
    361 	if (qflag) {
    362 		for (i=0; i<numqvs; i++) {
    363 			msg = getovermsg(&qvs[i],
    364 					 quota_idtype_getname(qup->qh, i),
    365 					 now);
    366 			if (msg != NULL) {
    367 				heading(idtype, idtypename, id, idname, "");
    368 				printf("\t%s %s\n", msg, qup->fsname);
    369 			}
    370 		}
    371 		return;
    372 	}
    373 
    374 	/*
    375 	 * XXX this behavior appears to be demanded by the ATF tests,
    376 	 * although it seems to be at variance with the preexisting
    377 	 * logic in quota.c.
    378 	 */
    379 	if (unlimited(qvs, numqvs) && !anyusage(qvs, numqvs)) {
    380 		return;
    381 	}
    382 
    383 	/*
    384 	 * XXX: anyover can in fact be true if anyusage is not true,
    385 	 * if there's a quota of zero set on some volume. This is
    386 	 * because the check we do checks if adding one more thing
    387 	 * will go over. That is reasonable, I suppose, but arguably
    388 	 * the resulting behavior with usage 0 is a bug. (Also, what
    389 	 * reason do we have to believe that the reported grace expire
    390 	 * time is valid if we aren't in fact over yet?)
    391 	 */
    392 
    393 	if (vflag || dflag || anyover(qvs, numqvs, now) ||
    394 	    anyusage(qvs, numqvs)) {
    395 		heading(idtype, idtypename, id, idname, "");
    396 		if (strlen(qup->fsname) > 4) {
    397 			printf("%s\n", qup->fsname);
    398 			printf("%12s", "");
    399 		} else {
    400 			printf("%12s", qup->fsname);
    401 		}
    402 
    403 		for (i=0; i<numqvs; i++) {
    404 			printqv(&qvs[i],
    405 				quota_objtype_isbytes(qup->qh, i), now);
    406 		}
    407 		printf("\n");
    408 	}
    409 }
    410 
    411 static void
    412 heading(int idtype, const char *idtypename, id_t id, const char *idname,
    413 	const char *tag)
    414 {
    415 	if (needheading == 0)
    416 		return;
    417 	needheading = 0;
    418 
    419 	if (dflag)
    420 		printf("Default %s disk quotas: %s\n", idtypename, tag);
    421 	else
    422 		printf("Disk quotas for %s %s (%cid %u): %s\n",
    423 		       idtypename, idname, idtypename[0], 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 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 (vflag && qv->qv_grace != QUOTA_NOTIME) {
    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 idtype)
    484 {
    485 	struct quotause *qup, *quptail;
    486 	struct quotause *quphead;
    487 	struct statvfs *fst;
    488 	struct quotakey qk;
    489 	int nfst, i;
    490 	unsigned j;
    491 
    492 	qup = quphead = quptail = NULL;
    493 
    494 	nfst = getmntinfo(&fst, MNT_WAIT);
    495 	if (nfst == 0)
    496 		errx(2, "no filesystems mounted!");
    497 	for (i = 0; i < nfst; i++) {
    498 		if (qup == NULL) {
    499 			if ((qup = malloc(sizeof *qup)) == NULL)
    500 				err(1, "Out of memory");
    501 		}
    502 		qup->qh = quota_open(fst[i].f_mntonname);
    503 		if (qup->qh == NULL) {
    504 			if (errno == EOPNOTSUPP || errno == ENXIO) {
    505 				continue;
    506 			}
    507 			err(1, "%s: quota_open", fst[i].f_mntonname);
    508 		}
    509 #if 0
    510 		if (strncmp(fst[i].f_fstypename, "nfs",
    511 		    sizeof(fst[i].f_fstypename)) == 0) {
    512 			version = 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 (getnfsquota(fst[i].f_mntfromname,
    519 			    qup->qvs, id, ufs_quota_class_names[idtype]) != 1)
    520 				continue;
    521 		} else if ((fst[i].f_flag & ST_QUOTA) != 0) {
    522 			qup->numqvs = QUOTA_NLIMITS;
    523 			qup->qvs = malloc(qup->numqvs * sizeof(qup->qvs[0]));
    524 			if (qup->qvs == NULL) {
    525 				err(1, "Out of memory");
    526 			}
    527 			if (getvfsquota(fst[i].f_mntonname, qup->qvs, &version,
    528 			    id, idtype, dflag, Dflag) != 1)
    529 				continue;
    530 		} else
    531 			continue;
    532 #else
    533 		qup->numqvs = quota_getnumidtypes(qup->qh);
    534 		qup->qvs = malloc(qup->numqvs * sizeof(qup->qvs[0]));
    535 		if (qup->qvs == NULL) {
    536 			err(1, "Out of memory");
    537 		}
    538 		qk.qk_idtype = idtype;
    539 		if (dflag) {
    540 			qk.qk_id = QUOTA_DEFAULTID;
    541 		} else {
    542 			qk.qk_id = id;
    543 		}
    544 		for (j=0; j<qup->numqvs; j++) {
    545 			qk.qk_objtype = j;
    546 			if (quota_get(qup->qh, &qk, &qup->qvs[j]) < 0) {
    547 				if (errno != ENOENT && errno != ENODEV) {
    548 					warn("%s: quota_get (objtype %u)",
    549 					     fst[i].f_mntonname, j);
    550 				}
    551 				quotaval_clear(&qup->qvs[j]);
    552 			}
    553 		}
    554 #endif
    555 		(void)strlcpy(qup->fsname, fst[i].f_mntonname,
    556 		    sizeof(qup->fsname));
    557 		if (quphead == NULL)
    558 			quphead = qup;
    559 		else
    560 			quptail->next = qup;
    561 		quptail = qup;
    562 		quptail->next = 0;
    563 		qup = NULL;
    564 	}
    565 	free(qup);
    566 	return quphead;
    567 }
    568 
    569 static int
    570 unlimited(struct quotaval *qvs, unsigned numqvs)
    571 {
    572 	unsigned i;
    573 
    574 	for (i=0; i<numqvs; i++) {
    575 		if (qvs[i].qv_softlimit != QUOTA_NOLIMIT ||
    576 		    qvs[i].qv_hardlimit != QUOTA_NOLIMIT) {
    577 			return 0;
    578 		}
    579 	}
    580 	return 1;
    581 }
    582 
    583 static int
    584 anyusage(struct quotaval *qvs, unsigned numqvs)
    585 {
    586 	unsigned i;
    587 
    588 	for (i=0; i<numqvs; i++) {
    589 		if (qvs[i].qv_usage > 0) {
    590 			return 1;
    591 		}
    592 	}
    593 	return 0;
    594 }
    595 
    596 static int
    597 anyover(struct quotaval *qvs, unsigned numqvs, time_t now)
    598 {
    599 	unsigned i;
    600 
    601 	for (i=0; i<numqvs; i++) {
    602 		if (isover(&qvs[i], now)) {
    603 			return 1;
    604 		}
    605 	}
    606 	return 0;
    607 }
    608 
    609 static int
    610 isover(struct quotaval *qv, time_t now)
    611 {
    612 	return (qv->qv_usage >= qv->qv_hardlimit ||
    613 		qv->qv_usage >= qv->qv_softlimit);
    614 }
    615 
    616 static const char *
    617 getovermsg(struct quotaval *qv, const char *what, time_t now)
    618 {
    619 	static char buf[64];
    620 
    621 	if (qv->qv_usage >= qv->qv_hardlimit) {
    622 		snprintf(buf, sizeof(buf), "%c%s limit reached on",
    623 			 toupper((unsigned char)what[0]), what+1);
    624 		return buf;
    625 	}
    626 
    627 	if (qv->qv_usage < qv->qv_softlimit) {
    628 		/* Ok */
    629 		return NULL;
    630 	}
    631 
    632 	if (now > qv->qv_expiretime) {
    633 		snprintf(buf, sizeof(buf), "Over %s quota on", what);
    634 		return buf;
    635 	}
    636 
    637 	snprintf(buf, sizeof(buf), "In %s grace period on", what);
    638 	return buf;
    639 }
    640