|
|
1.1 root 1: /*
2: * Copyright (c) 1980, 1990 Regents of the University of California.
3: * 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 are permitted provided
9: * that: (1) source distributions retain this entire copyright notice and
10: * comment, and (2) distributions including binaries display the following
11: * acknowledgement: ``This product includes software developed by the
12: * University of California, Berkeley and its contributors'' in the
13: * documentation or other materials provided with the distribution and in
14: * all advertising materials mentioning features or use of this software.
15: * Neither the name of the University nor the names of its contributors may
16: * be used to endorse or promote products derived from this software without
17: * specific prior written permission.
18: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
19: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
20: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21: */
22:
23: #ifndef lint
24: char copyright[] =
25: "@(#) Copyright (c) 1980, 1990 Regents of the University of California.\n\
26: All rights reserved.\n";
27: #endif /* not lint */
28:
29: #ifndef lint
30: static char sccsid[] = "@(#)edquota.c 5.14 (Berkeley) 6/19/90";
31: #endif /* not lint */
32:
33: /*
34: * Disk quota editor.
35: */
36: #include <sys/param.h>
37: #include <sys/stat.h>
38: #include <sys/file.h>
39: #include <sys/wait.h>
40: #include <ufs/quota.h>
41: #include <errno.h>
42: #include <fstab.h>
43: #include <pwd.h>
44: #include <grp.h>
45: #include <ctype.h>
46: #include <stdio.h>
47: #include <string.h>
48: #include "pathnames.h"
49:
50: char tmpfil[] = _PATH_TMP;
51:
52: struct quotause {
53: struct quotause *next;
54: long flags;
55: struct dqblk dqblk;
56: char fsname[MAXPATHLEN + 1];
57: char qfname[1]; /* actually longer */
58: } *getprivs();
59: #define FOUND 0x01
60:
61: main(argc, argv)
62: register char **argv;
63: int argc;
64: {
65: register struct quotause *qup, *protoprivs, *curprivs;
66: extern char *optarg;
67: extern int optind;
68: register long id, protoid;
69: register int quotatype, tmpfd;
70: char *protoname, ch;
71: int tflag = 0, pflag = 0;
72:
73: if (argc < 2)
74: usage();
75: if (getuid()) {
76: fprintf(stderr, "edquota: permission denied\n");
77: exit(1);
78: }
79: quotatype = USRQUOTA;
80: while ((ch = getopt(argc, argv, "ugtp:")) != EOF) {
81: switch(ch) {
82: case 'p':
83: protoname = optarg;
84: pflag++;
85: break;
86: case 'g':
87: quotatype = GRPQUOTA;
88: break;
89: case 'u':
90: quotatype = USRQUOTA;
91: break;
92: case 't':
93: tflag++;
94: break;
95: default:
96: usage();
97: }
98: }
99: argc -= optind;
100: argv += optind;
101: if (pflag) {
102: if ((protoid = getentry(protoname, quotatype)) == -1)
103: exit(1);
104: protoprivs = getprivs(protoid, quotatype);
105: for (qup = protoprivs; qup; qup = qup->next) {
106: qup->dqblk.dqb_btime = 0;
107: qup->dqblk.dqb_itime = 0;
108: }
109: while (argc-- > 0) {
110: if ((id = getentry(*argv++, quotatype)) < 0)
111: continue;
112: putprivs(id, quotatype, protoprivs);
113: }
114: exit(0);
115: }
116: tmpfd = mkstemp(tmpfil);
117: fchown(tmpfd, getuid(), getgid());
118: if (tflag) {
119: protoprivs = getprivs(0, quotatype);
120: if (writetimes(protoprivs, tmpfd, quotatype) == 0)
121: exit(1);
122: if (editit(tmpfil) && readtimes(protoprivs, tmpfd))
123: putprivs(0, quotatype, protoprivs);
124: freeprivs(protoprivs);
125: exit(0);
126: }
127: for ( ; argc > 0; argc--, argv++) {
128: if ((id = getentry(*argv, quotatype)) == -1)
129: continue;
130: curprivs = getprivs(id, quotatype);
131: if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
132: continue;
133: if (editit(tmpfil) && readprivs(curprivs, tmpfd))
134: putprivs(id, quotatype, curprivs);
135: freeprivs(curprivs);
136: }
137: close(tmpfd);
138: unlink(tmpfil);
139: exit(0);
140: }
141:
142: usage()
143: {
144: fprintf(stderr, "%s%s%s%s",
145: "Usage: edquota [-u] [-p username] username ...\n",
146: "\tedquota -g [-p groupname] groupname ...\n",
147: "\tedquota [-u] -t\n", "\tedquota -g -t\n");
148: exit(1);
149: }
150:
151: /*
152: * This routine converts a name for a particular quota type to
153: * an identifier. This routine must agree with the kernel routine
154: * getinoquota as to the interpretation of quota types.
155: */
156: getentry(name, quotatype)
157: char *name;
158: int quotatype;
159: {
160: struct passwd *pw;
161: struct group *gr;
162:
163: if (alldigits(name))
164: return (atoi(name));
165: switch(quotatype) {
166: case USRQUOTA:
167: if (pw = getpwnam(name))
168: return (pw->pw_uid);
169: fprintf(stderr, "%s: no such user\n", name);
170: break;
171: case GRPQUOTA:
172: if (gr = getgrnam(name))
173: return (gr->gr_gid);
174: fprintf(stderr, "%s: no such group\n", name);
175: break;
176: default:
177: fprintf(stderr, "%d: unknown quota type\n", quotatype);
178: break;
179: }
180: sleep(1);
181: return (-1);
182: }
183:
184: /*
185: * Collect the requested quota information.
186: */
187: struct quotause *
188: getprivs(id, quotatype)
189: register long id;
190: int quotatype;
191: {
192: register struct fstab *fs;
193: register struct quotause *qup, *quptail;
194: struct quotause *quphead;
195: int qcmd, qupsize, fd;
196: char *qfpathname;
197: static int warned = 0;
198: extern int errno;
199:
200: setfsent();
201: quphead = (struct quotause *)0;
202: qcmd = QCMD(Q_GETQUOTA, quotatype);
203: while (fs = getfsent()) {
204: if (strcmp(fs->fs_vfstype, "ufs"))
205: continue;
206: if (!hasquota(fs, quotatype, &qfpathname))
207: continue;
208: qupsize = sizeof(*qup) + strlen(qfpathname);
209: if ((qup = (struct quotause *)malloc(qupsize)) == NULL) {
210: fprintf(stderr, "edquota: out of memory\n");
211: exit(2);
212: }
213: if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
214: if (errno == EOPNOTSUPP && !warned) {
215: warned++;
216: fprintf(stderr, "Warning: %s\n",
217: "Quotas are not compiled into this kernel");
218: sleep(3);
219: }
220: if ((fd = open(qfpathname, O_RDONLY)) < 0) {
221: fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
222: if (fd < 0 && errno != ENOENT) {
223: perror(qfpathname);
224: free(qup);
225: continue;
226: }
227: fprintf(stderr, "Creating quota file %s\n",
228: qfpathname);
229: sleep(3);
230: (void) fchown(fd, getuid(),
231: getentry(quotagroup, GRPQUOTA));
232: (void) fchmod(fd, 0640);
233: }
234: lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET);
235: switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
236: case 0: /* EOF */
237: /*
238: * Convert implicit 0 quota (EOF)
239: * into an explicit one (zero'ed dqblk)
240: */
241: bzero((caddr_t)&qup->dqblk,
242: sizeof(struct dqblk));
243: break;
244:
245: case sizeof(struct dqblk): /* OK */
246: break;
247:
248: default: /* ERROR */
249: fprintf(stderr, "edquota: read error in ");
250: perror(qfpathname);
251: close(fd);
252: free(qup);
253: continue;
254: }
255: close(fd);
256: }
257: strcpy(qup->qfname, qfpathname);
258: strcpy(qup->fsname, fs->fs_file);
259: if (quphead == NULL)
260: quphead = qup;
261: else
262: quptail->next = qup;
263: quptail = qup;
264: qup->next = 0;
265: }
266: endfsent();
267: return (quphead);
268: }
269:
270: /*
271: * Store the requested quota information.
272: */
273: putprivs(id, quotatype, quplist)
274: long id;
275: int quotatype;
276: struct quotause *quplist;
277: {
278: register struct quotause *qup;
279: int qcmd, fd;
280:
281: qcmd = QCMD(Q_SETQUOTA, quotatype);
282: for (qup = quplist; qup; qup = qup->next) {
283: if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
284: continue;
285: if ((fd = open(qup->qfname, O_WRONLY)) < 0) {
286: perror(qup->qfname);
287: } else {
288: lseek(fd, (long)id * (long)sizeof (struct dqblk), 0);
289: if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
290: sizeof (struct dqblk)) {
291: fprintf(stderr, "edquota: ");
292: perror(qup->qfname);
293: }
294: close(fd);
295: }
296: }
297: }
298:
299: /*
300: * Take a list of priviledges and get it edited.
301: */
302: editit(tmpfile)
303: char *tmpfile;
304: {
305: long omask;
306: int pid, stat;
307: extern char *getenv();
308:
309: omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
310: top:
311: if ((pid = fork()) < 0) {
312: extern errno;
313:
314: if (errno == EPROCLIM) {
315: fprintf(stderr, "You have too many processes\n");
316: return(0);
317: }
318: if (errno == EAGAIN) {
319: sleep(1);
320: goto top;
321: }
322: perror("fork");
323: return (0);
324: }
325: if (pid == 0) {
326: register char *ed;
327:
328: sigsetmask(omask);
329: setgid(getgid());
330: setuid(getuid());
331: if ((ed = getenv("EDITOR")) == (char *)0)
332: ed = _PATH_VI;
333: execlp(ed, ed, tmpfile, 0);
334: perror(ed);
335: exit(1);
336: }
337: waitpid(pid, &stat, 0);
338: sigsetmask(omask);
339: if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0)
340: return (0);
341: return (1);
342: }
343:
344: /*
345: * Convert a quotause list to an ASCII file.
346: */
347: writeprivs(quplist, outfd, name, quotatype)
348: struct quotause *quplist;
349: int outfd;
350: char *name;
351: int quotatype;
352: {
353: register struct quotause *qup;
354: FILE *fd;
355:
356: ftruncate(outfd, 0);
357: lseek(outfd, 0, L_SET);
358: if ((fd = fdopen(dup(outfd), "w")) == NULL) {
359: fprintf(stderr, "edquota: ");
360: perror(tmpfil);
361: exit(1);
362: }
363: fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
364: for (qup = quplist; qup; qup = qup->next) {
365: fprintf(fd, "%s: %s %d, limits (soft = %d, hard = %d)\n",
366: qup->fsname, "blocks in use:",
367: dbtob(qup->dqblk.dqb_curblocks) / 1024,
368: dbtob(qup->dqblk.dqb_bsoftlimit) / 1024,
369: dbtob(qup->dqblk.dqb_bhardlimit) / 1024);
370: fprintf(fd, "%s %d, limits (soft = %d, hard = %d)\n",
371: "\tinodes in use:", qup->dqblk.dqb_curinodes,
372: qup->dqblk.dqb_isoftlimit, qup->dqblk.dqb_ihardlimit);
373: }
374: fclose(fd);
375: return (1);
376: }
377:
378: /*
379: * Merge changes to an ASCII file into a quotause list.
380: */
381: readprivs(quplist, infd)
382: struct quotause *quplist;
383: int infd;
384: {
385: register struct quotause *qup;
386: FILE *fd;
387: int cnt;
388: register char *cp;
389: struct dqblk dqblk;
390: char *fsp, line1[BUFSIZ], line2[BUFSIZ];
391:
392: lseek(infd, 0, L_SET);
393: fd = fdopen(dup(infd), "r");
394: if (fd == NULL) {
395: fprintf(stderr, "Can't re-read temp file!!\n");
396: return (0);
397: }
398: /*
399: * Discard title line, then read pairs of lines to process.
400: */
401: (void) fgets(line1, sizeof (line1), fd);
402: while (fgets(line1, sizeof (line1), fd) != NULL &&
403: fgets(line2, sizeof (line2), fd) != NULL) {
404: if ((fsp = strtok(line1, " \t:")) == NULL) {
405: fprintf(stderr, "%s: bad format\n", line1);
406: return (0);
407: }
408: if ((cp = strtok((char *)0, "\n")) == NULL) {
409: fprintf(stderr, "%s: %s: bad format\n", fsp,
410: &fsp[strlen(fsp) + 1]);
411: return (0);
412: }
413: cnt = sscanf(cp,
414: " blocks in use: %d, limits (soft = %d, hard = %d)",
415: &dqblk.dqb_curblocks, &dqblk.dqb_bsoftlimit,
416: &dqblk.dqb_bhardlimit);
417: if (cnt != 3) {
418: fprintf(stderr, "%s:%s: bad format\n", fsp, cp);
419: return (0);
420: }
421: dqblk.dqb_curblocks = btodb(dqblk.dqb_curblocks * 1024);
422: dqblk.dqb_bsoftlimit = btodb(dqblk.dqb_bsoftlimit * 1024);
423: dqblk.dqb_bhardlimit = btodb(dqblk.dqb_bhardlimit * 1024);
424: if ((cp = strtok(line2, "\n")) == NULL) {
425: fprintf(stderr, "%s: %s: bad format\n", fsp, line2);
426: return (0);
427: }
428: cnt = sscanf(cp,
429: "\tinodes in use: %d, limits (soft = %d, hard = %d)",
430: &dqblk.dqb_curinodes, &dqblk.dqb_isoftlimit,
431: &dqblk.dqb_ihardlimit);
432: if (cnt != 3) {
433: fprintf(stderr, "%s: %s: bad format\n", fsp, line2);
434: return (0);
435: }
436: for (qup = quplist; qup; qup = qup->next) {
437: if (strcmp(fsp, qup->fsname))
438: continue;
439: /*
440: * Cause time limit to be reset when the quota
441: * is next used if previously had no soft limit
442: * or were under it, but now have a soft limit
443: * and are over it.
444: */
445: if (dqblk.dqb_bsoftlimit &&
446: qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
447: (qup->dqblk.dqb_bsoftlimit == 0 ||
448: qup->dqblk.dqb_curblocks <
449: qup->dqblk.dqb_bsoftlimit))
450: qup->dqblk.dqb_btime = 0;
451: if (dqblk.dqb_isoftlimit &&
452: qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
453: (qup->dqblk.dqb_isoftlimit == 0 ||
454: qup->dqblk.dqb_curinodes <
455: qup->dqblk.dqb_isoftlimit))
456: qup->dqblk.dqb_itime = 0;
457: qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
458: qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
459: qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
460: qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
461: qup->flags |= FOUND;
462: if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
463: dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
464: break;
465: fprintf(stderr,
466: "%s: cannot change current allocation\n", fsp);
467: break;
468: }
469: }
470: fclose(fd);
471: /*
472: * Disable quotas for any filesystems that have not been found.
473: */
474: for (qup = quplist; qup; qup = qup->next) {
475: if (qup->flags & FOUND) {
476: qup->flags &= ~FOUND;
477: continue;
478: }
479: qup->dqblk.dqb_bsoftlimit = 0;
480: qup->dqblk.dqb_bhardlimit = 0;
481: qup->dqblk.dqb_isoftlimit = 0;
482: qup->dqblk.dqb_ihardlimit = 0;
483: }
484: return (1);
485: }
486:
487: /*
488: * Convert a quotause list to an ASCII file of grace times.
489: */
490: writetimes(quplist, outfd, quotatype)
491: struct quotause *quplist;
492: int outfd;
493: int quotatype;
494: {
495: register struct quotause *qup;
496: char *cvtstoa();
497: FILE *fd;
498:
499: ftruncate(outfd, 0);
500: lseek(outfd, 0, L_SET);
501: if ((fd = fdopen(dup(outfd), "w")) == NULL) {
502: fprintf(stderr, "edquota: ");
503: perror(tmpfil);
504: exit(1);
505: }
506: fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
507: fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
508: qfextension[quotatype]);
509: for (qup = quplist; qup; qup = qup->next) {
510: fprintf(fd, "%s: block grace period: %s, ",
511: qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
512: fprintf(fd, "file grace period: %s\n",
513: cvtstoa(qup->dqblk.dqb_itime));
514: }
515: fclose(fd);
516: return (1);
517: }
518:
519: /*
520: * Merge changes of grace times in an ASCII file into a quotause list.
521: */
522: readtimes(quplist, infd)
523: struct quotause *quplist;
524: int infd;
525: {
526: register struct quotause *qup;
527: FILE *fd;
528: int cnt;
529: register char *cp;
530: time_t itime, btime, iseconds, bseconds;
531: char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
532:
533: lseek(infd, 0, L_SET);
534: fd = fdopen(dup(infd), "r");
535: if (fd == NULL) {
536: fprintf(stderr, "Can't re-read temp file!!\n");
537: return (0);
538: }
539: /*
540: * Discard two title lines, then read lines to process.
541: */
542: (void) fgets(line1, sizeof (line1), fd);
543: (void) fgets(line1, sizeof (line1), fd);
544: while (fgets(line1, sizeof (line1), fd) != NULL) {
545: if ((fsp = strtok(line1, " \t:")) == NULL) {
546: fprintf(stderr, "%s: bad format\n", line1);
547: return (0);
548: }
549: if ((cp = strtok((char *)0, "\n")) == NULL) {
550: fprintf(stderr, "%s: %s: bad format\n", fsp,
551: &fsp[strlen(fsp) + 1]);
552: return (0);
553: }
554: cnt = sscanf(cp,
555: " block grace period: %d %s file grace period: %d %s",
556: &btime, bunits, &itime, iunits);
557: if (cnt != 4) {
558: fprintf(stderr, "%s:%s: bad format\n", fsp, cp);
559: return (0);
560: }
561: if (cvtatos(btime, bunits, &bseconds) == 0)
562: return (0);
563: if (cvtatos(itime, iunits, &iseconds) == 0)
564: return (0);
565: for (qup = quplist; qup; qup = qup->next) {
566: if (strcmp(fsp, qup->fsname))
567: continue;
568: qup->dqblk.dqb_btime = bseconds;
569: qup->dqblk.dqb_itime = iseconds;
570: qup->flags |= FOUND;
571: break;
572: }
573: }
574: fclose(fd);
575: /*
576: * reset default grace periods for any filesystems
577: * that have not been found.
578: */
579: for (qup = quplist; qup; qup = qup->next) {
580: if (qup->flags & FOUND) {
581: qup->flags &= ~FOUND;
582: continue;
583: }
584: qup->dqblk.dqb_btime = 0;
585: qup->dqblk.dqb_itime = 0;
586: }
587: return (1);
588: }
589:
590: /*
591: * Convert seconds to ASCII times.
592: */
593: char *
594: cvtstoa(time)
595: time_t time;
596: {
597: static char buf[20];
598:
599: if (time % (24 * 60 * 60) == 0) {
600: time /= 24 * 60 * 60;
601: sprintf(buf, "%d day%s", time, time == 1 ? "" : "s");
602: } else if (time % (60 * 60) == 0) {
603: time /= 60 * 60;
604: sprintf(buf, "%d hour%s", time, time == 1 ? "" : "s");
605: } else if (time % 60 == 0) {
606: time /= 60;
607: sprintf(buf, "%d minute%s", time, time == 1 ? "" : "s");
608: } else
609: sprintf(buf, "%d second%s", time, time == 1 ? "" : "s");
610: return (buf);
611: }
612:
613: /*
614: * Convert ASCII input times to seconds.
615: */
616: cvtatos(time, units, seconds)
617: time_t time;
618: char *units;
619: time_t *seconds;
620: {
621:
622: if (bcmp(units, "second", 6) == 0)
623: *seconds = time;
624: else if (bcmp(units, "minute", 6) == 0)
625: *seconds = time * 60;
626: else if (bcmp(units, "hour", 4) == 0)
627: *seconds = time * 60 * 60;
628: else if (bcmp(units, "day", 3) == 0)
629: *seconds = time * 24 * 60 * 60;
630: else {
631: printf("%s: bad units, specify %s\n", units,
632: "days, hours, minutes, or seconds");
633: return (0);
634: }
635: return (1);
636: }
637:
638: /*
639: * Free a list of quotause structures.
640: */
641: freeprivs(quplist)
642: struct quotause *quplist;
643: {
644: register struct quotause *qup, *nextqup;
645:
646: for (qup = quplist; qup; qup = nextqup) {
647: nextqup = qup->next;
648: free(qup);
649: }
650: }
651:
652: /*
653: * Check whether a string is completely composed of digits.
654: */
655: alldigits(s)
656: register char *s;
657: {
658: register c;
659:
660: c = *s++;
661: do {
662: if (!isdigit(c))
663: return (0);
664: } while (c = *s++);
665: return (1);
666: }
667:
668: /*
669: * Check to see if a particular quota is to be enabled.
670: */
671: hasquota(fs, type, qfnamep)
672: register struct fstab *fs;
673: int type;
674: char **qfnamep;
675: {
676: register char *opt;
677: char *cp, *index(), *strtok();
678: static char initname, usrname[100], grpname[100];
679: static char buf[BUFSIZ];
680:
681: if (!initname) {
682: sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
683: sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
684: initname = 1;
685: }
686: strcpy(buf, fs->fs_mntops);
687: for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
688: if (cp = index(opt, '='))
689: *cp++ = '\0';
690: if (type == USRQUOTA && strcmp(opt, usrname) == 0)
691: break;
692: if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
693: break;
694: }
695: if (!opt)
696: return (0);
697: if (cp) {
698: *qfnamep = cp;
699: return (1);
700: }
701: (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
702: *qfnamep = buf;
703: return (1);
704: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.