edquota.c revision 180187
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 180187 2008-07-02 15:51:59Z des $");
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
101180187Sdesmain(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				}
248180187Sdes				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
282180187Sdesusage(void)
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
302180187Sdesgetentry(const char *name, int quotatype)
3031553Srgrimes{
3041553Srgrimes	struct passwd *pw;
3051553Srgrimes	struct group *gr;
3061553Srgrimes
3071553Srgrimes	if (alldigits(name))
3081553Srgrimes		return (atoi(name));
3091553Srgrimes	switch(quotatype) {
3101553Srgrimes	case USRQUOTA:
31129529Scharnier		if ((pw = getpwnam(name)))
3121553Srgrimes			return (pw->pw_uid);
31329529Scharnier		warnx("%s: no such user", name);
3141553Srgrimes		break;
3151553Srgrimes	case GRPQUOTA:
31629529Scharnier		if ((gr = getgrnam(name)))
3171553Srgrimes			return (gr->gr_gid);
31829529Scharnier		warnx("%s: no such group", name);
3191553Srgrimes		break;
3201553Srgrimes	default:
32129529Scharnier		warnx("%d: unknown quota type", quotatype);
3221553Srgrimes		break;
3231553Srgrimes	}
3241553Srgrimes	sleep(1);
3251553Srgrimes	return (-1);
3261553Srgrimes}
3271553Srgrimes
3281553Srgrimes/*
3291553Srgrimes * Collect the requested quota information.
3301553Srgrimes */
3311553Srgrimesstruct quotause *
332180187Sdesgetprivs(long id, int quotatype, char *fspath)
3331553Srgrimes{
334180187Sdes	struct fstab *fs;
335180187Sdes	struct quotause *qup, *quptail;
3361553Srgrimes	struct quotause *quphead;
3371553Srgrimes	int qcmd, qupsize, fd;
3381553Srgrimes	char *qfpathname;
3391553Srgrimes	static int warned = 0;
3401553Srgrimes
3411553Srgrimes	setfsent();
342162295Scharnier	quphead = quptail = NULL;
3431553Srgrimes	qcmd = QCMD(Q_GETQUOTA, quotatype);
34429529Scharnier	while ((fs = getfsent())) {
34584081Syar		if (fspath && *fspath && strcmp(fspath, fs->fs_spec) &&
34684081Syar		    strcmp(fspath, fs->fs_file))
34784081Syar			continue;
3481553Srgrimes		if (strcmp(fs->fs_vfstype, "ufs"))
3491553Srgrimes			continue;
3501553Srgrimes		if (!hasquota(fs, quotatype, &qfpathname))
3511553Srgrimes			continue;
3521553Srgrimes		qupsize = sizeof(*qup) + strlen(qfpathname);
35329529Scharnier		if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
35429529Scharnier			errx(2, "out of memory");
3551553Srgrimes		if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
356180187Sdes			if (errno == EOPNOTSUPP && !warned) {
3571553Srgrimes				warned++;
35829529Scharnier		warnx("warning: quotas are not compiled into this kernel");
3591553Srgrimes				sleep(3);
3601553Srgrimes			}
3611553Srgrimes			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
3621553Srgrimes				fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
3631553Srgrimes				if (fd < 0 && errno != ENOENT) {
36429529Scharnier					warn("%s", qfpathname);
3651553Srgrimes					free(qup);
3661553Srgrimes					continue;
3671553Srgrimes				}
36829529Scharnier				warnx("creating quota file %s", qfpathname);
3691553Srgrimes				sleep(3);
3701553Srgrimes				(void) fchown(fd, getuid(),
3711553Srgrimes				    getentry(quotagroup, GRPQUOTA));
3721553Srgrimes				(void) fchmod(fd, 0640);
3731553Srgrimes			}
374166480Smpp			if (lseek(fd, (off_t)id * sizeof(struct dqblk),
375166480Smpp			    L_SET) < 0) {
376166480Smpp				warn("seek error on %s", qfpathname);
377166480Smpp				close(fd);
378166480Smpp				free(qup);
379166480Smpp				continue;
380166480Smpp			}
3811553Srgrimes			switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
3821553Srgrimes			case 0:			/* EOF */
3831553Srgrimes				/*
3841553Srgrimes				 * Convert implicit 0 quota (EOF)
3851553Srgrimes				 * into an explicit one (zero'ed dqblk)
3861553Srgrimes				 */
3871553Srgrimes				bzero((caddr_t)&qup->dqblk,
3881553Srgrimes				    sizeof(struct dqblk));
3891553Srgrimes				break;
3901553Srgrimes
3911553Srgrimes			case sizeof(struct dqblk):	/* OK */
3921553Srgrimes				break;
3931553Srgrimes
3941553Srgrimes			default:		/* ERROR */
39529529Scharnier				warn("read error in %s", qfpathname);
3961553Srgrimes				close(fd);
3971553Srgrimes				free(qup);
3981553Srgrimes				continue;
3991553Srgrimes			}
4001553Srgrimes			close(fd);
4011553Srgrimes		}
4021553Srgrimes		strcpy(qup->qfname, qfpathname);
4031553Srgrimes		strcpy(qup->fsname, fs->fs_file);
4041553Srgrimes		if (quphead == NULL)
4051553Srgrimes			quphead = qup;
4061553Srgrimes		else
4071553Srgrimes			quptail->next = qup;
4081553Srgrimes		quptail = qup;
4091553Srgrimes		qup->next = 0;
4101553Srgrimes	}
4111553Srgrimes	endfsent();
4121553Srgrimes	return (quphead);
4131553Srgrimes}
4141553Srgrimes
4151553Srgrimes/*
4161553Srgrimes * Store the requested quota information.
4171553Srgrimes */
41829529Scharniervoid
419180187Sdesputprivs(long id, int quotatype, struct quotause *quplist)
4201553Srgrimes{
421180187Sdes	struct quotause *qup;
4221553Srgrimes	int qcmd, fd;
423166480Smpp	struct dqblk dqbuf;
4241553Srgrimes
4251553Srgrimes	qcmd = QCMD(Q_SETQUOTA, quotatype);
4261553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
4271553Srgrimes		if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
4281553Srgrimes			continue;
429166480Smpp		if ((fd = open(qup->qfname, O_RDWR)) < 0) {
43029529Scharnier			warn("%s", qup->qfname);
431166480Smpp			continue;
432166480Smpp		}
433166480Smpp		if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
434166480Smpp			warn("seek error on %s", qup->qfname);
4351553Srgrimes			close(fd);
436166480Smpp			continue;
4371553Srgrimes		}
438166480Smpp		switch (read(fd, &dqbuf, sizeof(struct dqblk))) {
439166480Smpp		case 0:			/* EOF */
440166480Smpp			/*
441166480Smpp			 * Convert implicit 0 quota (EOF)
442166480Smpp			 * into an explicit one (zero'ed dqblk)
443166480Smpp			 */
444166480Smpp			bzero(&dqbuf, sizeof(struct dqblk));
445166480Smpp			break;
446166480Smpp
447166480Smpp		case sizeof(struct dqblk):	/* OK */
448166480Smpp			break;
449166480Smpp
450166480Smpp		default:		/* ERROR */
451166480Smpp			warn("read error in %s", qup->qfname);
452166480Smpp			close(fd);
453166480Smpp			continue;
454166480Smpp		}
455166480Smpp		/*
456166480Smpp		 * Reset time limit if have a soft limit and were
457166480Smpp		 * previously under it, but are now over it
458180187Sdes		 * or if there previously was no soft limit, but
459166480Smpp		 * now have one and are over it.
460166480Smpp		 */
461166480Smpp		if (dqbuf.dqb_bsoftlimit && id != 0 &&
462166480Smpp		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
463166480Smpp		    dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
464166480Smpp			qup->dqblk.dqb_btime = 0;
465166480Smpp		if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
466166480Smpp		    dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
467166480Smpp			qup->dqblk.dqb_btime = 0;
468166480Smpp		if (dqbuf.dqb_isoftlimit && id != 0 &&
469166480Smpp		    dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
470166480Smpp		    dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
471166480Smpp			qup->dqblk.dqb_itime = 0;
472166480Smpp		if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
473166480Smpp		    dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
474166480Smpp			qup->dqblk.dqb_itime = 0;
475166480Smpp		qup->dqblk.dqb_curinodes = dqbuf.dqb_curinodes;
476166480Smpp		qup->dqblk.dqb_curblocks = dqbuf.dqb_curblocks;
477166480Smpp		if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
478166480Smpp			warn("seek error on %s", qup->qfname);
479166480Smpp			close(fd);
480166480Smpp			continue;
481166480Smpp		}
482166480Smpp		if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
483166480Smpp		    sizeof (struct dqblk)) {
484166480Smpp			warn("%s", qup->qfname);
485166480Smpp			}
486166480Smpp		close(fd);
4871553Srgrimes	}
4881553Srgrimes}
4891553Srgrimes
4901553Srgrimes/*
4911553Srgrimes * Take a list of priviledges and get it edited.
4921553Srgrimes */
49329529Scharnierint
494180187Sdeseditit(char *tmpf)
4951553Srgrimes{
4961553Srgrimes	long omask;
49787596Smikeh	int pid, status;
4981553Srgrimes
4991553Srgrimes	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
5001553Srgrimes top:
5011553Srgrimes	if ((pid = fork()) < 0) {
5021553Srgrimes
5031553Srgrimes		if (errno == EPROCLIM) {
50429529Scharnier			warnx("you have too many processes");
5051553Srgrimes			return(0);
5061553Srgrimes		}
5071553Srgrimes		if (errno == EAGAIN) {
5081553Srgrimes			sleep(1);
5091553Srgrimes			goto top;
5101553Srgrimes		}
51129529Scharnier		warn("fork");
5121553Srgrimes		return (0);
5131553Srgrimes	}
5141553Srgrimes	if (pid == 0) {
515180187Sdes		const char *ed;
5161553Srgrimes
5171553Srgrimes		sigsetmask(omask);
5181553Srgrimes		setgid(getgid());
5191553Srgrimes		setuid(getuid());
5201553Srgrimes		if ((ed = getenv("EDITOR")) == (char *)0)
5211553Srgrimes			ed = _PATH_VI;
52287596Smikeh		execlp(ed, ed, tmpf, (char *)0);
52329529Scharnier		err(1, "%s", ed);
5241553Srgrimes	}
52587596Smikeh	waitpid(pid, &status, 0);
5261553Srgrimes	sigsetmask(omask);
52787596Smikeh	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
5281553Srgrimes		return (0);
5291553Srgrimes	return (1);
5301553Srgrimes}
5311553Srgrimes
5321553Srgrimes/*
5331553Srgrimes * Convert a quotause list to an ASCII file.
5341553Srgrimes */
53529529Scharnierint
536180187Sdeswriteprivs(struct quotause *quplist, int outfd, char *name, int quotatype)
5371553Srgrimes{
538180187Sdes	struct quotause *qup;
5391553Srgrimes	FILE *fd;
5401553Srgrimes
5411553Srgrimes	ftruncate(outfd, 0);
5421553Srgrimes	lseek(outfd, 0, L_SET);
54329529Scharnier	if ((fd = fdopen(dup(outfd), "w")) == NULL)
54429529Scharnier		err(1, "%s", tmpfil);
5451553Srgrimes	fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
5461553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
54767794Sgallatin		fprintf(fd, "%s: %s %lu, limits (soft = %lu, hard = %lu)\n",
548102362Sschweikh		    qup->fsname, "kbytes in use:",
5498325Sbde		    (unsigned long)(dbtob(qup->dqblk.dqb_curblocks) / 1024),
5508325Sbde		    (unsigned long)(dbtob(qup->dqblk.dqb_bsoftlimit) / 1024),
5518325Sbde		    (unsigned long)(dbtob(qup->dqblk.dqb_bhardlimit) / 1024));
55267794Sgallatin		fprintf(fd, "%s %lu, limits (soft = %lu, hard = %lu)\n",
55367794Sgallatin		    "\tinodes in use:",
55467794Sgallatin		    (unsigned long)qup->dqblk.dqb_curinodes,
55567794Sgallatin		    (unsigned long)qup->dqblk.dqb_isoftlimit,
55667794Sgallatin		    (unsigned long)qup->dqblk.dqb_ihardlimit);
5571553Srgrimes	}
5581553Srgrimes	fclose(fd);
5591553Srgrimes	return (1);
5601553Srgrimes}
5611553Srgrimes
5621553Srgrimes/*
5631553Srgrimes * Merge changes to an ASCII file into a quotause list.
5641553Srgrimes */
56529529Scharnierint
566180187Sdesreadprivs(struct quotause *quplist, char *inname)
5671553Srgrimes{
568180187Sdes	struct quotause *qup;
5691553Srgrimes	FILE *fd;
57067794Sgallatin	unsigned long bhardlimit, bsoftlimit, curblocks;
57167794Sgallatin	unsigned long ihardlimit, isoftlimit, curinodes;
5721553Srgrimes	int cnt;
573180187Sdes	char *cp;
5741553Srgrimes	struct dqblk dqblk;
5751553Srgrimes	char *fsp, line1[BUFSIZ], line2[BUFSIZ];
5761553Srgrimes
5774782Sache	fd = fopen(inname, "r");
5781553Srgrimes	if (fd == NULL) {
57929529Scharnier		warnx("can't re-read temp file!!");
5801553Srgrimes		return (0);
5811553Srgrimes	}
5821553Srgrimes	/*
5831553Srgrimes	 * Discard title line, then read pairs of lines to process.
5841553Srgrimes	 */
5851553Srgrimes	(void) fgets(line1, sizeof (line1), fd);
5861553Srgrimes	while (fgets(line1, sizeof (line1), fd) != NULL &&
5871553Srgrimes	       fgets(line2, sizeof (line2), fd) != NULL) {
5881553Srgrimes		if ((fsp = strtok(line1, " \t:")) == NULL) {
58929529Scharnier			warnx("%s: bad format", line1);
5901553Srgrimes			return (0);
5911553Srgrimes		}
5921553Srgrimes		if ((cp = strtok((char *)0, "\n")) == NULL) {
59329529Scharnier			warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
5941553Srgrimes			return (0);
5951553Srgrimes		}
5961553Srgrimes		cnt = sscanf(cp,
597102362Sschweikh		    " kbytes in use: %lu, limits (soft = %lu, hard = %lu)",
59867794Sgallatin		    &curblocks, &bsoftlimit, &bhardlimit);
5991553Srgrimes		if (cnt != 3) {
60029529Scharnier			warnx("%s:%s: bad format", fsp, cp);
6011553Srgrimes			return (0);
6021553Srgrimes		}
60367794Sgallatin		dqblk.dqb_curblocks = btodb((off_t)curblocks * 1024);
60467794Sgallatin		dqblk.dqb_bsoftlimit = btodb((off_t)bsoftlimit * 1024);
60567794Sgallatin		dqblk.dqb_bhardlimit = btodb((off_t)bhardlimit * 1024);
6061553Srgrimes		if ((cp = strtok(line2, "\n")) == NULL) {
60729529Scharnier			warnx("%s: %s: bad format", fsp, line2);
6081553Srgrimes			return (0);
6091553Srgrimes		}
6101553Srgrimes		cnt = sscanf(cp,
61167794Sgallatin		    "\tinodes in use: %lu, limits (soft = %lu, hard = %lu)",
61267794Sgallatin		    &curinodes, &isoftlimit, &ihardlimit);
6131553Srgrimes		if (cnt != 3) {
61429529Scharnier			warnx("%s: %s: bad format", fsp, line2);
6151553Srgrimes			return (0);
6161553Srgrimes		}
61767794Sgallatin		dqblk.dqb_curinodes = curinodes;
61867794Sgallatin		dqblk.dqb_isoftlimit = isoftlimit;
61967794Sgallatin		dqblk.dqb_ihardlimit = ihardlimit;
6201553Srgrimes		for (qup = quplist; qup; qup = qup->next) {
6211553Srgrimes			if (strcmp(fsp, qup->fsname))
6221553Srgrimes				continue;
6231553Srgrimes			/*
6241553Srgrimes			 * Cause time limit to be reset when the quota
6251553Srgrimes			 * is next used if previously had no soft limit
6261553Srgrimes			 * or were under it, but now have a soft limit
6271553Srgrimes			 * and are over it.
6281553Srgrimes			 */
6291553Srgrimes			if (dqblk.dqb_bsoftlimit &&
6301553Srgrimes			    qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
6311553Srgrimes			    (qup->dqblk.dqb_bsoftlimit == 0 ||
6321553Srgrimes			     qup->dqblk.dqb_curblocks <
6331553Srgrimes			     qup->dqblk.dqb_bsoftlimit))
6341553Srgrimes				qup->dqblk.dqb_btime = 0;
6351553Srgrimes			if (dqblk.dqb_isoftlimit &&
6361553Srgrimes			    qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
6371553Srgrimes			    (qup->dqblk.dqb_isoftlimit == 0 ||
6381553Srgrimes			     qup->dqblk.dqb_curinodes <
6391553Srgrimes			     qup->dqblk.dqb_isoftlimit))
6401553Srgrimes				qup->dqblk.dqb_itime = 0;
6411553Srgrimes			qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
6421553Srgrimes			qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
6431553Srgrimes			qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
6441553Srgrimes			qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
6451553Srgrimes			qup->flags |= FOUND;
6461553Srgrimes			if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
6471553Srgrimes			    dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
6481553Srgrimes				break;
64929529Scharnier			warnx("%s: cannot change current allocation", fsp);
6501553Srgrimes			break;
6511553Srgrimes		}
6521553Srgrimes	}
6531553Srgrimes	fclose(fd);
6541553Srgrimes	/*
6551553Srgrimes	 * Disable quotas for any filesystems that have not been found.
6561553Srgrimes	 */
6571553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
6581553Srgrimes		if (qup->flags & FOUND) {
6591553Srgrimes			qup->flags &= ~FOUND;
6601553Srgrimes			continue;
6611553Srgrimes		}
6621553Srgrimes		qup->dqblk.dqb_bsoftlimit = 0;
6631553Srgrimes		qup->dqblk.dqb_bhardlimit = 0;
6641553Srgrimes		qup->dqblk.dqb_isoftlimit = 0;
6651553Srgrimes		qup->dqblk.dqb_ihardlimit = 0;
6661553Srgrimes	}
6671553Srgrimes	return (1);
6681553Srgrimes}
6691553Srgrimes
6701553Srgrimes/*
6711553Srgrimes * Convert a quotause list to an ASCII file of grace times.
6721553Srgrimes */
67329529Scharnierint
674180187Sdeswritetimes(struct quotause *quplist, int outfd, int quotatype)
6751553Srgrimes{
676180187Sdes	struct quotause *qup;
6771553Srgrimes	FILE *fd;
6781553Srgrimes
6791553Srgrimes	ftruncate(outfd, 0);
6801553Srgrimes	lseek(outfd, 0, L_SET);
68129529Scharnier	if ((fd = fdopen(dup(outfd), "w")) == NULL)
68229529Scharnier		err(1, "%s", tmpfil);
6831553Srgrimes	fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
6841553Srgrimes	fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
6851553Srgrimes	    qfextension[quotatype]);
6861553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
6871553Srgrimes		fprintf(fd, "%s: block grace period: %s, ",
6881553Srgrimes		    qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
6891553Srgrimes		fprintf(fd, "file grace period: %s\n",
6901553Srgrimes		    cvtstoa(qup->dqblk.dqb_itime));
6911553Srgrimes	}
6921553Srgrimes	fclose(fd);
6931553Srgrimes	return (1);
6941553Srgrimes}
6951553Srgrimes
6961553Srgrimes/*
6971553Srgrimes * Merge changes of grace times in an ASCII file into a quotause list.
6981553Srgrimes */
69929529Scharnierint
700180187Sdesreadtimes(struct quotause *quplist, char *inname)
7011553Srgrimes{
702180187Sdes	struct quotause *qup;
7031553Srgrimes	FILE *fd;
7041553Srgrimes	int cnt;
705180187Sdes	char *cp;
7061553Srgrimes	time_t itime, btime, iseconds, bseconds;
70767794Sgallatin	long l_itime, l_btime;
7081553Srgrimes	char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
7091553Srgrimes
7104782Sache	fd = fopen(inname, "r");
7111553Srgrimes	if (fd == NULL) {
71229529Scharnier		warnx("can't re-read temp file!!");
7131553Srgrimes		return (0);
7141553Srgrimes	}
7151553Srgrimes	/*
7161553Srgrimes	 * Discard two title lines, then read lines to process.
7171553Srgrimes	 */
7181553Srgrimes	(void) fgets(line1, sizeof (line1), fd);
7191553Srgrimes	(void) fgets(line1, sizeof (line1), fd);
7201553Srgrimes	while (fgets(line1, sizeof (line1), fd) != NULL) {
7211553Srgrimes		if ((fsp = strtok(line1, " \t:")) == NULL) {
72229529Scharnier			warnx("%s: bad format", line1);
7231553Srgrimes			return (0);
7241553Srgrimes		}
7251553Srgrimes		if ((cp = strtok((char *)0, "\n")) == NULL) {
72629529Scharnier			warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
7271553Srgrimes			return (0);
7281553Srgrimes		}
7291553Srgrimes		cnt = sscanf(cp,
7308325Sbde		    " block grace period: %ld %s file grace period: %ld %s",
73167794Sgallatin		    &l_btime, bunits, &l_itime, iunits);
7321553Srgrimes		if (cnt != 4) {
73329529Scharnier			warnx("%s:%s: bad format", fsp, cp);
7341553Srgrimes			return (0);
7351553Srgrimes		}
73667794Sgallatin		btime = l_btime;
73767794Sgallatin		itime = l_itime;
7381553Srgrimes		if (cvtatos(btime, bunits, &bseconds) == 0)
7391553Srgrimes			return (0);
7401553Srgrimes		if (cvtatos(itime, iunits, &iseconds) == 0)
7411553Srgrimes			return (0);
7421553Srgrimes		for (qup = quplist; qup; qup = qup->next) {
7431553Srgrimes			if (strcmp(fsp, qup->fsname))
7441553Srgrimes				continue;
7451553Srgrimes			qup->dqblk.dqb_btime = bseconds;
7461553Srgrimes			qup->dqblk.dqb_itime = iseconds;
7471553Srgrimes			qup->flags |= FOUND;
7481553Srgrimes			break;
7491553Srgrimes		}
7501553Srgrimes	}
7511553Srgrimes	fclose(fd);
7521553Srgrimes	/*
7531553Srgrimes	 * reset default grace periods for any filesystems
7541553Srgrimes	 * that have not been found.
7551553Srgrimes	 */
7561553Srgrimes	for (qup = quplist; qup; qup = qup->next) {
7571553Srgrimes		if (qup->flags & FOUND) {
7581553Srgrimes			qup->flags &= ~FOUND;
7591553Srgrimes			continue;
7601553Srgrimes		}
7611553Srgrimes		qup->dqblk.dqb_btime = 0;
7621553Srgrimes		qup->dqblk.dqb_itime = 0;
7631553Srgrimes	}
7641553Srgrimes	return (1);
7651553Srgrimes}
7661553Srgrimes
7671553Srgrimes/*
7681553Srgrimes * Convert seconds to ASCII times.
7691553Srgrimes */
7701553Srgrimeschar *
771180187Sdescvtstoa(time_t secs)
7721553Srgrimes{
7731553Srgrimes	static char buf[20];
7741553Srgrimes
77587596Smikeh	if (secs % (24 * 60 * 60) == 0) {
77687596Smikeh		secs /= 24 * 60 * 60;
77787596Smikeh		sprintf(buf, "%ld day%s", (long)secs, secs == 1 ? "" : "s");
77887596Smikeh	} else if (secs % (60 * 60) == 0) {
77987596Smikeh		secs /= 60 * 60;
78087596Smikeh		sprintf(buf, "%ld hour%s", (long)secs, secs == 1 ? "" : "s");
78187596Smikeh	} else if (secs % 60 == 0) {
78287596Smikeh		secs /= 60;
78387596Smikeh		sprintf(buf, "%ld minute%s", (long)secs, secs == 1 ? "" : "s");
7841553Srgrimes	} else
78587596Smikeh		sprintf(buf, "%ld second%s", (long)secs, secs == 1 ? "" : "s");
7861553Srgrimes	return (buf);
7871553Srgrimes}
7881553Srgrimes
7891553Srgrimes/*
7901553Srgrimes * Convert ASCII input times to seconds.
7911553Srgrimes */
79229529Scharnierint
793180187Sdescvtatos(time_t period, char *units, time_t *seconds)
7941553Srgrimes{
7951553Srgrimes
7961553Srgrimes	if (bcmp(units, "second", 6) == 0)
79787596Smikeh		*seconds = period;
7981553Srgrimes	else if (bcmp(units, "minute", 6) == 0)
79987596Smikeh		*seconds = period * 60;
8001553Srgrimes	else if (bcmp(units, "hour", 4) == 0)
80187596Smikeh		*seconds = period * 60 * 60;
8021553Srgrimes	else if (bcmp(units, "day", 3) == 0)
80387596Smikeh		*seconds = period * 24 * 60 * 60;
8041553Srgrimes	else {
8051553Srgrimes		printf("%s: bad units, specify %s\n", units,
8061553Srgrimes		    "days, hours, minutes, or seconds");
8071553Srgrimes		return (0);
8081553Srgrimes	}
8091553Srgrimes	return (1);
8101553Srgrimes}
8111553Srgrimes
8121553Srgrimes/*
8131553Srgrimes * Free a list of quotause structures.
8141553Srgrimes */
81529529Scharniervoid
816180187Sdesfreeprivs(struct quotause *quplist)
8171553Srgrimes{
818180187Sdes	struct quotause *qup, *nextqup;
8191553Srgrimes
8201553Srgrimes	for (qup = quplist; qup; qup = nextqup) {
8211553Srgrimes		nextqup = qup->next;
8221553Srgrimes		free(qup);
8231553Srgrimes	}
8241553Srgrimes}
8251553Srgrimes
8261553Srgrimes/*
8271553Srgrimes * Check whether a string is completely composed of digits.
8281553Srgrimes */
82929529Scharnierint
830180187Sdesalldigits(const char *s)
8311553Srgrimes{
832180187Sdes	int c;
8331553Srgrimes
8341553Srgrimes	c = *s++;
8351553Srgrimes	do {
8361553Srgrimes		if (!isdigit(c))
8371553Srgrimes			return (0);
83829529Scharnier	} while ((c = *s++));
8391553Srgrimes	return (1);
8401553Srgrimes}
8411553Srgrimes
8421553Srgrimes/*
8431553Srgrimes * Check to see if a particular quota is to be enabled.
8441553Srgrimes */
84529529Scharnierint
846180187Sdeshasquota(struct fstab *fs, int type, char **qfnamep)
8471553Srgrimes{
848166485Smpp	char *opt;
84929529Scharnier	char *cp;
850166485Smpp	struct statfs sfb;
8511553Srgrimes	static char initname, usrname[100], grpname[100];
8521553Srgrimes	static char buf[BUFSIZ];
8531553Srgrimes
8541553Srgrimes	if (!initname) {
855166485Smpp		(void)snprintf(usrname, sizeof(usrname), "%s%s",
856166485Smpp		    qfextension[USRQUOTA], qfname);
857166485Smpp		(void)snprintf(grpname, sizeof(grpname), "%s%s",
858166485Smpp		    qfextension[GRPQUOTA], qfname);
8591553Srgrimes		initname = 1;
8601553Srgrimes	}
8611553Srgrimes	strcpy(buf, fs->fs_mntops);
8621553Srgrimes	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
86329529Scharnier		if ((cp = index(opt, '=')))
8641553Srgrimes			*cp++ = '\0';
8651553Srgrimes		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
8661553Srgrimes			break;
8671553Srgrimes		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
8681553Srgrimes			break;
8691553Srgrimes	}
8701553Srgrimes	if (!opt)
8711553Srgrimes		return (0);
872166485Smpp	if (cp)
8731553Srgrimes		*qfnamep = cp;
874166485Smpp	else {
875166485Smpp		(void)snprintf(buf, sizeof(buf), "%s/%s.%s", fs->fs_file,
876166485Smpp		    qfname, qfextension[type]);
877166485Smpp		*qfnamep = buf;
8781553Srgrimes	}
879166485Smpp	if (statfs(fs->fs_file, &sfb) != 0) {
880166485Smpp		warn("cannot statfs mount point %s", fs->fs_file);
881166485Smpp		return (0);
882166485Smpp	}
883166485Smpp	if (strcmp(fs->fs_file, sfb.f_mntonname)) {
884166485Smpp		warnx("%s not mounted for %s quotas", fs->fs_file,
885166485Smpp		    type == USRQUOTA ? "user" : "group");
886166485Smpp		sleep(3);
887166485Smpp		return (0);
888166485Smpp	}
8891553Srgrimes	return (1);
8901553Srgrimes}
891