Annotation of src/usr.bin/quota/quota.c, Revision 1.2
1.1 deraadt 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";*/
1.2 ! deraadt 45: static char rcsid[] = "$Id: quota.c,v 1.1.1.1 1995/10/18 08:45:58 deraadt Exp $";
1.1 deraadt 46: #endif /* not lint */
47:
48: /*
49: * Disk quota reporting program.
50: */
51: #include <sys/param.h>
52: #include <sys/types.h>
53: #include <sys/file.h>
54: #include <sys/stat.h>
55: #include <sys/mount.h>
56: #include <sys/socket.h>
57: #include <ufs/ufs/quota.h>
58: #include <stdio.h>
59: #include <stdlib.h>
60: #include <fstab.h>
61: #include <ctype.h>
62: #include <string.h>
63: #include <pwd.h>
64: #include <grp.h>
65: #include <errno.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: char *timeprt __P((time_t seconds));
84: struct quotause *getprivs __P((long id, int quotatype));
85:
86: int qflag;
87: int vflag;
88:
89: main(argc, argv)
90: char *argv[];
91: {
92: int ngroups;
93: gid_t mygid, gidset[NGROUPS];
94: int i, gflag = 0, uflag = 0;
95: char ch;
96: extern char *optarg;
97: extern int optind, errno;
98:
99: while ((ch = getopt(argc, argv, "ugvq")) != EOF) {
100: switch(ch) {
101: case 'g':
102: gflag++;
103: break;
104: case 'u':
105: uflag++;
106: break;
107: case 'v':
108: vflag++;
109: break;
110: case 'q':
111: qflag++;
112: break;
113: default:
114: usage();
115: }
116: }
117: argc -= optind;
118: argv += optind;
119: if (!uflag && !gflag)
120: uflag++;
121: if (argc == 0) {
122: if (uflag)
123: showuid(getuid());
124: if (gflag) {
125: mygid = getgid();
126: ngroups = getgroups(NGROUPS, gidset);
127: if (ngroups < 0) {
128: perror("quota: getgroups");
129: exit(1);
130: }
131: showgid(mygid);
132: for (i = 0; i < ngroups; i++)
133: if (gidset[i] != mygid)
134: showgid(gidset[i]);
135: }
136: exit(0);
137: }
138: if (uflag && gflag)
139: usage();
140: if (uflag) {
141: for (; argc > 0; argc--, argv++) {
142: if (alldigits(*argv))
143: showuid(atoi(*argv));
144: else
145: showusrname(*argv);
146: }
147: exit(0);
148: }
149: if (gflag) {
150: for (; argc > 0; argc--, argv++) {
151: if (alldigits(*argv))
152: showgid(atoi(*argv));
153: else
154: showgrpname(*argv);
155: }
156: exit(0);
157: }
158: }
159:
160: usage()
161: {
162:
163: fprintf(stderr, "%s\n%s\n%s\n",
164: "Usage: quota [-guqv]",
165: "\tquota [-qv] -u username ...",
166: "\tquota [-qv] -g groupname ...");
167: exit(1);
168: }
169:
170: /*
171: * Print out quotas for a specified user identifier.
172: */
173: showuid(uid)
174: u_long uid;
175: {
176: struct passwd *pwd = getpwuid(uid);
177: u_long myuid;
178: char *name;
179:
180: if (pwd == NULL)
181: name = "(no account)";
182: else
183: name = pwd->pw_name;
184: myuid = getuid();
185: if (uid != myuid && myuid != 0) {
186: printf("quota: %s (uid %d): permission denied\n", name, uid);
187: return;
188: }
189: showquotas(USRQUOTA, uid, name);
190: }
191:
192: /*
193: * Print out quotas for a specifed user name.
194: */
195: showusrname(name)
196: char *name;
197: {
198: struct passwd *pwd = getpwnam(name);
199: u_long myuid;
200:
201: if (pwd == NULL) {
202: fprintf(stderr, "quota: %s: unknown user\n", name);
203: return;
204: }
205: myuid = getuid();
206: if (pwd->pw_uid != myuid && myuid != 0) {
207: fprintf(stderr, "quota: %s (uid %d): permission denied\n",
208: name, pwd->pw_uid);
209: return;
210: }
211: showquotas(USRQUOTA, pwd->pw_uid, name);
212: }
213:
214: /*
215: * Print out quotas for a specified group identifier.
216: */
217: showgid(gid)
218: u_long gid;
219: {
220: struct group *grp = getgrgid(gid);
221: int ngroups;
222: gid_t mygid, gidset[NGROUPS];
223: register int i;
224: char *name;
225:
226: if (grp == NULL)
227: name = "(no entry)";
228: else
229: name = grp->gr_name;
230: mygid = getgid();
231: ngroups = getgroups(NGROUPS, gidset);
232: if (ngroups < 0) {
233: perror("quota: getgroups");
234: return;
235: }
236: if (gid != mygid) {
237: for (i = 0; i < ngroups; i++)
238: if (gid == gidset[i])
239: break;
240: if (i >= ngroups && getuid() != 0) {
241: fprintf(stderr,
242: "quota: %s (gid %d): permission denied\n",
243: name, gid);
244: return;
245: }
246: }
247: showquotas(GRPQUOTA, gid, name);
248: }
249:
250: /*
251: * Print out quotas for a specifed group name.
252: */
253: showgrpname(name)
254: char *name;
255: {
256: struct group *grp = getgrnam(name);
257: int ngroups;
258: gid_t mygid, gidset[NGROUPS];
259: register int i;
260:
261: if (grp == NULL) {
262: fprintf(stderr, "quota: %s: unknown group\n", name);
263: return;
264: }
265: mygid = getgid();
266: ngroups = getgroups(NGROUPS, gidset);
267: if (ngroups < 0) {
268: perror("quota: getgroups");
269: return;
270: }
271: if (grp->gr_gid != mygid) {
272: for (i = 0; i < ngroups; i++)
273: if (grp->gr_gid == gidset[i])
274: break;
275: if (i >= ngroups && getuid() != 0) {
276: fprintf(stderr,
277: "quota: %s (gid %d): permission denied\n",
278: name, grp->gr_gid);
279: return;
280: }
281: }
282: showquotas(GRPQUOTA, grp->gr_gid, name);
283: }
284:
285: showquotas(type, id, name)
286: int type;
287: u_long id;
288: char *name;
289: {
290: register struct quotause *qup;
291: struct quotause *quplist;
292: char *msgi, *msgb, *nam;
293: int myuid, fd, lines = 0;
294: static int first;
295: static time_t now;
296:
297: if (now == 0)
298: time(&now);
299: quplist = getprivs(id, type);
300: for (qup = quplist; qup; qup = qup->next) {
301: if (!vflag &&
302: qup->dqblk.dqb_isoftlimit == 0 &&
303: qup->dqblk.dqb_ihardlimit == 0 &&
304: qup->dqblk.dqb_bsoftlimit == 0 &&
305: qup->dqblk.dqb_bhardlimit == 0)
306: continue;
307: msgi = (char *)0;
308: if (qup->dqblk.dqb_ihardlimit &&
309: qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit)
310: msgi = "File limit reached on";
311: else if (qup->dqblk.dqb_isoftlimit &&
312: qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
313: if (qup->dqblk.dqb_itime > now)
314: msgi = "In file grace period on";
315: else
316: msgi = "Over file quota on";
317: msgb = (char *)0;
318: if (qup->dqblk.dqb_bhardlimit &&
319: qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit)
320: msgb = "Block limit reached on";
321: else if (qup->dqblk.dqb_bsoftlimit &&
322: qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
323: if (qup->dqblk.dqb_btime > now)
324: msgb = "In block grace period on";
325: else
326: msgb = "Over block quota on";
327: if (qflag) {
328: if ((msgi != (char *)0 || msgb != (char *)0) &&
329: lines++ == 0)
330: heading(type, id, name, "");
331: if (msgi != (char *)0)
332: printf("\t%s %s\n", msgi, qup->fsname);
333: if (msgb != (char *)0)
334: printf("\t%s %s\n", msgb, qup->fsname);
335: continue;
336: }
337: if (vflag ||
338: qup->dqblk.dqb_curblocks ||
339: qup->dqblk.dqb_curinodes) {
340: if (lines++ == 0)
341: heading(type, id, name, "");
342: nam = qup->fsname;
343: if (strlen(qup->fsname) > 15) {
344: printf("%s\n", qup->fsname);
345: nam = "";
346: }
347: printf("%15s%8d%c%7d%8d%8s"
348: , nam
349: , dbtob(qup->dqblk.dqb_curblocks) / 1024
350: , (msgb == (char *)0) ? ' ' : '*'
351: , dbtob(qup->dqblk.dqb_bsoftlimit) / 1024
352: , dbtob(qup->dqblk.dqb_bhardlimit) / 1024
353: , (msgb == (char *)0) ? ""
354: : timeprt(qup->dqblk.dqb_btime));
355: printf("%8d%c%7d%8d%8s\n"
356: , qup->dqblk.dqb_curinodes
357: , (msgi == (char *)0) ? ' ' : '*'
358: , qup->dqblk.dqb_isoftlimit
359: , qup->dqblk.dqb_ihardlimit
360: , (msgi == (char *)0) ? ""
361: : timeprt(qup->dqblk.dqb_itime)
362: );
363: continue;
364: }
365: }
366: if (!qflag && lines == 0)
367: heading(type, id, name, "none");
368: }
369:
370: heading(type, id, name, tag)
371: int type;
372: u_long id;
373: char *name, *tag;
374: {
375:
376: printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type],
377: name, *qfextension[type], id, tag);
378: if (!qflag && tag[0] == '\0') {
379: printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
380: , "Filesystem"
381: , "blocks"
382: , "quota"
383: , "limit"
384: , "grace"
385: , "files"
386: , "quota"
387: , "limit"
388: , "grace"
389: );
390: }
391: }
392:
393: /*
394: * Calculate the grace period and return a printable string for it.
395: */
396: char *
397: timeprt(seconds)
398: time_t seconds;
399: {
400: time_t hours, minutes;
401: static char buf[20];
402: static time_t now;
403:
404: if (now == 0)
405: time(&now);
406: if (now > seconds)
407: return ("none");
408: seconds -= now;
409: minutes = (seconds + 30) / 60;
410: hours = (minutes + 30) / 60;
411: if (hours >= 36) {
412: sprintf(buf, "%ddays", (hours + 12) / 24);
413: return (buf);
414: }
415: if (minutes >= 60) {
416: sprintf(buf, "%2d:%d", minutes / 60, minutes % 60);
417: return (buf);
418: }
419: sprintf(buf, "%2d", minutes);
420: return (buf);
421: }
422:
423: /*
424: * Collect the requested quota information.
425: */
426: struct quotause *
427: getprivs(id, quotatype)
428: register long id;
429: int quotatype;
430: {
431: register struct quotause *qup, *quptail;
432: register struct fstab *fs;
433: struct quotause *quphead;
434: struct statfs *fst;
435: int nfst, i;
436:
437: qup = quphead = (struct quotause *)0;
438:
439: nfst = getmntinfo(&fst, MNT_WAIT);
440: if (nfst == 0) {
441: fprintf(stderr, "quota: no filesystems mounted!\n");
442: exit(2);
443: }
444: setfsent();
445: for (i=0; i<nfst; i++) {
446: if (qup == NULL) {
447: if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) {
448: fprintf(stderr, "quota: out of memory\n");
449: exit(2);
450: }
451: }
452: if (strncmp(fst[i].f_fstypename, "nfs", MFSNAMELEN) == 0) {
453: if (getnfsquota(&fst[i], NULL, qup, id, quotatype) == 0)
454: continue;
1.2 ! deraadt 455: } else if ((strncmp(fst[i].f_fstypename, "ffs",
! 456: MFSNAMELEN) == 0) || (strncmp(fst[i].f_fstypename, "ufs",
! 457: MFSNAMELEN) == 0)) {
1.1 deraadt 458: /*
459: * XXX
460: * UFS filesystems must be in /etc/fstab, and must
461: * indicate that they have quotas on (?!) This is quite
462: * unlike SunOS where quotas can be enabled/disabled
463: * on a filesystem independent of /etc/fstab, and it
464: * will still print quotas for them.
465: */
466: if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL)
467: continue;
468: if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0)
469: continue;
470: } else
471: continue;
472: strcpy(qup->fsname, fst[i].f_mntonname);
473: if (quphead == NULL)
474: quphead = qup;
475: else
476: quptail->next = qup;
477: quptail = qup;
478: quptail->next = 0;
479: qup = NULL;
480: }
481: if (qup)
482: free(qup);
483: endfsent();
484: return (quphead);
485: }
486:
487: /*
488: * Check to see if a particular quota is to be enabled.
489: */
490: ufshasquota(fs, type, qfnamep)
491: register struct fstab *fs;
492: int type;
493: char **qfnamep;
494: {
495: static char initname, usrname[100], grpname[100];
496: static char buf[BUFSIZ];
497: char *opt, *cp;
498:
499: if (!initname) {
500: sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
501: sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
502: initname = 1;
503: }
504: strcpy(buf, fs->fs_mntops);
505: for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
506: if (cp = index(opt, '='))
507: *cp++ = '\0';
508: if (type == USRQUOTA && strcmp(opt, usrname) == 0)
509: break;
510: if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
511: break;
512: }
513: if (!opt)
514: return (0);
515: if (cp) {
516: *qfnamep = cp;
517: return (1);
518: }
519: (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
520: *qfnamep = buf;
521: return (1);
522: }
523:
524: int
525: getufsquota(fst, fs, qup, id, quotatype)
526: struct statfs *fst;
527: struct fstab *fs;
528: struct quotause *qup;
529: long id;
530: int quotatype;
531: {
532: char *qfpathname;
533: int fd, qcmd;
534:
535: qcmd = QCMD(Q_GETQUOTA, quotatype);
536: if (!ufshasquota(fs, quotatype, &qfpathname))
537: return (0);
538:
539: if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
540: if ((fd = open(qfpathname, O_RDONLY)) < 0) {
541: perror(qfpathname);
542: return (0);
543: }
544: (void) lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET);
545: switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
546: case 0: /* EOF */
547: /*
548: * Convert implicit 0 quota (EOF)
549: * into an explicit one (zero'ed dqblk)
550: */
551: bzero((caddr_t)&qup->dqblk, sizeof(struct dqblk));
552: break;
553: case sizeof(struct dqblk): /* OK */
554: break;
555: default: /* ERROR */
556: fprintf(stderr, "quota: read error");
557: perror(qfpathname);
558: close(fd);
559: return (0);
560: }
561: close(fd);
562: }
563: return (1);
564: }
565:
566: int
567: getnfsquota(fst, fs, qup, id, quotatype)
568: struct statfs *fst;
569: struct fstab *fs;
570: struct quotause *qup;
571: long id;
572: int quotatype;
573: {
574: struct getquota_args gq_args;
575: struct getquota_rslt gq_rslt;
576: struct dqblk *dqp = &qup->dqblk;
577: struct timeval tv;
578: char *cp;
579:
580: if (fst->f_flags & MNT_LOCAL)
581: return (0);
582:
583: /*
584: * rpc.rquotad does not support group quotas
585: */
586: if (quotatype != USRQUOTA)
587: return (0);
588:
589: /*
590: * must be some form of "hostname:/path"
591: */
592: cp = strchr(fst->f_mntfromname, ':');
593: if (cp == NULL) {
594: fprintf(stderr, "cannot find hostname for %s\n",
595: fst->f_mntfromname);
596: return (0);
597: }
598:
599: *cp = '\0';
600: if (*(cp+1) != '/') {
601: *cp = ':';
602: return (0);
603: }
604:
605: gq_args.gqa_pathp = cp + 1;
606: gq_args.gqa_uid = id;
607: if (callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
608: RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args,
609: xdr_getquota_rslt, &gq_rslt) != 0) {
610: *cp = ':';
611: return (0);
612: }
613:
614: switch (gq_rslt.status) {
615: case Q_NOQUOTA:
616: break;
617: case Q_EPERM:
618: fprintf(stderr, "quota permission error, host: %s\n",
619: fst->f_mntfromname);
620: break;
621: case Q_OK:
622: gettimeofday(&tv, NULL);
623: /* blocks*/
624: dqp->dqb_bhardlimit =
625: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
626: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
627: dqp->dqb_bsoftlimit =
628: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
629: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
630: dqp->dqb_curblocks =
631: gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
632: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
633: /* inodes */
634: dqp->dqb_ihardlimit =
635: gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
636: dqp->dqb_isoftlimit =
637: gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
638: dqp->dqb_curinodes =
639: gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
640: /* grace times */
641: dqp->dqb_btime =
642: tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
643: dqp->dqb_itime =
644: tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
645: *cp = ':';
646: return (1);
647: default:
648: fprintf(stderr, "bad rpc result, host: %s\n",
649: fst->f_mntfromname);
650: break;
651: }
652: *cp = ':';
653: return (0);
654: }
655:
656: int
657: callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
658: char *host;
659: xdrproc_t inproc, outproc;
660: char *in, *out;
661: {
662: struct sockaddr_in server_addr;
663: enum clnt_stat clnt_stat;
664: struct hostent *hp;
665: struct timeval timeout, tottimeout;
666:
667: CLIENT *client = NULL;
668: int socket = RPC_ANYSOCK;
669:
670: if ((hp = gethostbyname(host)) == NULL)
671: return ((int) RPC_UNKNOWNHOST);
672: timeout.tv_usec = 0;
673: timeout.tv_sec = 6;
674: bcopy(hp->h_addr, &server_addr.sin_addr, hp->h_length);
675: server_addr.sin_family = AF_INET;
676: server_addr.sin_port = 0;
677:
678: if ((client = clntudp_create(&server_addr, prognum,
679: versnum, timeout, &socket)) == NULL)
680: return ((int) rpc_createerr.cf_stat);
681:
682: client->cl_auth = authunix_create_default();
683: tottimeout.tv_sec = 25;
684: tottimeout.tv_usec = 0;
685: clnt_stat = clnt_call(client, procnum, inproc, in,
686: outproc, out, tottimeout);
687:
688: return ((int) clnt_stat);
689: }
690:
691: alldigits(s)
692: register char *s;
693: {
694: register c;
695:
696: c = *s++;
697: do {
698: if (!isdigit(c))
699: return (0);
700: } while (c = *s++);
701: return (1);
702: }