121520Smckusick /* 262193Sbostic * Copyright (c) 1980, 1990, 1993 362193Sbostic * The Regents of the University of California. 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 1262193Sbostic static char copyright[] = 1362193Sbostic "@(#) Copyright (c) 1980, 1990, 1993\n\ 1462193Sbostic The Regents of the University of California. All rights reserved.\n"; 1534372Sbostic #endif /* not lint */ 1612710Smckusick 1721520Smckusick #ifndef lint 18*67979Smckusick static char sccsid[] = "@(#)quota.c 8.2 (Berkeley) 11/22/94"; 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*67979Smckusick #include <sys/queue.h> 2851628Sbostic #include <ufs/ufs/quota.h> 2912710Smckusick #include <stdio.h> 3012710Smckusick #include <fstab.h> 3112710Smckusick #include <ctype.h> 3212710Smckusick #include <pwd.h> 3341407Smckusick #include <grp.h> 3421392Smckusick #include <errno.h> 3512710Smckusick 3645253Smckusick char *qfname = QUOTAFILENAME; 3745253Smckusick char *qfextension[] = INITQFNAMES; 3845253Smckusick 3941407Smckusick struct quotause { 4041407Smckusick struct quotause *next; 4141407Smckusick long flags; 4241407Smckusick struct dqblk dqblk; 4341407Smckusick char fsname[MAXPATHLEN + 1]; 4441407Smckusick } *getprivs(); 4541407Smckusick #define FOUND 0x01 4612710Smckusick 4712710Smckusick int qflag; 4812710Smckusick int vflag; 4912710Smckusick 5012710Smckusick main(argc, argv) 5112710Smckusick char *argv[]; 5212710Smckusick { 5341407Smckusick int ngroups, gidset[NGROUPS]; 5441407Smckusick int i, gflag = 0, uflag = 0; 5541407Smckusick char ch; 5641407Smckusick extern char *optarg; 5741407Smckusick extern int optind, errno; 5812710Smckusick 5941407Smckusick if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) { 6021392Smckusick fprintf(stderr, "There are no quotas on this system\n"); 6121392Smckusick exit(0); 6221392Smckusick } 6341407Smckusick while ((ch = getopt(argc, argv, "ugvq")) != EOF) { 6441407Smckusick switch(ch) { 6541407Smckusick case 'g': 6641407Smckusick gflag++; 6712710Smckusick break; 6841407Smckusick case 'u': 6941407Smckusick uflag++; 7041407Smckusick break; 7141407Smckusick case 'v': 7241407Smckusick vflag++; 7341407Smckusick break; 7441407Smckusick case 'q': 7541407Smckusick qflag++; 7641407Smckusick break; 7741407Smckusick default: 7841407Smckusick usage(); 7941407Smckusick } 8012710Smckusick } 8141407Smckusick argc -= optind; 8241407Smckusick argv += optind; 8341407Smckusick if (!uflag && !gflag) 8441407Smckusick uflag++; 8512710Smckusick if (argc == 0) { 8641407Smckusick if (uflag) 8741407Smckusick showuid(getuid()); 8841407Smckusick if (gflag) { 8941407Smckusick ngroups = getgroups(NGROUPS, gidset); 9041407Smckusick if (ngroups < 0) { 9141407Smckusick perror("quota: getgroups"); 9241407Smckusick exit(1); 9341407Smckusick } 9441407Smckusick for (i = 1; i < ngroups; i++) 9541407Smckusick showgid(gidset[i]); 9641407Smckusick } 9712710Smckusick exit(0); 9812710Smckusick } 9941407Smckusick if (uflag && gflag) 10041407Smckusick usage(); 10141407Smckusick if (uflag) { 10241407Smckusick for (; argc > 0; argc--, argv++) { 10341407Smckusick if (alldigits(*argv)) 10441407Smckusick showuid(atoi(*argv)); 10541407Smckusick else 10641407Smckusick showusrname(*argv); 10741407Smckusick } 10841407Smckusick exit(0); 10912710Smckusick } 11041407Smckusick if (gflag) { 11141407Smckusick for (; argc > 0; argc--, argv++) { 11241407Smckusick if (alldigits(*argv)) 11341407Smckusick showgid(atoi(*argv)); 11441407Smckusick else 11541407Smckusick showgrpname(*argv); 11641407Smckusick } 11741407Smckusick exit(0); 11841407Smckusick } 11912710Smckusick } 12012710Smckusick 12141407Smckusick usage() 12241407Smckusick { 12341407Smckusick 12441407Smckusick fprintf(stderr, "%s\n%s\n%s\n", 12541407Smckusick "Usage: quota [-guqv]", 12641407Smckusick "\tquota [-qv] -u username ...", 12741407Smckusick "\tquota [-qv] -g groupname ..."); 12841407Smckusick exit(1); 12941407Smckusick } 13041407Smckusick 13141407Smckusick /* 13241407Smckusick * Print out quotas for a specified user identifier. 13341407Smckusick */ 13412710Smckusick showuid(uid) 13541407Smckusick u_long uid; 13612710Smckusick { 13712710Smckusick struct passwd *pwd = getpwuid(uid); 13841407Smckusick u_long myuid; 13941407Smckusick char *name; 14012710Smckusick 14112710Smckusick if (pwd == NULL) 14241407Smckusick name = "(no account)"; 14312710Smckusick else 14441407Smckusick name = pwd->pw_name; 14541407Smckusick myuid = getuid(); 14641407Smckusick if (uid != myuid && myuid != 0) { 14741407Smckusick printf("quota: %s (uid %d): permission denied\n", name, uid); 14841407Smckusick return; 14941407Smckusick } 15041407Smckusick showquotas(USRQUOTA, uid, name); 15112710Smckusick } 15212710Smckusick 15341407Smckusick /* 15441407Smckusick * Print out quotas for a specifed user name. 15541407Smckusick */ 15641407Smckusick showusrname(name) 15712710Smckusick char *name; 15812710Smckusick { 15912710Smckusick struct passwd *pwd = getpwnam(name); 16041407Smckusick u_long myuid; 16112710Smckusick 16212710Smckusick if (pwd == NULL) { 16312710Smckusick fprintf(stderr, "quota: %s: unknown user\n", name); 16412710Smckusick return; 16512710Smckusick } 16641407Smckusick myuid = getuid(); 16741407Smckusick if (pwd->pw_uid != myuid && myuid != 0) { 16841407Smckusick fprintf(stderr, "quota: %s (uid %d): permission denied\n", 16941407Smckusick name, pwd->pw_uid); 17041407Smckusick return; 17141407Smckusick } 17241407Smckusick showquotas(USRQUOTA, pwd->pw_uid, name); 17312710Smckusick } 17412710Smckusick 17541407Smckusick /* 17641407Smckusick * Print out quotas for a specified group identifier. 17741407Smckusick */ 17841407Smckusick showgid(gid) 17941407Smckusick u_long gid; 18041407Smckusick { 18141407Smckusick struct group *grp = getgrgid(gid); 18241407Smckusick int ngroups, gidset[NGROUPS]; 18341407Smckusick register int i; 18412710Smckusick char *name; 18541407Smckusick 18641407Smckusick if (grp == NULL) 18741407Smckusick name = "(no entry)"; 18841407Smckusick else 18941407Smckusick name = grp->gr_name; 19041407Smckusick ngroups = getgroups(NGROUPS, gidset); 19141407Smckusick if (ngroups < 0) { 19241407Smckusick perror("quota: getgroups"); 19341407Smckusick return; 19441407Smckusick } 19541407Smckusick for (i = 1; i < ngroups; i++) 19641407Smckusick if (gid == gidset[i]) 19741407Smckusick break; 19841407Smckusick if (i >= ngroups && getuid() != 0) { 19941407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 20041407Smckusick name, gid); 20141407Smckusick return; 20241407Smckusick } 20341407Smckusick showquotas(GRPQUOTA, gid, name); 20441407Smckusick } 20541407Smckusick 20641407Smckusick /* 20741407Smckusick * Print out quotas for a specifed group name. 20841407Smckusick */ 20941407Smckusick showgrpname(name) 21041407Smckusick char *name; 21112710Smckusick { 21241407Smckusick struct group *grp = getgrnam(name); 21341407Smckusick int ngroups, gidset[NGROUPS]; 21441407Smckusick register int i; 21512710Smckusick 21641407Smckusick if (grp == NULL) { 21741407Smckusick fprintf(stderr, "quota: %s: unknown group\n", name); 21812710Smckusick return; 21912710Smckusick } 22041407Smckusick ngroups = getgroups(NGROUPS, gidset); 22141407Smckusick if (ngroups < 0) { 22241407Smckusick perror("quota: getgroups"); 22341407Smckusick return; 22441407Smckusick } 22541407Smckusick for (i = 1; i < ngroups; i++) 22641407Smckusick if (grp->gr_gid == gidset[i]) 22741407Smckusick break; 22841407Smckusick if (i >= ngroups && getuid() != 0) { 22941407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 23041407Smckusick name, grp->gr_gid); 23141407Smckusick return; 23241407Smckusick } 23341407Smckusick showquotas(GRPQUOTA, grp->gr_gid, name); 23441407Smckusick } 23541407Smckusick 23641407Smckusick showquotas(type, id, name) 23741407Smckusick int type; 23841407Smckusick u_long id; 23941407Smckusick char *name; 24041407Smckusick { 24141407Smckusick register struct quotause *qup; 24241407Smckusick struct quotause *quplist, *getprivs(); 24341407Smckusick char *msgi, *msgb, *timeprt(); 24441407Smckusick int myuid, fd, lines = 0; 24541407Smckusick static int first; 24641407Smckusick static time_t now; 24741407Smckusick 24841407Smckusick if (now == 0) 24941407Smckusick time(&now); 25041407Smckusick quplist = getprivs(id, type); 25141407Smckusick for (qup = quplist; qup; qup = qup->next) { 25241407Smckusick if (!vflag && 25341407Smckusick qup->dqblk.dqb_isoftlimit == 0 && 25441407Smckusick qup->dqblk.dqb_ihardlimit == 0 && 25541407Smckusick qup->dqblk.dqb_bsoftlimit == 0 && 25641407Smckusick qup->dqblk.dqb_bhardlimit == 0) 25741407Smckusick continue; 25841407Smckusick msgi = (char *)0; 25941407Smckusick if (qup->dqblk.dqb_ihardlimit && 26041407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) 26141407Smckusick msgi = "File limit reached on"; 26241407Smckusick else if (qup->dqblk.dqb_isoftlimit && 26341407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) 26441407Smckusick if (qup->dqblk.dqb_itime > now) 26541407Smckusick msgi = "In file grace period on"; 26641407Smckusick else 26741407Smckusick msgi = "Over file quota on"; 26841407Smckusick msgb = (char *)0; 26941407Smckusick if (qup->dqblk.dqb_bhardlimit && 27041407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) 27141407Smckusick msgb = "Block limit reached on"; 27241407Smckusick else if (qup->dqblk.dqb_bsoftlimit && 27341407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) 27441407Smckusick if (qup->dqblk.dqb_btime > now) 27541407Smckusick msgb = "In block grace period on"; 27641407Smckusick else 27741407Smckusick msgb = "Over block quota on"; 27841407Smckusick if (qflag) { 27941407Smckusick if ((msgi != (char *)0 || msgb != (char *)0) && 28041407Smckusick lines++ == 0) 28141407Smckusick heading(type, id, name, ""); 28241407Smckusick if (msgi != (char *)0) 28341407Smckusick printf("\t%s %s\n", msgi, qup->fsname); 28441407Smckusick if (msgb != (char *)0) 28541407Smckusick printf("\t%s %s\n", msgb, qup->fsname); 28641407Smckusick continue; 28741407Smckusick } 28841407Smckusick if (vflag || 28941407Smckusick qup->dqblk.dqb_curblocks || 29041407Smckusick qup->dqblk.dqb_curinodes) { 29141407Smckusick if (lines++ == 0) 29241407Smckusick heading(type, id, name, ""); 29341407Smckusick printf("%15s%8d%c%7d%8d%8s" 29441407Smckusick , qup->fsname 29541407Smckusick , dbtob(qup->dqblk.dqb_curblocks) / 1024 29641407Smckusick , (msgb == (char *)0) ? ' ' : '*' 29741407Smckusick , dbtob(qup->dqblk.dqb_bsoftlimit) / 1024 29841407Smckusick , dbtob(qup->dqblk.dqb_bhardlimit) / 1024 29941407Smckusick , (msgb == (char *)0) ? "" 30041407Smckusick : timeprt(qup->dqblk.dqb_btime)); 30141407Smckusick printf("%8d%c%7d%8d%8s\n" 30241407Smckusick , qup->dqblk.dqb_curinodes 30341407Smckusick , (msgi == (char *)0) ? ' ' : '*' 30441407Smckusick , qup->dqblk.dqb_isoftlimit 30541407Smckusick , qup->dqblk.dqb_ihardlimit 30641407Smckusick , (msgi == (char *)0) ? "" 30741407Smckusick : timeprt(qup->dqblk.dqb_itime) 30841407Smckusick ); 30941407Smckusick continue; 31041407Smckusick } 31141407Smckusick } 31241407Smckusick if (!qflag && lines == 0) 31341407Smckusick heading(type, id, name, "none"); 31441407Smckusick } 31541407Smckusick 31641407Smckusick heading(type, id, name, tag) 31741407Smckusick int type; 31841407Smckusick u_long id; 31941407Smckusick char *name, *tag; 32041407Smckusick { 32141407Smckusick 32241407Smckusick printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type], 32341407Smckusick name, *qfextension[type], id, tag); 32441407Smckusick if (!qflag && tag[0] == '\0') { 32541407Smckusick printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n" 32641407Smckusick , "Filesystem" 32741407Smckusick , "blocks" 32841407Smckusick , "quota" 32941407Smckusick , "limit" 33041407Smckusick , "grace" 33141407Smckusick , "files" 33241407Smckusick , "quota" 33341407Smckusick , "limit" 33441407Smckusick , "grace" 33541407Smckusick ); 33641407Smckusick } 33741407Smckusick } 33841407Smckusick 33941407Smckusick /* 34041407Smckusick * Calculate the grace period and return a printable string for it. 34141407Smckusick */ 34241407Smckusick char * 34341407Smckusick timeprt(seconds) 34441407Smckusick time_t seconds; 34541407Smckusick { 34641407Smckusick time_t hours, minutes; 34741407Smckusick static char buf[20]; 34841407Smckusick static time_t now; 34941407Smckusick 35041407Smckusick if (now == 0) 35141407Smckusick time(&now); 35241407Smckusick if (now > seconds) 35341407Smckusick return ("none"); 35441407Smckusick seconds -= now; 35541407Smckusick minutes = (seconds + 30) / 60; 35641407Smckusick hours = (minutes + 30) / 60; 35741407Smckusick if (hours >= 36) { 35841407Smckusick sprintf(buf, "%ddays", (hours + 12) / 24); 35941407Smckusick return (buf); 36041407Smckusick } 36141407Smckusick if (minutes >= 60) { 36241407Smckusick sprintf(buf, "%2d:%d", minutes / 60, minutes % 60); 36341407Smckusick return (buf); 36441407Smckusick } 36541407Smckusick sprintf(buf, "%2d", minutes); 36641407Smckusick return (buf); 36741407Smckusick } 36841407Smckusick 36941407Smckusick /* 37041407Smckusick * Collect the requested quota information. 37141407Smckusick */ 37241407Smckusick struct quotause * 37341407Smckusick getprivs(id, quotatype) 37441407Smckusick register long id; 37541407Smckusick int quotatype; 37641407Smckusick { 37741407Smckusick register struct fstab *fs; 37841407Smckusick register struct quotause *qup, *quptail; 37941407Smckusick struct quotause *quphead; 38041440Smckusick char *qfpathname; 38141407Smckusick int qcmd, fd; 38241407Smckusick 38341407Smckusick setfsent(); 38441407Smckusick quphead = (struct quotause *)0; 38541407Smckusick qcmd = QCMD(Q_GETQUOTA, quotatype); 38612710Smckusick while (fs = getfsent()) { 38741440Smckusick if (strcmp(fs->fs_vfstype, "ufs")) 38812710Smckusick continue; 38941440Smckusick if (!hasquota(fs, quotatype, &qfpathname)) 39012710Smckusick continue; 39141407Smckusick if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) { 39241407Smckusick fprintf(stderr, "quota: out of memory\n"); 39341407Smckusick exit(2); 39441407Smckusick } 39541440Smckusick if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) { 39641440Smckusick if ((fd = open(qfpathname, O_RDONLY)) < 0) { 39741440Smckusick perror(qfpathname); 39841440Smckusick free(qup); 39941440Smckusick continue; 40041440Smckusick } 40141407Smckusick lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET); 40241407Smckusick switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 40324684Sserge case 0: /* EOF */ 40424684Sserge /* 40524684Sserge * Convert implicit 0 quota (EOF) 40641407Smckusick * into an explicit one (zero'ed dqblk) 40724684Sserge */ 40841407Smckusick bzero((caddr_t)&qup->dqblk, 40941407Smckusick sizeof(struct dqblk)); 41024684Sserge break; 41124684Sserge 41241407Smckusick case sizeof(struct dqblk): /* OK */ 41324684Sserge break; 41424684Sserge 41524684Sserge default: /* ERROR */ 41641407Smckusick fprintf(stderr, "quota: read error"); 41741440Smckusick perror(qfpathname); 41841407Smckusick close(fd); 41941407Smckusick free(qup); 42012710Smckusick continue; 42112710Smckusick } 42241440Smckusick close(fd); 42312710Smckusick } 42441407Smckusick strcpy(qup->fsname, fs->fs_file); 42541407Smckusick if (quphead == NULL) 42641407Smckusick quphead = qup; 42712710Smckusick else 42841407Smckusick quptail->next = qup; 42941407Smckusick quptail = qup; 43041407Smckusick qup->next = 0; 43112710Smckusick } 43241407Smckusick endfsent(); 43341407Smckusick return (quphead); 43412710Smckusick } 43512710Smckusick 43641407Smckusick /* 43741407Smckusick * Check to see if a particular quota is to be enabled. 43841407Smckusick */ 43941440Smckusick hasquota(fs, type, qfnamep) 44041440Smckusick register struct fstab *fs; 44141407Smckusick int type; 44241440Smckusick char **qfnamep; 44312710Smckusick { 44441407Smckusick register char *opt; 44541440Smckusick char *cp, *index(), *strtok(); 44641407Smckusick static char initname, usrname[100], grpname[100]; 44741440Smckusick static char buf[BUFSIZ]; 44812710Smckusick 44941407Smckusick if (!initname) { 45041407Smckusick sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 45141407Smckusick sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 45241407Smckusick initname = 1; 45312710Smckusick } 45441440Smckusick strcpy(buf, fs->fs_mntops); 45541407Smckusick for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 45641440Smckusick if (cp = index(opt, '=')) 45741440Smckusick *cp++ = '\0'; 45841407Smckusick if (type == USRQUOTA && strcmp(opt, usrname) == 0) 45941440Smckusick break; 46041407Smckusick if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 46141440Smckusick break; 46212710Smckusick } 46341440Smckusick if (!opt) 46441440Smckusick return (0); 46541440Smckusick if (cp) { 46641440Smckusick *qfnamep = cp; 46741440Smckusick return (1); 46841440Smckusick } 46941440Smckusick (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 47041440Smckusick *qfnamep = buf; 47141440Smckusick return (1); 47212710Smckusick } 47312710Smckusick 47412710Smckusick alldigits(s) 47512710Smckusick register char *s; 47612710Smckusick { 47712710Smckusick register c; 47812710Smckusick 47912710Smckusick c = *s++; 48012710Smckusick do { 48112710Smckusick if (!isdigit(c)) 48212710Smckusick return (0); 48312710Smckusick } while (c = *s++); 48412710Smckusick return (1); 48512710Smckusick } 486