121520Smckusick /* 2*41407Smckusick * Copyright (c) 1980, 1990 Regents of the University of California. 334372Sbostic * All rights reserved. 434372Sbostic * 5*41407Smckusick * This code is derived from software contributed to Berkeley by 6*41407Smckusick * Robert Elz at The University of Melbourne. 7*41407Smckusick * 834372Sbostic * Redistribution and use in source and binary forms are permitted 934911Sbostic * provided that the above copyright notice and this paragraph are 1034911Sbostic * duplicated in all such forms and that any documentation, 1134911Sbostic * advertising materials, and other materials related to such 1234911Sbostic * distribution and use acknowledge that the software was developed 1334911Sbostic * by the University of California, Berkeley. The name of the 1434911Sbostic * University may not be used to endorse or promote products derived 1534911Sbostic * from this software without specific prior written permission. 1634911Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1734911Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1834911Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1921520Smckusick */ 2021520Smckusick 2112710Smckusick #ifndef lint 2221520Smckusick char copyright[] = 23*41407Smckusick "@(#) Copyright (c) 1980, 1990 Regents of the University of California.\n\ 2421520Smckusick All rights reserved.\n"; 2534372Sbostic #endif /* not lint */ 2612710Smckusick 2721520Smckusick #ifndef lint 28*41407Smckusick static char sccsid[] = "@(#)quota.c 5.9 (Berkeley) 05/04/90"; 2934372Sbostic #endif /* not lint */ 3021520Smckusick 3112710Smckusick /* 3212710Smckusick * Disk quota reporting program. 3312710Smckusick */ 3437078Sbostic #include <sys/param.h> 3537078Sbostic #include <sys/file.h> 3637078Sbostic #include <sys/stat.h> 3738515Sbostic #include <ufs/quota.h> 3812710Smckusick #include <stdio.h> 3912710Smckusick #include <fstab.h> 4012710Smckusick #include <ctype.h> 4112710Smckusick #include <pwd.h> 42*41407Smckusick #include <grp.h> 4321392Smckusick #include <errno.h> 4412710Smckusick 45*41407Smckusick struct quotause { 46*41407Smckusick struct quotause *next; 47*41407Smckusick long flags; 48*41407Smckusick struct dqblk dqblk; 49*41407Smckusick char fsname[MAXPATHLEN + 1]; 50*41407Smckusick } *getprivs(); 51*41407Smckusick #define FOUND 0x01 5212710Smckusick 5312710Smckusick int qflag; 5412710Smckusick int vflag; 5512710Smckusick 5612710Smckusick main(argc, argv) 5712710Smckusick char *argv[]; 5812710Smckusick { 59*41407Smckusick int ngroups, gidset[NGROUPS]; 60*41407Smckusick int i, gflag = 0, uflag = 0; 61*41407Smckusick char ch; 62*41407Smckusick extern char *optarg; 63*41407Smckusick extern int optind, errno; 6412710Smckusick 65*41407Smckusick if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) { 6621392Smckusick fprintf(stderr, "There are no quotas on this system\n"); 6721392Smckusick exit(0); 6821392Smckusick } 69*41407Smckusick while ((ch = getopt(argc, argv, "ugvq")) != EOF) { 70*41407Smckusick switch(ch) { 71*41407Smckusick case 'g': 72*41407Smckusick gflag++; 7312710Smckusick break; 74*41407Smckusick case 'u': 75*41407Smckusick uflag++; 76*41407Smckusick break; 77*41407Smckusick case 'v': 78*41407Smckusick vflag++; 79*41407Smckusick break; 80*41407Smckusick case 'q': 81*41407Smckusick qflag++; 82*41407Smckusick break; 83*41407Smckusick default: 84*41407Smckusick usage(); 85*41407Smckusick } 8612710Smckusick } 87*41407Smckusick argc -= optind; 88*41407Smckusick argv += optind; 89*41407Smckusick if (!uflag && !gflag) 90*41407Smckusick uflag++; 9112710Smckusick if (argc == 0) { 92*41407Smckusick if (uflag) 93*41407Smckusick showuid(getuid()); 94*41407Smckusick if (gflag) { 95*41407Smckusick ngroups = getgroups(NGROUPS, gidset); 96*41407Smckusick if (ngroups < 0) { 97*41407Smckusick perror("quota: getgroups"); 98*41407Smckusick exit(1); 99*41407Smckusick } 100*41407Smckusick for (i = 1; i < ngroups; i++) 101*41407Smckusick showgid(gidset[i]); 102*41407Smckusick } 10312710Smckusick exit(0); 10412710Smckusick } 105*41407Smckusick if (uflag && gflag) 106*41407Smckusick usage(); 107*41407Smckusick if (uflag) { 108*41407Smckusick for (; argc > 0; argc--, argv++) { 109*41407Smckusick if (alldigits(*argv)) 110*41407Smckusick showuid(atoi(*argv)); 111*41407Smckusick else 112*41407Smckusick showusrname(*argv); 113*41407Smckusick } 114*41407Smckusick exit(0); 11512710Smckusick } 116*41407Smckusick if (gflag) { 117*41407Smckusick for (; argc > 0; argc--, argv++) { 118*41407Smckusick if (alldigits(*argv)) 119*41407Smckusick showgid(atoi(*argv)); 120*41407Smckusick else 121*41407Smckusick showgrpname(*argv); 122*41407Smckusick } 123*41407Smckusick exit(0); 124*41407Smckusick } 12512710Smckusick } 12612710Smckusick 127*41407Smckusick usage() 128*41407Smckusick { 129*41407Smckusick 130*41407Smckusick fprintf(stderr, "%s\n%s\n%s\n", 131*41407Smckusick "Usage: quota [-guqv]", 132*41407Smckusick "\tquota [-qv] -u username ...", 133*41407Smckusick "\tquota [-qv] -g groupname ..."); 134*41407Smckusick exit(1); 135*41407Smckusick } 136*41407Smckusick 137*41407Smckusick /* 138*41407Smckusick * Print out quotas for a specified user identifier. 139*41407Smckusick */ 14012710Smckusick showuid(uid) 141*41407Smckusick u_long uid; 14212710Smckusick { 14312710Smckusick struct passwd *pwd = getpwuid(uid); 144*41407Smckusick u_long myuid; 145*41407Smckusick char *name; 14612710Smckusick 14712710Smckusick if (pwd == NULL) 148*41407Smckusick name = "(no account)"; 14912710Smckusick else 150*41407Smckusick name = pwd->pw_name; 151*41407Smckusick myuid = getuid(); 152*41407Smckusick if (uid != myuid && myuid != 0) { 153*41407Smckusick printf("quota: %s (uid %d): permission denied\n", name, uid); 154*41407Smckusick return; 155*41407Smckusick } 156*41407Smckusick showquotas(USRQUOTA, uid, name); 15712710Smckusick } 15812710Smckusick 159*41407Smckusick /* 160*41407Smckusick * Print out quotas for a specifed user name. 161*41407Smckusick */ 162*41407Smckusick showusrname(name) 16312710Smckusick char *name; 16412710Smckusick { 16512710Smckusick struct passwd *pwd = getpwnam(name); 166*41407Smckusick u_long myuid; 16712710Smckusick 16812710Smckusick if (pwd == NULL) { 16912710Smckusick fprintf(stderr, "quota: %s: unknown user\n", name); 17012710Smckusick return; 17112710Smckusick } 172*41407Smckusick myuid = getuid(); 173*41407Smckusick if (pwd->pw_uid != myuid && myuid != 0) { 174*41407Smckusick fprintf(stderr, "quota: %s (uid %d): permission denied\n", 175*41407Smckusick name, pwd->pw_uid); 176*41407Smckusick return; 177*41407Smckusick } 178*41407Smckusick showquotas(USRQUOTA, pwd->pw_uid, name); 17912710Smckusick } 18012710Smckusick 181*41407Smckusick /* 182*41407Smckusick * Print out quotas for a specified group identifier. 183*41407Smckusick */ 184*41407Smckusick showgid(gid) 185*41407Smckusick u_long gid; 186*41407Smckusick { 187*41407Smckusick struct group *grp = getgrgid(gid); 188*41407Smckusick int ngroups, gidset[NGROUPS]; 189*41407Smckusick register int i; 19012710Smckusick char *name; 191*41407Smckusick 192*41407Smckusick if (grp == NULL) 193*41407Smckusick name = "(no entry)"; 194*41407Smckusick else 195*41407Smckusick name = grp->gr_name; 196*41407Smckusick ngroups = getgroups(NGROUPS, gidset); 197*41407Smckusick if (ngroups < 0) { 198*41407Smckusick perror("quota: getgroups"); 199*41407Smckusick return; 200*41407Smckusick } 201*41407Smckusick for (i = 1; i < ngroups; i++) 202*41407Smckusick if (gid == gidset[i]) 203*41407Smckusick break; 204*41407Smckusick if (i >= ngroups && getuid() != 0) { 205*41407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 206*41407Smckusick name, gid); 207*41407Smckusick return; 208*41407Smckusick } 209*41407Smckusick showquotas(GRPQUOTA, gid, name); 210*41407Smckusick } 211*41407Smckusick 212*41407Smckusick /* 213*41407Smckusick * Print out quotas for a specifed group name. 214*41407Smckusick */ 215*41407Smckusick showgrpname(name) 216*41407Smckusick char *name; 21712710Smckusick { 218*41407Smckusick struct group *grp = getgrnam(name); 219*41407Smckusick int ngroups, gidset[NGROUPS]; 220*41407Smckusick register int i; 22112710Smckusick 222*41407Smckusick if (grp == NULL) { 223*41407Smckusick fprintf(stderr, "quota: %s: unknown group\n", name); 22412710Smckusick return; 22512710Smckusick } 226*41407Smckusick ngroups = getgroups(NGROUPS, gidset); 227*41407Smckusick if (ngroups < 0) { 228*41407Smckusick perror("quota: getgroups"); 229*41407Smckusick return; 230*41407Smckusick } 231*41407Smckusick for (i = 1; i < ngroups; i++) 232*41407Smckusick if (grp->gr_gid == gidset[i]) 233*41407Smckusick break; 234*41407Smckusick if (i >= ngroups && getuid() != 0) { 235*41407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 236*41407Smckusick name, grp->gr_gid); 237*41407Smckusick return; 238*41407Smckusick } 239*41407Smckusick showquotas(GRPQUOTA, grp->gr_gid, name); 240*41407Smckusick } 241*41407Smckusick 242*41407Smckusick showquotas(type, id, name) 243*41407Smckusick int type; 244*41407Smckusick u_long id; 245*41407Smckusick char *name; 246*41407Smckusick { 247*41407Smckusick register struct quotause *qup; 248*41407Smckusick struct quotause *quplist, *getprivs(); 249*41407Smckusick char *msgi, *msgb, *timeprt(); 250*41407Smckusick int myuid, fd, lines = 0; 251*41407Smckusick static int first; 252*41407Smckusick static time_t now; 253*41407Smckusick 254*41407Smckusick if (now == 0) 255*41407Smckusick time(&now); 256*41407Smckusick quplist = getprivs(id, type); 257*41407Smckusick for (qup = quplist; qup; qup = qup->next) { 258*41407Smckusick if (!vflag && 259*41407Smckusick qup->dqblk.dqb_isoftlimit == 0 && 260*41407Smckusick qup->dqblk.dqb_ihardlimit == 0 && 261*41407Smckusick qup->dqblk.dqb_bsoftlimit == 0 && 262*41407Smckusick qup->dqblk.dqb_bhardlimit == 0) 263*41407Smckusick continue; 264*41407Smckusick msgi = (char *)0; 265*41407Smckusick if (qup->dqblk.dqb_ihardlimit && 266*41407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) 267*41407Smckusick msgi = "File limit reached on"; 268*41407Smckusick else if (qup->dqblk.dqb_isoftlimit && 269*41407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) 270*41407Smckusick if (qup->dqblk.dqb_itime > now) 271*41407Smckusick msgi = "In file grace period on"; 272*41407Smckusick else 273*41407Smckusick msgi = "Over file quota on"; 274*41407Smckusick msgb = (char *)0; 275*41407Smckusick if (qup->dqblk.dqb_bhardlimit && 276*41407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) 277*41407Smckusick msgb = "Block limit reached on"; 278*41407Smckusick else if (qup->dqblk.dqb_bsoftlimit && 279*41407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) 280*41407Smckusick if (qup->dqblk.dqb_btime > now) 281*41407Smckusick msgb = "In block grace period on"; 282*41407Smckusick else 283*41407Smckusick msgb = "Over block quota on"; 284*41407Smckusick if (qflag) { 285*41407Smckusick if ((msgi != (char *)0 || msgb != (char *)0) && 286*41407Smckusick lines++ == 0) 287*41407Smckusick heading(type, id, name, ""); 288*41407Smckusick if (msgi != (char *)0) 289*41407Smckusick printf("\t%s %s\n", msgi, qup->fsname); 290*41407Smckusick if (msgb != (char *)0) 291*41407Smckusick printf("\t%s %s\n", msgb, qup->fsname); 292*41407Smckusick continue; 293*41407Smckusick } 294*41407Smckusick if (vflag || 295*41407Smckusick qup->dqblk.dqb_curblocks || 296*41407Smckusick qup->dqblk.dqb_curinodes) { 297*41407Smckusick if (lines++ == 0) 298*41407Smckusick heading(type, id, name, ""); 299*41407Smckusick printf("%15s%8d%c%7d%8d%8s" 300*41407Smckusick , qup->fsname 301*41407Smckusick , dbtob(qup->dqblk.dqb_curblocks) / 1024 302*41407Smckusick , (msgb == (char *)0) ? ' ' : '*' 303*41407Smckusick , dbtob(qup->dqblk.dqb_bsoftlimit) / 1024 304*41407Smckusick , dbtob(qup->dqblk.dqb_bhardlimit) / 1024 305*41407Smckusick , (msgb == (char *)0) ? "" 306*41407Smckusick : timeprt(qup->dqblk.dqb_btime)); 307*41407Smckusick printf("%8d%c%7d%8d%8s\n" 308*41407Smckusick , qup->dqblk.dqb_curinodes 309*41407Smckusick , (msgi == (char *)0) ? ' ' : '*' 310*41407Smckusick , qup->dqblk.dqb_isoftlimit 311*41407Smckusick , qup->dqblk.dqb_ihardlimit 312*41407Smckusick , (msgi == (char *)0) ? "" 313*41407Smckusick : timeprt(qup->dqblk.dqb_itime) 314*41407Smckusick ); 315*41407Smckusick continue; 316*41407Smckusick } 317*41407Smckusick } 318*41407Smckusick if (!qflag && lines == 0) 319*41407Smckusick heading(type, id, name, "none"); 320*41407Smckusick } 321*41407Smckusick 322*41407Smckusick heading(type, id, name, tag) 323*41407Smckusick int type; 324*41407Smckusick u_long id; 325*41407Smckusick char *name, *tag; 326*41407Smckusick { 327*41407Smckusick 328*41407Smckusick printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type], 329*41407Smckusick name, *qfextension[type], id, tag); 330*41407Smckusick if (!qflag && tag[0] == '\0') { 331*41407Smckusick printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n" 332*41407Smckusick , "Filesystem" 333*41407Smckusick , "blocks" 334*41407Smckusick , "quota" 335*41407Smckusick , "limit" 336*41407Smckusick , "grace" 337*41407Smckusick , "files" 338*41407Smckusick , "quota" 339*41407Smckusick , "limit" 340*41407Smckusick , "grace" 341*41407Smckusick ); 342*41407Smckusick } 343*41407Smckusick } 344*41407Smckusick 345*41407Smckusick /* 346*41407Smckusick * Calculate the grace period and return a printable string for it. 347*41407Smckusick */ 348*41407Smckusick char * 349*41407Smckusick timeprt(seconds) 350*41407Smckusick time_t seconds; 351*41407Smckusick { 352*41407Smckusick time_t hours, minutes; 353*41407Smckusick static char buf[20]; 354*41407Smckusick static time_t now; 355*41407Smckusick 356*41407Smckusick if (now == 0) 357*41407Smckusick time(&now); 358*41407Smckusick if (now > seconds) 359*41407Smckusick return ("none"); 360*41407Smckusick seconds -= now; 361*41407Smckusick minutes = (seconds + 30) / 60; 362*41407Smckusick hours = (minutes + 30) / 60; 363*41407Smckusick if (hours >= 36) { 364*41407Smckusick sprintf(buf, "%ddays", (hours + 12) / 24); 365*41407Smckusick return (buf); 366*41407Smckusick } 367*41407Smckusick if (minutes >= 60) { 368*41407Smckusick sprintf(buf, "%2d:%d", minutes / 60, minutes % 60); 369*41407Smckusick return (buf); 370*41407Smckusick } 371*41407Smckusick sprintf(buf, "%2d", minutes); 372*41407Smckusick return (buf); 373*41407Smckusick } 374*41407Smckusick 375*41407Smckusick /* 376*41407Smckusick * Collect the requested quota information. 377*41407Smckusick */ 378*41407Smckusick struct quotause * 379*41407Smckusick getprivs(id, quotatype) 380*41407Smckusick register long id; 381*41407Smckusick int quotatype; 382*41407Smckusick { 383*41407Smckusick register struct fstab *fs; 384*41407Smckusick char qfilename[MAXPATHLEN + 1]; 385*41407Smckusick register struct quotause *qup, *quptail; 386*41407Smckusick struct quotause *quphead; 387*41407Smckusick int qcmd, fd; 388*41407Smckusick 389*41407Smckusick setfsent(); 390*41407Smckusick quphead = (struct quotause *)0; 391*41407Smckusick qcmd = QCMD(Q_GETQUOTA, quotatype); 39212710Smckusick while (fs = getfsent()) { 393*41407Smckusick if (!hasquota(fs->fs_mntops, quotatype)) 39412710Smckusick continue; 395*41407Smckusick sprintf(qfilename, "%s/%s.%s", fs->fs_file, qfname, 396*41407Smckusick qfextension[quotatype]); 397*41407Smckusick if ((fd = open(qfilename, O_RDONLY)) < 0) { 398*41407Smckusick perror(qfilename); 39912710Smckusick continue; 400*41407Smckusick } 401*41407Smckusick if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) { 402*41407Smckusick fprintf(stderr, "quota: out of memory\n"); 403*41407Smckusick exit(2); 404*41407Smckusick } 405*41407Smckusick if (quotactl(qfilename, qcmd, id, &qup->dqblk) != 0) { 406*41407Smckusick lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET); 407*41407Smckusick switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 40824684Sserge case 0: /* EOF */ 40924684Sserge /* 41024684Sserge * Convert implicit 0 quota (EOF) 411*41407Smckusick * into an explicit one (zero'ed dqblk) 41224684Sserge */ 413*41407Smckusick bzero((caddr_t)&qup->dqblk, 414*41407Smckusick sizeof(struct dqblk)); 41524684Sserge break; 41624684Sserge 417*41407Smckusick case sizeof(struct dqblk): /* OK */ 41824684Sserge break; 41924684Sserge 42024684Sserge default: /* ERROR */ 421*41407Smckusick fprintf(stderr, "quota: read error"); 42224684Sserge perror(qfilename); 423*41407Smckusick close(fd); 424*41407Smckusick free(qup); 42512710Smckusick continue; 42612710Smckusick } 42712710Smckusick } 428*41407Smckusick close(fd); 429*41407Smckusick strcpy(qup->fsname, fs->fs_file); 430*41407Smckusick if (quphead == NULL) 431*41407Smckusick quphead = qup; 43212710Smckusick else 433*41407Smckusick quptail->next = qup; 434*41407Smckusick quptail = qup; 435*41407Smckusick qup->next = 0; 43612710Smckusick } 437*41407Smckusick endfsent(); 438*41407Smckusick return (quphead); 43912710Smckusick } 44012710Smckusick 441*41407Smckusick /* 442*41407Smckusick * Check to see if a particular quota is to be enabled. 443*41407Smckusick */ 444*41407Smckusick hasquota(options, type) 445*41407Smckusick char *options; 446*41407Smckusick int type; 44712710Smckusick { 448*41407Smckusick register char *opt; 449*41407Smckusick char buf[BUFSIZ]; 450*41407Smckusick char *strtok(); 451*41407Smckusick static char initname, usrname[100], grpname[100]; 45212710Smckusick 453*41407Smckusick if (!initname) { 454*41407Smckusick sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 455*41407Smckusick sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 456*41407Smckusick initname = 1; 45712710Smckusick } 458*41407Smckusick strcpy(buf, options); 459*41407Smckusick for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 460*41407Smckusick if (type == USRQUOTA && strcmp(opt, usrname) == 0) 461*41407Smckusick return(1); 462*41407Smckusick if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 463*41407Smckusick return(1); 46412710Smckusick } 465*41407Smckusick return (0); 46612710Smckusick } 46712710Smckusick 46812710Smckusick alldigits(s) 46912710Smckusick register char *s; 47012710Smckusick { 47112710Smckusick register c; 47212710Smckusick 47312710Smckusick c = *s++; 47412710Smckusick do { 47512710Smckusick if (!isdigit(c)) 47612710Smckusick return (0); 47712710Smckusick } while (c = *s++); 47812710Smckusick return (1); 47912710Smckusick } 480