xref: /original-bsd/usr.sbin/edquota/edquota.c (revision 87a44d1b)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)edquota.c	5.7 (Berkeley) 03/05/89";
26 #endif /* not lint */
27 
28 /*
29  * Disk quota editor.
30  */
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <sys/file.h>
34 #include <sys/quota.h>
35 #include <signal.h>
36 #include <errno.h>
37 #include <fstab.h>
38 #include <pwd.h>
39 #include <ctype.h>
40 #include <stdio.h>
41 
42 #define	DEFEDITOR	"/usr/ucb/vi"
43 
44 struct	dquot dq[NMOUNT];
45 struct	dquot odq[NMOUNT];
46 char	dqf[NMOUNT][MAXPATHLEN + 1];
47 char	odqf[NMOUNT][MAXPATHLEN + 1];
48 
49 char	tmpfil[] = "/tmp/EdP.aXXXXX";
50 char	*qfname = "quotas";
51 char	*getenv();
52 
53 main(argc, argv)
54 	char **argv;
55 {
56 	int uid;
57 	char *arg0;
58 
59 	mktemp(tmpfil);
60 	close(creat(tmpfil, 0600));
61 	chown(tmpfil, getuid(), getgid());
62 	arg0 = *argv++;
63 	if (argc < 2) {
64 		fprintf(stderr, "Usage: %s [-p username] username ...\n", arg0);
65 		unlink(tmpfil);
66 		exit(1);
67 	}
68 	--argc;
69 	if (getuid()) {
70 		fprintf(stderr, "%s: permission denied\n", arg0);
71 		unlink(tmpfil);
72 		exit(1);
73 	}
74 	if (argc > 2 && strcmp(*argv, "-p") == 0) {
75 		argc--, argv++;
76 		uid = getentry(*argv++);
77 		if (uid < 0) {
78 			unlink(tmpfil);
79 			exit(1);
80 		}
81 		getprivs(uid);
82 		argc--;
83 		while (argc-- > 0) {
84 			uid = getentry(*argv++);
85 			if (uid < 0)
86 				continue;
87 			getdiscq(uid, odq, odqf);
88 			putprivs(uid);
89 		}
90 		unlink(tmpfil);
91 		exit(0);
92 	}
93 	while (--argc >= 0) {
94 		uid = getentry(*argv++);
95 		if (uid < 0)
96 			continue;
97 		getprivs(uid);
98 		if (editit())
99 			putprivs(uid);
100 	}
101 	unlink(tmpfil);
102 	exit(0);
103 }
104 
105 getentry(name)
106 	char *name;
107 {
108 	struct passwd *pw;
109 	int uid;
110 
111 	if (alldigits(name))
112 		uid = atoi(name);
113 	else if (pw = getpwnam(name))
114 		uid = pw->pw_uid;
115 	else {
116 		fprintf(stderr, "%s: no such user\n", name);
117 		sleep(1);
118 		return (-1);
119 	}
120 	return (uid);
121 }
122 
123 editit()
124 {
125 	register int pid, xpid;
126 	long omask;
127 	int stat;
128 
129 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
130  top:
131 	if ((pid = fork()) < 0) {
132 		extern errno;
133 
134 		if (errno == EPROCLIM) {
135 			fprintf(stderr, "You have too many processes\n");
136 			return(0);
137 		}
138 		if (errno == EAGAIN) {
139 			sleep(1);
140 			goto top;
141 		}
142 		perror("fork");
143 		return (0);
144 	}
145 	if (pid == 0) {
146 		register char *ed;
147 
148 		sigsetmask(omask);
149 		setgid(getgid());
150 		setuid(getuid());
151 		if ((ed = getenv("EDITOR")) == (char *)0)
152 			ed = DEFEDITOR;
153 		execlp(ed, ed, tmpfil, 0);
154 		perror(ed);
155 		exit(1);
156 	}
157 	while ((xpid = wait(&stat)) >= 0)
158 		if (xpid == pid)
159 			break;
160 	sigsetmask(omask);
161 	return (!stat);
162 }
163 
164 getprivs(uid)
165 	register uid;
166 {
167 	register i;
168 	FILE *fd;
169 
170 	getdiscq(uid, dq, dqf);
171 	for (i = 0; i < NMOUNT; i++) {
172 		odq[i] = dq[i];
173 		strcpy(odqf[i], dqf[i]);
174 	}
175 	if ((fd = fopen(tmpfil, "w")) == NULL) {
176 		fprintf(stderr, "edquota: ");
177 		perror(tmpfil);
178 		exit(1);
179 	}
180 	for (i = 0; i < NMOUNT; i++) {
181 		if (*dqf[i] == '\0')
182 			continue;
183 		fprintf(fd,
184 "fs %s blocks (soft = %d, hard = %d) inodes (soft = %d, hard = %d)\n"
185 			, dqf[i]
186 			, dbtob(dq[i].dq_bsoftlimit) / 1024
187 			, dbtob(dq[i].dq_bhardlimit) / 1024
188 			, dq[i].dq_isoftlimit
189 			, dq[i].dq_ihardlimit
190 		);
191 	}
192 	fclose(fd);
193 }
194 
195 putprivs(uid)
196 	register uid;
197 {
198 	register i, j;
199 	int n;
200 	FILE *fd;
201 	char line[BUFSIZ];
202 
203 	fd = fopen(tmpfil, "r");
204 	if (fd == NULL) {
205 		fprintf(stderr, "Can't re-read temp file!!\n");
206 		return;
207 	}
208 	for (i = 0; i < NMOUNT; i++) {
209 		char *cp, *dp, *next();
210 
211 		if (fgets(line, sizeof (line), fd) == NULL)
212 			break;
213 		cp = next(line, " \t");
214 		if (cp == NULL)
215 			break;
216 		*cp++ = '\0';
217 		while (*cp && *cp == '\t' && *cp == ' ')
218 			cp++;
219 		dp = cp, cp = next(cp, " \t");
220 		if (cp == NULL)
221 			break;
222 		*cp++ = '\0';
223 		while (*cp && *cp == '\t' && *cp == ' ')
224 			cp++;
225 		strcpy(dqf[i], dp);
226 		n = sscanf(cp,
227 "blocks (soft = %d, hard = %d) inodes (soft = %hd, hard = %hd)\n"
228 			, &dq[i].dq_bsoftlimit
229 			, &dq[i].dq_bhardlimit
230 			, &dq[i].dq_isoftlimit
231 			, &dq[i].dq_ihardlimit
232 		);
233 		if (n != 4) {
234 			fprintf(stderr, "%s: bad format\n", cp);
235 			continue;
236 		}
237 		dq[i].dq_bsoftlimit = btodb(dq[i].dq_bsoftlimit * 1024);
238 		dq[i].dq_bhardlimit = btodb(dq[i].dq_bhardlimit * 1024);
239 	}
240 	fclose(fd);
241 	n = i;
242 	for (i = 0; i < n; i++) {
243 		if (*dqf[i] == '\0')
244 			break;
245 		for (j = 0; j < NMOUNT; j++) {
246 			if (strcmp(dqf[i], odqf[j]) == 0)
247 				break;
248 		}
249 		if (j >= NMOUNT)
250 			continue;
251 		*odqf[j] = '\0';
252 		/*
253 		 * This isn't really good enough, it is quite likely
254 		 * to have changed while we have been away editing,
255 		 * but it's not important enough to worry about at
256 		 * the minute.
257 		 */
258 		dq[i].dq_curblocks = odq[j].dq_curblocks;
259 		dq[i].dq_curinodes = odq[j].dq_curinodes;
260 		/*
261 		 * If we've upped the inode or disk block limits
262 		 * and the guy is out of warnings, reinitialize.
263 		 */
264 		if (dq[i].dq_bsoftlimit > odq[j].dq_bsoftlimit &&
265 		    dq[i].dq_bwarn == 0)
266 			dq[i].dq_bwarn = MAX_DQ_WARN;
267 		if (dq[i].dq_isoftlimit > odq[j].dq_isoftlimit &&
268 		    dq[i].dq_iwarn == 0)
269 			dq[i].dq_iwarn = MAX_IQ_WARN;
270 	}
271 	if (i < NMOUNT) {
272 		for (j = 0; j < NMOUNT; j++) {
273 			if (*odqf[j] == '\0')
274 				continue;
275 			strcpy(dqf[i], odqf[j]);
276 			dq[i].dq_isoftlimit = 0;
277 			dq[i].dq_ihardlimit = 0;
278 			dq[i].dq_bsoftlimit = 0;
279 			dq[i].dq_bhardlimit = 0;
280 			/*
281 			 * Same applies as just above
282 			 * but matters not at all, as we are just
283 			 * turning quota'ing off for this filesys.
284 			 */
285 			dq[i].dq_curblocks = odq[j].dq_curblocks;
286 			dq[i].dq_curinodes = odq[j].dq_curinodes;
287 			if (++i >= NMOUNT)
288 				break;
289 		}
290 	}
291 	if (*dqf[0])
292 		putdiscq(uid, dq, dqf);
293 }
294 
295 char *
296 next(cp, match)
297 	register char *cp;
298 	char *match;
299 {
300 	register char *dp;
301 
302 	while (cp && *cp) {
303 		for (dp = match; dp && *dp; dp++)
304 			if (*dp == *cp)
305 				return (cp);
306 		cp++;
307 	}
308 	return ((char *)0);
309 }
310 
311 alldigits(s)
312 	register char *s;
313 {
314 	register c;
315 
316 	c = *s++;
317 	do {
318 		if (!isdigit(c))
319 			return (0);
320 	} while (c = *s++);
321 	return (1);
322 }
323 
324 getdiscq(uid, dq, dqf)
325 	register uid;
326 	register struct dquot *dq;
327 	register char (*dqf)[MAXPATHLEN + 1];
328 {
329 	register struct fstab *fs;
330 	char qfilename[MAXPATHLEN + 1];
331 	struct stat statb;
332 	struct dqblk dqblk;
333 	dev_t fsdev;
334 	int fd;
335 	static int warned = 0;
336 	extern int errno;
337 
338 	setfsent();
339 	while (fs = getfsent()) {
340 		if (stat(fs->fs_spec, &statb) < 0)
341 			continue;
342 		fsdev = statb.st_rdev;
343 		sprintf(qfilename, "%s/%s", fs->fs_file, qfname);
344 		if (stat(qfilename, &statb) < 0 || statb.st_dev != fsdev)
345 			continue;
346 		if (quota(Q_GETDLIM, uid, fsdev, &dqblk) != 0) {
347 	    		if (errno == EINVAL && !warned) {
348 				warned++;
349 				fprintf(stderr, "Warning: %s\n",
350 				    "Quotas are not compiled into this kernel");
351 				sleep(3);
352 			}
353 			fd = open(qfilename, O_RDONLY);
354 			if (fd < 0)
355 				continue;
356 			lseek(fd, (long)(uid * sizeof dqblk), L_SET);
357 			switch (read(fd, &dqblk, sizeof dqblk)) {
358 			case 0:			/* EOF */
359 				/*
360 				 * Convert implicit 0 quota (EOF)
361 				 * into an explicit one (zero'ed dqblk)
362 				 */
363 				bzero((caddr_t)&dqblk, sizeof dqblk);
364 				break;
365 
366 			case sizeof dqblk:	/* OK */
367 				break;
368 
369 			default:		/* ERROR */
370 				fprintf(stderr, "edquota: read error in ");
371 				perror(qfilename);
372 				close(fd);
373 				continue;
374 			}
375 			close(fd);
376 		}
377 		dq->dq_dqb = dqblk;
378 		dq->dq_dev = fsdev;
379 		strcpy(*dqf, fs->fs_file);
380 		dq++, dqf++;
381 	}
382 	endfsent();
383 	**dqf = '\0';
384 }
385 
386 putdiscq(uid, dq, dqf)
387 	register uid;
388 	register struct dquot *dq;
389 	register char (*dqf)[MAXPATHLEN + 1];
390 {
391 	register fd, cnt;
392 	struct stat sb;
393 	struct fstab *fs;
394 
395 	cnt = 0;
396 	for (cnt = 0; ++cnt <= NMOUNT && **dqf; dq++, dqf++) {
397 		fs = getfsfile(*dqf);
398 		if (fs == NULL) {
399 			fprintf(stderr, "%s: not in /etc/fstab\n", *dqf);
400 			continue;
401 		}
402 		strcat(*dqf, "/");
403 		strcat(*dqf, qfname);
404 		if (stat(*dqf, &sb) >= 0)
405 			quota(Q_SETDLIM, uid, sb.st_dev, &dq->dq_dqb);
406 		if ((fd = open(*dqf, 1)) < 0) {
407 			perror(*dqf);
408 		} else {
409 			lseek(fd, (long)uid * (long)sizeof (struct dqblk), 0);
410 			if (write(fd, &dq->dq_dqb, sizeof (struct dqblk)) !=
411 			    sizeof (struct dqblk)) {
412 				fprintf(stderr, "edquota: ");
413 				perror(*dqf);
414 			}
415 			close(fd);
416 		}
417 	}
418 }
419