121520Smckusick /* 241407Smckusick * Copyright (c) 1980, 1990 Regents of the University of California. 334372Sbostic * All rights reserved. 434372Sbostic * 541407Smckusick * This code is derived from software contributed to Berkeley by 641407Smckusick * Robert Elz at The University of Melbourne. 741407Smckusick * 842759Sbostic * %sccs.include.redist.c% 921520Smckusick */ 1021520Smckusick 1112710Smckusick #ifndef lint 1221520Smckusick char copyright[] = 1341407Smckusick "@(#) Copyright (c) 1980, 1990 Regents of the University of California.\n\ 1421520Smckusick All rights reserved.\n"; 1534372Sbostic #endif /* not lint */ 1612710Smckusick 1721520Smckusick #ifndef lint 18*51628Sbostic static char sccsid[] = "@(#)quota.c 5.13 (Berkeley) 11/11/91"; 1934372Sbostic #endif /* not lint */ 2021520Smckusick 2112710Smckusick /* 2212710Smckusick * Disk quota reporting program. 2312710Smckusick */ 2437078Sbostic #include <sys/param.h> 2537078Sbostic #include <sys/file.h> 2637078Sbostic #include <sys/stat.h> 27*51628Sbostic #include <ufs/ufs/quota.h> 2812710Smckusick #include <stdio.h> 2912710Smckusick #include <fstab.h> 3012710Smckusick #include <ctype.h> 3112710Smckusick #include <pwd.h> 3241407Smckusick #include <grp.h> 3321392Smckusick #include <errno.h> 3412710Smckusick 3545253Smckusick char *qfname = QUOTAFILENAME; 3645253Smckusick char *qfextension[] = INITQFNAMES; 3745253Smckusick 3841407Smckusick struct quotause { 3941407Smckusick struct quotause *next; 4041407Smckusick long flags; 4141407Smckusick struct dqblk dqblk; 4241407Smckusick char fsname[MAXPATHLEN + 1]; 4341407Smckusick } *getprivs(); 4441407Smckusick #define FOUND 0x01 4512710Smckusick 4612710Smckusick int qflag; 4712710Smckusick int vflag; 4812710Smckusick 4912710Smckusick main(argc, argv) 5012710Smckusick char *argv[]; 5112710Smckusick { 5241407Smckusick int ngroups, gidset[NGROUPS]; 5341407Smckusick int i, gflag = 0, uflag = 0; 5441407Smckusick char ch; 5541407Smckusick extern char *optarg; 5641407Smckusick extern int optind, errno; 5712710Smckusick 5841407Smckusick if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) { 5921392Smckusick fprintf(stderr, "There are no quotas on this system\n"); 6021392Smckusick exit(0); 6121392Smckusick } 6241407Smckusick while ((ch = getopt(argc, argv, "ugvq")) != EOF) { 6341407Smckusick switch(ch) { 6441407Smckusick case 'g': 6541407Smckusick gflag++; 6612710Smckusick break; 6741407Smckusick case 'u': 6841407Smckusick uflag++; 6941407Smckusick break; 7041407Smckusick case 'v': 7141407Smckusick vflag++; 7241407Smckusick break; 7341407Smckusick case 'q': 7441407Smckusick qflag++; 7541407Smckusick break; 7641407Smckusick default: 7741407Smckusick usage(); 7841407Smckusick } 7912710Smckusick } 8041407Smckusick argc -= optind; 8141407Smckusick argv += optind; 8241407Smckusick if (!uflag && !gflag) 8341407Smckusick uflag++; 8412710Smckusick if (argc == 0) { 8541407Smckusick if (uflag) 8641407Smckusick showuid(getuid()); 8741407Smckusick if (gflag) { 8841407Smckusick ngroups = getgroups(NGROUPS, gidset); 8941407Smckusick if (ngroups < 0) { 9041407Smckusick perror("quota: getgroups"); 9141407Smckusick exit(1); 9241407Smckusick } 9341407Smckusick for (i = 1; i < ngroups; i++) 9441407Smckusick showgid(gidset[i]); 9541407Smckusick } 9612710Smckusick exit(0); 9712710Smckusick } 9841407Smckusick if (uflag && gflag) 9941407Smckusick usage(); 10041407Smckusick if (uflag) { 10141407Smckusick for (; argc > 0; argc--, argv++) { 10241407Smckusick if (alldigits(*argv)) 10341407Smckusick showuid(atoi(*argv)); 10441407Smckusick else 10541407Smckusick showusrname(*argv); 10641407Smckusick } 10741407Smckusick exit(0); 10812710Smckusick } 10941407Smckusick if (gflag) { 11041407Smckusick for (; argc > 0; argc--, argv++) { 11141407Smckusick if (alldigits(*argv)) 11241407Smckusick showgid(atoi(*argv)); 11341407Smckusick else 11441407Smckusick showgrpname(*argv); 11541407Smckusick } 11641407Smckusick exit(0); 11741407Smckusick } 11812710Smckusick } 11912710Smckusick 12041407Smckusick usage() 12141407Smckusick { 12241407Smckusick 12341407Smckusick fprintf(stderr, "%s\n%s\n%s\n", 12441407Smckusick "Usage: quota [-guqv]", 12541407Smckusick "\tquota [-qv] -u username ...", 12641407Smckusick "\tquota [-qv] -g groupname ..."); 12741407Smckusick exit(1); 12841407Smckusick } 12941407Smckusick 13041407Smckusick /* 13141407Smckusick * Print out quotas for a specified user identifier. 13241407Smckusick */ 13312710Smckusick showuid(uid) 13441407Smckusick u_long uid; 13512710Smckusick { 13612710Smckusick struct passwd *pwd = getpwuid(uid); 13741407Smckusick u_long myuid; 13841407Smckusick char *name; 13912710Smckusick 14012710Smckusick if (pwd == NULL) 14141407Smckusick name = "(no account)"; 14212710Smckusick else 14341407Smckusick name = pwd->pw_name; 14441407Smckusick myuid = getuid(); 14541407Smckusick if (uid != myuid && myuid != 0) { 14641407Smckusick printf("quota: %s (uid %d): permission denied\n", name, uid); 14741407Smckusick return; 14841407Smckusick } 14941407Smckusick showquotas(USRQUOTA, uid, name); 15012710Smckusick } 15112710Smckusick 15241407Smckusick /* 15341407Smckusick * Print out quotas for a specifed user name. 15441407Smckusick */ 15541407Smckusick showusrname(name) 15612710Smckusick char *name; 15712710Smckusick { 15812710Smckusick struct passwd *pwd = getpwnam(name); 15941407Smckusick u_long myuid; 16012710Smckusick 16112710Smckusick if (pwd == NULL) { 16212710Smckusick fprintf(stderr, "quota: %s: unknown user\n", name); 16312710Smckusick return; 16412710Smckusick } 16541407Smckusick myuid = getuid(); 16641407Smckusick if (pwd->pw_uid != myuid && myuid != 0) { 16741407Smckusick fprintf(stderr, "quota: %s (uid %d): permission denied\n", 16841407Smckusick name, pwd->pw_uid); 16941407Smckusick return; 17041407Smckusick } 17141407Smckusick showquotas(USRQUOTA, pwd->pw_uid, name); 17212710Smckusick } 17312710Smckusick 17441407Smckusick /* 17541407Smckusick * Print out quotas for a specified group identifier. 17641407Smckusick */ 17741407Smckusick showgid(gid) 17841407Smckusick u_long gid; 17941407Smckusick { 18041407Smckusick struct group *grp = getgrgid(gid); 18141407Smckusick int ngroups, gidset[NGROUPS]; 18241407Smckusick register int i; 18312710Smckusick char *name; 18441407Smckusick 18541407Smckusick if (grp == NULL) 18641407Smckusick name = "(no entry)"; 18741407Smckusick else 18841407Smckusick name = grp->gr_name; 18941407Smckusick ngroups = getgroups(NGROUPS, gidset); 19041407Smckusick if (ngroups < 0) { 19141407Smckusick perror("quota: getgroups"); 19241407Smckusick return; 19341407Smckusick } 19441407Smckusick for (i = 1; i < ngroups; i++) 19541407Smckusick if (gid == gidset[i]) 19641407Smckusick break; 19741407Smckusick if (i >= ngroups && getuid() != 0) { 19841407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 19941407Smckusick name, gid); 20041407Smckusick return; 20141407Smckusick } 20241407Smckusick showquotas(GRPQUOTA, gid, name); 20341407Smckusick } 20441407Smckusick 20541407Smckusick /* 20641407Smckusick * Print out quotas for a specifed group name. 20741407Smckusick */ 20841407Smckusick showgrpname(name) 20941407Smckusick char *name; 21012710Smckusick { 21141407Smckusick struct group *grp = getgrnam(name); 21241407Smckusick int ngroups, gidset[NGROUPS]; 21341407Smckusick register int i; 21412710Smckusick 21541407Smckusick if (grp == NULL) { 21641407Smckusick fprintf(stderr, "quota: %s: unknown group\n", name); 21712710Smckusick return; 21812710Smckusick } 21941407Smckusick ngroups = getgroups(NGROUPS, gidset); 22041407Smckusick if (ngroups < 0) { 22141407Smckusick perror("quota: getgroups"); 22241407Smckusick return; 22341407Smckusick } 22441407Smckusick for (i = 1; i < ngroups; i++) 22541407Smckusick if (grp->gr_gid == gidset[i]) 22641407Smckusick break; 22741407Smckusick if (i >= ngroups && getuid() != 0) { 22841407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 22941407Smckusick name, grp->gr_gid); 23041407Smckusick return; 23141407Smckusick } 23241407Smckusick showquotas(GRPQUOTA, grp->gr_gid, name); 23341407Smckusick } 23441407Smckusick 23541407Smckusick showquotas(type, id, name) 23641407Smckusick int type; 23741407Smckusick u_long id; 23841407Smckusick char *name; 23941407Smckusick { 24041407Smckusick register struct quotause *qup; 24141407Smckusick struct quotause *quplist, *getprivs(); 24241407Smckusick char *msgi, *msgb, *timeprt(); 24341407Smckusick int myuid, fd, lines = 0; 24441407Smckusick static int first; 24541407Smckusick static time_t now; 24641407Smckusick 24741407Smckusick if (now == 0) 24841407Smckusick time(&now); 24941407Smckusick quplist = getprivs(id, type); 25041407Smckusick for (qup = quplist; qup; qup = qup->next) { 25141407Smckusick if (!vflag && 25241407Smckusick qup->dqblk.dqb_isoftlimit == 0 && 25341407Smckusick qup->dqblk.dqb_ihardlimit == 0 && 25441407Smckusick qup->dqblk.dqb_bsoftlimit == 0 && 25541407Smckusick qup->dqblk.dqb_bhardlimit == 0) 25641407Smckusick continue; 25741407Smckusick msgi = (char *)0; 25841407Smckusick if (qup->dqblk.dqb_ihardlimit && 25941407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) 26041407Smckusick msgi = "File limit reached on"; 26141407Smckusick else if (qup->dqblk.dqb_isoftlimit && 26241407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) 26341407Smckusick if (qup->dqblk.dqb_itime > now) 26441407Smckusick msgi = "In file grace period on"; 26541407Smckusick else 26641407Smckusick msgi = "Over file quota on"; 26741407Smckusick msgb = (char *)0; 26841407Smckusick if (qup->dqblk.dqb_bhardlimit && 26941407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) 27041407Smckusick msgb = "Block limit reached on"; 27141407Smckusick else if (qup->dqblk.dqb_bsoftlimit && 27241407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) 27341407Smckusick if (qup->dqblk.dqb_btime > now) 27441407Smckusick msgb = "In block grace period on"; 27541407Smckusick else 27641407Smckusick msgb = "Over block quota on"; 27741407Smckusick if (qflag) { 27841407Smckusick if ((msgi != (char *)0 || msgb != (char *)0) && 27941407Smckusick lines++ == 0) 28041407Smckusick heading(type, id, name, ""); 28141407Smckusick if (msgi != (char *)0) 28241407Smckusick printf("\t%s %s\n", msgi, qup->fsname); 28341407Smckusick if (msgb != (char *)0) 28441407Smckusick printf("\t%s %s\n", msgb, qup->fsname); 28541407Smckusick continue; 28641407Smckusick } 28741407Smckusick if (vflag || 28841407Smckusick qup->dqblk.dqb_curblocks || 28941407Smckusick qup->dqblk.dqb_curinodes) { 29041407Smckusick if (lines++ == 0) 29141407Smckusick heading(type, id, name, ""); 29241407Smckusick printf("%15s%8d%c%7d%8d%8s" 29341407Smckusick , qup->fsname 29441407Smckusick , dbtob(qup->dqblk.dqb_curblocks) / 1024 29541407Smckusick , (msgb == (char *)0) ? ' ' : '*' 29641407Smckusick , dbtob(qup->dqblk.dqb_bsoftlimit) / 1024 29741407Smckusick , dbtob(qup->dqblk.dqb_bhardlimit) / 1024 29841407Smckusick , (msgb == (char *)0) ? "" 29941407Smckusick : timeprt(qup->dqblk.dqb_btime)); 30041407Smckusick printf("%8d%c%7d%8d%8s\n" 30141407Smckusick , qup->dqblk.dqb_curinodes 30241407Smckusick , (msgi == (char *)0) ? ' ' : '*' 30341407Smckusick , qup->dqblk.dqb_isoftlimit 30441407Smckusick , qup->dqblk.dqb_ihardlimit 30541407Smckusick , (msgi == (char *)0) ? "" 30641407Smckusick : timeprt(qup->dqblk.dqb_itime) 30741407Smckusick ); 30841407Smckusick continue; 30941407Smckusick } 31041407Smckusick } 31141407Smckusick if (!qflag && lines == 0) 31241407Smckusick heading(type, id, name, "none"); 31341407Smckusick } 31441407Smckusick 31541407Smckusick heading(type, id, name, tag) 31641407Smckusick int type; 31741407Smckusick u_long id; 31841407Smckusick char *name, *tag; 31941407Smckusick { 32041407Smckusick 32141407Smckusick printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type], 32241407Smckusick name, *qfextension[type], id, tag); 32341407Smckusick if (!qflag && tag[0] == '\0') { 32441407Smckusick printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n" 32541407Smckusick , "Filesystem" 32641407Smckusick , "blocks" 32741407Smckusick , "quota" 32841407Smckusick , "limit" 32941407Smckusick , "grace" 33041407Smckusick , "files" 33141407Smckusick , "quota" 33241407Smckusick , "limit" 33341407Smckusick , "grace" 33441407Smckusick ); 33541407Smckusick } 33641407Smckusick } 33741407Smckusick 33841407Smckusick /* 33941407Smckusick * Calculate the grace period and return a printable string for it. 34041407Smckusick */ 34141407Smckusick char * 34241407Smckusick timeprt(seconds) 34341407Smckusick time_t seconds; 34441407Smckusick { 34541407Smckusick time_t hours, minutes; 34641407Smckusick static char buf[20]; 34741407Smckusick static time_t now; 34841407Smckusick 34941407Smckusick if (now == 0) 35041407Smckusick time(&now); 35141407Smckusick if (now > seconds) 35241407Smckusick return ("none"); 35341407Smckusick seconds -= now; 35441407Smckusick minutes = (seconds + 30) / 60; 35541407Smckusick hours = (minutes + 30) / 60; 35641407Smckusick if (hours >= 36) { 35741407Smckusick sprintf(buf, "%ddays", (hours + 12) / 24); 35841407Smckusick return (buf); 35941407Smckusick } 36041407Smckusick if (minutes >= 60) { 36141407Smckusick sprintf(buf, "%2d:%d", minutes / 60, minutes % 60); 36241407Smckusick return (buf); 36341407Smckusick } 36441407Smckusick sprintf(buf, "%2d", minutes); 36541407Smckusick return (buf); 36641407Smckusick } 36741407Smckusick 36841407Smckusick /* 36941407Smckusick * Collect the requested quota information. 37041407Smckusick */ 37141407Smckusick struct quotause * 37241407Smckusick getprivs(id, quotatype) 37341407Smckusick register long id; 37441407Smckusick int quotatype; 37541407Smckusick { 37641407Smckusick register struct fstab *fs; 37741407Smckusick register struct quotause *qup, *quptail; 37841407Smckusick struct quotause *quphead; 37941440Smckusick char *qfpathname; 38041407Smckusick int qcmd, fd; 38141407Smckusick 38241407Smckusick setfsent(); 38341407Smckusick quphead = (struct quotause *)0; 38441407Smckusick qcmd = QCMD(Q_GETQUOTA, quotatype); 38512710Smckusick while (fs = getfsent()) { 38641440Smckusick if (strcmp(fs->fs_vfstype, "ufs")) 38712710Smckusick continue; 38841440Smckusick if (!hasquota(fs, quotatype, &qfpathname)) 38912710Smckusick continue; 39041407Smckusick if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) { 39141407Smckusick fprintf(stderr, "quota: out of memory\n"); 39241407Smckusick exit(2); 39341407Smckusick } 39441440Smckusick if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) { 39541440Smckusick if ((fd = open(qfpathname, O_RDONLY)) < 0) { 39641440Smckusick perror(qfpathname); 39741440Smckusick free(qup); 39841440Smckusick continue; 39941440Smckusick } 40041407Smckusick lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET); 40141407Smckusick switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 40224684Sserge case 0: /* EOF */ 40324684Sserge /* 40424684Sserge * Convert implicit 0 quota (EOF) 40541407Smckusick * into an explicit one (zero'ed dqblk) 40624684Sserge */ 40741407Smckusick bzero((caddr_t)&qup->dqblk, 40841407Smckusick sizeof(struct dqblk)); 40924684Sserge break; 41024684Sserge 41141407Smckusick case sizeof(struct dqblk): /* OK */ 41224684Sserge break; 41324684Sserge 41424684Sserge default: /* ERROR */ 41541407Smckusick fprintf(stderr, "quota: read error"); 41641440Smckusick perror(qfpathname); 41741407Smckusick close(fd); 41841407Smckusick free(qup); 41912710Smckusick continue; 42012710Smckusick } 42141440Smckusick close(fd); 42212710Smckusick } 42341407Smckusick strcpy(qup->fsname, fs->fs_file); 42441407Smckusick if (quphead == NULL) 42541407Smckusick quphead = qup; 42612710Smckusick else 42741407Smckusick quptail->next = qup; 42841407Smckusick quptail = qup; 42941407Smckusick qup->next = 0; 43012710Smckusick } 43141407Smckusick endfsent(); 43241407Smckusick return (quphead); 43312710Smckusick } 43412710Smckusick 43541407Smckusick /* 43641407Smckusick * Check to see if a particular quota is to be enabled. 43741407Smckusick */ 43841440Smckusick hasquota(fs, type, qfnamep) 43941440Smckusick register struct fstab *fs; 44041407Smckusick int type; 44141440Smckusick char **qfnamep; 44212710Smckusick { 44341407Smckusick register char *opt; 44441440Smckusick char *cp, *index(), *strtok(); 44541407Smckusick static char initname, usrname[100], grpname[100]; 44641440Smckusick static char buf[BUFSIZ]; 44712710Smckusick 44841407Smckusick if (!initname) { 44941407Smckusick sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 45041407Smckusick sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 45141407Smckusick initname = 1; 45212710Smckusick } 45341440Smckusick strcpy(buf, fs->fs_mntops); 45441407Smckusick for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 45541440Smckusick if (cp = index(opt, '=')) 45641440Smckusick *cp++ = '\0'; 45741407Smckusick if (type == USRQUOTA && strcmp(opt, usrname) == 0) 45841440Smckusick break; 45941407Smckusick if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 46041440Smckusick break; 46112710Smckusick } 46241440Smckusick if (!opt) 46341440Smckusick return (0); 46441440Smckusick if (cp) { 46541440Smckusick *qfnamep = cp; 46641440Smckusick return (1); 46741440Smckusick } 46841440Smckusick (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 46941440Smckusick *qfnamep = buf; 47041440Smckusick return (1); 47112710Smckusick } 47212710Smckusick 47312710Smckusick alldigits(s) 47412710Smckusick register char *s; 47512710Smckusick { 47612710Smckusick register c; 47712710Smckusick 47812710Smckusick c = *s++; 47912710Smckusick do { 48012710Smckusick if (!isdigit(c)) 48112710Smckusick return (0); 48212710Smckusick } while (c = *s++); 48312710Smckusick return (1); 48412710Smckusick } 485