Annotation of src/usr.bin/quota/quota.c, Revision 1.1.1.1
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";*/
45: static char rcsid[] = "$Id: quota.c,v 1.9 1995/06/18 11:00:49 cgd Exp $";
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;
455: } else if (strncmp(fst[i].f_fstypename, "ufs",
456: MFSNAMELEN) == 0) {
457: /*
458: * XXX
459: * UFS filesystems must be in /etc/fstab, and must
460: * indicate that they have quotas on (?!) This is quite
461: * unlike SunOS where quotas can be enabled/disabled
462: * on a filesystem independent of /etc/fstab, and it
463: * will still print quotas for them.
464: */
465: if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL)
466: continue;
467: if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0)
468: continue;
469: } else
470: continue;
471: strcpy(qup->fsname, fst[i].f_mntonname);
472: if (quphead == NULL)
473: quphead = qup;
474: else
475: quptail->next = qup;
476: quptail = qup;
477: quptail->next = 0;
478: qup = NULL;
479: }
480: if (qup)
481: free(qup);
482: endfsent();
483: return (quphead);
484: }
485:
486: /*
487: * Check to see if a particular quota is to be enabled.
488: */
489: ufshasquota(fs, type, qfnamep)
490: register struct fstab *fs;
491: int type;
492: char **qfnamep;
493: {
494: static char initname, usrname[100], grpname[100];
495: static char buf[BUFSIZ];
496: char *opt, *cp;
497:
498: if (!initname) {
499: sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
500: sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
501: initname = 1;
502: }
503: strcpy(buf, fs->fs_mntops);
504: for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
505: if (cp = index(opt, '='))
506: *cp++ = '\0';
507: if (type == USRQUOTA && strcmp(opt, usrname) == 0)
508: break;
509: if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
510: break;
511: }
512: if (!opt)
513: return (0);
514: if (cp) {
515: *qfnamep = cp;
516: return (1);
517: }
518: (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
519: *qfnamep = buf;
520: return (1);
521: }
522:
523: int
524: getufsquota(fst, fs, qup, id, quotatype)
525: struct statfs *fst;
526: struct fstab *fs;
527: struct quotause *qup;
528: long id;
529: int quotatype;
530: {
531: char *qfpathname;
532: int fd, qcmd;
533:
534: qcmd = QCMD(Q_GETQUOTA, quotatype);
535: if (!ufshasquota(fs, quotatype, &qfpathname))
536: return (0);
537:
538: if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
539: if ((fd = open(qfpathname, O_RDONLY)) < 0) {
540: perror(qfpathname);
541: return (0);
542: }
543: (void) lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET);
544: switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
545: case 0: /* EOF */
546: /*
547: * Convert implicit 0 quota (EOF)
548: * into an explicit one (zero'ed dqblk)
549: */
550: bzero((caddr_t)&qup->dqblk, sizeof(struct dqblk));
551: break;
552: case sizeof(struct dqblk): /* OK */
553: break;
554: default: /* ERROR */
555: fprintf(stderr, "quota: read error");
556: perror(qfpathname);
557: close(fd);
558: return (0);
559: }
560: close(fd);
561: }
562: return (1);
563: }
564:
565: int
566: getnfsquota(fst, fs, qup, id, quotatype)
567: struct statfs *fst;
568: struct fstab *fs;
569: struct quotause *qup;
570: long id;
571: int quotatype;
572: {
573: struct getquota_args gq_args;
574: struct getquota_rslt gq_rslt;
575: struct dqblk *dqp = &qup->dqblk;
576: struct timeval tv;
577: char *cp;
578:
579: if (fst->f_flags & MNT_LOCAL)
580: return (0);
581:
582: /*
583: * rpc.rquotad does not support group quotas
584: */
585: if (quotatype != USRQUOTA)
586: return (0);
587:
588: /*
589: * must be some form of "hostname:/path"
590: */
591: cp = strchr(fst->f_mntfromname, ':');
592: if (cp == NULL) {
593: fprintf(stderr, "cannot find hostname for %s\n",
594: fst->f_mntfromname);
595: return (0);
596: }
597:
598: *cp = '\0';
599: if (*(cp+1) != '/') {
600: *cp = ':';
601: return (0);
602: }
603:
604: gq_args.gqa_pathp = cp + 1;
605: gq_args.gqa_uid = id;
606: if (callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
607: RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args,
608: xdr_getquota_rslt, &gq_rslt) != 0) {
609: *cp = ':';
610: return (0);
611: }
612:
613: switch (gq_rslt.status) {
614: case Q_NOQUOTA:
615: break;
616: case Q_EPERM:
617: fprintf(stderr, "quota permission error, host: %s\n",
618: fst->f_mntfromname);
619: break;
620: case Q_OK:
621: gettimeofday(&tv, NULL);
622: /* blocks*/
623: dqp->dqb_bhardlimit =
624: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
625: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
626: dqp->dqb_bsoftlimit =
627: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
628: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
629: dqp->dqb_curblocks =
630: gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
631: gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
632: /* inodes */
633: dqp->dqb_ihardlimit =
634: gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
635: dqp->dqb_isoftlimit =
636: gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
637: dqp->dqb_curinodes =
638: gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
639: /* grace times */
640: dqp->dqb_btime =
641: tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
642: dqp->dqb_itime =
643: tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
644: *cp = ':';
645: return (1);
646: default:
647: fprintf(stderr, "bad rpc result, host: %s\n",
648: fst->f_mntfromname);
649: break;
650: }
651: *cp = ':';
652: return (0);
653: }
654:
655: int
656: callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
657: char *host;
658: xdrproc_t inproc, outproc;
659: char *in, *out;
660: {
661: struct sockaddr_in server_addr;
662: enum clnt_stat clnt_stat;
663: struct hostent *hp;
664: struct timeval timeout, tottimeout;
665:
666: CLIENT *client = NULL;
667: int socket = RPC_ANYSOCK;
668:
669: if ((hp = gethostbyname(host)) == NULL)
670: return ((int) RPC_UNKNOWNHOST);
671: timeout.tv_usec = 0;
672: timeout.tv_sec = 6;
673: bcopy(hp->h_addr, &server_addr.sin_addr, hp->h_length);
674: server_addr.sin_family = AF_INET;
675: server_addr.sin_port = 0;
676:
677: if ((client = clntudp_create(&server_addr, prognum,
678: versnum, timeout, &socket)) == NULL)
679: return ((int) rpc_createerr.cf_stat);
680:
681: client->cl_auth = authunix_create_default();
682: tottimeout.tv_sec = 25;
683: tottimeout.tv_usec = 0;
684: clnt_stat = clnt_call(client, procnum, inproc, in,
685: outproc, out, tottimeout);
686:
687: return ((int) clnt_stat);
688: }
689:
690: alldigits(s)
691: register char *s;
692: {
693: register c;
694:
695: c = *s++;
696: do {
697: if (!isdigit(c))
698: return (0);
699: } while (c = *s++);
700: return (1);
701: }