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*68943Sbostic static char sccsid[] = "@(#)quota.c 8.3 (Berkeley) 04/27/95"; 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> 2767979Smckusick #include <sys/queue.h> 28*68943Sbostic 2951628Sbostic #include <ufs/ufs/quota.h> 30*68943Sbostic 31*68943Sbostic #include <ctype.h> 32*68943Sbostic #include <errno.h> 3312710Smckusick #include <fstab.h> 34*68943Sbostic #include <grp.h> 3512710Smckusick #include <pwd.h> 36*68943Sbostic #include <stdio.h> 3712710Smckusick 3845253Smckusick char *qfname = QUOTAFILENAME; 3945253Smckusick char *qfextension[] = INITQFNAMES; 4045253Smckusick 4141407Smckusick struct quotause { 4241407Smckusick struct quotause *next; 4341407Smckusick long flags; 4441407Smckusick struct dqblk dqblk; 4541407Smckusick char fsname[MAXPATHLEN + 1]; 4641407Smckusick } *getprivs(); 4741407Smckusick #define FOUND 0x01 4812710Smckusick 4912710Smckusick int qflag; 5012710Smckusick int vflag; 5112710Smckusick 5212710Smckusick main(argc, argv) 5312710Smckusick char *argv[]; 5412710Smckusick { 5541407Smckusick int ngroups, gidset[NGROUPS]; 5641407Smckusick int i, gflag = 0, uflag = 0; 5741407Smckusick char ch; 5841407Smckusick extern char *optarg; 5941407Smckusick extern int optind, errno; 6012710Smckusick 6141407Smckusick if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) { 6221392Smckusick fprintf(stderr, "There are no quotas on this system\n"); 6321392Smckusick exit(0); 6421392Smckusick } 6541407Smckusick while ((ch = getopt(argc, argv, "ugvq")) != EOF) { 6641407Smckusick switch(ch) { 6741407Smckusick case 'g': 6841407Smckusick gflag++; 6912710Smckusick break; 7041407Smckusick case 'u': 7141407Smckusick uflag++; 7241407Smckusick break; 7341407Smckusick case 'v': 7441407Smckusick vflag++; 7541407Smckusick break; 7641407Smckusick case 'q': 7741407Smckusick qflag++; 7841407Smckusick break; 7941407Smckusick default: 8041407Smckusick usage(); 8141407Smckusick } 8212710Smckusick } 8341407Smckusick argc -= optind; 8441407Smckusick argv += optind; 8541407Smckusick if (!uflag && !gflag) 8641407Smckusick uflag++; 8712710Smckusick if (argc == 0) { 8841407Smckusick if (uflag) 8941407Smckusick showuid(getuid()); 9041407Smckusick if (gflag) { 9141407Smckusick ngroups = getgroups(NGROUPS, gidset); 9241407Smckusick if (ngroups < 0) { 9341407Smckusick perror("quota: getgroups"); 9441407Smckusick exit(1); 9541407Smckusick } 9641407Smckusick for (i = 1; i < ngroups; i++) 9741407Smckusick showgid(gidset[i]); 9841407Smckusick } 9912710Smckusick exit(0); 10012710Smckusick } 10141407Smckusick if (uflag && gflag) 10241407Smckusick usage(); 10341407Smckusick if (uflag) { 10441407Smckusick for (; argc > 0; argc--, argv++) { 10541407Smckusick if (alldigits(*argv)) 10641407Smckusick showuid(atoi(*argv)); 10741407Smckusick else 10841407Smckusick showusrname(*argv); 10941407Smckusick } 11041407Smckusick exit(0); 11112710Smckusick } 11241407Smckusick if (gflag) { 11341407Smckusick for (; argc > 0; argc--, argv++) { 11441407Smckusick if (alldigits(*argv)) 11541407Smckusick showgid(atoi(*argv)); 11641407Smckusick else 11741407Smckusick showgrpname(*argv); 11841407Smckusick } 11941407Smckusick exit(0); 12041407Smckusick } 12112710Smckusick } 12212710Smckusick 12341407Smckusick usage() 12441407Smckusick { 12541407Smckusick 12641407Smckusick fprintf(stderr, "%s\n%s\n%s\n", 12741407Smckusick "Usage: quota [-guqv]", 12841407Smckusick "\tquota [-qv] -u username ...", 12941407Smckusick "\tquota [-qv] -g groupname ..."); 13041407Smckusick exit(1); 13141407Smckusick } 13241407Smckusick 13341407Smckusick /* 13441407Smckusick * Print out quotas for a specified user identifier. 13541407Smckusick */ 13612710Smckusick showuid(uid) 13741407Smckusick u_long uid; 13812710Smckusick { 13912710Smckusick struct passwd *pwd = getpwuid(uid); 14041407Smckusick u_long myuid; 14141407Smckusick char *name; 14212710Smckusick 14312710Smckusick if (pwd == NULL) 14441407Smckusick name = "(no account)"; 14512710Smckusick else 14641407Smckusick name = pwd->pw_name; 14741407Smckusick myuid = getuid(); 14841407Smckusick if (uid != myuid && myuid != 0) { 14941407Smckusick printf("quota: %s (uid %d): permission denied\n", name, uid); 15041407Smckusick return; 15141407Smckusick } 15241407Smckusick showquotas(USRQUOTA, uid, name); 15312710Smckusick } 15412710Smckusick 15541407Smckusick /* 15641407Smckusick * Print out quotas for a specifed user name. 15741407Smckusick */ 15841407Smckusick showusrname(name) 15912710Smckusick char *name; 16012710Smckusick { 16112710Smckusick struct passwd *pwd = getpwnam(name); 16241407Smckusick u_long myuid; 16312710Smckusick 16412710Smckusick if (pwd == NULL) { 16512710Smckusick fprintf(stderr, "quota: %s: unknown user\n", name); 16612710Smckusick return; 16712710Smckusick } 16841407Smckusick myuid = getuid(); 16941407Smckusick if (pwd->pw_uid != myuid && myuid != 0) { 17041407Smckusick fprintf(stderr, "quota: %s (uid %d): permission denied\n", 17141407Smckusick name, pwd->pw_uid); 17241407Smckusick return; 17341407Smckusick } 17441407Smckusick showquotas(USRQUOTA, pwd->pw_uid, name); 17512710Smckusick } 17612710Smckusick 17741407Smckusick /* 17841407Smckusick * Print out quotas for a specified group identifier. 17941407Smckusick */ 18041407Smckusick showgid(gid) 18141407Smckusick u_long gid; 18241407Smckusick { 18341407Smckusick struct group *grp = getgrgid(gid); 18441407Smckusick int ngroups, gidset[NGROUPS]; 18541407Smckusick register int i; 18612710Smckusick char *name; 18741407Smckusick 18841407Smckusick if (grp == NULL) 18941407Smckusick name = "(no entry)"; 19041407Smckusick else 19141407Smckusick name = grp->gr_name; 19241407Smckusick ngroups = getgroups(NGROUPS, gidset); 19341407Smckusick if (ngroups < 0) { 19441407Smckusick perror("quota: getgroups"); 19541407Smckusick return; 19641407Smckusick } 19741407Smckusick for (i = 1; i < ngroups; i++) 19841407Smckusick if (gid == gidset[i]) 19941407Smckusick break; 20041407Smckusick if (i >= ngroups && getuid() != 0) { 20141407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 20241407Smckusick name, gid); 20341407Smckusick return; 20441407Smckusick } 20541407Smckusick showquotas(GRPQUOTA, gid, name); 20641407Smckusick } 20741407Smckusick 20841407Smckusick /* 20941407Smckusick * Print out quotas for a specifed group name. 21041407Smckusick */ 21141407Smckusick showgrpname(name) 21241407Smckusick char *name; 21312710Smckusick { 21441407Smckusick struct group *grp = getgrnam(name); 21541407Smckusick int ngroups, gidset[NGROUPS]; 21641407Smckusick register int i; 21712710Smckusick 21841407Smckusick if (grp == NULL) { 21941407Smckusick fprintf(stderr, "quota: %s: unknown group\n", name); 22012710Smckusick return; 22112710Smckusick } 22241407Smckusick ngroups = getgroups(NGROUPS, gidset); 22341407Smckusick if (ngroups < 0) { 22441407Smckusick perror("quota: getgroups"); 22541407Smckusick return; 22641407Smckusick } 22741407Smckusick for (i = 1; i < ngroups; i++) 22841407Smckusick if (grp->gr_gid == gidset[i]) 22941407Smckusick break; 23041407Smckusick if (i >= ngroups && getuid() != 0) { 23141407Smckusick fprintf(stderr, "quota: %s (gid %d): permission denied\n", 23241407Smckusick name, grp->gr_gid); 23341407Smckusick return; 23441407Smckusick } 23541407Smckusick showquotas(GRPQUOTA, grp->gr_gid, name); 23641407Smckusick } 23741407Smckusick 23841407Smckusick showquotas(type, id, name) 23941407Smckusick int type; 24041407Smckusick u_long id; 24141407Smckusick char *name; 24241407Smckusick { 24341407Smckusick register struct quotause *qup; 24441407Smckusick struct quotause *quplist, *getprivs(); 24541407Smckusick char *msgi, *msgb, *timeprt(); 24641407Smckusick int myuid, fd, lines = 0; 24741407Smckusick static int first; 24841407Smckusick static time_t now; 24941407Smckusick 25041407Smckusick if (now == 0) 25141407Smckusick time(&now); 25241407Smckusick quplist = getprivs(id, type); 25341407Smckusick for (qup = quplist; qup; qup = qup->next) { 25441407Smckusick if (!vflag && 25541407Smckusick qup->dqblk.dqb_isoftlimit == 0 && 25641407Smckusick qup->dqblk.dqb_ihardlimit == 0 && 25741407Smckusick qup->dqblk.dqb_bsoftlimit == 0 && 25841407Smckusick qup->dqblk.dqb_bhardlimit == 0) 25941407Smckusick continue; 26041407Smckusick msgi = (char *)0; 26141407Smckusick if (qup->dqblk.dqb_ihardlimit && 26241407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) 26341407Smckusick msgi = "File limit reached on"; 26441407Smckusick else if (qup->dqblk.dqb_isoftlimit && 26541407Smckusick qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) 26641407Smckusick if (qup->dqblk.dqb_itime > now) 26741407Smckusick msgi = "In file grace period on"; 26841407Smckusick else 26941407Smckusick msgi = "Over file quota on"; 27041407Smckusick msgb = (char *)0; 27141407Smckusick if (qup->dqblk.dqb_bhardlimit && 27241407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) 27341407Smckusick msgb = "Block limit reached on"; 27441407Smckusick else if (qup->dqblk.dqb_bsoftlimit && 27541407Smckusick qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) 27641407Smckusick if (qup->dqblk.dqb_btime > now) 27741407Smckusick msgb = "In block grace period on"; 27841407Smckusick else 27941407Smckusick msgb = "Over block quota on"; 28041407Smckusick if (qflag) { 28141407Smckusick if ((msgi != (char *)0 || msgb != (char *)0) && 28241407Smckusick lines++ == 0) 28341407Smckusick heading(type, id, name, ""); 28441407Smckusick if (msgi != (char *)0) 28541407Smckusick printf("\t%s %s\n", msgi, qup->fsname); 28641407Smckusick if (msgb != (char *)0) 28741407Smckusick printf("\t%s %s\n", msgb, qup->fsname); 28841407Smckusick continue; 28941407Smckusick } 29041407Smckusick if (vflag || 29141407Smckusick qup->dqblk.dqb_curblocks || 29241407Smckusick qup->dqblk.dqb_curinodes) { 29341407Smckusick if (lines++ == 0) 29441407Smckusick heading(type, id, name, ""); 29541407Smckusick printf("%15s%8d%c%7d%8d%8s" 29641407Smckusick , qup->fsname 29741407Smckusick , dbtob(qup->dqblk.dqb_curblocks) / 1024 29841407Smckusick , (msgb == (char *)0) ? ' ' : '*' 29941407Smckusick , dbtob(qup->dqblk.dqb_bsoftlimit) / 1024 30041407Smckusick , dbtob(qup->dqblk.dqb_bhardlimit) / 1024 30141407Smckusick , (msgb == (char *)0) ? "" 30241407Smckusick : timeprt(qup->dqblk.dqb_btime)); 30341407Smckusick printf("%8d%c%7d%8d%8s\n" 30441407Smckusick , qup->dqblk.dqb_curinodes 30541407Smckusick , (msgi == (char *)0) ? ' ' : '*' 30641407Smckusick , qup->dqblk.dqb_isoftlimit 30741407Smckusick , qup->dqblk.dqb_ihardlimit 30841407Smckusick , (msgi == (char *)0) ? "" 30941407Smckusick : timeprt(qup->dqblk.dqb_itime) 31041407Smckusick ); 31141407Smckusick continue; 31241407Smckusick } 31341407Smckusick } 31441407Smckusick if (!qflag && lines == 0) 31541407Smckusick heading(type, id, name, "none"); 31641407Smckusick } 31741407Smckusick 31841407Smckusick heading(type, id, name, tag) 31941407Smckusick int type; 32041407Smckusick u_long id; 32141407Smckusick char *name, *tag; 32241407Smckusick { 32341407Smckusick 32441407Smckusick printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type], 32541407Smckusick name, *qfextension[type], id, tag); 32641407Smckusick if (!qflag && tag[0] == '\0') { 32741407Smckusick printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n" 32841407Smckusick , "Filesystem" 32941407Smckusick , "blocks" 33041407Smckusick , "quota" 33141407Smckusick , "limit" 33241407Smckusick , "grace" 33341407Smckusick , "files" 33441407Smckusick , "quota" 33541407Smckusick , "limit" 33641407Smckusick , "grace" 33741407Smckusick ); 33841407Smckusick } 33941407Smckusick } 34041407Smckusick 34141407Smckusick /* 34241407Smckusick * Calculate the grace period and return a printable string for it. 34341407Smckusick */ 34441407Smckusick char * 34541407Smckusick timeprt(seconds) 34641407Smckusick time_t seconds; 34741407Smckusick { 34841407Smckusick time_t hours, minutes; 34941407Smckusick static char buf[20]; 35041407Smckusick static time_t now; 35141407Smckusick 35241407Smckusick if (now == 0) 35341407Smckusick time(&now); 35441407Smckusick if (now > seconds) 35541407Smckusick return ("none"); 35641407Smckusick seconds -= now; 35741407Smckusick minutes = (seconds + 30) / 60; 35841407Smckusick hours = (minutes + 30) / 60; 35941407Smckusick if (hours >= 36) { 36041407Smckusick sprintf(buf, "%ddays", (hours + 12) / 24); 36141407Smckusick return (buf); 36241407Smckusick } 36341407Smckusick if (minutes >= 60) { 36441407Smckusick sprintf(buf, "%2d:%d", minutes / 60, minutes % 60); 36541407Smckusick return (buf); 36641407Smckusick } 36741407Smckusick sprintf(buf, "%2d", minutes); 36841407Smckusick return (buf); 36941407Smckusick } 37041407Smckusick 37141407Smckusick /* 37241407Smckusick * Collect the requested quota information. 37341407Smckusick */ 37441407Smckusick struct quotause * 37541407Smckusick getprivs(id, quotatype) 37641407Smckusick register long id; 37741407Smckusick int quotatype; 37841407Smckusick { 37941407Smckusick register struct fstab *fs; 38041407Smckusick register struct quotause *qup, *quptail; 38141407Smckusick struct quotause *quphead; 38241440Smckusick char *qfpathname; 38341407Smckusick int qcmd, fd; 38441407Smckusick 38541407Smckusick setfsent(); 38641407Smckusick quphead = (struct quotause *)0; 38741407Smckusick qcmd = QCMD(Q_GETQUOTA, quotatype); 38812710Smckusick while (fs = getfsent()) { 38941440Smckusick if (strcmp(fs->fs_vfstype, "ufs")) 39012710Smckusick continue; 39141440Smckusick if (!hasquota(fs, quotatype, &qfpathname)) 39212710Smckusick continue; 39341407Smckusick if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) { 39441407Smckusick fprintf(stderr, "quota: out of memory\n"); 39541407Smckusick exit(2); 39641407Smckusick } 39741440Smckusick if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) { 39841440Smckusick if ((fd = open(qfpathname, O_RDONLY)) < 0) { 39941440Smckusick perror(qfpathname); 40041440Smckusick free(qup); 40141440Smckusick continue; 40241440Smckusick } 403*68943Sbostic lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET); 40441407Smckusick switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 40524684Sserge case 0: /* EOF */ 40624684Sserge /* 40724684Sserge * Convert implicit 0 quota (EOF) 40841407Smckusick * into an explicit one (zero'ed dqblk) 40924684Sserge */ 41041407Smckusick bzero((caddr_t)&qup->dqblk, 41141407Smckusick sizeof(struct dqblk)); 41224684Sserge break; 41324684Sserge 41441407Smckusick case sizeof(struct dqblk): /* OK */ 41524684Sserge break; 41624684Sserge 41724684Sserge default: /* ERROR */ 41841407Smckusick fprintf(stderr, "quota: read error"); 41941440Smckusick perror(qfpathname); 42041407Smckusick close(fd); 42141407Smckusick free(qup); 42212710Smckusick continue; 42312710Smckusick } 42441440Smckusick close(fd); 42512710Smckusick } 42641407Smckusick strcpy(qup->fsname, fs->fs_file); 42741407Smckusick if (quphead == NULL) 42841407Smckusick quphead = qup; 42912710Smckusick else 43041407Smckusick quptail->next = qup; 43141407Smckusick quptail = qup; 43241407Smckusick qup->next = 0; 43312710Smckusick } 43441407Smckusick endfsent(); 43541407Smckusick return (quphead); 43612710Smckusick } 43712710Smckusick 43841407Smckusick /* 43941407Smckusick * Check to see if a particular quota is to be enabled. 44041407Smckusick */ 44141440Smckusick hasquota(fs, type, qfnamep) 44241440Smckusick register struct fstab *fs; 44341407Smckusick int type; 44441440Smckusick char **qfnamep; 44512710Smckusick { 44641407Smckusick register char *opt; 44741440Smckusick char *cp, *index(), *strtok(); 44841407Smckusick static char initname, usrname[100], grpname[100]; 44941440Smckusick static char buf[BUFSIZ]; 45012710Smckusick 45141407Smckusick if (!initname) { 45241407Smckusick sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 45341407Smckusick sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 45441407Smckusick initname = 1; 45512710Smckusick } 45641440Smckusick strcpy(buf, fs->fs_mntops); 45741407Smckusick for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 45841440Smckusick if (cp = index(opt, '=')) 45941440Smckusick *cp++ = '\0'; 46041407Smckusick if (type == USRQUOTA && strcmp(opt, usrname) == 0) 46141440Smckusick break; 46241407Smckusick if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 46341440Smckusick break; 46412710Smckusick } 46541440Smckusick if (!opt) 46641440Smckusick return (0); 46741440Smckusick if (cp) { 46841440Smckusick *qfnamep = cp; 46941440Smckusick return (1); 47041440Smckusick } 47141440Smckusick (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 47241440Smckusick *qfnamep = buf; 47341440Smckusick return (1); 47412710Smckusick } 47512710Smckusick 47612710Smckusick alldigits(s) 47712710Smckusick register char *s; 47812710Smckusick { 47912710Smckusick register c; 48012710Smckusick 48112710Smckusick c = *s++; 48212710Smckusick do { 48312710Smckusick if (!isdigit(c)) 48412710Smckusick return (0); 48512710Smckusick } while (c = *s++); 48612710Smckusick return (1); 48712710Smckusick } 488