xref: /csrg-svn/usr.sbin/edquota/edquota.c (revision 21087)
112707Smckusick #ifndef lint
2*21087Smckusick static char sccsid[] = "@(#)edquota.c	4.6 (Berkeley, from Melbourne) 05/24/85";
312707Smckusick #endif
412707Smckusick 
512707Smckusick /*
612707Smckusick  * Disk quota editor.
712707Smckusick  */
812707Smckusick #include <stdio.h>
912707Smckusick #include <signal.h>
1012707Smckusick #include <errno.h>
1112707Smckusick #include <pwd.h>
1212707Smckusick #include <ctype.h>
1312707Smckusick #include <fstab.h>
1412707Smckusick 
1512707Smckusick #include <sys/param.h>
1612803Smckusick #include <sys/stat.h>
1712803Smckusick #include <sys/file.h>
1812707Smckusick #include <sys/quota.h>
1912707Smckusick 
2012707Smckusick #define	DEFEDITOR	"/usr/ucb/vi"
2112707Smckusick 
2212707Smckusick struct	dquot dq[NMOUNT];
2312707Smckusick struct	dquot odq[NMOUNT];
2412707Smckusick char	dqf[NMOUNT][MAXPATHLEN + 1];
2512707Smckusick char	odqf[NMOUNT][MAXPATHLEN + 1];
2612707Smckusick 
2712707Smckusick char	tmpfil[] = "/tmp/EdP.aXXXXX";
2812716Smckusick char	*qfname = "quotas";
2912707Smckusick char	*getenv();
3012707Smckusick 
3112707Smckusick main(argc, argv)
3212707Smckusick 	char **argv;
3312707Smckusick {
3412803Smckusick 	int uid;
3512803Smckusick 	char *arg0;
3612707Smckusick 
3712707Smckusick 	mktemp(tmpfil);
3812707Smckusick 	close(creat(tmpfil, 0600));
3912707Smckusick 	chown(tmpfil, getuid(), getgid());
4012707Smckusick 	arg0 = *argv++;
4112707Smckusick 	if (argc < 2) {
4212803Smckusick 		fprintf(stderr, "Usage: %s [-p username] username ...\n", arg0);
4312707Smckusick 		unlink(tmpfil);
4412707Smckusick 		exit(1);
4512707Smckusick 	}
4612707Smckusick 	--argc;
4712707Smckusick 	if (getuid()) {
4812707Smckusick 		fprintf(stderr, "%s: permission denied\n", arg0);
4912707Smckusick 		unlink(tmpfil);
5012707Smckusick 		exit(1);
5112707Smckusick 	}
5212803Smckusick 	if (argc > 2 && strcmp(*argv, "-p") == 0) {
5312803Smckusick 		argc--, argv++;
5412803Smckusick 		uid = getentry(*argv++);
5512803Smckusick 		if (uid < 0) {
5612803Smckusick 			unlink(tmpfil);
5712803Smckusick 			exit(1);
5812803Smckusick 		}
5912803Smckusick 		getprivs(uid);
6012803Smckusick 		argc--;
6112803Smckusick 		while (argc-- > 0) {
6212803Smckusick 			uid = getentry(*argv++);
6312803Smckusick 			if (uid < 0)
6412803Smckusick 				continue;
6512803Smckusick 			getdiscq(uid, odq, odqf);
6612803Smckusick 			putprivs(uid);
6712803Smckusick 		}
6812803Smckusick 		unlink(tmpfil);
6912803Smckusick 		exit(0);
7012803Smckusick 	}
7112803Smckusick 	while (--argc >= 0) {
7212803Smckusick 		uid = getentry(*argv++);
7312803Smckusick 		if (uid < 0)
7412803Smckusick 			continue;
7512803Smckusick 		getprivs(uid);
7612803Smckusick 		if (editit())
7712803Smckusick 			putprivs(uid);
7812803Smckusick 	}
7912707Smckusick 	unlink(tmpfil);
8012707Smckusick 	exit(0);
8112707Smckusick }
8212707Smckusick 
8312803Smckusick getentry(name)
8412803Smckusick 	char *name;
8512707Smckusick {
8612803Smckusick 	struct passwd *pw;
8712803Smckusick 	int uid;
8812707Smckusick 
8912707Smckusick 	if (alldigits(name))
9012707Smckusick 		uid = atoi(name);
9112707Smckusick 	else if (pw = getpwnam(name))
9212707Smckusick 		uid = pw->pw_uid;
9312707Smckusick 	else {
9412803Smckusick 		fprintf(stderr, "%s: no such user\n", name);
9512707Smckusick 		sleep(1);
9612803Smckusick 		return (-1);
9712707Smckusick 	}
9812803Smckusick 	return (uid);
9912707Smckusick }
10012707Smckusick 
10112707Smckusick editit()
10212707Smckusick {
10312707Smckusick 	register pid, xpid;
10413025Ssam 	int stat, omask;
10512707Smckusick 
10613025Ssam #define	mask(s)	(1<<((s)-1))
10713025Ssam 	omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGHUP));
10812707Smckusick  top:
10912707Smckusick 	if ((pid = fork()) < 0) {
11012707Smckusick 		extern errno;
11112707Smckusick 
11212707Smckusick 		if (errno == EPROCLIM) {
11312707Smckusick 			fprintf(stderr, "You have too many processes\n");
11412707Smckusick 			return(0);
11512707Smckusick 		}
11612707Smckusick 		if (errno == EAGAIN) {
11712707Smckusick 			sleep(1);
11812707Smckusick 			goto top;
11912707Smckusick 		}
12012707Smckusick 		perror("fork");
12112707Smckusick 		return (0);
12212707Smckusick 	}
12312707Smckusick 	if (pid == 0) {
12412707Smckusick 		register char *ed;
12512707Smckusick 
12613025Ssam 		sigsetmask(omask);
12712707Smckusick 		setgid(getgid());
12812707Smckusick 		setuid(getuid());
12912707Smckusick 		if ((ed = getenv("EDITOR")) == (char *)0)
13012707Smckusick 			ed = DEFEDITOR;
13112707Smckusick 		execlp(ed, ed, tmpfil, 0);
13212707Smckusick 		perror(ed);
13312707Smckusick 		exit(1);
13412707Smckusick 	}
13512707Smckusick 	while ((xpid = wait(&stat)) >= 0)
13612707Smckusick 		if (xpid == pid)
13712707Smckusick 			break;
13813025Ssam 	sigsetmask(omask);
13912707Smckusick 	return (!stat);
14012707Smckusick }
14112707Smckusick 
14212707Smckusick getprivs(uid)
14312707Smckusick 	register uid;
14412707Smckusick {
14512707Smckusick 	register i;
14612707Smckusick 	FILE *fd;
14712707Smckusick 
14812707Smckusick 	getdiscq(uid, dq, dqf);
14912707Smckusick 	for (i = 0; i < NMOUNT; i++) {
15012707Smckusick 		odq[i] = dq[i];
15112707Smckusick 		strcpy(odqf[i], dqf[i]);
15212707Smckusick 	}
15312707Smckusick 	if ((fd = fopen(tmpfil, "w")) == NULL) {
15412707Smckusick 		fprintf(stderr, "edquota: ");
15512707Smckusick 		perror(tmpfil);
15612707Smckusick 		exit(1);
15712707Smckusick 	}
15812707Smckusick 	for (i = 0; i < NMOUNT; i++) {
15912707Smckusick 		if (*dqf[i] == '\0')
16012707Smckusick 			continue;
16112707Smckusick 		fprintf(fd,
16212707Smckusick "fs %s blocks (soft = %d, hard = %d) inodes (soft = %d, hard = %d)\n"
16312707Smckusick 			, dqf[i]
16412716Smckusick 			, dq[i].dq_bsoftlimit / btodb(1024)
16512716Smckusick 			, dq[i].dq_bhardlimit / btodb(1024)
16612707Smckusick 			, dq[i].dq_isoftlimit
16712707Smckusick 			, dq[i].dq_ihardlimit
16812707Smckusick 		);
16912707Smckusick 	}
17012707Smckusick 	fclose(fd);
17112707Smckusick }
17212707Smckusick 
17312707Smckusick putprivs(uid)
17412707Smckusick 	register uid;
17512707Smckusick {
17612707Smckusick 	register i, j;
17712707Smckusick 	int n;
17812707Smckusick 	FILE *fd;
17912707Smckusick 	char line[BUFSIZ];
18012707Smckusick 
18112707Smckusick 	fd = fopen(tmpfil, "r");
18212707Smckusick 	if (fd == NULL) {
18312707Smckusick 		fprintf(stderr, "Can't re-read temp file!!\n");
18412707Smckusick 		return;
18512707Smckusick 	}
18612707Smckusick 	for (i = 0; i < NMOUNT; i++) {
18712707Smckusick 		char *cp, *dp, *next();
18812707Smckusick 
18912707Smckusick 		if (fgets(line, sizeof (line), fd) == NULL)
19012707Smckusick 			break;
19112707Smckusick 		cp = next(line, " \t");
19212707Smckusick 		if (cp == NULL)
19312707Smckusick 			break;
19412707Smckusick 		*cp++ = '\0';
19512707Smckusick 		while (*cp && *cp == '\t' && *cp == ' ')
19612707Smckusick 			cp++;
19712707Smckusick 		dp = cp, cp = next(cp, " \t");
19812707Smckusick 		if (cp == NULL)
19912707Smckusick 			break;
20012707Smckusick 		*cp++ = '\0';
20112707Smckusick 		while (*cp && *cp == '\t' && *cp == ' ')
20212707Smckusick 			cp++;
20312707Smckusick 		strcpy(dqf[i], dp);
20412707Smckusick 		n = sscanf(cp,
20512707Smckusick "blocks (soft = %d, hard = %d) inodes (soft = %hd, hard = %hd)\n"
20612707Smckusick 			, &dq[i].dq_bsoftlimit
20712707Smckusick 			, &dq[i].dq_bhardlimit
20812707Smckusick 			, &dq[i].dq_isoftlimit
20912707Smckusick 			, &dq[i].dq_ihardlimit
21012707Smckusick 		);
21112716Smckusick 		if (n != 4) {
21212716Smckusick 			fprintf(stderr, "%s: bad format\n", cp);
21312716Smckusick 			continue;
21412716Smckusick 		}
21512716Smckusick 		dq[i].dq_bsoftlimit *= btodb(1024);
21612716Smckusick 		dq[i].dq_bhardlimit *= btodb(1024);
21712707Smckusick 	}
21812707Smckusick 	fclose(fd);
21912707Smckusick 	n = i;
22012707Smckusick 	for (i = 0; i < n; i++) {
22112707Smckusick 		if (*dqf[i] == '\0')
22212707Smckusick 			break;
22312707Smckusick 		for (j = 0; j < NMOUNT; j++) {
22412707Smckusick 			if (strcmp(dqf[i], odqf[j]) == 0)
22512707Smckusick 				break;
22612707Smckusick 		}
22712707Smckusick 		if (j >= NMOUNT)
22812707Smckusick 			continue;
22912707Smckusick 		*odqf[j] = '\0';
23012707Smckusick 		if (dq[i].dq_isoftlimit == odq[j].dq_isoftlimit &&
23112707Smckusick 		    dq[i].dq_ihardlimit == odq[j].dq_ihardlimit &&
23212707Smckusick 		    dq[i].dq_bsoftlimit == odq[j].dq_bsoftlimit &&
23312707Smckusick 		    dq[i].dq_bhardlimit == odq[j].dq_bhardlimit) {
23412716Smckusick 			for (j = i; j < NMOUNT; j++) {
23512707Smckusick 				dq[j] = dq[j+1];
23612707Smckusick 				strcpy(dqf[j], dqf[j+1]);
23712707Smckusick 			}
23812707Smckusick 			*dqf[j] = '\0';
23912707Smckusick 			i--;
24012707Smckusick 			continue;
24112707Smckusick 		}
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];
321*21087Smckusick 	struct stat statb;
322*21087Smckusick 	struct dqblk dqblk;
323*21087Smckusick 	dev_t fsdev;
324*21087Smckusick 	int fd;
325*21087Smckusick 	static int warned = 0;
326*21087Smckusick 	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) {
337*21087Smckusick 	    		if (errno == EINVAL && !warned) {
338*21087Smckusick 				warned++;
339*21087Smckusick 				fprintf(stderr, "Warning: %s\n",
340*21087Smckusick 				    "Quotas are not compiled into this kernel");
341*21087Smckusick 				sleep(3);
342*21087Smckusick 			}
343*21087Smckusick 			fd = open(qfilename, O_RDONLY);
34412707Smckusick 			if (fd < 0)
34512707Smckusick 				continue;
34612722Smckusick 			lseek(fd, (long)(uid * sizeof dqblk), L_SET);
34712707Smckusick 			if (read(fd, &dqblk, sizeof dqblk) != sizeof (dqblk)) {
34812707Smckusick 				close(fd);
34912707Smckusick 				continue;
35012707Smckusick 			}
35112707Smckusick 			close(fd);
35212707Smckusick 		}
35312707Smckusick 		dq->dq_dqb = dqblk;
35412707Smckusick 		dq->dq_dev = fsdev;
35512707Smckusick 		strcpy(*dqf, fs->fs_file);
35612707Smckusick 		dq++, dqf++;
35712707Smckusick 	}
35812707Smckusick 	endfsent();
35912707Smckusick 	**dqf = '\0';
36012707Smckusick }
36112707Smckusick 
36212707Smckusick putdiscq(uid, dq, dqf)
36312707Smckusick 	register uid;
36412707Smckusick 	register struct dquot *dq;
36512707Smckusick 	register char (*dqf)[MAXPATHLEN + 1];
36612707Smckusick {
36712707Smckusick 	register fd, cnt;
36812707Smckusick 	struct stat sb;
36912707Smckusick 	struct fstab *fs;
37012707Smckusick 
37112707Smckusick 	cnt = 0;
37212707Smckusick 	for (cnt = 0; ++cnt <= NMOUNT && **dqf; dq++, dqf++) {
37312707Smckusick 		fs = getfsfile(*dqf);
37412716Smckusick 		if (fs == NULL) {
37512716Smckusick 			fprintf(stderr, "%s: not in /etc/fstab\n", *dqf);
37612716Smckusick 			continue;
37712716Smckusick 		}
37812716Smckusick 		strcat(*dqf, "/");
37912716Smckusick 		strcat(*dqf, qfname);
38012716Smckusick 		if (stat(*dqf, &sb) >= 0)
38112716Smckusick 			quota(Q_SETDLIM, uid, sb.st_dev, &dq->dq_dqb);
38212716Smckusick 		if ((fd = open(*dqf, 1)) < 0) {
38312716Smckusick 			perror(*dqf);
38412716Smckusick 		} else {
38512707Smckusick 			lseek(fd, (long)uid * (long)sizeof (struct dqblk), 0);
38612707Smckusick 			if (write(fd, &dq->dq_dqb, sizeof (struct dqblk)) !=
38712707Smckusick 			    sizeof (struct dqblk)) {
38812707Smckusick 				fprintf(stderr, "edquota: ");
38912707Smckusick 				perror(*dqf);
39012707Smckusick 			}
39112707Smckusick 			close(fd);
39212707Smckusick 		}
39312707Smckusick 	}
39412707Smckusick }
395