1 /* $NetBSD: quota.c,v 1.36 2011/03/06 22:36:07 christos 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.36 2011/03/06 22:36:07 christos 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 <ctype.h> 60 #include <err.h> 61 #include <errno.h> 62 #include <fstab.h> 63 #include <grp.h> 64 #include <netdb.h> 65 #include <pwd.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <time.h> 70 #include <unistd.h> 71 72 #include <ufs/ufs/quota2.h> 73 #include <ufs/ufs/quota1.h> 74 75 #include <rpc/rpc.h> 76 #include <rpc/pmap_prot.h> 77 #include <rpcsvc/rquota.h> 78 79 #include "printquota.h" 80 #include "quotautil.h" 81 #include "getvfsquota.h" 82 83 struct quotause { 84 struct quotause *next; 85 long flags; 86 struct quota2_entry q2e; 87 char fsname[MAXPATHLEN + 1]; 88 }; 89 #define FOUND 0x01 90 #define QUOTA2 0x02 91 92 static int callaurpc(const char *, rpcprog_t, rpcvers_t, rpcproc_t, 93 xdrproc_t, void *, xdrproc_t, void *); 94 static int getnfsquota(struct statvfs *, struct quotause *, uint32_t, int); 95 static struct quotause *getprivs(uint32_t, int); 96 static void heading(int, uint32_t, const char *, const char *); 97 static void showgid(gid_t); 98 static void showgrpname(const char *); 99 static void showquotas(int, uint32_t, const char *); 100 static void showuid(uid_t); 101 static void showusrname(const char *); 102 static void usage(void) __attribute__((__noreturn__)); 103 104 static int qflag = 0; 105 static int vflag = 0; 106 static int hflag = 0; 107 static int dflag = 0; 108 static int Dflag = 0; 109 static uid_t myuid; 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(USRQUOTA, 0, ""); 158 if (gflag) 159 showquotas(GRPQUOTA, 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(USRQUOTA, 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(USRQUOTA, 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(GRPQUOTA, 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(GRPQUOTA, grp->gr_gid, name); 321 } 322 323 static void 324 showquotas(int type, uint32_t id, const char *name) 325 { 326 struct quotause *qup; 327 struct quotause *quplist; 328 const char *msgi, *msgb, *nam, *timemsg; 329 int lines = 0; 330 static time_t now; 331 char b0[20], b1[20], b2[20], b3[20]; 332 333 if (now == 0) 334 time(&now); 335 quplist = getprivs(id, type); 336 for (qup = quplist; qup; qup = qup->next) { 337 int ql_stat; 338 struct quota2_val *q = qup->q2e.q2e_val; 339 if (!vflag && 340 q[QL_BLOCK].q2v_softlimit == UQUAD_MAX && 341 q[QL_BLOCK].q2v_hardlimit == UQUAD_MAX && 342 q[QL_FILE].q2v_softlimit == UQUAD_MAX && 343 q[QL_FILE].q2v_hardlimit == UQUAD_MAX) 344 continue; 345 ql_stat = quota2_check_limit(&q[QL_FILE], 1, now); 346 switch(QL_STATUS(ql_stat)) { 347 case QL_S_DENY_HARD: 348 msgi = "File limit reached on"; 349 break; 350 case QL_S_DENY_GRACE: 351 msgi = "Over file quota on"; 352 break; 353 case QL_S_ALLOW_SOFT: 354 msgi = "In file grace period on"; 355 break; 356 default: 357 msgi = NULL; 358 } 359 ql_stat = quota2_check_limit(&q[QL_BLOCK], 1, now); 360 switch(QL_STATUS(ql_stat)) { 361 case QL_S_DENY_HARD: 362 msgb = "Block limit reached on"; 363 break; 364 case QL_S_DENY_GRACE: 365 msgb = "Over block quota on"; 366 break; 367 case QL_S_ALLOW_SOFT: 368 msgb = "In block grace period on"; 369 break; 370 default: 371 msgb = NULL; 372 } 373 if (qflag) { 374 if ((msgi != NULL || msgb != NULL) && 375 lines++ == 0) 376 heading(type, id, name, ""); 377 if (msgi != NULL) 378 printf("\t%s %s\n", msgi, qup->fsname); 379 if (msgb != NULL) 380 printf("\t%s %s\n", msgb, qup->fsname); 381 continue; 382 } 383 if (vflag || dflag || msgi || msgb || q[QL_BLOCK].q2v_cur || 384 q[QL_FILE].q2v_cur) { 385 if (lines++ == 0) 386 heading(type, id, name, ""); 387 nam = qup->fsname; 388 if (strlen(qup->fsname) > 4) { 389 printf("%s\n", qup->fsname); 390 nam = ""; 391 } 392 if (msgb) 393 timemsg = timeprt(b0, 9, now, 394 q[QL_BLOCK].q2v_time); 395 else if ((qup->flags & QUOTA2) != 0 && vflag) 396 timemsg = timeprt(b0, 9, 0, 397 q[QL_BLOCK].q2v_grace); 398 else 399 timemsg = ""; 400 401 printf("%12s%9s%c%8s%9s%8s", 402 nam, 403 intprt(b1, 9, q[QL_BLOCK].q2v_cur, 404 HN_B, hflag), 405 (msgb == NULL) ? ' ' : '*', 406 intprt(b2, 9, q[QL_BLOCK].q2v_softlimit, 407 HN_B, hflag), 408 intprt(b3, 9, q[QL_BLOCK].q2v_hardlimit, 409 HN_B, hflag), 410 timemsg); 411 412 if (msgi) 413 timemsg = timeprt(b0, 9, now, 414 q[QL_FILE].q2v_time); 415 else if ((qup->flags & QUOTA2) != 0 && vflag) 416 timemsg = timeprt(b0, 9, 0, 417 q[QL_FILE].q2v_grace); 418 else 419 timemsg = ""; 420 421 printf("%8s%c%7s%8s%8s\n", 422 intprt(b1, 8, q[QL_FILE].q2v_cur, 0, hflag), 423 (msgi == NULL) ? ' ' : '*', 424 intprt(b2, 8, q[QL_FILE].q2v_softlimit, 0, hflag), 425 intprt(b3, 8, q[QL_FILE].q2v_hardlimit, 0, hflag), 426 timemsg); 427 continue; 428 } 429 } 430 if (!qflag && lines == 0) 431 heading(type, id, name, "none"); 432 } 433 434 static void 435 heading(int type, uint32_t id, const char *name, const char *tag) 436 { 437 if (dflag) 438 printf("Default %s disk quotas: %s\n", 439 qfextension[type], tag); 440 else 441 printf("Disk quotas for %s %s (%cid %u): %s\n", 442 qfextension[type], name, *qfextension[type], 443 id, tag); 444 445 if (!qflag && tag[0] == '\0') { 446 printf("%12s%9s %8s%9s%8s%8s %7s%8s%8s\n" 447 , "Filesystem" 448 , "blocks" 449 , "quota" 450 , "limit" 451 , "grace" 452 , "files" 453 , "quota" 454 , "limit" 455 , "grace" 456 ); 457 } 458 } 459 460 /* 461 * Collect the requested quota information. 462 */ 463 static struct quotause * 464 getprivs(uint32_t id, int quotatype) 465 { 466 struct quotause *qup, *quptail; 467 struct quotause *quphead; 468 struct statvfs *fst; 469 int nfst, i; 470 int8_t version; 471 472 qup = quphead = quptail = NULL; 473 474 nfst = getmntinfo(&fst, MNT_WAIT); 475 if (nfst == 0) 476 errx(2, "no filesystems mounted!"); 477 for (i = 0; i < nfst; i++) { 478 if (qup == NULL) { 479 if ((qup = malloc(sizeof *qup)) == NULL) 480 err(1, "out of memory"); 481 } 482 if (strncmp(fst[i].f_fstypename, "nfs", 483 sizeof(fst[i].f_fstypename)) == 0) { 484 version = 0; 485 if (getnfsquota(&fst[i], qup, id, quotatype) == 0) 486 continue; 487 } else if ((fst[i].f_flag & ST_QUOTA) != 0) { 488 if (getvfsquota(fst[i].f_mntonname, &qup->q2e, &version, 489 id, quotatype, dflag, Dflag) == 0) 490 continue; 491 } else 492 continue; 493 (void)strncpy(qup->fsname, fst[i].f_mntonname, 494 sizeof(qup->fsname) - 1); 495 if (version == 2) 496 qup->flags |= QUOTA2; 497 if (quphead == NULL) 498 quphead = qup; 499 else 500 quptail->next = qup; 501 quptail = qup; 502 quptail->next = 0; 503 qup = NULL; 504 } 505 free(qup); 506 return quphead; 507 } 508 509 static int 510 getnfsquota(struct statvfs *fst, struct quotause *qup, 511 uint32_t id, int quotatype) 512 { 513 struct getquota_args gq_args; 514 struct ext_getquota_args ext_gq_args; 515 struct getquota_rslt gq_rslt; 516 struct quota2_entry *q2e = &qup->q2e; 517 struct dqblk dqblk; 518 struct timeval tv; 519 char *cp; 520 int ret; 521 522 if (fst->f_flag & MNT_LOCAL) 523 return 0; 524 525 /* 526 * must be some form of "hostname:/path" 527 */ 528 cp = strchr(fst->f_mntfromname, ':'); 529 if (cp == NULL) { 530 warnx("cannot find hostname for %s", fst->f_mntfromname); 531 return 0; 532 } 533 534 *cp = '\0'; 535 if (*(cp+1) != '/') { 536 *cp = ':'; 537 return 0; 538 } 539 540 ext_gq_args.gqa_pathp = cp + 1; 541 ext_gq_args.gqa_id = id; 542 ext_gq_args.gqa_type = 543 (quotatype == USRQUOTA) ? RQUOTA_USRQUOTA : RQUOTA_GRPQUOTA; 544 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, EXT_RQUOTAVERS, 545 RQUOTAPROC_GETQUOTA, xdr_ext_getquota_args, &ext_gq_args, 546 xdr_getquota_rslt, &gq_rslt); 547 if (ret == RPC_PROGVERSMISMATCH) { 548 if (quotatype != USRQUOTA) { 549 *cp = ':'; 550 return 0; 551 } 552 /* try RQUOTAVERS */ 553 gq_args.gqa_pathp = cp + 1; 554 gq_args.gqa_uid = id; 555 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS, 556 RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args, 557 xdr_getquota_rslt, &gq_rslt); 558 } 559 if (ret != RPC_SUCCESS) { 560 *cp = ':'; 561 return 0; 562 } 563 564 switch (gq_rslt.status) { 565 case Q_NOQUOTA: 566 break; 567 case Q_EPERM: 568 warnx("quota permission error, host: %s", fst->f_mntfromname); 569 break; 570 case Q_OK: 571 gettimeofday(&tv, NULL); 572 573 /* blocks*/ 574 q2e->q2e_val[QL_BLOCK].q2v_hardlimit = 575 dqblk.dqb_bhardlimit = 576 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit * 577 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); 578 dqblk.dqb_bsoftlimit = 579 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit * 580 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); 581 dqblk.dqb_curblocks = 582 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks * 583 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); 584 585 /* inodes */ 586 dqblk.dqb_ihardlimit = 587 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit; 588 dqblk.dqb_isoftlimit = 589 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit; 590 dqblk.dqb_curinodes = 591 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles; 592 593 /* grace times */ 594 dqblk.dqb_btime = (int)(tv.tv_sec + 595 gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft); 596 dqblk.dqb_itime = (int)(tv.tv_sec + 597 gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft); 598 dqblk2q2e(&dqblk, q2e); 599 *cp = ':'; 600 return 1; 601 default: 602 warnx("bad rpc result, host: %s", fst->f_mntfromname); 603 break; 604 } 605 *cp = ':'; 606 return 0; 607 } 608 609 static int 610 callaurpc(const char *host, rpcprog_t prognum, rpcvers_t versnum, 611 rpcproc_t procnum, xdrproc_t inproc, void *in, xdrproc_t outproc, void *out) 612 { 613 struct sockaddr_in server_addr; 614 enum clnt_stat clnt_stat; 615 struct hostent *hp; 616 struct timeval timeout, tottimeout; 617 618 CLIENT *client = NULL; 619 int sock = RPC_ANYSOCK; 620 621 if ((hp = gethostbyname(host)) == NULL) 622 return (int) RPC_UNKNOWNHOST; 623 timeout.tv_usec = 0; 624 timeout.tv_sec = 6; 625 memmove(&server_addr.sin_addr, hp->h_addr, hp->h_length); 626 server_addr.sin_family = AF_INET; 627 server_addr.sin_port = 0; 628 629 if ((client = clntudp_create(&server_addr, prognum, 630 versnum, timeout, &sock)) == NULL) 631 return (int) rpc_createerr.cf_stat; 632 633 client->cl_auth = authunix_create_default(); 634 tottimeout.tv_sec = 25; 635 tottimeout.tv_usec = 0; 636 clnt_stat = clnt_call(client, procnum, inproc, in, 637 outproc, out, tottimeout); 638 639 return (int) clnt_stat; 640 } 641