edquota.c revision 166485
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1980, 1990, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * This code is derived from software contributed to Berkeley by
61553Srgrimes * Robert Elz at The University of Melbourne.
71553Srgrimes *
81553Srgrimes * Redistribution and use in source and binary forms, with or without
91553Srgrimes * modification, are permitted provided that the following conditions
101553Srgrimes * are met:
111553Srgrimes * 1. Redistributions of source code must retain the above copyright
121553Srgrimes *    notice, this list of conditions and the following disclaimer.
131553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141553Srgrimes *    notice, this list of conditions and the following disclaimer in the
151553Srgrimes *    documentation and/or other materials provided with the distribution.
161553Srgrimes * 4. Neither the name of the University nor the names of its contributors
171553Srgrimes *    may be used to endorse or promote products derived from this software
181553Srgrimes *    without specific prior written permission.
191553Srgrimes *
201553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301553Srgrimes * SUCH DAMAGE.
311553Srgrimes */
321553Srgrimes
33114601Sobrien#if 0
341553Srgrimes#ifndef lint
3529529Scharnierstatic const char copyright[] =
361553Srgrimes"@(#) Copyright (c) 1980, 1990, 1993\n\
371553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381553Srgrimes#endif /* not lint */
391553Srgrimes
401553Srgrimes#ifndef lint
411553Srgrimesstatic char sccsid[] = "@(#)edquota.c	8.1 (Berkeley) 6/6/93";
42114601Sobrien#endif /* not lint */
4329529Scharnier#endif
44162295Scharnier
45114601Sobrien#include <sys/cdefs.h>
46114601Sobrien__FBSDID("$FreeBSD: head/usr.sbin/edquota/edquota.c 166485 2007-02-04 06:33:15Z mpp $");
471553Srgrimes
481553Srgrimes/*
491553Srgrimes * Disk quota editor.
501553Srgrimes */
51162295Scharnier
521553Srgrimes#include <sys/param.h>
531553Srgrimes#include <sys/stat.h>
541553Srgrimes#include <sys/file.h>
55166485Smpp#include <sys/mount.h>
561553Srgrimes#include <sys/wait.h>
571553Srgrimes#include <ufs/ufs/quota.h>
5829529Scharnier#include <ctype.h>
5929529Scharnier#include <err.h>
601553Srgrimes#include <errno.h>
611553Srgrimes#include <fstab.h>
6229529Scharnier#include <grp.h>
631553Srgrimes#include <pwd.h>
6429529Scharnier#include <signal.h>
651553Srgrimes#include <stdio.h>
6629529Scharnier#include <stdlib.h>
671553Srgrimes#include <string.h>
681553Srgrimes#include <unistd.h>
691553Srgrimes#include "pathnames.h"
701553Srgrimes
7187596Smikehconst char *qfname = QUOTAFILENAME;
7287596Smikehconst char *qfextension[] = INITQFNAMES;
7387596Smikehconst char *quotagroup = QUOTAGROUP;
741553Srgrimeschar tmpfil[] = _PATH_TMP;
751553Srgrimes
761553Srgrimesstruct quotause {
771553Srgrimes	struct	quotause *next;
781553Srgrimes	long	flags;
791553Srgrimes	struct	dqblk dqblk;
801553Srgrimes	char	fsname[MAXPATHLEN + 1];
811553Srgrimes	char	qfname[1];	/* actually longer */
8229529Scharnier};
831553Srgrimes#define	FOUND	0x01
841553Srgrimes
8599800Salfredint alldigits(const char *s);
8699800Salfredint cvtatos(time_t, char *, time_t *);
8799800Salfredchar *cvtstoa(time_t);
8899800Salfredint editit(char *);
8999800Salfredvoid freeprivs(struct quotause *);
9099800Salfredint getentry(const char *, int);
9199800Salfredstruct quotause *getprivs(long, int, char *);
9299800Salfredint hasquota(struct fstab *, int, char **);
9399800Salfredvoid putprivs(long, int, struct quotause *);
9499800Salfredint readprivs(struct quotause *, char *);
9599800Salfredint readtimes(struct quotause *, char *);
9699800Salfredstatic void usage(void);
9799800Salfredint writetimes(struct quotause *, int, int);
9899800Salfredint writeprivs(struct quotause *, int, char *, int);
9929529Scharnier
10029529Scharnierint
10199820Salfredmain(int argc, char **argv)
1021553Srgrimes{
103103071Ssobomax	struct quotause *qup, *protoprivs, *curprivs;
104103071Ssobomax	long id, protoid;
105103071Ssobomax	long long lim;
106103071Ssobomax	int i, quotatype, range, tmpfd;
107103071Ssobomax	uid_t startuid, enduid;
108103071Ssobomax	u_int32_t *limp;
109124830Sgrehan	char *protoname, *cp, *oldoptarg;
110124830Sgrehan	int eflag = 0, tflag = 0, pflag = 0, ch;
11184081Syar	char *fspath = NULL;
112126201Sceri	char buf[MAXLOGNAME];
1131553Srgrimes
1141553Srgrimes	if (argc < 2)
1151553Srgrimes		usage();
11629529Scharnier	if (getuid())
11729529Scharnier		errx(1, "permission denied");
1181553Srgrimes	quotatype = USRQUOTA;
119103071Ssobomax	protoprivs = NULL;
120162295Scharnier	curprivs = NULL;
121162295Scharnier	protoname = NULL;
122103071Ssobomax	while ((ch = getopt(argc, argv, "ugtf:p:e:")) != -1) {
1231553Srgrimes		switch(ch) {
12484081Syar		case 'f':
12584081Syar			fspath = optarg;
12684081Syar			break;
1271553Srgrimes		case 'p':
1281553Srgrimes			protoname = optarg;
1291553Srgrimes			pflag++;
1301553Srgrimes			break;
1311553Srgrimes		case 'g':
1321553Srgrimes			quotatype = GRPQUOTA;
1331553Srgrimes			break;
1341553Srgrimes		case 'u':
1351553Srgrimes			quotatype = USRQUOTA;
1361553Srgrimes			break;
1371553Srgrimes		case 't':
1381553Srgrimes			tflag++;
1391553Srgrimes			break;
140103071Ssobomax		case 'e':
141103071Ssobomax			if ((qup = malloc(sizeof(*qup))) == NULL)
142103071Ssobomax				errx(2, "out of memory");
143103071Ssobomax			bzero(qup, sizeof(*qup));
144103071Ssobomax			i = 0;
145103071Ssobomax			oldoptarg = optarg;
146103071Ssobomax			for (cp = optarg; (cp = strsep(&optarg, ":")) != NULL;
147103071Ssobomax			    i++) {
148103071Ssobomax				if (cp != oldoptarg)
149103071Ssobomax					*(cp - 1) = ':';
150103071Ssobomax				limp = NULL;
151103071Ssobomax				switch (i) {
152103071Ssobomax				case 0:
153103071Ssobomax					strlcpy(qup->fsname, cp,
154103071Ssobomax					    sizeof(qup->fsname));
155103071Ssobomax					break;
156103071Ssobomax				case 1:
157103071Ssobomax					limp = &qup->dqblk.dqb_bsoftlimit;
158103071Ssobomax					break;
159103071Ssobomax				case 2:
160103071Ssobomax					limp = &qup->dqblk.dqb_bhardlimit;
161103071Ssobomax					break;
162103071Ssobomax				case 3:
163103071Ssobomax					limp = &qup->dqblk.dqb_isoftlimit;
164103071Ssobomax					break;
165103071Ssobomax				case 4:
166103071Ssobomax					limp = &qup->dqblk.dqb_ihardlimit;
167103071Ssobomax					break;
168103071Ssobomax				default:
169103071Ssobomax					warnx("incorrect quota specification: "
170103071Ssobomax					    "%s", oldoptarg);
171103071Ssobomax					usage();
172103071Ssobomax					break; /* XXX: report an error */
173103071Ssobomax				}
174103071Ssobomax				if (limp != NULL) {
175103071Ssobomax					lim = strtoll(cp, NULL, 10);
176103071Ssobomax					if (lim < 0 || lim > UINT_MAX)
177103071Ssobomax						errx(1, "invalid limit value: "
178103071Ssobomax						    "%lld", lim);
179103071Ssobomax					*limp = (u_int32_t)lim;
180103071Ssobomax				}
181103071Ssobomax			}
182103071Ssobomax			qup->dqblk.dqb_bsoftlimit =
183103071Ssobomax			    btodb((off_t)qup->dqblk.dqb_bsoftlimit * 1024);
184103071Ssobomax			qup->dqblk.dqb_bhardlimit =
185103071Ssobomax			    btodb((off_t)qup->dqblk.dqb_bhardlimit * 1024);
186103071Ssobomax			if (protoprivs == NULL) {
187103071Ssobomax				protoprivs = curprivs = qup;
188103071Ssobomax			} else {
189103071Ssobomax				curprivs->next = qup;
190103071Ssobomax				curprivs = qup;
191103071Ssobomax			}
192103071Ssobomax			eflag++;
193103071Ssobomax			pflag++;
194103071Ssobomax			break;
1951553Srgrimes		default:
1961553Srgrimes			usage();
1971553Srgrimes		}
1981553Srgrimes	}
1991553Srgrimes	argc -= optind;
2001553Srgrimes	argv += optind;
2011553Srgrimes	if (pflag) {
202103071Ssobomax		if (protoprivs == NULL) {
203103071Ssobomax			if ((protoid = getentry(protoname, quotatype)) == -1)
204103071Ssobomax				exit(1);
205103071Ssobomax			protoprivs = getprivs(protoid, quotatype, fspath);
206103071Ssobomax			for (qup = protoprivs; qup; qup = qup->next) {
207103071Ssobomax				qup->dqblk.dqb_btime = 0;
208103071Ssobomax				qup->dqblk.dqb_itime = 0;
209103071Ssobomax			}
2101553Srgrimes		}
211101546Siedowse		for (; argc-- > 0; argv++) {
212101546Siedowse			if (strspn(*argv, "0123456789-") == strlen(*argv) &&
21314961Smpp			    (cp = strchr(*argv, '-')) != NULL) {
21414961Smpp				*cp++ = '\0';
21514961Smpp				startuid = atoi(*argv);
21614961Smpp				enduid = atoi(cp);
21729529Scharnier				if (enduid < startuid)
21829529Scharnier					errx(1,
21929529Scharnier	"ending uid (%d) must be >= starting uid (%d) when using uid ranges",
22014961Smpp						enduid, startuid);
221103071Ssobomax				range = 1;
222103071Ssobomax			} else {
223103071Ssobomax				startuid = enduid = 0;
224103071Ssobomax				range = 0;
225103071Ssobomax			}
226103071Ssobomax			for ( ; startuid <= enduid; startuid++) {
227103071Ssobomax				if (range)
228103071Ssobomax					snprintf(buf, sizeof(buf), "%d",
22914961Smpp					    startuid);
230103071Ssobomax				else
231103071Ssobomax					snprintf(buf, sizeof(buf), "%s",
232103071Ssobomax						*argv);
233103071Ssobomax				if ((id = getentry(buf, quotatype)) < 0)
234103071Ssobomax					continue;
235103071Ssobomax				if (eflag) {
236103071Ssobomax					for (qup = protoprivs; qup;
237103071Ssobomax					    qup = qup->next) {
238103071Ssobomax						curprivs = getprivs(id,
239103071Ssobomax						    quotatype, qup->fsname);
240103071Ssobomax						if (curprivs == NULL)
241103071Ssobomax							continue;
242103071Ssobomax						strcpy(qup->qfname,
243103071Ssobomax						    curprivs->qfname);
244103071Ssobomax						strcpy(qup->fsname,
245103071Ssobomax						    curprivs->fsname);
246103071Ssobomax					}
24714961Smpp				}
248103071Ssobomax				putprivs(id, quotatype, protoprivs);
24914961Smpp			}
2501553Srgrimes		}
2511553Srgrimes		exit(0);
2521553Srgrimes	}
2531553Srgrimes	tmpfd = mkstemp(tmpfil);
2541553Srgrimes	fchown(tmpfd, getuid(), getgid());
2551553Srgrimes	if (tflag) {
25684081Syar		protoprivs = getprivs(0, quotatype, fspath);
2571553Srgrimes		if (writetimes(protoprivs, tmpfd, quotatype) == 0)
2581553Srgrimes			exit(1);
2594782Sache		if (editit(tmpfil) && readtimes(protoprivs, tmpfil))
260162295Scharnier			putprivs(0L, quotatype, protoprivs);
2611553Srgrimes		freeprivs(protoprivs);
26228431Sjlemon		close(tmpfd);
26328431Sjlemon		unlink(tmpfil);
2641553Srgrimes		exit(0);
2651553Srgrimes	}
2661553Srgrimes	for ( ; argc > 0; argc--, argv++) {
2671553Srgrimes		if ((id = getentry(*argv, quotatype)) == -1)
2681553Srgrimes			continue;
26984081Syar		curprivs = getprivs(id, quotatype, fspath);
2701553Srgrimes		if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
2711553Srgrimes			continue;
2724782Sache		if (editit(tmpfil) && readprivs(curprivs, tmpfil))
2731553Srgrimes			putprivs(id, quotatype, curprivs);
2741553Srgrimes		freeprivs(curprivs);
2751553Srgrimes	}
2761553Srgrimes	close(tmpfd);
2771553Srgrimes	unlink(tmpfil);
2781553Srgrimes	exit(0);
2791553Srgrimes}
2801553Srgrimes
28129529Scharnierstatic void
2821553Srgrimesusage()
2831553Srgrimes{
284103071Ssobomax	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
28584081Syar		"usage: edquota [-u] [-f fspath] [-p username] username ...",
286103071Ssobomax		"       edquota [-u] -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]",
287103071Ssobomax		"               username ...",
28884081Syar		"       edquota -g [-f fspath] [-p groupname] groupname ...",
289103071Ssobomax		"       edquota -g -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]",
290103071Ssobomax		"               groupname ...",
29184081Syar		"       edquota [-u] -t [-f fspath]",
29284081Syar		"       edquota -g -t [-f fspath]");
2931553Srgrimes	exit(1);
2941553Srgrimes}
2951553Srgrimes
2961553Srgrimes/*
2971553Srgrimes * This routine converts a name for a particular quota type to
2981553Srgrimes * an identifier. This routine must agree with the kernel routine
2991553Srgrimes * getinoquota as to the interpretation of quota types.
3001553Srgrimes */
30129529Scharnierint
3021553Srgrimesgetentry(name, quotatype)
30387596Smikeh	const char *name;
3041553Srgrimes	int quotatype;
3051553Srgrimes{
3061553Srgrimes	struct passwd *pw;
3071553Srgrimes	struct group *gr;
3081553Srgrimes
3091553Srgrimes	if (alldigits(name))
3101553Srgrimes		return (atoi(name));
3111553Srgrimes	switch(quotatype) {
3121553Srgrimes	case USRQUOTA:
31329529Scharnier		if ((pw = getpwnam(name)))
3141553Srgrimes			return (pw->pw_uid);
31529529Scharnier		warnx("%s: no such user", name);
3161553Srgrimes		break;
3171553Srgrimes	case GRPQUOTA:
31829529Scharnier		if ((gr = getgrnam(name)))
3191553Srgrimes			return (gr->gr_gid);
32029529Scharnier		warnx("%s: no such group", name);
3211553Srgrimes		break;
3221553Srgrimes	default:
32329529Scharnier		warnx("%d: unknown quota type", quotatype);
3241553Srgrimes		break;
3251553Srgrimes	}
3261553Srgrimes	sleep(1);
3271553Srgrimes	return (-1);
3281553Srgrimes}
3291553Srgrimes
3301553Srgrimes/*
3311553Srgrimes * Collect the requested quota information.
3321553Srgrimes */
3331553Srgrimesstruct quotause *
33484081Syargetprivs(id, quotatype, fspath)
3351553Srgrimes	register long id;
3361553Srgrimes	int quotatype;
33784081Syar	char *fspath;
3381553Srgrimes{
3391553Srgrimes	register struct fstab *fs;
3401553Srgrimes	register struct quotause *qup, *quptail;
3411553Srgrimes	struct quotause *quphead;
3421553Srgrimes	int qcmd, qupsize, fd;
3431553Srgrimes	char *qfpathname;
3441553Srgrimes	static int warned = 0;
3451553Srgrimes
3461553Srgrimes	setfsent();
347162295Scharnier	quphead = quptail = NULL;
3481553Srgrimes	qcmd = QCMD(Q_GETQUOTA, quotatype);
34929529Scharnier	while ((fs = getfsent())) {
35084081Syar		if (fspath && *fspath && strcmp(fspath, fs->fs_spec) &&
35184081Syar		    strcmp(fspath, fs->fs_file))
35284081Syar			continue;
3531553Srgrimes		if (strcmp(fs->fs_vfstype, "ufs"))
3541553Srgrimes			continue;
3551553Srgrimes		if (!hasquota(fs, quotatype, &qfpathname))
3561553Srgrimes			continue;
3571553Srgrimes		qupsize = sizeof(*qup) + strlen(qfpathname);
35829529Scharnier		if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
35929529Scharnier			errx(2, "out of memory");
3601553Srgrimes		if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
3611553Srgrimes	    		if (errno == EOPNOTSUPP && !warned) {
3621553Srgrimes				warned++;
36329529Scharnier		warnx("warning: quotas are not compiled into this kernel");
3641553Srgrimes				sleep(3);
3651553Srgrimes			}
3661553Srgrimes			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
3671553Srgrimes				fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
3681553Srgrimes				if (fd < 0 && errno != ENOENT) {
36929529Scharnier					warn("%s", qfpathname);
3701553Srgrimes					free(qup);
3711553Srgrimes					continue;
3721553Srgrimes				}
37329529Scharnier				warnx("creating quota file %s", qfpathname);
3741553Srgrimes				sleep(3);
3751553Srgrimes				(void) fchown(fd, getuid(),
3761553Srgrimes				    getentry(quotagroup, GRPQUOTA));
3771553Srgrimes				(void) fchmod(fd, 0640);
3781553Srgrimes			}
379166480Smpp			if (lseek(fd, (off_t)id * sizeof(struct dqblk),
380166480Smpp			    L_SET) < 0) {
381166480Smpp				warn("seek error on %s", qfpathname);
382166480Smpp				close(fd);
383166480Smpp				free(qup);
384166480Smpp				continue;
385166480Smpp			}
3861553Srgrimes			switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
3871553Srgrimes			case 0:			/* EOF */
3881553Srgrimes				/*
3891553Srgrimes				 * Convert implicit 0 quota (EOF)
3901553Srgrimes				 * into an explicit one (zero'ed dqblk)
3911553Srgrimes				 */
3921553Srgrimes				bzero((caddr_t)&qup->dqblk,
3931553Srgrimes				    sizeof(struct dqblk));
3941553Srgrimes				break;
3951553Srgrimes
3961553Srgrimes			case sizeof(struct dqblk):	/* OK */
3971553Srgrimes				break;
3981553Srgrimes
3991553Srgrimes			default:		/* ERROR */
40029529Scharnier				warn("read error in %s", qfpathname);
4011553Srgrimes				close(fd);
4021553Srgrimes				free(qup);
4031553Srgrimes				continue;
4041553Srgrimes			}
4051553Srgrimes			close(fd);
4061553Srgrimes		}
4071553Srgrimes		strcpy(qup->qfname, qfpathname);
4081553Srgrimes		strcpy(qup->fsname, fs->fs_file);
4091553Srgrimes		if (quphead == NULL)
4101553Srgrimes			quphead = qup;
4111553Srgrimes		else
4121553Srgrimes			quptail->next = qup;
4131553Srgrimes		quptail = qup;
4141553Srgrimes		qup->next = 0;
4151553Srgrimes	}
4161553Srgrimes	endfsent();
4171553Srgrimes	return (quphead);
4181553Srgrimes}
4191553Srgrimes
4201553Srgrimes/*
4211553Srgrimes * Store the requested quota information.
4221553Srgrimes */
42329529Scharniervoid
4241553Srgrimesputprivs(id, quotatype, quplist)
4251553Srgrimes	long id;
4261553Srgrimes	int quotatype;
4271553Srgrimes	struct quotause *quplist;
4281553Srgrimes{
4291553Srgrimes	register struct quotause *qup;
4301553Srgrimes	int qcmd, fd;
431166480Smpp	struct dqblk dqbuf;
4321553Srgrimes
4331553Srgrimes	qcmd = QCMD(Q_SETQUOTA, quotatype);
4341553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
4351553Srgrimes		if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
4361553Srgrimes			continue;
437166480Smpp		if ((fd = open(qup->qfname, O_RDWR)) < 0) {
43829529Scharnier			warn("%s", qup->qfname);
439166480Smpp			continue;
440166480Smpp		}
441166480Smpp		if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
442166480Smpp			warn("seek error on %s", qup->qfname);
4431553Srgrimes			close(fd);
444166480Smpp			continue;
4451553Srgrimes		}
446166480Smpp		switch (read(fd, &dqbuf, sizeof(struct dqblk))) {
447166480Smpp		case 0:			/* EOF */
448166480Smpp			/*
449166480Smpp			 * Convert implicit 0 quota (EOF)
450166480Smpp			 * into an explicit one (zero'ed dqblk)
451166480Smpp			 */
452166480Smpp			bzero(&dqbuf, sizeof(struct dqblk));
453166480Smpp			break;
454166480Smpp
455166480Smpp		case sizeof(struct dqblk):	/* OK */
456166480Smpp			break;
457166480Smpp
458166480Smpp		default:		/* ERROR */
459166480Smpp			warn("read error in %s", qup->qfname);
460166480Smpp			close(fd);
461166480Smpp			continue;
462166480Smpp		}
463166480Smpp		/*
464166480Smpp		 * Reset time limit if have a soft limit and were
465166480Smpp		 * previously under it, but are now over it
466166480Smpp		 * or if there previously was no soft limit, but
467166480Smpp		 * now have one and are over it.
468166480Smpp		 */
469166480Smpp		if (dqbuf.dqb_bsoftlimit && id != 0 &&
470166480Smpp		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
471166480Smpp		    dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
472166480Smpp			qup->dqblk.dqb_btime = 0;
473166480Smpp		if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
474166480Smpp		    dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
475166480Smpp			qup->dqblk.dqb_btime = 0;
476166480Smpp		if (dqbuf.dqb_isoftlimit && id != 0 &&
477166480Smpp		    dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
478166480Smpp		    dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
479166480Smpp			qup->dqblk.dqb_itime = 0;
480166480Smpp		if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
481166480Smpp		    dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
482166480Smpp			qup->dqblk.dqb_itime = 0;
483166480Smpp		qup->dqblk.dqb_curinodes = dqbuf.dqb_curinodes;
484166480Smpp		qup->dqblk.dqb_curblocks = dqbuf.dqb_curblocks;
485166480Smpp		if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
486166480Smpp			warn("seek error on %s", qup->qfname);
487166480Smpp			close(fd);
488166480Smpp			continue;
489166480Smpp		}
490166480Smpp		if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
491166480Smpp		    sizeof (struct dqblk)) {
492166480Smpp			warn("%s", qup->qfname);
493166480Smpp			}
494166480Smpp		close(fd);
4951553Srgrimes	}
4961553Srgrimes}
4971553Srgrimes
4981553Srgrimes/*
4991553Srgrimes * Take a list of priviledges and get it edited.
5001553Srgrimes */
50129529Scharnierint
50287596Smikeheditit(tmpf)
50387596Smikeh	char *tmpf;
5041553Srgrimes{
5051553Srgrimes	long omask;
50687596Smikeh	int pid, status;
5071553Srgrimes
5081553Srgrimes	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
5091553Srgrimes top:
5101553Srgrimes	if ((pid = fork()) < 0) {
5111553Srgrimes
5121553Srgrimes		if (errno == EPROCLIM) {
51329529Scharnier			warnx("you have too many processes");
5141553Srgrimes			return(0);
5151553Srgrimes		}
5161553Srgrimes		if (errno == EAGAIN) {
5171553Srgrimes			sleep(1);
5181553Srgrimes			goto top;
5191553Srgrimes		}
52029529Scharnier		warn("fork");
5211553Srgrimes		return (0);
5221553Srgrimes	}
5231553Srgrimes	if (pid == 0) {
52487596Smikeh		register const char *ed;
5251553Srgrimes
5261553Srgrimes		sigsetmask(omask);
5271553Srgrimes		setgid(getgid());
5281553Srgrimes		setuid(getuid());
5291553Srgrimes		if ((ed = getenv("EDITOR")) == (char *)0)
5301553Srgrimes			ed = _PATH_VI;
53187596Smikeh		execlp(ed, ed, tmpf, (char *)0);
53229529Scharnier		err(1, "%s", ed);
5331553Srgrimes	}
53487596Smikeh	waitpid(pid, &status, 0);
5351553Srgrimes	sigsetmask(omask);
53687596Smikeh	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
5371553Srgrimes		return (0);
5381553Srgrimes	return (1);
5391553Srgrimes}
5401553Srgrimes
5411553Srgrimes/*
5421553Srgrimes * Convert a quotause list to an ASCII file.
5431553Srgrimes */
54429529Scharnierint
5451553Srgrimeswriteprivs(quplist, outfd, name, quotatype)
5461553Srgrimes	struct quotause *quplist;
5471553Srgrimes	int outfd;
5481553Srgrimes	char *name;
5491553Srgrimes	int quotatype;
5501553Srgrimes{
5511553Srgrimes	register struct quotause *qup;
5521553Srgrimes	FILE *fd;
5531553Srgrimes
5541553Srgrimes	ftruncate(outfd, 0);
5551553Srgrimes	lseek(outfd, 0, L_SET);
55629529Scharnier	if ((fd = fdopen(dup(outfd), "w")) == NULL)
55729529Scharnier		err(1, "%s", tmpfil);
5581553Srgrimes	fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
5591553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
56067794Sgallatin		fprintf(fd, "%s: %s %lu, limits (soft = %lu, hard = %lu)\n",
561102362Sschweikh		    qup->fsname, "kbytes in use:",
5628325Sbde		    (unsigned long)(dbtob(qup->dqblk.dqb_curblocks) / 1024),
5638325Sbde		    (unsigned long)(dbtob(qup->dqblk.dqb_bsoftlimit) / 1024),
5648325Sbde		    (unsigned long)(dbtob(qup->dqblk.dqb_bhardlimit) / 1024));
56567794Sgallatin		fprintf(fd, "%s %lu, limits (soft = %lu, hard = %lu)\n",
56667794Sgallatin		    "\tinodes in use:",
56767794Sgallatin		    (unsigned long)qup->dqblk.dqb_curinodes,
56867794Sgallatin		    (unsigned long)qup->dqblk.dqb_isoftlimit,
56967794Sgallatin		    (unsigned long)qup->dqblk.dqb_ihardlimit);
5701553Srgrimes	}
5711553Srgrimes	fclose(fd);
5721553Srgrimes	return (1);
5731553Srgrimes}
5741553Srgrimes
5751553Srgrimes/*
5761553Srgrimes * Merge changes to an ASCII file into a quotause list.
5771553Srgrimes */
57829529Scharnierint
5794782Sachereadprivs(quplist, inname)
5801553Srgrimes	struct quotause *quplist;
5814782Sache	char *inname;
5821553Srgrimes{
5831553Srgrimes	register struct quotause *qup;
5841553Srgrimes	FILE *fd;
58567794Sgallatin	unsigned long bhardlimit, bsoftlimit, curblocks;
58667794Sgallatin	unsigned long ihardlimit, isoftlimit, curinodes;
5871553Srgrimes	int cnt;
5881553Srgrimes	register char *cp;
5891553Srgrimes	struct dqblk dqblk;
5901553Srgrimes	char *fsp, line1[BUFSIZ], line2[BUFSIZ];
5911553Srgrimes
5924782Sache	fd = fopen(inname, "r");
5931553Srgrimes	if (fd == NULL) {
59429529Scharnier		warnx("can't re-read temp file!!");
5951553Srgrimes		return (0);
5961553Srgrimes	}
5971553Srgrimes	/*
5981553Srgrimes	 * Discard title line, then read pairs of lines to process.
5991553Srgrimes	 */
6001553Srgrimes	(void) fgets(line1, sizeof (line1), fd);
6011553Srgrimes	while (fgets(line1, sizeof (line1), fd) != NULL &&
6021553Srgrimes	       fgets(line2, sizeof (line2), fd) != NULL) {
6031553Srgrimes		if ((fsp = strtok(line1, " \t:")) == NULL) {
60429529Scharnier			warnx("%s: bad format", line1);
6051553Srgrimes			return (0);
6061553Srgrimes		}
6071553Srgrimes		if ((cp = strtok((char *)0, "\n")) == NULL) {
60829529Scharnier			warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
6091553Srgrimes			return (0);
6101553Srgrimes		}
6111553Srgrimes		cnt = sscanf(cp,
612102362Sschweikh		    " kbytes in use: %lu, limits (soft = %lu, hard = %lu)",
61367794Sgallatin		    &curblocks, &bsoftlimit, &bhardlimit);
6141553Srgrimes		if (cnt != 3) {
61529529Scharnier			warnx("%s:%s: bad format", fsp, cp);
6161553Srgrimes			return (0);
6171553Srgrimes		}
61867794Sgallatin		dqblk.dqb_curblocks = btodb((off_t)curblocks * 1024);
61967794Sgallatin		dqblk.dqb_bsoftlimit = btodb((off_t)bsoftlimit * 1024);
62067794Sgallatin		dqblk.dqb_bhardlimit = btodb((off_t)bhardlimit * 1024);
6211553Srgrimes		if ((cp = strtok(line2, "\n")) == NULL) {
62229529Scharnier			warnx("%s: %s: bad format", fsp, line2);
6231553Srgrimes			return (0);
6241553Srgrimes		}
6251553Srgrimes		cnt = sscanf(cp,
62667794Sgallatin		    "\tinodes in use: %lu, limits (soft = %lu, hard = %lu)",
62767794Sgallatin		    &curinodes, &isoftlimit, &ihardlimit);
6281553Srgrimes		if (cnt != 3) {
62929529Scharnier			warnx("%s: %s: bad format", fsp, line2);
6301553Srgrimes			return (0);
6311553Srgrimes		}
63267794Sgallatin		dqblk.dqb_curinodes = curinodes;
63367794Sgallatin		dqblk.dqb_isoftlimit = isoftlimit;
63467794Sgallatin		dqblk.dqb_ihardlimit = ihardlimit;
6351553Srgrimes		for (qup = quplist; qup; qup = qup->next) {
6361553Srgrimes			if (strcmp(fsp, qup->fsname))
6371553Srgrimes				continue;
6381553Srgrimes			/*
6391553Srgrimes			 * Cause time limit to be reset when the quota
6401553Srgrimes			 * is next used if previously had no soft limit
6411553Srgrimes			 * or were under it, but now have a soft limit
6421553Srgrimes			 * and are over it.
6431553Srgrimes			 */
6441553Srgrimes			if (dqblk.dqb_bsoftlimit &&
6451553Srgrimes			    qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
6461553Srgrimes			    (qup->dqblk.dqb_bsoftlimit == 0 ||
6471553Srgrimes			     qup->dqblk.dqb_curblocks <
6481553Srgrimes			     qup->dqblk.dqb_bsoftlimit))
6491553Srgrimes				qup->dqblk.dqb_btime = 0;
6501553Srgrimes			if (dqblk.dqb_isoftlimit &&
6511553Srgrimes			    qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
6521553Srgrimes			    (qup->dqblk.dqb_isoftlimit == 0 ||
6531553Srgrimes			     qup->dqblk.dqb_curinodes <
6541553Srgrimes			     qup->dqblk.dqb_isoftlimit))
6551553Srgrimes				qup->dqblk.dqb_itime = 0;
6561553Srgrimes			qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
6571553Srgrimes			qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
6581553Srgrimes			qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
6591553Srgrimes			qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
6601553Srgrimes			qup->flags |= FOUND;
6611553Srgrimes			if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
6621553Srgrimes			    dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
6631553Srgrimes				break;
66429529Scharnier			warnx("%s: cannot change current allocation", fsp);
6651553Srgrimes			break;
6661553Srgrimes		}
6671553Srgrimes	}
6681553Srgrimes	fclose(fd);
6691553Srgrimes	/*
6701553Srgrimes	 * Disable quotas for any filesystems that have not been found.
6711553Srgrimes	 */
6721553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
6731553Srgrimes		if (qup->flags & FOUND) {
6741553Srgrimes			qup->flags &= ~FOUND;
6751553Srgrimes			continue;
6761553Srgrimes		}
6771553Srgrimes		qup->dqblk.dqb_bsoftlimit = 0;
6781553Srgrimes		qup->dqblk.dqb_bhardlimit = 0;
6791553Srgrimes		qup->dqblk.dqb_isoftlimit = 0;
6801553Srgrimes		qup->dqblk.dqb_ihardlimit = 0;
6811553Srgrimes	}
6821553Srgrimes	return (1);
6831553Srgrimes}
6841553Srgrimes
6851553Srgrimes/*
6861553Srgrimes * Convert a quotause list to an ASCII file of grace times.
6871553Srgrimes */
68829529Scharnierint
6891553Srgrimeswritetimes(quplist, outfd, quotatype)
6901553Srgrimes	struct quotause *quplist;
6911553Srgrimes	int outfd;
6921553Srgrimes	int quotatype;
6931553Srgrimes{
6941553Srgrimes	register struct quotause *qup;
6951553Srgrimes	FILE *fd;
6961553Srgrimes
6971553Srgrimes	ftruncate(outfd, 0);
6981553Srgrimes	lseek(outfd, 0, L_SET);
69929529Scharnier	if ((fd = fdopen(dup(outfd), "w")) == NULL)
70029529Scharnier		err(1, "%s", tmpfil);
7011553Srgrimes	fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
7021553Srgrimes	fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
7031553Srgrimes	    qfextension[quotatype]);
7041553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
7051553Srgrimes		fprintf(fd, "%s: block grace period: %s, ",
7061553Srgrimes		    qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
7071553Srgrimes		fprintf(fd, "file grace period: %s\n",
7081553Srgrimes		    cvtstoa(qup->dqblk.dqb_itime));
7091553Srgrimes	}
7101553Srgrimes	fclose(fd);
7111553Srgrimes	return (1);
7121553Srgrimes}
7131553Srgrimes
7141553Srgrimes/*
7151553Srgrimes * Merge changes of grace times in an ASCII file into a quotause list.
7161553Srgrimes */
71729529Scharnierint
7184782Sachereadtimes(quplist, inname)
7191553Srgrimes	struct quotause *quplist;
7204782Sache	char *inname;
7211553Srgrimes{
7221553Srgrimes	register struct quotause *qup;
7231553Srgrimes	FILE *fd;
7241553Srgrimes	int cnt;
7251553Srgrimes	register char *cp;
7261553Srgrimes	time_t itime, btime, iseconds, bseconds;
72767794Sgallatin	long l_itime, l_btime;
7281553Srgrimes	char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
7291553Srgrimes
7304782Sache	fd = fopen(inname, "r");
7311553Srgrimes	if (fd == NULL) {
73229529Scharnier		warnx("can't re-read temp file!!");
7331553Srgrimes		return (0);
7341553Srgrimes	}
7351553Srgrimes	/*
7361553Srgrimes	 * Discard two title lines, then read lines to process.
7371553Srgrimes	 */
7381553Srgrimes	(void) fgets(line1, sizeof (line1), fd);
7391553Srgrimes	(void) fgets(line1, sizeof (line1), fd);
7401553Srgrimes	while (fgets(line1, sizeof (line1), fd) != NULL) {
7411553Srgrimes		if ((fsp = strtok(line1, " \t:")) == NULL) {
74229529Scharnier			warnx("%s: bad format", line1);
7431553Srgrimes			return (0);
7441553Srgrimes		}
7451553Srgrimes		if ((cp = strtok((char *)0, "\n")) == NULL) {
74629529Scharnier			warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
7471553Srgrimes			return (0);
7481553Srgrimes		}
7491553Srgrimes		cnt = sscanf(cp,
7508325Sbde		    " block grace period: %ld %s file grace period: %ld %s",
75167794Sgallatin		    &l_btime, bunits, &l_itime, iunits);
7521553Srgrimes		if (cnt != 4) {
75329529Scharnier			warnx("%s:%s: bad format", fsp, cp);
7541553Srgrimes			return (0);
7551553Srgrimes		}
75667794Sgallatin		btime = l_btime;
75767794Sgallatin		itime = l_itime;
7581553Srgrimes		if (cvtatos(btime, bunits, &bseconds) == 0)
7591553Srgrimes			return (0);
7601553Srgrimes		if (cvtatos(itime, iunits, &iseconds) == 0)
7611553Srgrimes			return (0);
7621553Srgrimes		for (qup = quplist; qup; qup = qup->next) {
7631553Srgrimes			if (strcmp(fsp, qup->fsname))
7641553Srgrimes				continue;
7651553Srgrimes			qup->dqblk.dqb_btime = bseconds;
7661553Srgrimes			qup->dqblk.dqb_itime = iseconds;
7671553Srgrimes			qup->flags |= FOUND;
7681553Srgrimes			break;
7691553Srgrimes		}
7701553Srgrimes	}
7711553Srgrimes	fclose(fd);
7721553Srgrimes	/*
7731553Srgrimes	 * reset default grace periods for any filesystems
7741553Srgrimes	 * that have not been found.
7751553Srgrimes	 */
7761553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
7771553Srgrimes		if (qup->flags & FOUND) {
7781553Srgrimes			qup->flags &= ~FOUND;
7791553Srgrimes			continue;
7801553Srgrimes		}
7811553Srgrimes		qup->dqblk.dqb_btime = 0;
7821553Srgrimes		qup->dqblk.dqb_itime = 0;
7831553Srgrimes	}
7841553Srgrimes	return (1);
7851553Srgrimes}
7861553Srgrimes
7871553Srgrimes/*
7881553Srgrimes * Convert seconds to ASCII times.
7891553Srgrimes */
7901553Srgrimeschar *
79187596Smikehcvtstoa(secs)
79287596Smikeh	time_t secs;
7931553Srgrimes{
7941553Srgrimes	static char buf[20];
7951553Srgrimes
79687596Smikeh	if (secs % (24 * 60 * 60) == 0) {
79787596Smikeh		secs /= 24 * 60 * 60;
79887596Smikeh		sprintf(buf, "%ld day%s", (long)secs, secs == 1 ? "" : "s");
79987596Smikeh	} else if (secs % (60 * 60) == 0) {
80087596Smikeh		secs /= 60 * 60;
80187596Smikeh		sprintf(buf, "%ld hour%s", (long)secs, secs == 1 ? "" : "s");
80287596Smikeh	} else if (secs % 60 == 0) {
80387596Smikeh		secs /= 60;
80487596Smikeh		sprintf(buf, "%ld minute%s", (long)secs, secs == 1 ? "" : "s");
8051553Srgrimes	} else
80687596Smikeh		sprintf(buf, "%ld second%s", (long)secs, secs == 1 ? "" : "s");
8071553Srgrimes	return (buf);
8081553Srgrimes}
8091553Srgrimes
8101553Srgrimes/*
8111553Srgrimes * Convert ASCII input times to seconds.
8121553Srgrimes */
81329529Scharnierint
81487596Smikehcvtatos(period, units, seconds)
81587596Smikeh	time_t period;
8161553Srgrimes	char *units;
8171553Srgrimes	time_t *seconds;
8181553Srgrimes{
8191553Srgrimes
8201553Srgrimes	if (bcmp(units, "second", 6) == 0)
82187596Smikeh		*seconds = period;
8221553Srgrimes	else if (bcmp(units, "minute", 6) == 0)
82387596Smikeh		*seconds = period * 60;
8241553Srgrimes	else if (bcmp(units, "hour", 4) == 0)
82587596Smikeh		*seconds = period * 60 * 60;
8261553Srgrimes	else if (bcmp(units, "day", 3) == 0)
82787596Smikeh		*seconds = period * 24 * 60 * 60;
8281553Srgrimes	else {
8291553Srgrimes		printf("%s: bad units, specify %s\n", units,
8301553Srgrimes		    "days, hours, minutes, or seconds");
8311553Srgrimes		return (0);
8321553Srgrimes	}
8331553Srgrimes	return (1);
8341553Srgrimes}
8351553Srgrimes
8361553Srgrimes/*
8371553Srgrimes * Free a list of quotause structures.
8381553Srgrimes */
83929529Scharniervoid
8401553Srgrimesfreeprivs(quplist)
8411553Srgrimes	struct quotause *quplist;
8421553Srgrimes{
8431553Srgrimes	register struct quotause *qup, *nextqup;
8441553Srgrimes
8451553Srgrimes	for (qup = quplist; qup; qup = nextqup) {
8461553Srgrimes		nextqup = qup->next;
8471553Srgrimes		free(qup);
8481553Srgrimes	}
8491553Srgrimes}
8501553Srgrimes
8511553Srgrimes/*
8521553Srgrimes * Check whether a string is completely composed of digits.
8531553Srgrimes */
85429529Scharnierint
8551553Srgrimesalldigits(s)
85687596Smikeh	register const char *s;
8571553Srgrimes{
85887596Smikeh	register int c;
8591553Srgrimes
8601553Srgrimes	c = *s++;
8611553Srgrimes	do {
8621553Srgrimes		if (!isdigit(c))
8631553Srgrimes			return (0);
86429529Scharnier	} while ((c = *s++));
8651553Srgrimes	return (1);
8661553Srgrimes}
8671553Srgrimes
8681553Srgrimes/*
8691553Srgrimes * Check to see if a particular quota is to be enabled.
8701553Srgrimes */
87129529Scharnierint
8721553Srgrimeshasquota(fs, type, qfnamep)
873166485Smpp	struct fstab *fs;
8741553Srgrimes	int type;
8751553Srgrimes	char **qfnamep;
8761553Srgrimes{
877166485Smpp	char *opt;
87829529Scharnier	char *cp;
879166485Smpp	struct statfs sfb;
8801553Srgrimes	static char initname, usrname[100], grpname[100];
8811553Srgrimes	static char buf[BUFSIZ];
8821553Srgrimes
8831553Srgrimes	if (!initname) {
884166485Smpp		(void)snprintf(usrname, sizeof(usrname), "%s%s",
885166485Smpp		    qfextension[USRQUOTA], qfname);
886166485Smpp		(void)snprintf(grpname, sizeof(grpname), "%s%s",
887166485Smpp		    qfextension[GRPQUOTA], qfname);
8881553Srgrimes		initname = 1;
8891553Srgrimes	}
8901553Srgrimes	strcpy(buf, fs->fs_mntops);
8911553Srgrimes	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
89229529Scharnier		if ((cp = index(opt, '=')))
8931553Srgrimes			*cp++ = '\0';
8941553Srgrimes		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
8951553Srgrimes			break;
8961553Srgrimes		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
8971553Srgrimes			break;
8981553Srgrimes	}
8991553Srgrimes	if (!opt)
9001553Srgrimes		return (0);
901166485Smpp	if (cp)
9021553Srgrimes		*qfnamep = cp;
903166485Smpp	else {
904166485Smpp		(void)snprintf(buf, sizeof(buf), "%s/%s.%s", fs->fs_file,
905166485Smpp		    qfname, qfextension[type]);
906166485Smpp		*qfnamep = buf;
9071553Srgrimes	}
908166485Smpp	if (statfs(fs->fs_file, &sfb) != 0) {
909166485Smpp		warn("cannot statfs mount point %s", fs->fs_file);
910166485Smpp		return (0);
911166485Smpp	}
912166485Smpp	if (strcmp(fs->fs_file, sfb.f_mntonname)) {
913166485Smpp		warnx("%s not mounted for %s quotas", fs->fs_file,
914166485Smpp		    type == USRQUOTA ? "user" : "group");
915166485Smpp		sleep(3);
916166485Smpp		return (0);
917166485Smpp	}
9181553Srgrimes	return (1);
9191553Srgrimes}
920