xref: /csrg-svn/usr.sbin/edquota/edquota.c (revision 25401)
121516Smckusick /*
221516Smckusick  * Copyright (c) 1980 Regents of the University of California.
321516Smckusick  * All rights reserved.  The Berkeley software License Agreement
421516Smckusick  * specifies the terms and conditions for redistribution.
521516Smckusick  */
621516Smckusick 
712707Smckusick #ifndef lint
821516Smckusick char copyright[] =
921516Smckusick "@(#) Copyright (c) 1980 Regents of the University of California.\n\
1021516Smckusick  All rights reserved.\n";
1121516Smckusick #endif not lint
1212707Smckusick 
1321516Smckusick #ifndef lint
14*25401Sbloom static char sccsid[] = "@(#)edquota.c	5.3 (Berkeley) 11/04/85";
1521516Smckusick #endif not lint
1621516Smckusick 
1712707Smckusick /*
1812707Smckusick  * Disk quota editor.
1912707Smckusick  */
2012707Smckusick #include <stdio.h>
2112707Smckusick #include <signal.h>
2212707Smckusick #include <errno.h>
2312707Smckusick #include <pwd.h>
2412707Smckusick #include <ctype.h>
2512707Smckusick #include <fstab.h>
2612707Smckusick 
2712707Smckusick #include <sys/param.h>
2812803Smckusick #include <sys/stat.h>
2912803Smckusick #include <sys/file.h>
3012707Smckusick #include <sys/quota.h>
3112707Smckusick 
3212707Smckusick #define	DEFEDITOR	"/usr/ucb/vi"
3312707Smckusick 
3412707Smckusick struct	dquot dq[NMOUNT];
3512707Smckusick struct	dquot odq[NMOUNT];
3612707Smckusick char	dqf[NMOUNT][MAXPATHLEN + 1];
3712707Smckusick char	odqf[NMOUNT][MAXPATHLEN + 1];
3812707Smckusick 
3912707Smckusick char	tmpfil[] = "/tmp/EdP.aXXXXX";
4012716Smckusick char	*qfname = "quotas";
4112707Smckusick char	*getenv();
4212707Smckusick 
4312707Smckusick main(argc, argv)
4412707Smckusick 	char **argv;
4512707Smckusick {
4612803Smckusick 	int uid;
4712803Smckusick 	char *arg0;
4812707Smckusick 
4912707Smckusick 	mktemp(tmpfil);
5012707Smckusick 	close(creat(tmpfil, 0600));
5112707Smckusick 	chown(tmpfil, getuid(), getgid());
5212707Smckusick 	arg0 = *argv++;
5312707Smckusick 	if (argc < 2) {
5412803Smckusick 		fprintf(stderr, "Usage: %s [-p username] username ...\n", arg0);
5512707Smckusick 		unlink(tmpfil);
5612707Smckusick 		exit(1);
5712707Smckusick 	}
5812707Smckusick 	--argc;
5912707Smckusick 	if (getuid()) {
6012707Smckusick 		fprintf(stderr, "%s: permission denied\n", arg0);
6112707Smckusick 		unlink(tmpfil);
6212707Smckusick 		exit(1);
6312707Smckusick 	}
6412803Smckusick 	if (argc > 2 && strcmp(*argv, "-p") == 0) {
6512803Smckusick 		argc--, argv++;
6612803Smckusick 		uid = getentry(*argv++);
6712803Smckusick 		if (uid < 0) {
6812803Smckusick 			unlink(tmpfil);
6912803Smckusick 			exit(1);
7012803Smckusick 		}
7112803Smckusick 		getprivs(uid);
7212803Smckusick 		argc--;
7312803Smckusick 		while (argc-- > 0) {
7412803Smckusick 			uid = getentry(*argv++);
7512803Smckusick 			if (uid < 0)
7612803Smckusick 				continue;
7712803Smckusick 			getdiscq(uid, odq, odqf);
7812803Smckusick 			putprivs(uid);
7912803Smckusick 		}
8012803Smckusick 		unlink(tmpfil);
8112803Smckusick 		exit(0);
8212803Smckusick 	}
8312803Smckusick 	while (--argc >= 0) {
8412803Smckusick 		uid = getentry(*argv++);
8512803Smckusick 		if (uid < 0)
8612803Smckusick 			continue;
8712803Smckusick 		getprivs(uid);
8812803Smckusick 		if (editit())
8912803Smckusick 			putprivs(uid);
9012803Smckusick 	}
9112707Smckusick 	unlink(tmpfil);
9212707Smckusick 	exit(0);
9312707Smckusick }
9412707Smckusick 
9512803Smckusick getentry(name)
9612803Smckusick 	char *name;
9712707Smckusick {
9812803Smckusick 	struct passwd *pw;
9912803Smckusick 	int uid;
10012707Smckusick 
10112707Smckusick 	if (alldigits(name))
10212707Smckusick 		uid = atoi(name);
10312707Smckusick 	else if (pw = getpwnam(name))
10412707Smckusick 		uid = pw->pw_uid;
10512707Smckusick 	else {
10612803Smckusick 		fprintf(stderr, "%s: no such user\n", name);
10712707Smckusick 		sleep(1);
10812803Smckusick 		return (-1);
10912707Smckusick 	}
11012803Smckusick 	return (uid);
11112707Smckusick }
11212707Smckusick 
11312707Smckusick editit()
11412707Smckusick {
11512707Smckusick 	register pid, xpid;
11613025Ssam 	int stat, omask;
11712707Smckusick 
11813025Ssam #define	mask(s)	(1<<((s)-1))
11913025Ssam 	omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGHUP));
12012707Smckusick  top:
12112707Smckusick 	if ((pid = fork()) < 0) {
12212707Smckusick 		extern errno;
12312707Smckusick 
12412707Smckusick 		if (errno == EPROCLIM) {
12512707Smckusick 			fprintf(stderr, "You have too many processes\n");
12612707Smckusick 			return(0);
12712707Smckusick 		}
12812707Smckusick 		if (errno == EAGAIN) {
12912707Smckusick 			sleep(1);
13012707Smckusick 			goto top;
13112707Smckusick 		}
13212707Smckusick 		perror("fork");
13312707Smckusick 		return (0);
13412707Smckusick 	}
13512707Smckusick 	if (pid == 0) {
13612707Smckusick 		register char *ed;
13712707Smckusick 
13813025Ssam 		sigsetmask(omask);
13912707Smckusick 		setgid(getgid());
14012707Smckusick 		setuid(getuid());
14112707Smckusick 		if ((ed = getenv("EDITOR")) == (char *)0)
14212707Smckusick 			ed = DEFEDITOR;
14312707Smckusick 		execlp(ed, ed, tmpfil, 0);
14412707Smckusick 		perror(ed);
14512707Smckusick 		exit(1);
14612707Smckusick 	}
14712707Smckusick 	while ((xpid = wait(&stat)) >= 0)
14812707Smckusick 		if (xpid == pid)
14912707Smckusick 			break;
15013025Ssam 	sigsetmask(omask);
15112707Smckusick 	return (!stat);
15212707Smckusick }
15312707Smckusick 
15412707Smckusick getprivs(uid)
15512707Smckusick 	register uid;
15612707Smckusick {
15712707Smckusick 	register i;
15812707Smckusick 	FILE *fd;
15912707Smckusick 
16012707Smckusick 	getdiscq(uid, dq, dqf);
16112707Smckusick 	for (i = 0; i < NMOUNT; i++) {
16212707Smckusick 		odq[i] = dq[i];
16312707Smckusick 		strcpy(odqf[i], dqf[i]);
16412707Smckusick 	}
16512707Smckusick 	if ((fd = fopen(tmpfil, "w")) == NULL) {
16612707Smckusick 		fprintf(stderr, "edquota: ");
16712707Smckusick 		perror(tmpfil);
16812707Smckusick 		exit(1);
16912707Smckusick 	}
17012707Smckusick 	for (i = 0; i < NMOUNT; i++) {
17112707Smckusick 		if (*dqf[i] == '\0')
17212707Smckusick 			continue;
17312707Smckusick 		fprintf(fd,
17412707Smckusick "fs %s blocks (soft = %d, hard = %d) inodes (soft = %d, hard = %d)\n"
17512707Smckusick 			, dqf[i]
176*25401Sbloom 			, dbtob(dq[i].dq_bsoftlimit) / 1024
177*25401Sbloom 			, dbtob(dq[i].dq_bhardlimit) / 1024
17812707Smckusick 			, dq[i].dq_isoftlimit
17912707Smckusick 			, dq[i].dq_ihardlimit
18012707Smckusick 		);
18112707Smckusick 	}
18212707Smckusick 	fclose(fd);
18312707Smckusick }
18412707Smckusick 
18512707Smckusick putprivs(uid)
18612707Smckusick 	register uid;
18712707Smckusick {
18812707Smckusick 	register i, j;
18912707Smckusick 	int n;
19012707Smckusick 	FILE *fd;
19112707Smckusick 	char line[BUFSIZ];
19212707Smckusick 
19312707Smckusick 	fd = fopen(tmpfil, "r");
19412707Smckusick 	if (fd == NULL) {
19512707Smckusick 		fprintf(stderr, "Can't re-read temp file!!\n");
19612707Smckusick 		return;
19712707Smckusick 	}
19812707Smckusick 	for (i = 0; i < NMOUNT; i++) {
19912707Smckusick 		char *cp, *dp, *next();
20012707Smckusick 
20112707Smckusick 		if (fgets(line, sizeof (line), fd) == NULL)
20212707Smckusick 			break;
20312707Smckusick 		cp = next(line, " \t");
20412707Smckusick 		if (cp == NULL)
20512707Smckusick 			break;
20612707Smckusick 		*cp++ = '\0';
20712707Smckusick 		while (*cp && *cp == '\t' && *cp == ' ')
20812707Smckusick 			cp++;
20912707Smckusick 		dp = cp, cp = next(cp, " \t");
21012707Smckusick 		if (cp == NULL)
21112707Smckusick 			break;
21212707Smckusick 		*cp++ = '\0';
21312707Smckusick 		while (*cp && *cp == '\t' && *cp == ' ')
21412707Smckusick 			cp++;
21512707Smckusick 		strcpy(dqf[i], dp);
21612707Smckusick 		n = sscanf(cp,
21712707Smckusick "blocks (soft = %d, hard = %d) inodes (soft = %hd, hard = %hd)\n"
21812707Smckusick 			, &dq[i].dq_bsoftlimit
21912707Smckusick 			, &dq[i].dq_bhardlimit
22012707Smckusick 			, &dq[i].dq_isoftlimit
22112707Smckusick 			, &dq[i].dq_ihardlimit
22212707Smckusick 		);
22312716Smckusick 		if (n != 4) {
22412716Smckusick 			fprintf(stderr, "%s: bad format\n", cp);
22512716Smckusick 			continue;
22612716Smckusick 		}
227*25401Sbloom 		dq[i].dq_bsoftlimit = btodb(dq[i].dq_bsoftlimit * 1024);
228*25401Sbloom 		dq[i].dq_bhardlimit = btodb(dq[i].dq_bhardlimit * 1024);
22912707Smckusick 	}
23012707Smckusick 	fclose(fd);
23112707Smckusick 	n = i;
23212707Smckusick 	for (i = 0; i < n; i++) {
23312707Smckusick 		if (*dqf[i] == '\0')
23412707Smckusick 			break;
23512707Smckusick 		for (j = 0; j < NMOUNT; j++) {
23612707Smckusick 			if (strcmp(dqf[i], odqf[j]) == 0)
23712707Smckusick 				break;
23812707Smckusick 		}
23912707Smckusick 		if (j >= NMOUNT)
24012707Smckusick 			continue;
24112707Smckusick 		*odqf[j] = '\0';
24212707Smckusick 		/*
24312707Smckusick 		 * This isn't really good enough, it is quite likely
24412707Smckusick 		 * to have changed while we have been away editing,
24512707Smckusick 		 * but it's not important enough to worry about at
24612707Smckusick 		 * the minute.
24712707Smckusick 		 */
24812707Smckusick 		dq[i].dq_curblocks = odq[j].dq_curblocks;
24912707Smckusick 		dq[i].dq_curinodes = odq[j].dq_curinodes;
25012707Smckusick 		/*
25112707Smckusick 		 * If we've upped the inode or disk block limits
25212707Smckusick 		 * and the guy is out of warnings, reinitialize.
25312707Smckusick 		 */
25412707Smckusick 		if (dq[i].dq_bsoftlimit > odq[j].dq_bsoftlimit &&
25512707Smckusick 		    dq[i].dq_bwarn == 0)
25612707Smckusick 			dq[i].dq_bwarn = MAX_DQ_WARN;
25712707Smckusick 		if (dq[i].dq_isoftlimit > odq[j].dq_isoftlimit &&
25812707Smckusick 		    dq[i].dq_iwarn == 0)
25912707Smckusick 			dq[i].dq_iwarn = MAX_IQ_WARN;
26012707Smckusick 	}
26112707Smckusick 	if (i < NMOUNT) {
26212707Smckusick 		for (j = 0; j < NMOUNT; j++) {
26312707Smckusick 			if (*odqf[j] == '\0')
26412707Smckusick 				continue;
26512707Smckusick 			strcpy(dqf[i], odqf[j]);
26612707Smckusick 			dq[i].dq_isoftlimit = 0;
26712707Smckusick 			dq[i].dq_ihardlimit = 0;
26812707Smckusick 			dq[i].dq_bsoftlimit = 0;
26912707Smckusick 			dq[i].dq_bhardlimit = 0;
27012707Smckusick 			/*
27112707Smckusick 			 * Same applies as just above
27212707Smckusick 			 * but matters not at all, as we are just
27312707Smckusick 			 * turning quota'ing off for this filesys.
27412707Smckusick 			 */
27512707Smckusick 			dq[i].dq_curblocks = odq[j].dq_curblocks;
27612707Smckusick 			dq[i].dq_curinodes = odq[j].dq_curinodes;
27712707Smckusick 			if (++i >= NMOUNT)
27812707Smckusick 				break;
27912707Smckusick 		}
28012707Smckusick 	}
28112707Smckusick 	if (*dqf[0])
28212707Smckusick 		putdiscq(uid, dq, dqf);
28312707Smckusick }
28412707Smckusick 
28512707Smckusick char *
28612707Smckusick next(cp, match)
28712707Smckusick 	register char *cp;
28812707Smckusick 	char *match;
28912707Smckusick {
29012707Smckusick 	register char *dp;
29112707Smckusick 
29212707Smckusick 	while (cp && *cp) {
29312707Smckusick 		for (dp = match; dp && *dp; dp++)
29412707Smckusick 			if (*dp == *cp)
29512707Smckusick 				return (cp);
29612707Smckusick 		cp++;
29712707Smckusick 	}
29812707Smckusick 	return ((char *)0);
29912707Smckusick }
30012707Smckusick 
30112707Smckusick alldigits(s)
30212707Smckusick 	register char *s;
30312707Smckusick {
30412707Smckusick 	register c;
30512707Smckusick 
30612707Smckusick 	c = *s++;
30712707Smckusick 	do {
30812707Smckusick 		if (!isdigit(c))
30912707Smckusick 			return (0);
31012707Smckusick 	} while (c = *s++);
31112707Smckusick 	return (1);
31212707Smckusick }
31312707Smckusick 
31412707Smckusick getdiscq(uid, dq, dqf)
31512707Smckusick 	register uid;
31612707Smckusick 	register struct dquot *dq;
31712707Smckusick 	register char (*dqf)[MAXPATHLEN + 1];
31812707Smckusick {
31912707Smckusick 	register struct fstab *fs;
32012716Smckusick 	char qfilename[MAXPATHLEN + 1];
32121087Smckusick 	struct stat statb;
32221087Smckusick 	struct dqblk dqblk;
32321087Smckusick 	dev_t fsdev;
32421087Smckusick 	int fd;
32521087Smckusick 	static int warned = 0;
32621087Smckusick 	extern int errno;
32712707Smckusick 
32812707Smckusick 	setfsent();
32912707Smckusick 	while (fs = getfsent()) {
33012707Smckusick 		if (stat(fs->fs_spec, &statb) < 0)
33112707Smckusick 			continue;
33212707Smckusick 		fsdev = statb.st_rdev;
33312716Smckusick 		sprintf(qfilename, "%s/%s", fs->fs_file, qfname);
33412716Smckusick 		if (stat(qfilename, &statb) < 0 || statb.st_dev != fsdev)
33512707Smckusick 			continue;
33612707Smckusick 		if (quota(Q_GETDLIM, uid, fsdev, &dqblk) != 0) {
33721087Smckusick 	    		if (errno == EINVAL && !warned) {
33821087Smckusick 				warned++;
33921087Smckusick 				fprintf(stderr, "Warning: %s\n",
34021087Smckusick 				    "Quotas are not compiled into this kernel");
34121087Smckusick 				sleep(3);
34221087Smckusick 			}
34321087Smckusick 			fd = open(qfilename, O_RDONLY);
34412707Smckusick 			if (fd < 0)
34512707Smckusick 				continue;
34612722Smckusick 			lseek(fd, (long)(uid * sizeof dqblk), L_SET);
34724659Sserge 			switch (read(fd, &dqblk, sizeof dqblk)) {
34824659Sserge 			case 0:			/* EOF */
34924659Sserge 				/*
35024659Sserge 				 * Convert implicit 0 quota (EOF)
35124659Sserge 				 * into an explicit one (zero'ed dqblk)
35224659Sserge 				 */
35324659Sserge 				bzero((caddr_t)&dqblk, sizeof dqblk);
35424659Sserge 				break;
35524659Sserge 
35624659Sserge 			case sizeof dqblk:	/* OK */
35724659Sserge 				break;
35824659Sserge 
35924659Sserge 			default:		/* ERROR */
36024659Sserge 				fprintf(stderr, "edquota: read error in ");
36124659Sserge 				perror(qfilename);
36212707Smckusick 				close(fd);
36312707Smckusick 				continue;
36412707Smckusick 			}
36512707Smckusick 			close(fd);
36612707Smckusick 		}
36712707Smckusick 		dq->dq_dqb = dqblk;
36812707Smckusick 		dq->dq_dev = fsdev;
36912707Smckusick 		strcpy(*dqf, fs->fs_file);
37012707Smckusick 		dq++, dqf++;
37112707Smckusick 	}
37212707Smckusick 	endfsent();
37312707Smckusick 	**dqf = '\0';
37412707Smckusick }
37512707Smckusick 
37612707Smckusick putdiscq(uid, dq, dqf)
37712707Smckusick 	register uid;
37812707Smckusick 	register struct dquot *dq;
37912707Smckusick 	register char (*dqf)[MAXPATHLEN + 1];
38012707Smckusick {
38112707Smckusick 	register fd, cnt;
38212707Smckusick 	struct stat sb;
38312707Smckusick 	struct fstab *fs;
38412707Smckusick 
38512707Smckusick 	cnt = 0;
38612707Smckusick 	for (cnt = 0; ++cnt <= NMOUNT && **dqf; dq++, dqf++) {
38712707Smckusick 		fs = getfsfile(*dqf);
38812716Smckusick 		if (fs == NULL) {
38912716Smckusick 			fprintf(stderr, "%s: not in /etc/fstab\n", *dqf);
39012716Smckusick 			continue;
39112716Smckusick 		}
39212716Smckusick 		strcat(*dqf, "/");
39312716Smckusick 		strcat(*dqf, qfname);
39412716Smckusick 		if (stat(*dqf, &sb) >= 0)
39512716Smckusick 			quota(Q_SETDLIM, uid, sb.st_dev, &dq->dq_dqb);
39612716Smckusick 		if ((fd = open(*dqf, 1)) < 0) {
39712716Smckusick 			perror(*dqf);
39812716Smckusick 		} else {
39912707Smckusick 			lseek(fd, (long)uid * (long)sizeof (struct dqblk), 0);
40012707Smckusick 			if (write(fd, &dq->dq_dqb, sizeof (struct dqblk)) !=
40112707Smckusick 			    sizeof (struct dqblk)) {
40212707Smckusick 				fprintf(stderr, "edquota: ");
40312707Smckusick 				perror(*dqf);
40412707Smckusick 			}
40512707Smckusick 			close(fd);
40612707Smckusick 		}
40712707Smckusick 	}
40812707Smckusick }
409