1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * Disk quota reporting program. 44 */ 45 #include <stdio.h> 46 #include <sys/mnttab.h> 47 #include <ctype.h> 48 #include <pwd.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <memory.h> 52 #include <sys/time.h> 53 #include <sys/param.h> 54 #include <sys/types.h> 55 #include <sys/sysmacros.h> 56 #include <sys/mntent.h> 57 #include <sys/file.h> 58 #include <sys/stat.h> 59 #include <sys/fs/ufs_quota.h> 60 61 int vflag; 62 int nolocalquota; 63 64 extern int optind; 65 extern char *optarg; 66 67 #define QFNAME "quotas" 68 69 #if DEV_BSIZE < 1024 70 #define kb(x) ((x) / (1024 / DEV_BSIZE)) 71 #else 72 #define kb(x) ((x) * (DEV_BSIZE / 1024)) 73 #endif 74 75 static int getnfsquota(char *, char *, uid_t, struct dqblk *); 76 77 main(argc, argv) 78 int argc; 79 char *argv[]; 80 { 81 int opt; 82 int i; 83 int status = 0; 84 85 while ((opt = getopt(argc, argv, "vV")) != EOF) { 86 switch (opt) { 87 88 case 'v': 89 vflag++; 90 break; 91 92 case 'V': /* Print command line */ 93 { 94 char *opt_text; 95 int opt_count; 96 97 (void) fprintf(stdout, "quota -F UFS "); 98 for (opt_count = 1; opt_count < argc; opt_count++) { 99 opt_text = argv[opt_count]; 100 if (opt_text) 101 (void) fprintf(stdout, " %s ", opt_text); 102 } 103 (void) fprintf(stdout, "\n"); 104 } 105 break; 106 107 case '?': 108 fprintf(stderr, "quota: %c: unknown option\n", 109 opt); 110 fprintf(stderr, "ufs usage: quota [-v] [username]\n"); 111 exit(32); 112 } 113 } 114 if (quotactl(Q_ALLSYNC, NULL, (uid_t)0, NULL) < 0 && errno == EINVAL) { 115 if (vflag) 116 fprintf(stderr, "There are no quotas on this system\n"); 117 nolocalquota++; 118 } 119 if (argc == optind) { 120 showuid(getuid()); 121 exit(0); 122 } 123 for (i = optind; i < argc; i++) { 124 if (alldigits(argv[i])) { 125 showuid((uid_t)atoi(argv[i])); 126 } else 127 status |= showname(argv[i]); 128 } 129 exit(status); 130 } 131 132 showuid(uid) 133 uid_t uid; 134 { 135 struct passwd *pwd = getpwuid(uid); 136 137 if (uid == 0) { 138 if (vflag) 139 printf("no disk quota for uid 0\n"); 140 return; 141 } 142 if (pwd == NULL) 143 showquotas(uid, "(no account)"); 144 else 145 showquotas(uid, pwd->pw_name); 146 } 147 148 int 149 showname(name) 150 char *name; 151 { 152 struct passwd *pwd = getpwnam(name); 153 154 if (pwd == NULL) { 155 fprintf(stderr, "quota: %s: unknown user\n", name); 156 return (32); 157 } 158 if (pwd->pw_uid == 0) { 159 if (vflag) 160 printf("no disk quota for %s (uid 0)\n", name); 161 return (0); 162 } 163 showquotas(pwd->pw_uid, name); 164 return (0); 165 } 166 167 #include "../../nfs/lib/replica.h" 168 169 showquotas(uid, name) 170 uid_t uid; 171 char *name; 172 { 173 struct mnttab mnt; 174 FILE *mtab; 175 struct dqblk dqblk; 176 uid_t myuid; 177 178 myuid = getuid(); 179 if (uid != myuid && myuid != 0) { 180 printf("quota: %s (uid %d): permission denied\n", name, uid); 181 exit(32); 182 } 183 if (vflag) 184 heading(uid, name); 185 mtab = fopen(MNTTAB, "r"); 186 while (getmntent(mtab, &mnt) == NULL) { 187 if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) == 0) { 188 if (nolocalquota || 189 (quotactl(Q_GETQUOTA, 190 mnt.mnt_mountp, uid, &dqblk) != 0 && 191 !(vflag && getdiskquota(&mnt, uid, &dqblk)))) 192 continue; 193 } else if (strcmp(mnt.mnt_fstype, MNTTYPE_NFS) == 0) { 194 195 struct replica *rl; 196 int count; 197 198 if (hasopt(MNTOPT_NOQUOTA, mnt.mnt_mntopts)) 199 continue; 200 201 /* 202 * Skip quota processing if mounted with public 203 * option. We are not likely to be able to pierce 204 * a fire wall to contact the quota server. 205 */ 206 if (hasopt(MNTOPT_PUBLIC, mnt.mnt_mntopts)) 207 continue; 208 209 rl = parse_replica(mnt.mnt_special, &count); 210 211 if (rl == NULL) { 212 213 if (count < 0) 214 fprintf(stderr, "cannot find hostname " 215 "and/or pathname for %s\n", 216 mnt.mnt_mountp); 217 else 218 fprintf(stderr, "no memory to parse " 219 "mnttab entry for %s\n", 220 mnt.mnt_mountp); 221 continue; 222 } 223 224 /* 225 * We skip quota reporting on mounts with replicas 226 * for the following reasons: 227 * 228 * (1) Very little point in reporting quotas on 229 * a set of read-only replicas ... how will the 230 * user correct the problem? 231 * 232 * (2) Which replica would we report the quota 233 * for? If we pick the current replica, what 234 * happens when a fail over event occurs? The 235 * next time quota is run, the quota will look 236 * all different, or there won't even be one. 237 * This has the potential to break scripts. 238 * 239 * If we prnt quouta for all replicas, how do 240 * we present the output without breaking scripts? 241 */ 242 243 if (count > 1) { 244 free_replica(rl, count); 245 continue; 246 } 247 248 /* 249 * Skip file systems mounted using public fh. 250 * We are not likely to be able to pierce 251 * a fire wall to contact the quota server. 252 */ 253 if (strcmp(rl[0].host, "nfs") == 0 && 254 strncmp(rl[0].path, "//", 2) == 0) { 255 free_replica(rl, count); 256 continue; 257 } 258 259 if (!getnfsquota(rl[0].host, rl[0].path, uid, &dqblk)) { 260 free_replica(rl, count); 261 continue; 262 } 263 264 free_replica(rl, count); 265 266 } else { 267 continue; 268 } 269 if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0 && 270 dqblk.dqb_fsoftlimit == 0 && dqblk.dqb_fhardlimit == 0) 271 continue; 272 if (vflag) 273 prquota(&mnt, &dqblk); 274 else 275 warn(&mnt, &dqblk); 276 } 277 fclose(mtab); 278 } 279 280 warn(mntp, dqp) 281 struct mnttab *mntp; 282 struct dqblk *dqp; 283 { 284 struct timeval tv; 285 286 time(&(tv.tv_sec)); 287 tv.tv_usec = 0; 288 if (dqp->dqb_bhardlimit && 289 dqp->dqb_curblocks >= dqp->dqb_bhardlimit) { 290 printf("Block limit reached on %s\n", mntp->mnt_mountp); 291 } else if (dqp->dqb_bsoftlimit && 292 dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) { 293 if (dqp->dqb_btimelimit == 0) { 294 printf("Over disk quota on %s, remove %luK\n", 295 mntp->mnt_mountp, 296 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1)); 297 } else if (dqp->dqb_btimelimit > tv.tv_sec) { 298 char btimeleft[80]; 299 300 fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec); 301 printf("Over disk quota on %s, remove %luK within %s\n", 302 mntp->mnt_mountp, 303 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1), 304 btimeleft); 305 } else { 306 printf( 307 "Over disk quota on %s, time limit has expired, remove %luK\n", 308 mntp->mnt_mountp, 309 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1)); 310 } 311 } 312 if (dqp->dqb_fhardlimit && 313 dqp->dqb_curfiles >= dqp->dqb_fhardlimit) { 314 printf("File count limit reached on %s\n", mntp->mnt_mountp); 315 } else if (dqp->dqb_fsoftlimit && 316 dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) { 317 if (dqp->dqb_ftimelimit == 0) { 318 printf("Over file quota on %s, remove %lu file%s\n", 319 mntp->mnt_mountp, 320 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1, 321 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ? 322 "s" : "")); 323 } else if (dqp->dqb_ftimelimit > tv.tv_sec) { 324 char ftimeleft[80]; 325 326 fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec); 327 printf( 328 "Over file quota on %s, remove %lu file%s within %s\n", 329 mntp->mnt_mountp, 330 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1, 331 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ? 332 "s" : ""), ftimeleft); 333 } else { 334 printf( 335 "Over file quota on %s, time limit has expired, remove %lu file%s\n", 336 mntp->mnt_mountp, 337 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1, 338 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ? 339 "s" : "")); 340 } 341 } 342 } 343 344 heading(uid, name) 345 uid_t uid; 346 char *name; 347 { 348 printf("Disk quotas for %s (uid %ld):\n", name, (long)uid); 349 printf("%-12s %7s%7s%7s%12s%7s%7s%7s%12s\n", 350 "Filesystem", 351 "usage", 352 "quota", 353 "limit", 354 "timeleft", 355 "files", 356 "quota", 357 "limit", 358 "timeleft"); 359 } 360 361 prquota(mntp, dqp) 362 register struct mnttab *mntp; 363 register struct dqblk *dqp; 364 { 365 struct timeval tv; 366 char ftimeleft[80], btimeleft[80]; 367 char *cp; 368 369 time(&(tv.tv_sec)); 370 tv.tv_usec = 0; 371 if (dqp->dqb_bsoftlimit && dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) { 372 if (dqp->dqb_btimelimit == 0) { 373 strcpy(btimeleft, "NOT STARTED"); 374 } else if (dqp->dqb_btimelimit > tv.tv_sec) { 375 fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec); 376 } else { 377 strcpy(btimeleft, "EXPIRED"); 378 } 379 } else { 380 btimeleft[0] = '\0'; 381 } 382 if (dqp->dqb_fsoftlimit && dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) { 383 if (dqp->dqb_ftimelimit == 0) { 384 strcpy(ftimeleft, "NOT STARTED"); 385 } else if (dqp->dqb_ftimelimit > tv.tv_sec) { 386 fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec); 387 } else { 388 strcpy(ftimeleft, "EXPIRED"); 389 } 390 } else { 391 ftimeleft[0] = '\0'; 392 } 393 if (strlen(mntp->mnt_mountp) > 12) { 394 printf("%s\n", mntp->mnt_mountp); 395 cp = ""; 396 } else { 397 cp = mntp->mnt_mountp; 398 } 399 printf("%-12.12s %7d %6d %6d %11s %6d %6d %6d %11s\n", 400 cp, 401 kb(dqp->dqb_curblocks), 402 kb(dqp->dqb_bsoftlimit), 403 kb(dqp->dqb_bhardlimit), 404 btimeleft, 405 dqp->dqb_curfiles, 406 dqp->dqb_fsoftlimit, 407 dqp->dqb_fhardlimit, 408 ftimeleft); 409 } 410 411 fmttime(buf, time) 412 char *buf; 413 register long time; 414 { 415 int i; 416 static struct { 417 int c_secs; /* conversion units in secs */ 418 char *c_str; /* unit string */ 419 } cunits [] = { 420 {60*60*24*28, "months"}, 421 {60*60*24*7, "weeks"}, 422 {60*60*24, "days"}, 423 {60*60, "hours"}, 424 {60, "mins"}, 425 {1, "secs"} 426 }; 427 428 if (time <= 0) { 429 strcpy(buf, "EXPIRED"); 430 return; 431 } 432 for (i = 0; i < sizeof (cunits)/sizeof (cunits[0]); i++) { 433 if (time >= cunits[i].c_secs) 434 break; 435 } 436 sprintf(buf, "%.1f %s", (double)time/cunits[i].c_secs, cunits[i].c_str); 437 } 438 439 alldigits(s) 440 register char *s; 441 { 442 register c; 443 444 c = *s++; 445 do { 446 if (!isdigit(c)) 447 return (0); 448 } while (c = *s++); 449 return (1); 450 } 451 452 int 453 getdiskquota(mntp, uid, dqp) 454 struct mnttab *mntp; 455 uid_t uid; 456 struct dqblk *dqp; 457 { 458 int fd; 459 dev_t fsdev; 460 struct stat64 statb; 461 char qfilename[MAXPATHLEN]; 462 463 if (stat64(mntp->mnt_special, &statb) < 0 || 464 (statb.st_mode & S_IFMT) != S_IFBLK) 465 return (0); 466 fsdev = statb.st_rdev; 467 (void) snprintf(qfilename, sizeof (qfilename), "%s/%s", 468 mntp->mnt_mountp, QFNAME); 469 if (stat64(qfilename, &statb) < 0 || statb.st_dev != fsdev) 470 return (0); 471 if ((fd = open64(qfilename, O_RDONLY)) < 0) 472 return (0); 473 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 474 switch (read(fd, dqp, sizeof (struct dqblk))) { 475 case 0: /* EOF */ 476 /* 477 * Convert implicit 0 quota (EOF) 478 * into an explicit one (zero'ed dqblk). 479 */ 480 memset((caddr_t)dqp, 0, sizeof (struct dqblk)); 481 break; 482 483 case sizeof (struct dqblk): /* OK */ 484 break; 485 486 default: /* ERROR */ 487 close(fd); 488 return (0); 489 } 490 close(fd); 491 return (1); 492 } 493 494 quotactl(cmd, mountp, uid, addr) 495 int cmd; 496 char *mountp; 497 uid_t uid; 498 caddr_t addr; 499 { 500 int fd; 501 int status; 502 struct quotctl quota; 503 char qfile[MAXPATHLEN]; 504 505 FILE *fstab; 506 struct mnttab mnt; 507 508 509 if ((mountp == NULL) && (cmd == Q_ALLSYNC)) { 510 /* 511 * Find the mount point of any mounted file system. This is 512 * because the ioctl that implements the quotactl call has 513 * to go to a real file, and not to the block device. 514 */ 515 if ((fstab = fopen(MNTTAB, "r")) == NULL) { 516 fprintf(stderr, "%s: ", MNTTAB); 517 perror("open"); 518 exit(32); 519 } 520 fd = -1; 521 while ((status = getmntent(fstab, &mnt)) == NULL) { 522 if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0 || 523 hasopt(MNTOPT_RO, mnt.mnt_mntopts)) 524 continue; 525 if ((strlcpy(qfile, mnt.mnt_mountp, 526 sizeof (qfile)) >= sizeof (qfile)) || 527 (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= 528 sizeof (qfile))) { 529 continue; 530 } 531 if ((fd = open64(qfile, O_RDONLY)) != -1) 532 break; 533 } 534 fclose(fstab); 535 if (fd == -1) { 536 errno = ENOENT; 537 return (-1); 538 } 539 } else { 540 if (mountp == NULL || mountp[0] == '\0') { 541 errno = ENOENT; 542 return (-1); 543 } 544 if ((strlcpy(qfile, mountp, sizeof (qfile)) >= sizeof 545 (qfile)) || 546 (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= sizeof 547 (qfile))) { 548 errno = ENOENT; 549 return (-1); 550 } 551 if ((fd = open64(qfile, O_RDONLY)) < 0) 552 return (-1); 553 } /* else */ 554 quota.op = cmd; 555 quota.uid = uid; 556 quota.addr = addr; 557 status = ioctl(fd, Q_QUOTACTL, "a); 558 if (fd != 0) 559 close(fd); 560 return (status); 561 } 562 563 564 /* 565 * Return 1 if opt appears in optlist 566 */ 567 int 568 hasopt(opt, optlist) 569 char *opt, *optlist; 570 { 571 char *value; 572 char *opts[2]; 573 574 opts[0] = opt; 575 opts[1] = NULL; 576 577 if (optlist == NULL) 578 return (0); 579 while (*optlist != '\0') { 580 if (getsubopt(&optlist, opts, &value) == 0) 581 return (1); 582 } 583 return (0); 584 } 585 586 #include <rpc/rpc.h> 587 #include <netdb.h> 588 #include <rpcsvc/rquota.h> 589 590 static int 591 getnfsquota(char *hostp, char *path, uid_t uid, struct dqblk *dqp) 592 { 593 struct getquota_args gq_args; 594 struct getquota_rslt gq_rslt; 595 struct rquota *rquota; 596 extern char *strchr(); 597 598 gq_args.gqa_pathp = path; 599 gq_args.gqa_uid = uid; 600 if (callaurpc(hostp, RQUOTAPROG, RQUOTAVERS, 601 (vflag? RQUOTAPROC_GETQUOTA: RQUOTAPROC_GETACTIVEQUOTA), 602 xdr_getquota_args, &gq_args, xdr_getquota_rslt, &gq_rslt) != 0) { 603 return (0); 604 } 605 switch (gq_rslt.status) { 606 case Q_OK: 607 { 608 struct timeval tv; 609 u_longlong_t limit; 610 611 rquota = &gq_rslt.getquota_rslt_u.gqr_rquota; 612 613 if (!vflag && rquota->rq_active == FALSE) 614 return (0); 615 gettimeofday(&tv, NULL); 616 limit = (u_longlong_t)(rquota->rq_bhardlimit) * 617 rquota->rq_bsize / DEV_BSIZE; 618 dqp->dqb_bhardlimit = limit; 619 limit = (u_longlong_t)(rquota->rq_bsoftlimit) * 620 rquota->rq_bsize / DEV_BSIZE; 621 dqp->dqb_bsoftlimit = limit; 622 limit = (u_longlong_t)(rquota->rq_curblocks) * 623 rquota->rq_bsize / DEV_BSIZE; 624 dqp->dqb_curblocks = limit; 625 dqp->dqb_fhardlimit = rquota->rq_fhardlimit; 626 dqp->dqb_fsoftlimit = rquota->rq_fsoftlimit; 627 dqp->dqb_curfiles = rquota->rq_curfiles; 628 dqp->dqb_btimelimit = 629 tv.tv_sec + rquota->rq_btimeleft; 630 dqp->dqb_ftimelimit = 631 tv.tv_sec + rquota->rq_ftimeleft; 632 return (1); 633 } 634 635 case Q_NOQUOTA: 636 break; 637 638 case Q_EPERM: 639 fprintf(stderr, "quota permission error, host: %s\n", hostp); 640 break; 641 642 default: 643 fprintf(stderr, "bad rpc result, host: %s\n", hostp); 644 break; 645 } 646 return (0); 647 } 648 649 callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out) 650 char *host; 651 xdrproc_t inproc, outproc; 652 char *in, *out; 653 { 654 static enum clnt_stat clnt_stat; 655 struct timeval tottimeout; 656 657 static CLIENT *cl = NULL; 658 static int oldprognum, oldversnum; 659 static char oldhost[MAXHOSTNAMELEN+1]; 660 661 /* 662 * Cache the client handle in case there are lots 663 * of entries in the /etc/mnttab for the same 664 * server. If the server returns an error, don't 665 * make further calls. 666 */ 667 if (cl == NULL || oldprognum != prognum || oldversnum != versnum || 668 strcmp(oldhost, host) != 0) { 669 if (cl) { 670 clnt_destroy(cl); 671 cl = NULL; 672 } 673 cl = clnt_create(host, prognum, versnum, "udp"); 674 if (cl == NULL) 675 return ((int)RPC_TIMEDOUT); 676 if ((cl->cl_auth = authunix_create_default()) == NULL) { 677 clnt_destroy(cl); 678 return (RPC_CANTSEND); 679 } 680 oldprognum = prognum; 681 oldversnum = versnum; 682 (void) strcpy(oldhost, host); 683 clnt_stat = RPC_SUCCESS; 684 } 685 686 if (clnt_stat != RPC_SUCCESS) 687 return ((int)clnt_stat); /* don't bother retrying */ 688 689 tottimeout.tv_sec = 5; 690 tottimeout.tv_usec = 0; 691 clnt_stat = clnt_call(cl, procnum, inproc, in, 692 outproc, out, tottimeout); 693 694 return ((int)clnt_stat); 695 } 696