1 /* 2 * Copyright (c) 1980, 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Robert Elz at The University of Melbourne. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1980, 1990, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 static char sccsid[] = "from: @(#)quota.c 8.1 (Berkeley) 6/6/93"; 45 #endif /* not lint */ 46 47 /* 48 * Disk quota reporting program. 49 */ 50 #include <sys/param.h> 51 #include <sys/types.h> 52 #include <sys/file.h> 53 #include <sys/stat.h> 54 #include <sys/mount.h> 55 #include <sys/socket.h> 56 #include <ufs/ufs/quota.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <fstab.h> 60 #include <ctype.h> 61 #include <string.h> 62 #include <pwd.h> 63 #include <grp.h> 64 #include <errno.h> 65 #include <unistd.h> 66 67 #include <netdb.h> 68 #include <rpc/rpc.h> 69 #include <rpc/pmap_prot.h> 70 #include <rpcsvc/rquota.h> 71 72 char *qfname = QUOTAFILENAME; 73 char *qfextension[] = INITQFNAMES; 74 75 struct quotause { 76 struct quotause *next; 77 long flags; 78 struct dqblk dqblk; 79 char fsname[MAXPATHLEN + 1]; 80 }; 81 #define FOUND 0x01 82 83 static char *timeprt __P((time_t seconds)); 84 static struct quotause *getprivs __P((long id, int quotatype)); 85 static void usage (); 86 static void showuid(u_long uid); 87 static void showgid(u_long gid); 88 static int alldigits(char *s); 89 static void showusrname(char *name); 90 static void showgrpname(char *name); 91 static void showquotas(int type, u_long id, char *name); 92 static void heading(int type, u_long id, char *name, char *tag); 93 static char *timeprt(time_t seconds); 94 static struct quotause *getprivs(long id, int quotatype); 95 static int ufshasquota(struct fstab *fs, int type, char **qfnamep); 96 static int getufsquota(struct statfs *fst, struct fstab *fs, 97 struct quotause *qup, long id, int quotatype); 98 static int getnfsquota(struct statfs *fst, struct fstab *fs, 99 struct quotause *qup, long id, int quotatype); 100 static int callaurpc(char *host, int prognum, int versnum, int procnum, 101 xdrproc_t inproc, char *in, xdrproc_t outproc, char *out); 102 static int alldigits(char *s); 103 104 int qflag; 105 int vflag; 106 107 int 108 main(argc, argv) 109 int argc; 110 char *argv[]; 111 { 112 int ngroups; 113 gid_t mygid, gidset[NGROUPS]; 114 int i, gflag = 0, uflag = 0; 115 char ch; 116 extern char *optarg; 117 extern int optind, errno; 118 119 while ((ch = getopt(argc, argv, "ugvq")) != EOF) { 120 switch(ch) { 121 case 'g': 122 gflag++; 123 break; 124 case 'u': 125 uflag++; 126 break; 127 case 'v': 128 vflag++; 129 break; 130 case 'q': 131 qflag++; 132 break; 133 default: 134 usage(); 135 } 136 } 137 argc -= optind; 138 argv += optind; 139 if (!uflag && !gflag) 140 uflag++; 141 if (argc == 0) { 142 if (uflag) 143 showuid(getuid()); 144 if (gflag) { 145 mygid = getgid(); 146 ngroups = getgroups(NGROUPS, gidset); 147 if (ngroups < 0) { 148 perror("quota: getgroups"); 149 return(1); 150 } 151 showgid(mygid); 152 for (i = 0; i < ngroups; i++) 153 if (gidset[i] != mygid) 154 showgid(gidset[i]); 155 } 156 return(0); 157 } 158 if (uflag && gflag) 159 usage(); 160 if (uflag) { 161 for (; argc > 0; argc--, argv++) { 162 if (alldigits(*argv)) 163 showuid(atoi(*argv)); 164 else 165 showusrname(*argv); 166 } 167 return(0); 168 } 169 if (gflag) { 170 for (; argc > 0; argc--, argv++) { 171 if (alldigits(*argv)) 172 showgid(atoi(*argv)); 173 else 174 showgrpname(*argv); 175 } 176 } 177 return(0); 178 } 179 180 static void 181 usage() 182 { 183 184 fprintf(stderr, "%s\n%s\n%s\n", 185 "Usage: quota [-guqv]", 186 "\tquota [-qv] -u username ...", 187 "\tquota [-qv] -g groupname ..."); 188 exit(1); 189 } 190 191 /* 192 * Print out quotas for a specified user identifier. 193 */ 194 static void 195 showuid(uid) 196 u_long uid; 197 { 198 struct passwd *pwd = getpwuid(uid); 199 u_long myuid; 200 char *name; 201 202 if (pwd == NULL) 203 name = "(no account)"; 204 else 205 name = pwd->pw_name; 206 myuid = getuid(); 207 if (uid != myuid && myuid != 0) { 208 printf("quota: %s (uid %lu): permission denied\n", name, uid); 209 return; 210 } 211 showquotas(USRQUOTA, uid, name); 212 } 213 214 /* 215 * Print out quotas for a specifed user name. 216 */ 217 static void 218 showusrname(name) 219 char *name; 220 { 221 struct passwd *pwd = getpwnam(name); 222 u_long myuid; 223 224 if (pwd == NULL) { 225 fprintf(stderr, "quota: %s: unknown user\n", name); 226 return; 227 } 228 myuid = getuid(); 229 if (pwd->pw_uid != myuid && myuid != 0) { 230 fprintf(stderr, "quota: %s (uid %u): permission denied\n", 231 name, pwd->pw_uid); 232 return; 233 } 234 showquotas(USRQUOTA, pwd->pw_uid, name); 235 } 236 237 /* 238 * Print out quotas for a specified group identifier. 239 */ 240 static void 241 showgid(gid) 242 u_long gid; 243 { 244 struct group *grp = getgrgid(gid); 245 int ngroups; 246 gid_t mygid, gidset[NGROUPS]; 247 register int i; 248 char *name; 249 250 if (grp == NULL) 251 name = "(no entry)"; 252 else 253 name = grp->gr_name; 254 mygid = getgid(); 255 ngroups = getgroups(NGROUPS, gidset); 256 if (ngroups < 0) { 257 perror("quota: getgroups"); 258 return; 259 } 260 if (gid != mygid) { 261 for (i = 0; i < ngroups; i++) 262 if (gid == gidset[i]) 263 break; 264 if (i >= ngroups && getuid() != 0) { 265 fprintf(stderr, 266 "quota: %s (gid %lu): permission denied\n", 267 name, gid); 268 return; 269 } 270 } 271 showquotas(GRPQUOTA, gid, name); 272 } 273 274 /* 275 * Print out quotas for a specifed group name. 276 */ 277 static void 278 showgrpname(name) 279 char *name; 280 { 281 struct group *grp = getgrnam(name); 282 int ngroups; 283 gid_t mygid, gidset[NGROUPS]; 284 register int i; 285 286 if (grp == NULL) { 287 fprintf(stderr, "quota: %s: unknown group\n", name); 288 return; 289 } 290 mygid = getgid(); 291 ngroups = getgroups(NGROUPS, gidset); 292 if (ngroups < 0) { 293 perror("quota: getgroups"); 294 return; 295 } 296 if (grp->gr_gid != mygid) { 297 for (i = 0; i < ngroups; i++) 298 if (grp->gr_gid == gidset[i]) 299 break; 300 if (i >= ngroups && getuid() != 0) { 301 fprintf(stderr, 302 "quota: %s (gid %u): permission denied\n", 303 name, grp->gr_gid); 304 return; 305 } 306 } 307 showquotas(GRPQUOTA, grp->gr_gid, name); 308 } 309 310 static void 311 showquotas(type, id, name) 312 int type; 313 u_long id; 314 char *name; 315 { 316 register struct quotause *qup; 317 struct quotause *quplist; 318 char *msgi, *msgb, *nam; 319 int lines = 0; 320 static time_t now; 321 322 if (now == 0) 323 time(&now); 324 quplist = getprivs(id, type); 325 for (qup = quplist; qup; qup = qup->next) { 326 if (!vflag && 327 qup->dqblk.dqb_isoftlimit == 0 && 328 qup->dqblk.dqb_ihardlimit == 0 && 329 qup->dqblk.dqb_bsoftlimit == 0 && 330 qup->dqblk.dqb_bhardlimit == 0) 331 continue; 332 msgi = (char *)0; 333 if (qup->dqblk.dqb_ihardlimit && 334 qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) 335 msgi = "File limit reached on"; 336 else if (qup->dqblk.dqb_isoftlimit && 337 qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) 338 if (qup->dqblk.dqb_itime > now) 339 msgi = "In file grace period on"; 340 else 341 msgi = "Over file quota on"; 342 msgb = (char *)0; 343 if (qup->dqblk.dqb_bhardlimit && 344 qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) 345 msgb = "Block limit reached on"; 346 else if (qup->dqblk.dqb_bsoftlimit && 347 qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) 348 if (qup->dqblk.dqb_btime > now) 349 msgb = "In block grace period on"; 350 else 351 msgb = "Over block quota on"; 352 if (qflag) { 353 if ((msgi != (char *)0 || msgb != (char *)0) && 354 lines++ == 0) 355 heading(type, id, name, ""); 356 if (msgi != (char *)0) 357 printf("\t%s %s\n", msgi, qup->fsname); 358 if (msgb != (char *)0) 359 printf("\t%s %s\n", msgb, qup->fsname); 360 continue; 361 } 362 if (vflag || 363 qup->dqblk.dqb_curblocks || 364 qup->dqblk.dqb_curinodes) { 365 if (lines++ == 0) 366 heading(type, id, name, ""); 367 nam = qup->fsname; 368 if (strlen(qup->fsname) > 15) { 369 printf("%s\n", qup->fsname); 370 nam = ""; 371 } 372 printf("%15s%8lu%c%7lu%8lu%8s" 373 , nam 374 , (u_long) (dbtob(qup->dqblk.dqb_curblocks) 375 / 1024) 376 , (msgb == (char *)0) ? ' ' : '*' 377 , (u_long) (dbtob(qup->dqblk.dqb_bsoftlimit) 378 / 1024) 379 , (u_long) (dbtob(qup->dqblk.dqb_bhardlimit) 380 / 1024) 381 , (msgb == (char *)0) ? "" 382 :timeprt(qup->dqblk.dqb_btime)); 383 printf("%8lu%c%7lu%8lu%8s\n" 384 , qup->dqblk.dqb_curinodes 385 , (msgi == (char *)0) ? ' ' : '*' 386 , qup->dqblk.dqb_isoftlimit 387 , qup->dqblk.dqb_ihardlimit 388 , (msgi == (char *)0) ? "" 389 : timeprt(qup->dqblk.dqb_itime) 390 ); 391 continue; 392 } 393 } 394 if (!qflag && lines == 0) 395 heading(type, id, name, "none"); 396 } 397 398 static void 399 heading(type, id, name, tag) 400 int type; 401 u_long id; 402 char *name, *tag; 403 { 404 405 printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type], 406 name, *qfextension[type], id, tag); 407 if (!qflag && tag[0] == '\0') { 408 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n" 409 , "Filesystem" 410 , "blocks" 411 , "quota" 412 , "limit" 413 , "grace" 414 , "files" 415 , "quota" 416 , "limit" 417 , "grace" 418 ); 419 } 420 } 421 422 /* 423 * Calculate the grace period and return a printable string for it. 424 */ 425 static char * 426 timeprt(seconds) 427 time_t seconds; 428 { 429 time_t hours, minutes; 430 static char buf[20]; 431 static time_t now; 432 433 if (now == 0) 434 time(&now); 435 if (now > seconds) 436 return ("none"); 437 seconds -= now; 438 minutes = (seconds + 30) / 60; 439 hours = (minutes + 30) / 60; 440 if (hours >= 36) { 441 sprintf(buf, "%lddays", (hours + 12) / 24); 442 return (buf); 443 } 444 if (minutes >= 60) { 445 sprintf(buf, "%2ld:%ld", minutes / 60, minutes % 60); 446 return (buf); 447 } 448 sprintf(buf, "%2ld", minutes); 449 return (buf); 450 } 451 452 /* 453 * Collect the requested quota information. 454 */ 455 static struct quotause * 456 getprivs(id, quotatype) 457 register long id; 458 int quotatype; 459 { 460 register struct quotause *qup, *quptail; 461 register struct fstab *fs; 462 struct quotause *quphead; 463 struct statfs *fst; 464 int nfst, i; 465 466 qup = quphead = (struct quotause *)0; 467 468 nfst = getmntinfo(&fst, MNT_WAIT); 469 if (nfst == 0) { 470 fprintf(stderr, "quota: no filesystems mounted!\n"); 471 exit(2); 472 } 473 setfsent(); 474 for (i=0; i<nfst; i++) { 475 if (qup == NULL) { 476 if ((qup = (struct quotause *)malloc(sizeof *qup)) 477 == NULL) { 478 fprintf(stderr, "quota: out of memory\n"); 479 exit(2); 480 } 481 } 482 if (fst[i].f_type == MOUNT_NFS) { 483 if (getnfsquota(&fst[i], NULL, qup, id, quotatype) 484 == 0) 485 continue; 486 } else if (fst[i].f_type == MOUNT_UFS) { 487 /* 488 * XXX 489 * UFS filesystems must be in /etc/fstab, and must 490 * indicate that they have quotas on (?!) This is quite 491 * unlike SunOS where quotas can be enabled/disabled 492 * on a filesystem independent of /etc/fstab, and it 493 * will still print quotas for them. 494 */ 495 if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL) 496 continue; 497 if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0) 498 continue; 499 } else 500 continue; 501 strcpy(qup->fsname, fst[i].f_mntonname); 502 if (quphead == NULL) 503 quphead = qup; 504 else 505 quptail->next = qup; 506 quptail = qup; 507 quptail->next = 0; 508 qup = NULL; 509 } 510 if (qup) 511 free(qup); 512 endfsent(); 513 return (quphead); 514 } 515 516 /* 517 * Check to see if a particular quota is to be enabled. 518 */ 519 static int 520 ufshasquota(fs, type, qfnamep) 521 register struct fstab *fs; 522 int type; 523 char **qfnamep; 524 { 525 static char initname, usrname[100], grpname[100]; 526 static char buf[BUFSIZ]; 527 char *opt, *cp; 528 529 if (!initname) { 530 sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 531 sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 532 initname = 1; 533 } 534 strcpy(buf, fs->fs_mntops); 535 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 536 if ((cp = index(opt, '='))) 537 *cp++ = '\0'; 538 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 539 break; 540 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 541 break; 542 } 543 if (!opt) 544 return (0); 545 if (cp) { 546 *qfnamep = cp; 547 return (1); 548 } 549 (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 550 *qfnamep = buf; 551 return (1); 552 } 553 554 static int 555 getufsquota(fst, fs, qup, id, quotatype) 556 struct statfs *fst; 557 struct fstab *fs; 558 struct quotause *qup; 559 long id; 560 int quotatype; 561 { 562 char *qfpathname; 563 int fd, qcmd; 564 565 qcmd = QCMD(Q_GETQUOTA, quotatype); 566 if (!ufshasquota(fs, quotatype, &qfpathname)) 567 return (0); 568 569 if (quotactl(fs->fs_file, qcmd, id, (char *)&qup->dqblk) != 0) { 570 if ((fd = open(qfpathname, O_RDONLY)) < 0) { 571 perror(qfpathname); 572 return (0); 573 } 574 (void) lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET); 575 switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 576 case 0: /* EOF */ 577 /* 578 * Convert implicit 0 quota (EOF) 579 * into an explicit one (zero'ed dqblk) 580 */ 581 bzero((caddr_t)&qup->dqblk, sizeof(struct dqblk)); 582 break; 583 case sizeof(struct dqblk): /* OK */ 584 break; 585 default: /* ERROR */ 586 fprintf(stderr, "quota: read error"); 587 perror(qfpathname); 588 close(fd); 589 return (0); 590 } 591 close(fd); 592 } 593 return (1); 594 } 595 596 static int 597 getnfsquota(fst, fs, qup, id, quotatype) 598 struct statfs *fst; 599 struct fstab *fs; 600 struct quotause *qup; 601 long id; 602 int quotatype; 603 { 604 struct getquota_args gq_args; 605 struct getquota_rslt gq_rslt; 606 struct dqblk *dqp = &qup->dqblk; 607 struct timeval tv; 608 char *cp; 609 610 if (fst->f_flags & MNT_LOCAL) 611 return (0); 612 613 /* 614 * rpc.rquotad does not support group quotas 615 */ 616 if (quotatype != USRQUOTA) 617 return (0); 618 619 /* 620 * must be some form of "hostname:/path" 621 */ 622 cp = strchr(fst->f_mntfromname, ':'); 623 if (cp == NULL) { 624 fprintf(stderr, "cannot find hostname for %s\n", 625 fst->f_mntfromname); 626 return (0); 627 } 628 629 *cp = '\0'; 630 if (*(cp+1) != '/') { 631 *cp = ':'; 632 return (0); 633 } 634 635 gq_args.gqa_pathp = cp + 1; 636 gq_args.gqa_uid = id; 637 if (callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS, 638 RQUOTAPROC_GETQUOTA, xdr_getquota_args, (char *)&gq_args, 639 xdr_getquota_rslt, (char *)&gq_rslt) != 0) { 640 *cp = ':'; 641 return (0); 642 } 643 644 switch (gq_rslt.status) { 645 case Q_NOQUOTA: 646 break; 647 case Q_EPERM: 648 fprintf(stderr, "quota permission error, host: %s\n", 649 fst->f_mntfromname); 650 break; 651 case Q_OK: 652 gettimeofday(&tv, NULL); 653 /* blocks*/ 654 dqp->dqb_bhardlimit = 655 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit * 656 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE; 657 dqp->dqb_bsoftlimit = 658 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit * 659 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE; 660 dqp->dqb_curblocks = 661 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks * 662 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE; 663 /* inodes */ 664 dqp->dqb_ihardlimit = 665 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit; 666 dqp->dqb_isoftlimit = 667 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit; 668 dqp->dqb_curinodes = 669 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles; 670 /* grace times */ 671 dqp->dqb_btime = 672 tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft; 673 dqp->dqb_itime = 674 tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft; 675 *cp = ':'; 676 return (1); 677 default: 678 fprintf(stderr, "bad rpc result, host: %s\n", 679 fst->f_mntfromname); 680 break; 681 } 682 *cp = ':'; 683 return (0); 684 } 685 686 static int 687 callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out) 688 char *host; 689 xdrproc_t inproc, outproc; 690 char *in, *out; 691 int prognum, versnum, procnum; 692 { 693 struct sockaddr_in server_addr; 694 enum clnt_stat clnt_stat; 695 struct hostent *hp; 696 struct timeval timeout, tottimeout; 697 698 CLIENT *client = NULL; 699 int socket = RPC_ANYSOCK; 700 701 if ((hp = gethostbyname(host)) == NULL) 702 return ((int) RPC_UNKNOWNHOST); 703 timeout.tv_usec = 0; 704 timeout.tv_sec = 6; 705 bcopy(hp->h_addr, &server_addr.sin_addr, hp->h_length); 706 server_addr.sin_family = AF_INET; 707 server_addr.sin_port = 0; 708 709 if ((client = clntudp_create(&server_addr, prognum, 710 versnum, timeout, &socket)) == NULL) 711 return ((int) rpc_createerr.cf_stat); 712 713 client->cl_auth = authunix_create_default(); 714 tottimeout.tv_sec = 25; 715 tottimeout.tv_usec = 0; 716 clnt_stat = clnt_call(client, procnum, inproc, in, 717 outproc, out, tottimeout); 718 719 return ((int) clnt_stat); 720 } 721 722 static int 723 alldigits(s) 724 register char *s; 725 { 726 register c; 727 728 c = *s++; 729 do { 730 if (!isdigit(c)) 731 return (0); 732 } while ((c = *s++)); 733 return (1); 734 } 735